Node immutability improved

This commit is contained in:
Michael Schimmel
2025-11-05 18:13:43 +01:00
parent 0915d6d90d
commit 1f0eef7698
11 changed files with 409 additions and 308 deletions
+15 -13
View File
@@ -454,9 +454,10 @@ begin
begin
for var expr in rootNode.AsBlockExpression.Expressions do
begin
if expr is TVariableDeclarationNode then
// Use interface 'is' check
if (expr.Kind = akVariableDeclaration) then
begin
var decl := expr as TVariableDeclarationNode;
var decl := expr.AsVariableDeclaration;
definitions.Add(decl.Identifier.Name, decl.Initializer);
end
// Handle macro definitions inside the block
@@ -468,9 +469,10 @@ begin
end;
end;
end
else if rootNode is TVariableDeclarationNode then // Handle single definition
// Use interface 'is' check
else if rootNode.Kind = akVariableDeclaration then // Handle single definition
begin
var decl := rootNode as TVariableDeclarationNode;
var decl := rootNode.AsVariableDeclaration;
definitions.Add(decl.Identifier.Name, decl.Initializer);
end
// Handle a single macro definition
@@ -828,7 +830,7 @@ begin
TAst.VarDecl(
TAst.Identifier('closeColumn'),
TAst.FunctionCall(TAst.Keyword('Close'), [TAst.Identifier('ohlcvSeries')])
// TAst.MemberAccess(TAst.Identifier('ohlcvSeries'), TAst.Identifier('Close'))
// TAst.MemberAccess(TAst.Identifier('ohlcvSeries'), TAst.Identifier('Close'))
),
TAst.Indexer(TAst.Identifier('closeColumn'), TAst.Constant(1))
]
@@ -1348,14 +1350,14 @@ begin
FWorkspace.Build(FCurrUnboundAst, TPointF.Create(X, Y));
//
// if FCurrAst <> nil then
// begin
// FWorkspace.Build(FCurrAst, TPointF.Create(X, Y));
// end
// else if FCurrUnboundAst <> nil then
// begin
// FWorkspace.Build(FCurrUnboundAst, TPointF.Create(X, Y));
// end;
// if FCurrAst <> nil then
// begin
// FWorkspace.Build(FCurrAst, TPointF.Create(X, Y));
// end
// else if FCurrUnboundAst <> nil then
// begin
// FWorkspace.Build(FCurrUnboundAst, TPointF.Create(X, Y));
// end;
end;
end.
+40 -20
View File
@@ -134,22 +134,33 @@ end;
function TUpvalueAnalyzer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var
N: TLambdaExpressionNode;
newParams: TArray<IIdentifierNode>;
newBody: IAstNode;
i: Integer;
begin
N := (Node as TLambdaExpressionNode);
// A lambda creates a new lexical scope, inheriting from the current one.
FCurrentDescriptor := TScope.CreateDescriptor(FCurrentDescriptor);
try
// Define the lambda's parameters within its new scope.
// We use TTypes.Unknown as type inference hasn't run yet.
for var param in N.Parameters do
FCurrentDescriptor.Define(Accept(param).AsIdentifier.Name, TTypes.Unknown);
// Manually visit parameters to define them
var oldParams := Node.Parameters;
SetLength(newParams, Length(oldParams));
for i := 0 to High(oldParams) do
begin
// Accept(param) will call VisitIdentifier, which does its job.
// We *must* use the result of Accept.
newParams[i] := Accept(oldParams[i]).AsIdentifier;
FCurrentDescriptor.Define(newParams[i].Name, TTypes.Unknown);
end;
// Traverse the lambda body within the new scope context.
N.Body := Accept(N.Body); // Manual traversal
newBody := Accept(Node.Body); // Manual traversal
Result := N;
// Rebuild node if changed
if (newParams = Node.Parameters) and (newBody = Node.Body) then
Result := Node
else
// Use factory, pass through other properties (which are nil/false at this stage)
Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, Node.StaticType);
finally
// Restore the parent scope after leaving the lambda.
FCurrentDescriptor := FCurrentDescriptor.Parent;
@@ -159,31 +170,40 @@ end;
function TUpvalueAnalyzer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
var
scopeDeclarations: TDictionary<string, IVariableDeclarationNode>;
N: TVariableDeclarationNode;
newInitializer: IAstNode;
newIdentifier: IIdentifierNode;
begin
N := (Node as TVariableDeclarationNode);
// Traverse the initializer first. It's evaluated in the current scope
// before the new variable is defined.
if Assigned(N.Initializer) then
N.Initializer := Accept(N.Initializer);
if Assigned(Node.Initializer) then
newInitializer := Accept(Node.Initializer)
else
newInitializer := nil;
// Traverse the identifier
Accept(N.Identifier);
// This calls VisitIdentifier, but VisitIdentifier is just an analyzer, it returns the same node.
newIdentifier := Accept(Node.Identifier).AsIdentifier;
// After processing the initializer, define the variable in the current scope.
// We use TTypes.Unknown as type inference hasn't run yet.
FCurrentDescriptor.Define(N.Identifier.Name, TTypes.Unknown);
FCurrentDescriptor.Define(newIdentifier.Name, TTypes.Unknown);
// Map this declaration node to its scope and name for later lookup.
// --- Rebuild Node ---
// Use CoW, check if anything changed
if (newInitializer = Node.Initializer) and (newIdentifier = Node.Identifier) then
Result := Node
else
// Use factory, pass through other properties (which are nil/false at this stage)
Result := TAst.VarDecl(newIdentifier, newInitializer, Node.StaticType, Node.IsBoxed);
// Map this *new* declaration node to its scope and name for later lookup.
if not FDeclarationMap.TryGetValue(FCurrentDescriptor, scopeDeclarations) then
begin
scopeDeclarations := TDictionary<string, IVariableDeclarationNode>.Create;
FDeclarationMap.Add(FCurrentDescriptor, scopeDeclarations);
end;
scopeDeclarations.Add(N.Identifier.Name, N);
Result := N;
// IMPORTANT: Store the *new* node (Result)
scopeDeclarations.Add(newIdentifier.Name, Result.AsVariableDeclaration);
end;
end.
+54 -47
View File
@@ -41,7 +41,8 @@ type
function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
// --- Transformation Logic ---
// --- Transformation Logic (Restored) ---
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
// --- Standard Traversal (Use inherited) ---
@@ -69,6 +70,7 @@ implementation
uses
System.Generics.Defaults,
System.Character,
Myc.Ast.Types, // Added for TTypes.Unknown
Myc.Data.Keyword;
type
@@ -172,6 +174,7 @@ end;
function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
begin
// --- Transformation: Keyword-as-Function ---
// This transformation must happen *before* type checking.
if Node.Callee.Kind = akKeyword then
begin
var keywordNode := Node.Callee.AsKeyword;
@@ -180,22 +183,24 @@ begin
'Keyword :%s expects exactly one argument (the record/map), but got %d',
[keywordNode.Value.Name, Length(Node.Arguments)]);
// Manually visit the argument, as we are replacing this node
// Manually visit the argument (which is the base)
var baseNode := Accept(Node.Arguments[0]);
var memberAccessNode := TAst.MemberAccess(baseNode, keywordNode);
// Manually visit the keyword (which is the member)
var memberNode := Accept(keywordNode).AsKeyword;
// Visit the *new* node to bind it
// Create the new MemberAccess node
var memberAccessNode := TAst.MemberAccess(baseNode, memberNode);
// Visit the *new* node to bind it (though it has no bindable symbols)
Result := Accept(memberAccessNode);
exit;
end;
// --- Default: Bind as a standard function call ---
// Use the inherited implementation to visit children (Callee, Arguments)
// and mutate their properties in place.
Result := inherited VisitFunctionCall(Node);
// Set metadata for *this* node
(Node as TFunctionCallNode).IsTailCall := False; // Default, TCO (Phase 5) will set this
// Set metadata for *this* node (IsTailCall is set later by TCO)
end;
function TAstBinder.VisitConstant(const Node: IConstantNode): IAstNode;
@@ -233,11 +238,14 @@ end;
function TAstBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var
i: integer;
N: TLambdaExpressionNode;
adr: TResolvedAddress;
scopeDescriptor: IScopeDescriptor;
upvalues: TArray<TResolvedAddress>;
hasNestedLambdas: Boolean;
lastNestedLambdaCount: Integer;
newParams: TArray<IIdentifierNode>;
newBody: IAstNode;
begin
N := (Node as TLambdaExpressionNode);
// We do *not* call inherited, as we must manage the scope manually.
FUpvalueStack.Push(TUpvalueMapping.Create(TResolvedAddressComparer.Create));
@@ -248,26 +256,24 @@ begin
FCurrentDescriptor.Define('<self>');
// Define parameters
for i := 0 to High(N.Parameters) do
SetLength(newParams, Length(Node.Parameters));
for i := 0 to High(Node.Parameters) do
begin
var paramNode := N.Parameters[i]; // This is a TIdentifierNode
var paramNode := Node.Parameters[i]; // This is a TIdentifierNode
var slotIndex := FCurrentDescriptor.Define(paramNode.Name);
adr := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
// --- Replace Node ---
// Create a new TBoundIdentifierNode (implementation is in Myc.Ast)
var boundParamNode := TAst.Identifier(paramNode.Name, adr);
// Replace it in the parameter array
N.Parameters[i] := boundParamNode;
// Create a new TIdentifierNode
newParams[i] := TAst.Identifier(paramNode.Name, adr, TTypes.Unknown);
end;
// Visit the body *within the new scope*
var lastNestedLambdaCount := FNestedLambdaCount;
N.Body := Accept(N.Body);
N.HasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount;
lastNestedLambdaCount := FNestedLambdaCount;
newBody := Accept(Node.Body);
hasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount;
// Save the descriptor for the evaluator
N.ScopeDescriptor := FCurrentDescriptor;
scopeDescriptor := FCurrentDescriptor;
finally
ExitScope;
end;
@@ -283,19 +289,18 @@ begin
)
);
var uvArr: TArray<TResolvedAddress>;
SetLength(uvArr, Length(sortedPairs));
for i := 0 to High(uvArr) do
uvArr[i] := sortedPairs[i].Key;
N.Upvalues := uvArr;
SetLength(upvalues, Length(sortedPairs));
for i := 0 to High(upvalues) do
upvalues[i] := sortedPairs[i].Key;
finally
FUpvalueStack.Pop;
end;
inc(FNestedLambdaCount);
// Type will be set by TypeChecker
Result := Node;
// Create new immutable node using the factory
Result := TAst.LambdaExpr(newParams, newBody, scopeDescriptor, upvalues, hasNestedLambdas);
end;
function TAstBinder.VisitRecurNode(const Node: IRecurNode): IAstNode;
@@ -310,8 +315,9 @@ var
symbol: TResolvedSymbol;
adr: TResolvedAddress;
begin
// We only bind nodes that are the base parser type.
if Node.Kind = akIdentifier then
// We only bind nodes that are the base parser type
// (i.e., address is unresolved).
if Node.Address.Kind = akUnresolved then
begin
symbol := FCurrentDescriptor.FindSymbol(Node.Name);
adr := symbol.Address;
@@ -344,12 +350,11 @@ begin
// else: It was already an upvalue (e.g. nested lambda), adr is correct.
// --- Replace Node ---
// Create the new TBoundIdentifierNode (implementation is in Myc.Ast)
Result := TAst.Identifier(Node.Name, adr);
Result := TAst.Identifier(Node.Name, adr, TTypes.Unknown); // TypeChecker will set type
end
else
begin
// It's already an akBoundIdentifier (or other specialized type), just return it.
// It's already bound (e.g., a parameter), just return it.
Result := Node;
end;
end;
@@ -358,29 +363,31 @@ function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNod
var
slotIndex: Integer;
address: TResolvedAddress;
N: TVariableDeclarationNode;
isBoxed: Boolean;
newInitializer: IAstNode;
newIdentifier: IIdentifierNode;
begin
N := (Node as TVariableDeclarationNode);
if not IsValidIdentifier(N.Identifier.Name) then
raise Exception.CreateFmt('Invalid identifier name: "%s".', [N.Identifier.Name]);
if not IsValidIdentifier(Node.Identifier.Name) then
raise Exception.CreateFmt('Invalid identifier name: "%s".', [Node.Identifier.Name]);
// 1. Visit initializer *first*
if Assigned(N.Initializer) then
N.Initializer := Accept(N.Initializer);
if Assigned(Node.Initializer) then
newInitializer := Accept(Node.Initializer)
else
newInitializer := nil;
// 2. Define variable in *current* scope
slotIndex := FCurrentDescriptor.Define(N.Identifier.Name);
slotIndex := FCurrentDescriptor.Define(Node.Identifier.Name);
address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
// 3. --- Replace Node ---
// Replace the TIdentifierNode with a new TBoundIdentifierNode
N.Identifier := TAst.Identifier(N.Identifier.Name, address);
// 3. Create the new bound identifier
newIdentifier := TAst.Identifier(Node.Identifier.Name, address, TTypes.Unknown);
// 4. Mutate this declaration node
N.IsBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node);
// 4. Check if this declaration should be boxed
isBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node);
Result := Node;
// 5. Create the new immutable node
Result := TAst.VarDecl(newIdentifier, newInitializer, TTypes.Unknown, isBoxed);
end;
end.
+10 -19
View File
@@ -184,29 +184,23 @@ end;
function TAstTCO.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var
N: TLambdaExpressionNode;
newParams: TArray<IIdentifierNode>;
newBody: IAstNode;
begin
N := (Node as TLambdaExpressionNode);
// Parameters are not in tail position (handled by AcceptParameters)
FNextIsTail := False;
newParams := AcceptParameters(N.Parameters);
newParams := AcceptParameters(Node.Parameters);
// The body of a lambda is *always* a tail position (relative to the lambda)
FNextIsTail := True;
newBody := Accept(N.Body);
newBody := Accept(Node.Body);
if (newParams = N.Parameters) and (newBody = N.Body) then
if (newParams = Node.Parameters) and (newBody = Node.Body) then
Result := Node
else
begin
Result := TLambdaExpressionNode.Create(newParams, newBody, N.StaticType);
// Copy runtime properties
(Result as TLambdaExpressionNode).ScopeDescriptor := N.ScopeDescriptor;
(Result as TLambdaExpressionNode).Upvalues := N.Upvalues;
(Result as TLambdaExpressionNode).HasNestedLambdas := N.HasNestedLambdas;
// Rebuild using factory and copy properties via interface
Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, Node.StaticType);
end;
end;
@@ -245,26 +239,23 @@ end;
function TAstTCO.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
var
isTailCall: Boolean;
N: TFunctionCallNode;
newCallee: IAstNode;
newArgs: TArray<IAstNode>;
begin
isTailCall := FIsTailStack.Peek;
N := (Node as TFunctionCallNode);
// Arguments are not in tail position
FNextIsTail := False;
newCallee := Accept(N.Callee);
newArgs := AcceptNodes(N.Arguments);
newCallee := Accept(Node.Callee);
newArgs := AcceptNodes(Node.Arguments);
// CoW check: Create a new node only if children changed OR IsTailCall needs update
if (newCallee = N.Callee) and (newArgs = N.Arguments) and (isTailCall = N.IsTailCall) then
if (newCallee = Node.Callee) and (newArgs = Node.Arguments) and (isTailCall = Node.IsTailCall) then
Result := Node
else
begin
Result := TFunctionCallNode.Create(newCallee, newArgs, N.StaticType);
// Mutate the *new* node with the TCO status
(Result as TFunctionCallNode).IsTailCall := isTailCall;
// Use factory to create new node, passing the *new* TCO status
Result := TAst.FunctionCall(newCallee, newArgs, Node.StaticType, isTailCall);
end;
end;
+17 -26
View File
@@ -73,7 +73,6 @@ implementation
uses
Myc.Data.Keyword;
// Myc.Ast.Binding.Nodes; // Removed
{ TAstDumper }
@@ -146,7 +145,7 @@ procedure TAstDumper.VisitIdentifier(const Node: IIdentifierNode);
var
adr: TResolvedAddress;
begin
adr := Node.AsIdentifier.Address;
adr := Node.Address;
if adr.Kind <> akUnresolved then
LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress(adr)])
@@ -209,38 +208,35 @@ procedure TAstDumper.VisitLambdaExpression(const Node: ILambdaExpressionNode);
var
upvalueAddr: TResolvedAddress;
pair: TPair<string, Integer>;
boundNode: TLambdaExpressionNode;
begin
boundNode := Node as TLambdaExpressionNode;
if Assigned(boundNode.ScopeDescriptor) then
if Assigned(Node.ScopeDescriptor) then
begin
LogFmt('LambdaExpression (HasNested: %s)', [boundNode.HasNestedLambdas.ToString(TUseBoolStrs.True)]);
LogFmt('LambdaExpression (HasNested: %s)', [Node.HasNestedLambdas.ToString(TUseBoolStrs.True)]);
Indent;
Log('Parameters:');
Indent;
for var param in boundNode.Parameters do
for var param in Node.Parameters do
param.Accept(Self);
Unindent;
LogFmt('Upvalues (%d):', [Length(boundNode.Upvalues)]);
LogFmt('Upvalues (%d):', [Length(Node.Upvalues)]);
Indent;
for upvalueAddr in boundNode.Upvalues do
for upvalueAddr in Node.Upvalues do
Log(FormatAddress(upvalueAddr));
Unindent;
Log('Scope Descriptor:');
Indent;
if Assigned(boundNode.ScopeDescriptor) then
for pair in boundNode.ScopeDescriptor.Symbols do
if Assigned(Node.ScopeDescriptor) then
for pair in Node.ScopeDescriptor.Symbols do
LogFmt('"%s" -> Slot %d', [pair.Key, pair.Value])
else
Log('(none)');
Unindent;
Log('Body:');
boundNode.Body.Accept(Self);
Node.Body.Accept(Self);
Unindent;
end
@@ -262,17 +258,15 @@ end;
procedure TAstDumper.VisitFunctionCall(const Node: IFunctionCallNode);
var
arg: IAstNode;
N: TFunctionCallNode;
begin
N := (Node as TFunctionCallNode);
LogFmt('FunctionCall (IsTailCall: %s)', [N.IsTailCall.ToString(TUseBoolStrs.True)]);
LogFmt('FunctionCall (IsTailCall: %s)', [Node.IsTailCall.ToString(TUseBoolStrs.True)]);
Indent;
Log('Callee:');
N.Callee.Accept(Self);
LogFmt('Arguments (%d):', [Length(N.Arguments)]);
Node.Callee.Accept(Self);
LogFmt('Arguments (%d):', [Length(Node.Arguments)]);
Indent;
for arg in N.Arguments do
for arg in Node.Arguments do
arg.Accept(Self);
Unindent;
Unindent;
@@ -331,18 +325,15 @@ begin
end;
procedure TAstDumper.VisitVariableDeclaration(const Node: IVariableDeclarationNode);
var
N: TVariableDeclarationNode;
begin
N := (Node as TVariableDeclarationNode);
LogFmt('VariableDeclaration (IsBoxed: %s)', [N.IsBoxed.ToString(TUseBoolStrs.True)]);
LogFmt('VariableDeclaration (IsBoxed: %s)', [Node.IsBoxed.ToString(TUseBoolStrs.True)]);
Indent;
N.Identifier.Accept(Self);
if Assigned(N.Initializer) then
Node.Identifier.Accept(Self);
if Assigned(Node.Initializer) then
begin
Log('Initializer:');
N.Initializer.Accept(Self);
Node.Initializer.Accept(Self);
end;
Unindent;
end;
+31 -37
View File
@@ -71,7 +71,8 @@ uses
Myc.Data.Keyword,
Myc.Data.Decimal,
Myc.Data.Series,
Myc.Data.Scalar.JSON;
Myc.Data.Scalar.JSON,
Myc.Ast.Types; // Added for VisitRecordLiteral
// Helper type for TCO via trampolining.
type
@@ -177,25 +178,21 @@ end;
function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
var
boundNode: TLambdaExpressionNode; // Changed
capturedCells: TArray<IValueCell>;
i: Integer;
sourceAddresses: TArray<TResolvedAddress>;
closureScope: IExecutionScope;
visitorFactory: TEvaluatorFactory;
begin
// Cast to the bound node to access binder-specific information.
boundNode := Node as TLambdaExpressionNode; // Changed
// Create the closure by capturing the value cells of all upvalues from the current scope.
sourceAddresses := boundNode.Upvalues;
// Access binder-specific information via the interface
sourceAddresses := Node.Upvalues;
SetLength(capturedCells, Length(sourceAddresses));
for i := 0 to High(sourceAddresses) do
capturedCells[i] := FScope.Capture(sourceAddresses[i]);
// Memory optimization: a lambda's scope does not need to be kept alive as a parent
// if it contains no nested lambdas that might need to capture from it later.
if boundNode.HasNestedLambdas then
if Node.HasNestedLambdas then
closureScope := FScope
else
closureScope := nil;
@@ -203,8 +200,8 @@ begin
// Get a factory to create the correct visitor (e.g., debug or production) for the lambda body.
visitorFactory := CreateVisitorFactory();
var scopeDescriptor := boundNode.ScopeDescriptor;
var params := boundNode.Parameters;
var scopeDescriptor := Node.ScopeDescriptor;
var params := Node.Parameters;
var cNode: ILambdaExpressionNode := Node;
// [unsafe] prevents a reference cycle since the closure captures itself for 'recur'.
@@ -234,7 +231,7 @@ begin
for i := 0 to High(ArgValues) do
begin
// Parameters in a bound lambda must be bound identifiers.
adr.SlotIndex := params[i].AsIdentifier.Address.SlotIndex; // Changed
adr.SlotIndex := params[i].Address.SlotIndex;
lambdaScope[adr] := ArgValues[i];
end;
@@ -274,7 +271,6 @@ function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDa
var
calleeValue: TDataValue;
argValues: TArray<TDataValue>;
boundNode: TFunctionCallNode; // Changed
begin
calleeValue := Node.Callee.Accept(Self);
if calleeValue.Kind <> vkMethod then
@@ -285,8 +281,7 @@ begin
for var i := 0 to High(argNodes) do
argValues[i] := argNodes[i].Accept(Self);
boundNode := Node as TFunctionCallNode; // Changed
if boundNode.IsTailCall then
if Node.IsTailCall then
begin
// This is a tail call. Return a thunk to be processed by the trampoline.
Result := TDataValue.FromGeneric<TThunk>(TThunk.Create(calleeValue, argValues, false));
@@ -334,7 +329,7 @@ var
itemValue, lookbackValue, seriesVar: TDataValue;
lookback: Int64;
begin
seriesVar := FScope[Node.Series.AsIdentifier.Address]; // Changed
seriesVar := FScope[Node.Series.Address];
itemValue := Node.Value.Accept(Self);
lookback := -1;
@@ -368,7 +363,7 @@ begin
// Evaluate the new value.
Result := Node.Value.Accept(Self);
// Assign it. The scope's SetValues implementation now handles boxing correctly.
FScope[Node.Identifier.AsIdentifier.Address] := Result; // Changed
FScope[Node.Identifier.Address] := Result;
end;
function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
@@ -405,7 +400,7 @@ end;
function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
begin
// The scope's GetValues implementation now handles unboxing automatically.
Result := FScope[Node.AsIdentifier.Address];
Result := FScope[Node.Address];
end;
function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
@@ -492,22 +487,23 @@ end;
function TEvaluatorVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
var
i: Integer;
staticType: IStaticType;
begin
// Check which type the binder created
if Node is TGenericRecordLiteralNode then // Changed
staticType := Node.StaticType; // Get the type set by TypeChecker
if staticType.Kind = stGenericRecord then
begin
// --- NEW GENERIC PATH ---
var boundNode := Node as TGenericRecordLiteralNode; // Changed
var genFields: TArray<TPair<IKeyword, TDataValue>>;
SetLength(genFields, Length(boundNode.Fields));
SetLength(genFields, Length(Node.Fields));
// Evaluate all field values
for i := 0 to High(boundNode.Fields) do
for i := 0 to High(Node.Fields) do
begin
genFields[i] :=
TPair<IKeyword, TDataValue>.Create(
boundNode.Fields[i].Key.Value,
boundNode.Fields[i].Value.Accept(Self) // Evaluate expression
Node.Fields[i].Key.Value,
Node.Fields[i].Value.Accept(Self) // Evaluate expression
);
end;
@@ -515,21 +511,21 @@ begin
var dynRec := TDynamicRecord.Create(genFields);
Result := TDataValue.FromGenericRecord(dynRec);
end
else if Node is TRecordLiteralNode then // Changed
else if staticType.Kind = stRecord then
begin
// --- EXISTING SCALAR PATH ---
var boundNode := Node as TRecordLiteralNode; // Changed
var values: TArray<TScalar.TValue>;
SetLength(values, Length(boundNode.Fields));
SetLength(values, Length(Node.Fields));
for i := 0 to High(boundNode.Fields) do
for i := 0 to High(Node.Fields) do
begin
var valData := boundNode.Fields[i].Value.Accept(Self);
// Binder guarantees these are vkScalar
var valData := Node.Fields[i].Value.Accept(Self);
// TypeChecker guarantees these are vkScalar
values[i] := valData.AsScalar.Value;
end;
var rec := TScalarRecord.Create(boundNode.Definition, values);
// Get the definition from the type
var rec := TScalarRecord.Create(staticType.Definition, values);
Result := TDataValue.FromRecord(rec);
end
else
@@ -539,7 +535,6 @@ end;
function TEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
var
address: TResolvedAddress;
boundNode: TVariableDeclarationNode; // Changed
begin
// First, evaluate the initializer to get the variable's initial value.
if Assigned(Node.Initializer) then
@@ -547,12 +542,11 @@ begin
else
Result := TDataValue.Void;
// After binding, all declaration nodes are TVariableDeclarationNode
boundNode := Node as TVariableDeclarationNode;
address := boundNode.Identifier.AsIdentifier.Address;
// Access properties via interface
address := Node.Identifier.Address;
// Check the IsBoxed flag set by the binder.
if boundNode.IsBoxed then
if Node.IsBoxed then
begin
// This is a captured variable. Use the clean scope method to create the box.
Assert(address.ScopeDepth = 0);
@@ -641,7 +635,7 @@ var
seriesValue: TDataValue;
len: Int64;
begin
seriesValue := FScope[Node.Series.AsIdentifier.Address]; // Changed
seriesValue := FScope[Node.Series.Address];
case seriesValue.Kind of
vkSeries: len := seriesValue.AsSeries.Count;
+20 -2
View File
@@ -91,12 +91,30 @@ var
left, right: IAstNode;
nodeType: IStaticType;
begin
// Get the type *before* replacing the node (it's an IAstTypedNode)
nodeType := Node.AsTypedNode.StaticType;
// --- Transformation: Keyword-as-Function ---
if Node.Callee.Kind = akKeyword then
begin
var keywordNode := Node.Callee.AsKeyword;
if Length(Node.Arguments) <> 1 then
raise EArgumentException.CreateFmt(
'Keyword :%s expects exactly one argument (the record/map), but got %d',
[keywordNode.Value.Name, Length(Node.Arguments)]);
// Visit the base node
var baseNode := Accept(Node.Arguments[0]);
// Create the new MemberAccess node, preserving the type
Result := TAst.MemberAccess(baseNode, keywordNode, nodeType);
exit;
end;
// --- Optimization: Operator Folding ---
if Node.Callee.Kind = akIdentifier then
begin
calleeIdentifier := Node.Callee.AsIdentifier;
// Get the type *before* replacing the node (it's an IAstTypedNode)
nodeType := Node.AsTypedNode.StaticType;
if (Length(Node.Arguments) = 2) then
begin
+10
View File
@@ -237,18 +237,26 @@ type
{$region 'private'}
function GetParameters: TArray<IIdentifierNode>;
function GetBody: IAstNode;
function GetScopeDescriptor: IScopeDescriptor;
function GetUpvalues: TArray<TResolvedAddress>;
function GetHasNestedLambdas: Boolean;
{$endregion}
property Parameters: TArray<IIdentifierNode> read GetParameters;
property Body: IAstNode read GetBody;
property ScopeDescriptor: IScopeDescriptor read GetScopeDescriptor;
property Upvalues: TArray<TResolvedAddress> read GetUpvalues;
property HasNestedLambdas: Boolean read GetHasNestedLambdas;
end;
IFunctionCallNode = interface(IAstTypedNode)
{$region 'private'}
function GetCallee: IAstNode;
function GetArguments: TArray<IAstNode>;
function GetIsTailCall: Boolean;
{$endregion}
property Callee: IAstNode read GetCallee;
property Arguments: TArray<IAstNode> read GetArguments;
property IsTailCall: Boolean read GetIsTailCall;
end;
// A node representing a macro call.
@@ -281,9 +289,11 @@ type
{$region 'private'}
function GetIdentifier: IIdentifierNode;
function GetInitializer: IAstNode;
function GetIsBoxed: Boolean;
{$endregion}
property Identifier: IIdentifierNode read GetIdentifier;
property Initializer: IAstNode read GetInitializer;
property IsBoxed: Boolean read GetIsBoxed;
end;
IAssignmentNode = interface(IAstTypedNode)
+33 -43
View File
@@ -111,7 +111,7 @@ begin
// Get the type from the descriptor (which was populated by Binder/RTL)
symbol := FCurrentDescriptor.FindSymbol(Node.Name);
adr := Node.AsIdentifier.Address;
adr := Node.Address;
// Create a new node, copying the address and assigning the inferred type
Result := TAst.Identifier(Node.Name, adr, symbol.StaticType);
@@ -135,9 +135,7 @@ function TTypeChecker.VisitVariableDeclaration(const Node: IVariableDeclarationN
var
initType: IStaticType;
newInitializer, newIdent: IAstNode;
boundIdent: IIdentifierNode;
adr: TResolvedAddress;
N: TVariableDeclarationNode;
begin
// 1. Visit Initializer first (if it exists)
if Assigned(Node.Initializer) then
@@ -151,23 +149,24 @@ begin
else
initType := TTypes.Unknown; // (def fib)
// 3. Get the address from the bound identifier (SAFE CAST)
boundIdent := Node.Identifier;
adr := boundIdent.AsIdentifier.Address;
// 3. Get the address from the bound identifier
adr := Node.Identifier.Address;
// 4. Update the type in the scope descriptor (which was set to Unknown by the binder).
if initType.Kind <> stUnknown then
FCurrentDescriptor.UpdateType(adr.SlotIndex, initType);
// 5. Create the new (typed) identifier node
newIdent := TAst.Identifier(boundIdent.Name, adr, initType);
newIdent := TAst.Identifier(Node.Identifier.Name, adr, initType);
// 6. Create the new VariableDeclaration node
Result := TVariableDeclarationNode.Create(newIdent.AsIdentifier, newInitializer, initType);
// 7. Copy runtime flags (IsBoxed)
N := (Result as TVariableDeclarationNode);
N.IsBoxed := (Node as TVariableDeclarationNode).IsBoxed;
// 6. Create the new VariableDeclaration node using the factory
Result :=
TAst.VarDecl(
newIdent.AsIdentifier,
newInitializer,
initType,
Node.IsBoxed // 7. Copy runtime flags (IsBoxed) via interface
);
end;
function TTypeChecker.VisitAssignment(const Node: IAssignmentNode): IAstNode;
@@ -206,7 +205,6 @@ end;
function TTypeChecker.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var
boundNode: TLambdaExpressionNode;
newParams: TArray<IIdentifierNode>;
newBody: IAstNode;
bodyType, methodType: IStaticType;
@@ -214,22 +212,20 @@ var
i: Integer;
savedDescriptor: IScopeDescriptor;
begin
boundNode := (Node as TLambdaExpressionNode);
// 1. Enter the lambda's scope (which Binder already created)
savedDescriptor := FCurrentDescriptor;
FCurrentDescriptor := boundNode.ScopeDescriptor;
FCurrentDescriptor := Node.ScopeDescriptor;
try
// 2. Visit parameters (they are already bound, just need typing)
SetLength(newParams, Length(boundNode.Parameters));
SetLength(paramTypes, Length(boundNode.Parameters));
SetLength(newParams, Length(Node.Parameters));
SetLength(paramTypes, Length(Node.Parameters));
for i := 0 to High(boundNode.Parameters) do
for i := 0 to High(Node.Parameters) do
begin
// Parameters are leaves, but we must *replace* them with typed versions
// (even if they are just TTypes.Unknown for now, for type inference placeholders)
var paramIdent := boundNode.Parameters[i];
var paramAdr := paramIdent.AsIdentifier.Address;
var paramIdent := Node.Parameters[i];
var paramAdr := paramIdent.Address;
var newParam := TAst.Identifier(paramIdent.Name, paramAdr);
newParams[i] := newParam;
@@ -237,7 +233,7 @@ begin
end;
// 3. Visit the body to infer its return type
newBody := Accept(boundNode.Body);
newBody := Accept(Node.Body);
bodyType := newBody.AsTypedNode.StaticType;
// 4. Create the final method type
@@ -251,14 +247,8 @@ begin
FCurrentDescriptor := savedDescriptor;
end;
// 7. Create the new (typed) lambda node
Result := TLambdaExpressionNode.Create(newParams, newBody, methodType);
// 8. Copy runtime properties
var newLambda := (Result as TLambdaExpressionNode);
newLambda.ScopeDescriptor := boundNode.ScopeDescriptor;
newLambda.Upvalues := boundNode.Upvalues;
newLambda.HasNestedLambdas := boundNode.HasNestedLambdas;
// 7. Create the new (typed) lambda node using the factory
Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, methodType);
end;
function TTypeChecker.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
@@ -300,11 +290,14 @@ begin
else if calleeType.Kind <> TStaticTypeKind.stUnknown then
raise ETypeException.CreateFmt('Cannot invoke type %s as a function.', [calleeType.ToString]);
// 4. Create the new (typed) call node
Result := TFunctionCallNode.Create(newCallee, newArgs, retType);
// 5. Copy runtime properties
(Result as TFunctionCallNode).IsTailCall := (Node as TFunctionCallNode).IsTailCall;
// 4. Create the new (typed) call node using the factory
Result :=
TAst.FunctionCall(
newCallee,
newArgs,
retType,
Node.IsTailCall // 5. Copy runtime properties
);
end;
function TTypeChecker.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
@@ -508,17 +501,14 @@ var
valType: IStaticType;
scalarKind: TScalar.TKind;
allScalar: Boolean;
oldNode: TGenericRecordLiteralNode; // Parser creates this type
newFields: TArray<TRecordFieldLiteral>;
begin
oldNode := (Node as TGenericRecordLiteralNode);
// 1. Visit all child nodes first to infer their types
SetLength(newFields, Length(oldNode.Fields));
for i := 0 to High(oldNode.Fields) do
SetLength(newFields, Length(Node.Fields));
for i := 0 to High(Node.Fields) do
begin
newFields[i].Key := Accept(oldNode.Fields[i].Key).AsKeyword;
newFields[i].Value := Accept(oldNode.Fields[i].Value);
newFields[i].Key := Accept(Node.Fields[i].Key).AsKeyword;
newFields[i].Value := Accept(Node.Fields[i].Value);
end;
SetLength(scalarDefFields, Length(newFields));
+21 -32
View File
@@ -527,22 +527,17 @@ function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode
var
newParams: TArray<IIdentifierNode>;
newBody: IAstNode;
N: TLambdaExpressionNode;
begin
N := (Node as TLambdaExpressionNode); // This cast is valid
newParams := AcceptParameters(N.Parameters);
newBody := Accept(N.Body);
// No longer cast to concrete class, use interface
newParams := AcceptParameters(Node.Parameters);
newBody := Accept(Node.Body);
if (newParams = N.Parameters) and (newBody = N.Body) then
if (newParams = Node.Parameters) and (newBody = Node.Body) then
Result := Node
else
begin
// Use TAst factory
Result := TAst.LambdaExpr(newParams, newBody, N.StaticType);
// Copy runtime properties (cast is valid)
(Result as TLambdaExpressionNode).ScopeDescriptor := N.ScopeDescriptor;
(Result as TLambdaExpressionNode).Upvalues := N.Upvalues;
(Result as TLambdaExpressionNode).HasNestedLambdas := N.HasNestedLambdas;
// Use TAst factory and copy properties via interface getters
Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, Node.StaticType);
end;
end;
@@ -550,20 +545,17 @@ function TAstTransformer.VisitFunctionCall(const Node: IFunctionCallNode): IAstN
var
newCallee: IAstNode;
newArgs: TArray<IAstNode>;
N: TFunctionCallNode;
begin
N := (Node as TFunctionCallNode); // This cast is valid
newCallee := Accept(N.Callee);
newArgs := AcceptNodes(N.Arguments);
// No longer cast to concrete class, use interface
newCallee := Accept(Node.Callee);
newArgs := AcceptNodes(Node.Arguments);
if (newCallee = N.Callee) and (newArgs = N.Arguments) then
if (newCallee = Node.Callee) and (newArgs = Node.Arguments) then
Result := Node
else
begin
// Use TAst factory
Result := TAst.FunctionCall(newCallee, newArgs, N.StaticType);
// Copy runtime properties (cast is valid)
(Result as TFunctionCallNode).IsTailCall := N.IsTailCall;
// Use TAst factory and copy properties via interface getters
Result := TAst.FunctionCall(newCallee, newArgs, Node.StaticType, Node.IsTailCall);
end;
end;
@@ -597,20 +589,17 @@ function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarati
var
newIdent: IIdentifierNode;
newInit: IAstNode;
N: TVariableDeclarationNode;
begin
N := (Node as TVariableDeclarationNode); // This cast is valid
newIdent := Accept(N.Identifier).AsIdentifier;
newInit := Accept(N.Initializer); // Accept handles nil
// No longer cast to concrete class, use interface
newIdent := Accept(Node.Identifier).AsIdentifier;
newInit := Accept(Node.Initializer); // Accept handles nil
if (newIdent = N.Identifier) and (newInit = N.Initializer) then
if (newIdent = Node.Identifier) and (newInit = Node.Initializer) then
Result := Node
else
begin
// Use TAst factory
Result := TAst.VarDecl(newIdent, newInit, N.StaticType);
// Copy runtime properties (cast is valid)
(Result as TVariableDeclarationNode).IsBoxed := N.IsBoxed;
// Use TAst factory and copy properties via interface getters
Result := TAst.VarDecl(newIdent, newInit, Node.StaticType, Node.IsBoxed);
end;
end;
@@ -708,12 +697,12 @@ end;
function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
var
N: TRecordLiteralNode;
N: IRecordLiteralNode; // Use interface
i: Integer;
newFields: TArray<TRecordFieldLiteral>;
hasChanged: Boolean;
begin
N := (Node as TRecordLiteralNode); // This cast is valid
N := Node;
SetLength(newFields, Length(N.Fields));
hasChanged := False;
@@ -737,7 +726,7 @@ begin
if N is TGenericRecordLiteralNode then
(Result as TGenericRecordLiteralNode).GenericDefinition := (N as TGenericRecordLiteralNode).GenericDefinition
else
(Result as TRecordLiteralNode).Definition := N.Definition;
(Result as TRecordLiteralNode).Definition := (N as TRecordLiteralNode).Definition;
end;
end;
+158 -69
View File
@@ -9,7 +9,7 @@ uses
Myc.Data.Scalar,
Myc.Data.Value,
Myc.Data.Keyword,
Myc.Ast.Nodes, // Contains TAstNodeKind, IAstNode, etc.
Myc.Ast.Nodes,
Myc.Ast.Scope,
Myc.Ast.Types;
@@ -63,11 +63,17 @@ type
const AThenBranch, AElseBranch: IAstNode;
const AStaticType: IStaticType = nil
): ITernaryExpressionNode; static;
// Updated Factory Signature
class function LambdaExpr(
const AParameters: TArray<IIdentifierNode>;
const ABody: IAstNode;
const AScopeDescriptor: IScopeDescriptor = nil;
const AUpvalues: TArray<TResolvedAddress> = nil;
const AHasNestedLambdas: Boolean = False;
const AStaticType: IStaticType = nil
): ILambdaExpressionNode; static;
class function MacroDef(
const AName: IIdentifierNode;
const AParameters: TArray<IIdentifierNode>;
@@ -76,22 +82,30 @@ type
class function Quasiquote(const AExpression: IAstNode): IQuasiquoteNode; static;
class function Unquote(const AExpression: IAstNode): IUnquoteNode; static;
class function UnquoteSplicing(const AExpression: IQuasiquoteNode): IUnquoteSplicingNode; static;
// Updated Factory Signature
class function FunctionCall(
const ACallee: IAstNode;
const AArguments: TArray<IAstNode>;
const AStaticType: IStaticType = nil
const AStaticType: IStaticType = nil;
const AIsTailCall: Boolean = False
): IFunctionCallNode; static;
class function MacroExpansionNode(
const AOriginalCallNode: IFunctionCallNode;
const AExpandedBody: IAstNode
): IMacroExpansionNode; static;
class function Recur(const AArguments: array of IAstNode; const AStaticType: IStaticType = nil): IRecurNode; static;
class function Block(const AExpressions: array of IAstNode; const AStaticType: IStaticType = nil): IBlockExpressionNode; static;
// Updated Factory Signature
class function VarDecl(
const AIdentifier: IIdentifierNode;
AInitializer: IAstNode = nil;
const AStaticType: IStaticType = nil
const AStaticType: IStaticType = nil;
const AIsBoxed: Boolean = False
): IVariableDeclarationNode; static;
class function Assign(
const AIdentifier: IIdentifierNode;
const AValue: IAstNode;
@@ -172,62 +186,8 @@ type
property StaticType: IStaticType read GetStaticType;
end;
TLambdaExpressionNode = class(TAstTypedNode, ILambdaExpressionNode)
private
FParameters: TArray<IIdentifierNode>;
FBody: IAstNode;
FScopeDescriptor: IScopeDescriptor;
FUpvalues: TArray<TResolvedAddress>;
FHasNestedLambdas: Boolean;
function GetParameters: TArray<IIdentifierNode>;
function GetBody: IAstNode;
function GetKind: TAstNodeKind; override;
public
constructor Create(const AParameters: TArray<IIdentifierNode>; const ABody: IAstNode; const AStaticType: IStaticType);
destructor Destroy; override;
function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsLambdaExpression: ILambdaExpressionNode; override;
property Body: IAstNode read FBody write FBody;
property Parameters: TArray<IIdentifierNode> read FParameters;
property ScopeDescriptor: IScopeDescriptor read FScopeDescriptor write FScopeDescriptor;
property Upvalues: TArray<TResolvedAddress> read FUpvalues write FUpvalues;
property HasNestedLambdas: Boolean read FHasNestedLambdas write FHasNestedLambdas;
end;
TFunctionCallNode = class(TAstTypedNode, IFunctionCallNode)
private
FCallee: IAstNode;
FArguments: TArray<IAstNode>;
FIsTailCall: Boolean;
function GetCallee: IAstNode;
function GetArguments: TArray<IAstNode>;
function GetKind: TAstNodeKind; override;
public
constructor Create(const ACallee: IAstNode; const AArguments: TArray<IAstNode>; const AStaticType: IStaticType);
function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsFunctionCall: IFunctionCallNode; override;
property Callee: IAstNode read FCallee;
property Arguments: TArray<IAstNode> read FArguments;
property IsTailCall: Boolean read FIsTailCall write FIsTailCall;
end;
TVariableDeclarationNode = class(TAstTypedNode, IVariableDeclarationNode)
private
FIdentifier: IIdentifierNode;
FInitializer: IAstNode;
FIsBoxed: Boolean;
function GetIdentifier: IIdentifierNode;
function GetInitializer: IAstNode;
function GetKind: TAstNodeKind; override;
public
constructor Create(const AIdentifier: IIdentifierNode; AInitializer: IAstNode; const AStaticType: IStaticType);
function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsVariableDeclaration: IVariableDeclarationNode; override;
property Identifier: IIdentifierNode read FIdentifier write FIdentifier;
property Initializer: IAstNode read FInitializer write FInitializer;
property IsBoxed: Boolean read FIsBoxed write FIsBoxed;
end;
// TRecordLiteralNode is still needed in the interface
// for TGenericRecordLiteralNode
TRecordLiteralNode = class(TAstTypedNode, IRecordLiteralNode)
private
FFields: TArray<TRecordFieldLiteral>;
@@ -256,6 +216,77 @@ uses
System.Generics.Defaults;
type
// --- Concrete Class Definitions moved to implementation ---
TLambdaExpressionNode = class(TAstTypedNode, ILambdaExpressionNode)
private
FParameters: TArray<IIdentifierNode>;
FBody: IAstNode;
FScopeDescriptor: IScopeDescriptor;
FUpvalues: TArray<TResolvedAddress>;
FHasNestedLambdas: Boolean;
function GetParameters: TArray<IIdentifierNode>;
function GetBody: IAstNode;
function GetScopeDescriptor: IScopeDescriptor;
function GetUpvalues: TArray<TResolvedAddress>;
function GetHasNestedLambdas: Boolean;
function GetKind: TAstNodeKind; override;
public
constructor Create(
const AParameters: TArray<IIdentifierNode>;
const ABody: IAstNode;
const AStaticType: IStaticType;
const AScopeDescriptor: IScopeDescriptor;
const AUpvalues: TArray<TResolvedAddress>;
const AHasNestedLambdas: Boolean
);
destructor Destroy; override;
function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsLambdaExpression: ILambdaExpressionNode; override;
end;
TFunctionCallNode = class(TAstTypedNode, IFunctionCallNode)
private
FCallee: IAstNode;
FArguments: TArray<IAstNode>;
FIsTailCall: Boolean;
function GetCallee: IAstNode;
function GetArguments: TArray<IAstNode>;
function GetIsTailCall: Boolean;
function GetKind: TAstNodeKind; override;
public
constructor Create(
const ACallee: IAstNode;
const AArguments: TArray<IAstNode>;
const AStaticType: IStaticType;
const AIsTailCall: Boolean
);
function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsFunctionCall: IFunctionCallNode; override;
end;
TVariableDeclarationNode = class(TAstTypedNode, IVariableDeclarationNode)
private
FIdentifier: IIdentifierNode;
FInitializer: IAstNode;
FIsBoxed: Boolean;
function GetIdentifier: IIdentifierNode;
function GetInitializer: IAstNode;
function GetIsBoxed: Boolean;
function GetKind: TAstNodeKind; override;
public
constructor Create(
const AIdentifier: IIdentifierNode;
AInitializer: IAstNode;
const AStaticType: IStaticType;
const AIsBoxed: Boolean
);
function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsVariableDeclaration: IVariableDeclarationNode; override;
end;
// --- Other internal node classes ---
TConstantNode = class(TAstTypedNode, IConstantNode)
private
FValue: TDataValue;
@@ -700,15 +731,18 @@ end;
class function TAst.FunctionCall(
const ACallee: IAstNode;
const AArguments: TArray<IAstNode>;
const AStaticType: IStaticType = nil
const AStaticType: IStaticType = nil;
const AIsTailCall: Boolean = False
): IFunctionCallNode;
begin
// Updated to call new constructor
Result :=
TFunctionCallNode.Create(
ACallee,
AArguments,
if AStaticType <> nil then AStaticType
else TTypes.Unknown
else TTypes.Unknown,
AIsTailCall
);
end;
@@ -769,15 +803,22 @@ end;
class function TAst.LambdaExpr(
const AParameters: TArray<IIdentifierNode>;
const ABody: IAstNode;
const AScopeDescriptor: IScopeDescriptor = nil;
const AUpvalues: TArray<TResolvedAddress> = nil;
const AHasNestedLambdas: Boolean = False;
const AStaticType: IStaticType = nil
): ILambdaExpressionNode;
begin
// Updated to call new constructor
Result :=
TLambdaExpressionNode.Create(
AParameters,
ABody,
if AStaticType <> nil then AStaticType
else TTypes.Unknown
else TTypes.Unknown,
AScopeDescriptor,
AUpvalues,
AHasNestedLambdas
);
end;
@@ -898,15 +939,18 @@ end;
class function TAst.VarDecl(
const AIdentifier: IIdentifierNode;
AInitializer: IAstNode = nil;
const AStaticType: IStaticType = nil
const AStaticType: IStaticType = nil;
const AIsBoxed: Boolean = False
): IVariableDeclarationNode;
begin
// Updated to call new constructor
Result :=
TVariableDeclarationNode.Create(
AIdentifier,
AInitializer,
if AStaticType <> nil then AStaticType
else TTypes.Unknown
else TTypes.Unknown,
AIsBoxed
);
end;
@@ -1350,11 +1394,21 @@ end;
{ TLambdaExpressionNode }
constructor TLambdaExpressionNode.Create(const AParameters: TArray<IIdentifierNode>; const ABody: IAstNode; const AStaticType: IStaticType);
constructor TLambdaExpressionNode.Create(
const AParameters: TArray<IIdentifierNode>;
const ABody: IAstNode;
const AStaticType: IStaticType;
const AScopeDescriptor: IScopeDescriptor;
const AUpvalues: TArray<TResolvedAddress>;
const AHasNestedLambdas: Boolean
);
begin
inherited Create(AStaticType);
FParameters := AParameters;
FBody := ABody;
FScopeDescriptor := AScopeDescriptor;
FUpvalues := AUpvalues;
FHasNestedLambdas := AHasNestedLambdas;
end;
destructor TLambdaExpressionNode.Destroy;
@@ -1383,6 +1437,21 @@ begin
Result := FParameters;
end;
function TLambdaExpressionNode.GetScopeDescriptor: IScopeDescriptor;
begin
Result := FScopeDescriptor;
end;
function TLambdaExpressionNode.GetUpvalues: TArray<TResolvedAddress>;
begin
Result := FUpvalues;
end;
function TLambdaExpressionNode.GetHasNestedLambdas: Boolean;
begin
Result := FHasNestedLambdas;
end;
function TLambdaExpressionNode.GetKind: TAstNodeKind;
begin
Result := akLambdaExpression;
@@ -1514,12 +1583,17 @@ end;
{ TFunctionCallNode }
constructor TFunctionCallNode.Create(const ACallee: IAstNode; const AArguments: TArray<IAstNode>; const AStaticType: IStaticType);
constructor TFunctionCallNode.Create(
const ACallee: IAstNode;
const AArguments: TArray<IAstNode>;
const AStaticType: IStaticType;
const AIsTailCall: Boolean
);
begin
inherited Create(AStaticType);
FCallee := ACallee;
FArguments := AArguments;
FIsTailCall := False;
FIsTailCall := AIsTailCall;
end;
function TFunctionCallNode.Accept(const Visitor: IAstVisitor): TDataValue;
@@ -1542,6 +1616,11 @@ begin
Result := FCallee;
end;
function TFunctionCallNode.GetIsTailCall: Boolean;
begin
Result := FIsTailCall;
end;
function TFunctionCallNode.GetKind: TAstNodeKind;
begin
Result := akFunctionCall;
@@ -1644,12 +1723,17 @@ end;
{ TVariableDeclarationNode }
constructor TVariableDeclarationNode.Create(const AIdentifier: IIdentifierNode; AInitializer: IAstNode; const AStaticType: IStaticType);
constructor TVariableDeclarationNode.Create(
const AIdentifier: IIdentifierNode;
AInitializer: IAstNode;
const AStaticType: IStaticType;
const AIsBoxed: Boolean
);
begin
inherited Create(AStaticType);
FIdentifier := AIdentifier;
FInitializer := AInitializer;
FIsBoxed := False;
FIsBoxed := AIsBoxed;
end;
function TVariableDeclarationNode.Accept(const Visitor: IAstVisitor): TDataValue;
@@ -1672,6 +1756,11 @@ begin
Result := FInitializer;
end;
function TVariableDeclarationNode.GetIsBoxed: Boolean;
begin
Result := FIsBoxed;
end;
function TVariableDeclarationNode.GetKind: TAstNodeKind;
begin
Result := akVariableDeclaration;