From c0a689d2bc2919dcf1e0e638fac6ecc00ced7207 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Wed, 5 Nov 2025 13:21:17 +0100 Subject: [PATCH] Refactoring for immutable nodes --- ASTPlayground/MainForm.pas | 10 +- ASTPlayground/Myc.Fmx.AstEditor.Node.pas | 2 +- Src/AST/Myc.Ast.Binding.pas | 4 +- Src/AST/Myc.Ast.Compiler.TCO.pas | 12 +- Src/AST/Myc.Ast.JSON.pas | 6 +- Src/AST/Myc.Ast.Lowering.pas | 6 +- Src/AST/Myc.Ast.MacroExpander.pas | 12 +- Src/AST/Myc.Ast.Nodes.pas | 2 + Src/AST/Myc.Ast.Scope.pas | 22 +- Src/AST/Myc.Ast.Script.pas | 4 +- Src/AST/Myc.Ast.TypeChecker.pas | 27 +-- Src/AST/Myc.Ast.Visitor.pas | 27 +-- Src/AST/Myc.Ast.pas | 273 +++++++++++++---------- 13 files changed, 217 insertions(+), 190 deletions(-) diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas index 247d73e..a501bc1 100644 --- a/ASTPlayground/MainForm.pas +++ b/ASTPlayground/MainForm.pas @@ -401,7 +401,7 @@ begin if sym.Address.Kind = akUnresolved then begin // Distinguish between loading a macro and loading a function. - if funcAst is TMacroDefinitionNode then + if funcAst.Kind = akMacroDefinition then begin // Macros are stored as raw AST nodes in the scope. FGScope.Define(pair.JsonString.Value, TDataValue.FromIntf(funcAst)); @@ -461,9 +461,9 @@ begin definitions.Add(decl.Identifier.Name, decl.Initializer); end // Handle macro definitions inside the block - else if expr is TMacroDefinitionNode then + else if expr.Kind = akMacroDefinition then begin - var macroDef := expr as TMacroDefinitionNode; + var macroDef := expr.AsMacroDefinition; // Store the entire macro definition node for serialization. definitions.Add(macroDef.Name.Name, macroDef); end; @@ -475,9 +475,9 @@ begin definitions.Add(decl.Identifier.Name, decl.Initializer); end // Handle a single macro definition - else if rootNode is TMacroDefinitionNode then + else if rootNode.Kind = akMacroDefinition then begin - var macroDef := rootNode as TMacroDefinitionNode; + var macroDef := rootNode.AsMacroDefinition; definitions.Add(macroDef.Name.Name, macroDef); end; diff --git a/ASTPlayground/Myc.Fmx.AstEditor.Node.pas b/ASTPlayground/Myc.Fmx.AstEditor.Node.pas index 6ef3d6b..6b2f937 100644 --- a/ASTPlayground/Myc.Fmx.AstEditor.Node.pas +++ b/ASTPlayground/Myc.Fmx.AstEditor.Node.pas @@ -1809,7 +1809,7 @@ end; function TMacroExpansionNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := TAst.MacroExpansionNode(FNode.CallNode, FExpandedBodyNode.CreateAst); + Result := TAst.MacroExpansionNode(FNode.CallNode, FExpandedBodyNode.CreateAst.AsTypedNode); end; { TRecurNodeHandler } diff --git a/Src/AST/Myc.Ast.Binding.pas b/Src/AST/Myc.Ast.Binding.pas index ed9ddee..97e0f3e 100644 --- a/Src/AST/Myc.Ast.Binding.pas +++ b/Src/AST/Myc.Ast.Binding.pas @@ -172,9 +172,9 @@ end; function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; begin // --- Transformation: Keyword-as-Function --- - if (Node.Callee is TKeywordNode) then + if Node.Callee.Kind = akKeyword then begin - var keywordNode := (Node.Callee as TKeywordNode); + 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', diff --git a/Src/AST/Myc.Ast.Compiler.TCO.pas b/Src/AST/Myc.Ast.Compiler.TCO.pas index 8cffe27..b49b8c4 100644 --- a/Src/AST/Myc.Ast.Compiler.TCO.pas +++ b/Src/AST/Myc.Ast.Compiler.TCO.pas @@ -117,11 +117,10 @@ end; function TAstTCO.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; var isContextTail: Boolean; - newExprs: TArray; // For CoW + newExprs: TArray; begin isContextTail := FIsTailStack.Peek; - // Call the base implementation which handles CoW var nTail := High(Node.Expressions); newExprs := AcceptNodes( @@ -237,20 +236,17 @@ end; function TAstTCO.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; var - N: TMacroExpansionNode; newBody: IAstNode; begin - N := (Node as TMacroExpansionNode); - // Propagate tail call status to the expanded body FNextIsTail := FIsTailStack.Peek; - newBody := Accept(N.ExpandedBody); + newBody := Accept(Node.ExpandedBody); - if newBody = N.ExpandedBody then + if newBody = Node.ExpandedBody then Result := Node else // Rebuild, preserving the original CallNode metadata - Result := TMacroExpansionNode.Create(N.CallNode, newBody, newBody.AsTypedNode.StaticType); + Result := TMacroExpansionNode.Create(Node.CallNode, newBody.AsTypedNode); end; function TAstTCO.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; diff --git a/Src/AST/Myc.Ast.JSON.pas b/Src/AST/Myc.Ast.JSON.pas index 58c0de3..c6d2f46 100644 --- a/Src/AST/Myc.Ast.JSON.pas +++ b/Src/AST/Myc.Ast.JSON.pas @@ -739,11 +739,7 @@ begin for i := 0 to fieldsArray.Count - 1 do begin fieldObj := fieldsArray.Items[i] as TJSONObject; - fields[i] := - TRecordFieldLiteral.Create( - TKeywordNode.Create(TKeywordRegistry.Intern(fieldObj.GetValue('Name'))), - JsonToNode(fieldObj.GetValue('Value')) - ); + fields[i] := TRecordFieldLiteral.Create(TAst.Keyword(fieldObj.GetValue('Name')), JsonToNode(fieldObj.GetValue('Value'))); end; Result := TAst.RecordLiteral(fields); end; diff --git a/Src/AST/Myc.Ast.Lowering.pas b/Src/AST/Myc.Ast.Lowering.pas index d32622b..c86e8f5 100644 --- a/Src/AST/Myc.Ast.Lowering.pas +++ b/Src/AST/Myc.Ast.Lowering.pas @@ -85,16 +85,16 @@ end; function TAstLowerer.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; var - calleeIdentifier: TIdentifierNode; + calleeIdentifier: IIdentifierNode; binaryOp: TScalar.TBinaryOp; unaryOp: TScalar.TUnaryOp; left, right: IAstNode; nodeType: IStaticType; begin // --- Optimization: Operator Folding --- - if (Node.Callee is TIdentifierNode) then + if Node.Callee.Kind = akIdentifier then begin - calleeIdentifier := Node.Callee as TIdentifierNode; + calleeIdentifier := Node.Callee.AsIdentifier; // Get the type *before* replacing the node (it's an IAstTypedNode) nodeType := Node.AsTypedNode.StaticType; diff --git a/Src/AST/Myc.Ast.MacroExpander.pas b/Src/AST/Myc.Ast.MacroExpander.pas index 1574333..9799d17 100644 --- a/Src/AST/Myc.Ast.MacroExpander.pas +++ b/Src/AST/Myc.Ast.MacroExpander.pas @@ -113,9 +113,9 @@ begin try for var node in ANodes do begin - if (node is TUnquoteSplicingNode) then + if node.Kind = akUnquoteSplicing then begin - var spliceExpr := (node as TUnquoteSplicingNode).Expression; + var spliceExpr := node.AsUnquoteSplicing.Expression; // Evaluate the unquoted expression var evaluatedSpliceValue := FEvaluate(spliceExpr); @@ -158,9 +158,9 @@ begin expr := Node.Expression; // Check if we are unquoting a macro parameter (simple identifier) - if (expr is TIdentifierNode) then + if expr.Kind = akIdentifier then begin - symbol := FMacroScope.CreateDescriptor.FindSymbol((expr as TIdentifierNode).Name); + symbol := FMacroScope.CreateDescriptor.FindSymbol(expr.AsIdentifier.Name); if (symbol.Address.Kind = akLocalOrParent) and (symbol.Address.ScopeDepth = 0) then begin // It's a parameter. Return the TDataValue containing the AST node passed as argument. @@ -269,7 +269,7 @@ end; function TMacroExpander.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; var - calleeIdentifier: TIdentifierNode; + calleeIdentifier: IIdentifierNode; macroDef: IMacroDefinitionNode; i: Integer; begin @@ -277,7 +277,7 @@ begin if Node.Callee.Kind <> akIdentifier then exit(inherited VisitFunctionCall(Node)); - calleeIdentifier := Node.Callee as TIdentifierNode; + calleeIdentifier := Node.Callee.AsIdentifier; // Check if this identifier is a macro macroDef := FCurrentDescriptor.FindMacro(calleeIdentifier.Name); diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index 6456d3b..5a87337 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -132,6 +132,7 @@ type IAstNode = interface(IInterface) {$region 'private'} function GetKind: TAstNodeKind; + function GetIsTyped: Boolean; {$endregion} function Accept(const Visitor: IAstVisitor): TDataValue; @@ -164,6 +165,7 @@ type function AsBoundIdentifierNode: IBoundIdentifierNode; function AsTypedNode: IAstTypedNode; + property IsTyped: Boolean read GetIsTyped; property Kind: TAstNodeKind read GetKind; end; diff --git a/Src/AST/Myc.Ast.Scope.pas b/Src/AST/Myc.Ast.Scope.pas index 75c8c52..be7d863 100644 --- a/Src/AST/Myc.Ast.Scope.pas +++ b/Src/AST/Myc.Ast.Scope.pas @@ -112,6 +112,7 @@ type TScopeItem = record Value: TDataValue; IsBoxed: Boolean; + function GetContent: TDataValue; end; private @@ -606,7 +607,6 @@ end; procedure TScopeDescriptor.PopulateFromScope(Scope: TExecutionScope); var - val: TDataValue; i: Integer; begin // Recreate descriptor by iterating all defined symbols in the runtime scope. @@ -627,11 +627,16 @@ begin // FSlotTypes[slotIndex] remains TTypes.Unknown. // Check if the symbol is also a macro and add it to the macro table. - val := Scope.ValuesArray[slotIndex].Value; - if (val.Kind = vkInterface) and (val.AsIntf is TMacroDefinitionNode) then + + var val := Scope.ValuesArray[slotIndex].GetContent; + if val.Kind = vkInterface then begin - if not FMacros.ContainsKey(name) then - FMacros.Add(name, val.AsIntf); + var node := val.AsIntf; + if (node <> nil) and (node.Kind = akMacroDefinition) then + begin + if not FMacros.ContainsKey(name) then + FMacros.Add(name, node.AsMacroDefinition); + end; end; end; end; @@ -652,4 +657,11 @@ begin Result := TExecutionScope.Create(Parent, Descriptor, CapturedUpvalues); end; +function TExecutionScope.TScopeItem.GetContent: TDataValue; +begin + Result := Value; + if IsBoxed then + Result := Result.AsIntf.Value; +end; + end. diff --git a/Src/AST/Myc.Ast.Script.pas b/Src/AST/Myc.Ast.Script.pas index fe6b144..18f0a5a 100644 --- a/Src/AST/Myc.Ast.Script.pas +++ b/Src/AST/Myc.Ast.Script.pas @@ -511,7 +511,7 @@ begin raise Exception.Create('Syntax Error: Missing value for key ' + fieldName + ' in record literal.'); fieldValue := ParseExpression.Node; - fields.Add(TRecordFieldLiteral.Create(TKeywordNode.Create(TKeywordRegistry.Intern(fieldName)), fieldValue)); + fields.Add(TRecordFieldLiteral.Create(TAst.Keyword(fieldName), fieldValue)); end; Result := TAst.RecordLiteral(fields.ToArray); @@ -945,7 +945,7 @@ var arg: IAstNode; begin // Special case for (quote ...) to print it as '... - if (Node.Callee is TIdentifierNode) and (TIdentifierNode(Node.Callee).Name = 'quote') and (Length(Node.Arguments) = 1) then + if (Node.Callee.Kind = akIdentifier) and (Node.Callee.AsIdentifier.Name = 'quote') and (Length(Node.Arguments) = 1) then begin Append(''''); Node.Arguments[0].Accept(Self); diff --git a/Src/AST/Myc.Ast.TypeChecker.pas b/Src/AST/Myc.Ast.TypeChecker.pas index d66066a..b8fa83b 100644 --- a/Src/AST/Myc.Ast.TypeChecker.pas +++ b/Src/AST/Myc.Ast.TypeChecker.pas @@ -87,28 +87,19 @@ begin end; function TTypeChecker.VisitConstant(const Node: IConstantNode): IAstNode; -var - constType: IStaticType; begin // This is a leaf node. - // Assign the type based on the literal value - case Node.Value.Kind of - TDataValueKind.vkScalar: constType := TTypes.FromScalarKind(Node.Value.AsScalar.Kind); - TDataValueKind.vkText: constType := TTypes.Text; - TDataValueKind.vkVoid: constType := TTypes.Void; - else - constType := TTypes.Unknown; - end; - - // Create a new node with the correct type - Result := TConstantNode.Create(Node.Value, constType); + // If the constructor couldn't set the type, nobody can + Assert(Node.StaticType.Kind <> stUnknown); + Result := Node; end; function TTypeChecker.VisitKeyword(const Node: IKeywordNode): IAstNode; begin // This is a leaf node. // The TKeywordNode constructor *forces* the type to be TTypes.Keyword. - Result := TKeywordNode.Create(Node.Value); + Assert(Node.StaticType.Kind = stKeyword); + Result := Node; end; function TTypeChecker.VisitIdentifier(const Node: IIdentifierNode): IAstNode; @@ -123,7 +114,7 @@ begin adr := Node.AsBoundIdentifierNode.Address; // Create a new node, copying the address and assigning the inferred type - Result := TBoundIdentifierNode.Create(Node.Name, adr, symbol.StaticType); + Result := TAst.BoundIdentifier(Node.Name, adr, symbol.StaticType); end; function TTypeChecker.VisitRecurNode(const Node: IRecurNode): IAstNode; @@ -169,7 +160,7 @@ begin FCurrentDescriptor.UpdateType(adr.SlotIndex, initType); // 5. Create the new (typed) identifier node - newIdent := TBoundIdentifierNode.Create(boundIdent.Name, adr, initType); + newIdent := TAst.BoundIdentifier(boundIdent.Name, adr, initType); // 6. Create the new VariableDeclaration node Result := TVariableDeclarationNode.Create(newIdent.AsIdentifier, newInitializer, initType); @@ -205,7 +196,7 @@ begin FCurrentDescriptor.UpdateType(adr.SlotIndex, sourceType); // Re-create the identifier node *with the new type* - newIdent := TBoundIdentifierNode.Create(newIdent.AsIdentifier.Name, adr, sourceType); + newIdent := TAst.BoundIdentifier(newIdent.AsIdentifier.Name, adr, sourceType); targetType := sourceType; end; @@ -239,7 +230,7 @@ begin // (even if they are just TTypes.Unknown for now, for type inference placeholders) var paramIdent := boundNode.Parameters[i]; var paramAdr := paramIdent.AsBoundIdentifierNode.Address; - var newParam := TBoundIdentifierNode.Create(paramIdent.Name, paramAdr, TTypes.Unknown); + var newParam := TAst.BoundIdentifier(paramIdent.Name, paramAdr); newParams[i] := newParam; paramTypes[i] := TTypes.Unknown; diff --git a/Src/AST/Myc.Ast.Visitor.pas b/Src/AST/Myc.Ast.Visitor.pas index eef5ddc..db6485b 100644 --- a/Src/AST/Myc.Ast.Visitor.pas +++ b/Src/AST/Myc.Ast.Visitor.pas @@ -571,28 +571,15 @@ end; function TAstTransformer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; var - newBody: IAstNode; - N: TMacroExpansionNode; - newType: IStaticType; + newBody: IAstTypedNode; begin - N := (Node as TMacroExpansionNode); + // Visit the body and check if it changed + newBody := Accept(Node.ExpandedBody).AsTypedNode; + if newBody = Node.ExpandedBody then + exit(Node); - // 1. Visit the body - newBody := Accept(N.ExpandedBody); - - // 2. Check if the body changed (Copy-on-Write) - if newBody = N.ExpandedBody then - begin - // No change, return the original immutable node - Result := Node; - end - else - begin - // The body changed. Create a NEW wrapper node. - // The type of the wrapper is the type of its body. - newType := newBody.AsTypedNode.StaticType; - Result := TMacroExpansionNode.Create(N.CallNode, newBody, newType); - end; + // The body changed. Create a NEW wrapper node. + Result := TMacroExpansionNode.Create(Node.CallNode, newBody); end; function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; diff --git a/Src/AST/Myc.Ast.pas b/Src/AST/Myc.Ast.pas index 5b86ad2..7952eb0 100644 --- a/Src/AST/Myc.Ast.pas +++ b/Src/AST/Myc.Ast.pas @@ -33,7 +33,7 @@ type class function Nop: IAstNode; static; // --- Factory functions --- - class function Constant(const AValue: TDataValue): IConstantNode; overload; static; + class function Constant(const AValue: TDataValue; const AStaticType: IStaticType = nil): IConstantNode; overload; static; class function Constant(const AValue: String): IConstantNode; overload; static; class function Keyword(const AName: string): IKeywordNode; static; class function Identifier(AName: string): IIdentifierNode; static; @@ -71,13 +71,18 @@ type ): IAddSeriesItemNode; static; class function SeriesLength(const ASeries: IIdentifierNode): ISeriesLengthNode; static; - class function BoundIdentifier(AName: string; const Address: TResolvedAddress): IBoundIdentifierNode; static; + class function BoundIdentifier( + AName: string; + const Address: TResolvedAddress; + const AStaticType: IStaticType = nil + ): IBoundIdentifierNode; static; end; // Common base class for AST nodes to reduce boilerplate. TAstNode = class(TInterfacedObject, IAstNode) private function GetKind: TAstNodeKind; virtual; abstract; + function GetIsTyped: Boolean; virtual; public constructor Create; function Accept(const Visitor: IAstVisitor): TDataValue; virtual; abstract; @@ -112,6 +117,7 @@ type function AsBoundIdentifierNode: IBoundIdentifierNode; virtual; function AsTypedNode: IAstTypedNode; virtual; + property IsTyped: Boolean read GetIsTyped; property Kind: TAstNodeKind read GetKind; end; @@ -119,59 +125,13 @@ type private FStaticType: IStaticType; function GetStaticType: IStaticType; + function GetIsTyped: Boolean; override; public constructor Create(const AStaticType: IStaticType); function AsTypedNode: IAstTypedNode; override; property StaticType: IStaticType read GetStaticType; end; - TConstantNode = class(TAstTypedNode, IConstantNode) - private - FValue: TDataValue; - function GetValue: TDataValue; - function GetKind: TAstNodeKind; override; - public - constructor Create(const AValue: TDataValue; const AStaticType: IStaticType); - function Accept(const Visitor: IAstVisitor): TDataValue; override; - function AsConstant: IConstantNode; override; - property Value: TDataValue read GetValue; // Value ist immutable - end; - - TIdentifierNode = class(TAstTypedNode, IIdentifierNode) - private - FName: string; - function GetName: string; - function GetKind: TAstNodeKind; override; - public - constructor Create(const AName: string; const AStaticType: IStaticType); - function Accept(const Visitor: IAstVisitor): TDataValue; override; - function AsIdentifier: IIdentifierNode; override; - property Name: string read FName; // Name ist immutable - end; - - TBoundIdentifierNode = class(TIdentifierNode, IBoundIdentifierNode) - private - FAddress: TResolvedAddress; - function GetAddress: TResolvedAddress; - function GetKind: TAstNodeKind; override; - public - constructor Create(const AName: string; const AAddress: TResolvedAddress; const AStaticType: IStaticType); - function AsBoundIdentifierNode: IBoundIdentifierNode; override; - property Address: TResolvedAddress read GetAddress; - end; - - TKeywordNode = class(TAstTypedNode, IKeywordNode) - private - FValue: IKeyword; - function GetValue: IKeyword; - function GetKind: TAstNodeKind; override; - public - constructor Create(const AValue: IKeyword); - function Accept(const Visitor: IAstVisitor): TDataValue; override; - function AsKeyword: IKeywordNode; override; - property Value: IKeyword read FValue; // Value ist immutable - end; - TBinaryExpressionNode = class(TAstTypedNode, IBinaryExpressionNode) private FLeft: IAstNode; @@ -263,60 +223,6 @@ type property HasNestedLambdas: Boolean read FHasNestedLambdas write FHasNestedLambdas; end; - TMacroDefinitionNode = class(TAstNode, IMacroDefinitionNode) - private - FName: IIdentifierNode; - FParameters: TArray; - FBody: IAstNode; - function GetName: IIdentifierNode; - function GetParameters: TArray; - function GetBody: IAstNode; - function GetKind: TAstNodeKind; override; - public - constructor Create(const AName: IIdentifierNode; const AParameters: TArray; const ABody: IAstNode); - function Accept(const Visitor: IAstVisitor): TDataValue; override; - function AsMacroDefinition: IMacroDefinitionNode; override; - property Name: IIdentifierNode read GetName; - property Parameters: TArray read FParameters; - property Body: IAstNode read FBody; - end; - - TQuasiquoteNode = class(TAstNode, IQuasiquoteNode) - private - FExpression: IAstNode; - function GetExpression: IAstNode; - function GetKind: TAstNodeKind; override; - public - constructor Create(const AExpression: IAstNode); - function Accept(const Visitor: IAstVisitor): TDataValue; override; - function AsQuasiquote: IQuasiquoteNode; override; - property Expression: IAstNode read GetExpression; - end; - - TUnquoteNode = class(TAstNode, IUnquoteNode) - private - FExpression: IAstNode; - function GetExpression: IAstNode; - function GetKind: TAstNodeKind; override; - public - constructor Create(const AExpression: IAstNode); - function Accept(const Visitor: IAstVisitor): TDataValue; override; - function AsUnquote: IUnquoteNode; override; - property Expression: IAstNode read GetExpression; - end; - - TUnquoteSplicingNode = class(TAstNode, IUnquoteSplicingNode) - private - FExpression: IQuasiquoteNode; - function GetExpression: IQuasiquoteNode; - function GetKind: TAstNodeKind; override; - public - constructor Create(const AExpression: IQuasiquoteNode); - function Accept(const Visitor: IAstVisitor): TDataValue; override; - function AsUnquoteSplicing: IUnquoteSplicingNode; override; - property Expression: IQuasiquoteNode read GetExpression; - end; - TFunctionCallNode = class(TAstTypedNode, IFunctionCallNode) private FCallee: IAstNode; @@ -349,16 +255,16 @@ type TMacroExpansionNode = class(TAstTypedNode, IMacroExpansionNode) private FCallNode: IFunctionCallNode; - FExpandedBody: IAstNode; + FExpandedBody: IAstTypedNode; function GetCallNode: IFunctionCallNode; function GetExpandedBody: IAstNode; function GetKind: TAstNodeKind; override; public - constructor Create(const ACallNode: IFunctionCallNode; const AExpandedBody: IAstNode; const AStaticType: IStaticType); + constructor Create(const ACallNode: IFunctionCallNode; const AExpandedBody: IAstTypedNode); function Accept(const Visitor: IAstVisitor): TDataValue; override; function AsMacroExpansion: IMacroExpansionNode; override; property CallNode: IFunctionCallNode read GetCallNode; - property ExpandedBody: IAstNode read FExpandedBody; + property ExpandedBody: IAstTypedNode read FExpandedBody; end; TBlockExpressionNode = class(TAstTypedNode, IBlockExpressionNode) @@ -508,8 +414,108 @@ implementation uses System.Generics.Defaults; -// Added Nop type + TConstantNode = class(TAstTypedNode, IConstantNode) + private + FValue: TDataValue; + function GetValue: TDataValue; + function GetKind: TAstNodeKind; override; + public + constructor Create(const AValue: TDataValue; const AStaticType: IStaticType); + function Accept(const Visitor: IAstVisitor): TDataValue; override; + function AsConstant: IConstantNode; override; + property Value: TDataValue read GetValue; + end; + + TIdentifierNode = class(TAstTypedNode, IIdentifierNode) + private + FName: string; + function GetName: string; + function GetKind: TAstNodeKind; override; + public + constructor Create(const AName: string; const AStaticType: IStaticType); + function Accept(const Visitor: IAstVisitor): TDataValue; override; + function AsIdentifier: IIdentifierNode; override; + property Name: string read FName; + end; + + TBoundIdentifierNode = class(TIdentifierNode, IBoundIdentifierNode) + private + FAddress: TResolvedAddress; + function GetAddress: TResolvedAddress; + function GetKind: TAstNodeKind; override; + public + constructor Create(const AName: string; const AAddress: TResolvedAddress; const AStaticType: IStaticType); + function AsBoundIdentifierNode: IBoundIdentifierNode; override; + property Address: TResolvedAddress read GetAddress; + end; + + TKeywordNode = class(TAstTypedNode, IKeywordNode) + private + FValue: IKeyword; + function GetValue: IKeyword; + function GetKind: TAstNodeKind; override; + public + constructor Create(const AValue: IKeyword); + function Accept(const Visitor: IAstVisitor): TDataValue; override; + function AsKeyword: IKeywordNode; override; + property Value: IKeyword read FValue; // Value ist immutable + end; + + TMacroDefinitionNode = class(TAstNode, IMacroDefinitionNode) + private + FName: IIdentifierNode; + FParameters: TArray; + FBody: IAstNode; + function GetName: IIdentifierNode; + function GetParameters: TArray; + function GetBody: IAstNode; + function GetKind: TAstNodeKind; override; + public + constructor Create(const AName: IIdentifierNode; const AParameters: TArray; const ABody: IAstNode); + function Accept(const Visitor: IAstVisitor): TDataValue; override; + function AsMacroDefinition: IMacroDefinitionNode; override; + property Name: IIdentifierNode read GetName; + property Parameters: TArray read FParameters; + property Body: IAstNode read FBody; + end; + + TQuasiquoteNode = class(TAstNode, IQuasiquoteNode) + private + FExpression: IAstNode; + function GetExpression: IAstNode; + function GetKind: TAstNodeKind; override; + public + constructor Create(const AExpression: IAstNode); + function Accept(const Visitor: IAstVisitor): TDataValue; override; + function AsQuasiquote: IQuasiquoteNode; override; + property Expression: IAstNode read GetExpression; + end; + + TUnquoteNode = class(TAstNode, IUnquoteNode) + private + FExpression: IAstNode; + function GetExpression: IAstNode; + function GetKind: TAstNodeKind; override; + public + constructor Create(const AExpression: IAstNode); + function Accept(const Visitor: IAstVisitor): TDataValue; override; + function AsUnquote: IUnquoteNode; override; + property Expression: IAstNode read GetExpression; + end; + + TUnquoteSplicingNode = class(TAstNode, IUnquoteSplicingNode) + private + FExpression: IQuasiquoteNode; + function GetExpression: IQuasiquoteNode; + function GetKind: TAstNodeKind; override; + public + constructor Create(const AExpression: IQuasiquoteNode); + function Accept(const Visitor: IAstVisitor): TDataValue; override; + function AsUnquoteSplicing: IUnquoteSplicingNode; override; + property Expression: IQuasiquoteNode read GetExpression; + end; + TNopNode = class(TAstNode, INopNode) private function GetKind: TAstNodeKind; override; @@ -574,14 +580,28 @@ begin Result := TBlockExpressionNode.Create(AExpressions, TTypes.Unknown); end; -class function TAst.Constant(const AValue: TDataValue): IConstantNode; +class function TAst.Constant(const AValue: TDataValue; const AStaticType: IStaticType = nil): IConstantNode; begin - Result := TConstantNode.Create(AValue, TTypes.Unknown); + var constType := AStaticType; + + if constType = nil then + begin + case AValue.Kind of + TDataValueKind.vkScalar: constType := TTypes.FromScalarKind(AValue.AsScalar.Kind); + TDataValueKind.vkText: constType := TTypes.Text; + TDataValueKind.vkVoid: constType := TTypes.Void; + else + constType := TTypes.Unknown; + end; + end; + + // Create a new node with the correct type + Result := TConstantNode.Create(AValue, constType); end; class function TAst.Constant(const AValue: String): IConstantNode; begin - Result := TConstantNode.Create(AValue, TTypes.Unknown); + Result := TConstantNode.Create(AValue, TTypes.Text); end; class constructor TAst.Create; @@ -594,9 +614,19 @@ begin FLibraries.Free; end; -class function TAst.BoundIdentifier(AName: string; const Address: TResolvedAddress): IBoundIdentifierNode; +class function TAst.BoundIdentifier( + AName: string; + const Address: TResolvedAddress; + const AStaticType: IStaticType = nil +): IBoundIdentifierNode; begin - Result := TBoundIdentifierNode.Create(AName, Address, TTypes.Unknown); + Result := + TBoundIdentifierNode.Create( + AName, + Address, + if AStaticType <> nil then AStaticType + else TTypes.Unknown + ); end; class procedure TAst.RegisterLibrary(const AProc: TRegisterLibraryProc); @@ -645,7 +675,10 @@ end; class function TAst.MacroExpansionNode(const AOriginalCallNode: IFunctionCallNode; const AExpandedBody: IAstNode): IMacroExpansionNode; begin - Result := TMacroExpansionNode.Create(AOriginalCallNode, AExpandedBody, TTypes.Unknown); + if AExpandedBody.IsTyped then + Result := TMacroExpansionNode.Create(AOriginalCallNode, AExpandedBody.AsTypedNode) + else + Result := TMacroExpansionNode.Create(AOriginalCallNode, Block([])); end; class function TAst.MemberAccess(const ABase: IAstNode; const AMember: IKeywordNode): IMemberAccessNode; @@ -871,6 +904,11 @@ begin raise ETypeException.Create('Node is not a VariableDeclaration'); end; +function TAstNode.GetIsTyped: Boolean; +begin + Result := false; +end; + { TAstTypedNode } constructor TAstTypedNode.Create(const AStaticType: IStaticType); @@ -885,6 +923,11 @@ begin Result := Self; end; +function TAstTypedNode.GetIsTyped: Boolean; +begin + Result := true; +end; + function TAstTypedNode.GetStaticType: IStaticType; begin Result := FStaticType; @@ -1334,10 +1377,10 @@ end; { TMacroExpansionNode } -constructor TMacroExpansionNode.Create(const ACallNode: IFunctionCallNode; const AExpandedBody: IAstNode; const AStaticType: IStaticType); +constructor TMacroExpansionNode.Create(const ACallNode: IFunctionCallNode; const AExpandedBody: IAstTypedNode); begin // Copy properties from the original call node - inherited Create(AStaticType); + inherited Create(AExpandedBody.StaticType); FExpandedBody := AExpandedBody; FCallNode := ACallNode; end;