diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas index 04a122f..331433c 100644 --- a/ASTPlayground/MainForm.pas +++ b/ASTPlayground/MainForm.pas @@ -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. diff --git a/Src/AST/Myc.Ast.Analyzer.pas b/Src/AST/Myc.Ast.Analyzer.pas index a63406d..df30989 100644 --- a/Src/AST/Myc.Ast.Analyzer.pas +++ b/Src/AST/Myc.Ast.Analyzer.pas @@ -134,22 +134,33 @@ end; function TUpvalueAnalyzer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; var - N: TLambdaExpressionNode; + newParams: TArray; + 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; - 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.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. diff --git a/Src/AST/Myc.Ast.Binding.pas b/Src/AST/Myc.Ast.Binding.pas index b18e1c7..41d0647 100644 --- a/Src/AST/Myc.Ast.Binding.pas +++ b/Src/AST/Myc.Ast.Binding.pas @@ -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; + hasNestedLambdas: Boolean; + lastNestedLambdaCount: Integer; + newParams: TArray; + 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(''); // 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; - 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. diff --git a/Src/AST/Myc.Ast.Compiler.TCO.pas b/Src/AST/Myc.Ast.Compiler.TCO.pas index 1a0fec6..eacc839 100644 --- a/Src/AST/Myc.Ast.Compiler.TCO.pas +++ b/Src/AST/Myc.Ast.Compiler.TCO.pas @@ -184,29 +184,23 @@ end; function TAstTCO.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; var - N: TLambdaExpressionNode; newParams: TArray; 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; 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; diff --git a/Src/AST/Myc.Ast.Dumper.pas b/Src/AST/Myc.Ast.Dumper.pas index d844398..ba7e1bf 100644 --- a/Src/AST/Myc.Ast.Dumper.pas +++ b/Src/AST/Myc.Ast.Dumper.pas @@ -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; - 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; diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index a230e28..e22814f 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -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; i: Integer; sourceAddresses: TArray; 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; - 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.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>; - 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.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; - 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; diff --git a/Src/AST/Myc.Ast.Lowering.pas b/Src/AST/Myc.Ast.Lowering.pas index 37c1388..a17fc26 100644 --- a/Src/AST/Myc.Ast.Lowering.pas +++ b/Src/AST/Myc.Ast.Lowering.pas @@ -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 diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index da826df..6815506 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -237,18 +237,26 @@ type {$region 'private'} function GetParameters: TArray; function GetBody: IAstNode; + function GetScopeDescriptor: IScopeDescriptor; + function GetUpvalues: TArray; + function GetHasNestedLambdas: Boolean; {$endregion} property Parameters: TArray read GetParameters; property Body: IAstNode read GetBody; + property ScopeDescriptor: IScopeDescriptor read GetScopeDescriptor; + property Upvalues: TArray read GetUpvalues; + property HasNestedLambdas: Boolean read GetHasNestedLambdas; end; IFunctionCallNode = interface(IAstTypedNode) {$region 'private'} function GetCallee: IAstNode; function GetArguments: TArray; + function GetIsTailCall: Boolean; {$endregion} property Callee: IAstNode read GetCallee; property Arguments: TArray 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) diff --git a/Src/AST/Myc.Ast.TypeChecker.pas b/Src/AST/Myc.Ast.TypeChecker.pas index 1c2d531..83ecf25 100644 --- a/Src/AST/Myc.Ast.TypeChecker.pas +++ b/Src/AST/Myc.Ast.TypeChecker.pas @@ -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; 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; 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)); diff --git a/Src/AST/Myc.Ast.Visitor.pas b/Src/AST/Myc.Ast.Visitor.pas index 9aa5c08..8909b03 100644 --- a/Src/AST/Myc.Ast.Visitor.pas +++ b/Src/AST/Myc.Ast.Visitor.pas @@ -527,22 +527,17 @@ function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode var newParams: TArray; 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; - 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; 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; diff --git a/Src/AST/Myc.Ast.pas b/Src/AST/Myc.Ast.pas index 9ab747a..b7e68ee 100644 --- a/Src/AST/Myc.Ast.pas +++ b/Src/AST/Myc.Ast.pas @@ -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; const ABody: IAstNode; + const AScopeDescriptor: IScopeDescriptor = nil; + const AUpvalues: TArray = nil; + const AHasNestedLambdas: Boolean = False; const AStaticType: IStaticType = nil ): ILambdaExpressionNode; static; + class function MacroDef( const AName: IIdentifierNode; const AParameters: TArray; @@ -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; - 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; - FBody: IAstNode; - FScopeDescriptor: IScopeDescriptor; - FUpvalues: TArray; - FHasNestedLambdas: Boolean; - function GetParameters: TArray; - function GetBody: IAstNode; - function GetKind: TAstNodeKind; override; - public - constructor Create(const AParameters: TArray; 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 read FParameters; - property ScopeDescriptor: IScopeDescriptor read FScopeDescriptor write FScopeDescriptor; - property Upvalues: TArray read FUpvalues write FUpvalues; - property HasNestedLambdas: Boolean read FHasNestedLambdas write FHasNestedLambdas; - end; - - TFunctionCallNode = class(TAstTypedNode, IFunctionCallNode) - private - FCallee: IAstNode; - FArguments: TArray; - FIsTailCall: Boolean; - function GetCallee: IAstNode; - function GetArguments: TArray; - function GetKind: TAstNodeKind; override; - public - constructor Create(const ACallee: IAstNode; const AArguments: TArray; const AStaticType: IStaticType); - function Accept(const Visitor: IAstVisitor): TDataValue; override; - function AsFunctionCall: IFunctionCallNode; override; - property Callee: IAstNode read FCallee; - property Arguments: TArray 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; @@ -256,6 +216,77 @@ uses System.Generics.Defaults; type + // --- Concrete Class Definitions moved to implementation --- + + TLambdaExpressionNode = class(TAstTypedNode, ILambdaExpressionNode) + private + FParameters: TArray; + FBody: IAstNode; + FScopeDescriptor: IScopeDescriptor; + FUpvalues: TArray; + FHasNestedLambdas: Boolean; + function GetParameters: TArray; + function GetBody: IAstNode; + function GetScopeDescriptor: IScopeDescriptor; + function GetUpvalues: TArray; + function GetHasNestedLambdas: Boolean; + function GetKind: TAstNodeKind; override; + public + constructor Create( + const AParameters: TArray; + const ABody: IAstNode; + const AStaticType: IStaticType; + const AScopeDescriptor: IScopeDescriptor; + const AUpvalues: TArray; + 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; + FIsTailCall: Boolean; + function GetCallee: IAstNode; + function GetArguments: TArray; + function GetIsTailCall: Boolean; + function GetKind: TAstNodeKind; override; + public + constructor Create( + const ACallee: IAstNode; + const AArguments: TArray; + 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; - 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; const ABody: IAstNode; + const AScopeDescriptor: IScopeDescriptor = nil; + const AUpvalues: TArray = 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; const ABody: IAstNode; const AStaticType: IStaticType); +constructor TLambdaExpressionNode.Create( + const AParameters: TArray; + const ABody: IAstNode; + const AStaticType: IStaticType; + const AScopeDescriptor: IScopeDescriptor; + const AUpvalues: TArray; + 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; +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; const AStaticType: IStaticType); +constructor TFunctionCallNode.Create( + const ACallee: IAstNode; + const AArguments: TArray; + 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;