Refactoring for immutable nodes

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