Binder refactoring, Monster refactoring

This commit is contained in:
Michael Schimmel
2025-11-02 19:38:52 +01:00
parent 8f29212cba
commit ea39a57b77
22 changed files with 3061 additions and 2638 deletions
File diff suppressed because one or more lines are too long
-1
View File
@@ -22,7 +22,6 @@ uses
Myc.Ast.Script in '..\Src\AST\Myc.Ast.Script.pas', Myc.Ast.Script in '..\Src\AST\Myc.Ast.Script.pas',
Myc.Ast.Types in '..\Src\AST\Myc.Ast.Types.pas', Myc.Ast.Types in '..\Src\AST\Myc.Ast.Types.pas',
Myc.Data.Keyword in '..\Src\Data\Myc.Data.Keyword.pas', Myc.Data.Keyword in '..\Src\Data\Myc.Data.Keyword.pas',
Myc.Ast.Binding.Nodes in '..\Src\AST\Myc.Ast.Binding.Nodes.pas',
Myc.Ast.MacroExpander in '..\Src\AST\Myc.Ast.MacroExpander.pas', Myc.Ast.MacroExpander in '..\Src\AST\Myc.Ast.MacroExpander.pas',
Myc.Ast.TypeChecker in '..\Src\AST\Myc.Ast.TypeChecker.pas', Myc.Ast.TypeChecker in '..\Src\AST\Myc.Ast.TypeChecker.pas',
Myc.Ast.Lowering in '..\Src\AST\Myc.Ast.Lowering.pas', Myc.Ast.Lowering in '..\Src\AST\Myc.Ast.Lowering.pas',
-1
View File
@@ -152,7 +152,6 @@
<DCCReference Include="..\Src\AST\Myc.Ast.Script.pas"/> <DCCReference Include="..\Src\AST\Myc.Ast.Script.pas"/>
<DCCReference Include="..\Src\AST\Myc.Ast.Types.pas"/> <DCCReference Include="..\Src\AST\Myc.Ast.Types.pas"/>
<DCCReference Include="..\Src\Data\Myc.Data.Keyword.pas"/> <DCCReference Include="..\Src\Data\Myc.Data.Keyword.pas"/>
<DCCReference Include="..\Src\AST\Myc.Ast.Binding.Nodes.pas"/>
<DCCReference Include="..\Src\AST\Myc.Ast.MacroExpander.pas"/> <DCCReference Include="..\Src\AST\Myc.Ast.MacroExpander.pas"/>
<DCCReference Include="..\Src\AST\Myc.Ast.TypeChecker.pas"/> <DCCReference Include="..\Src\AST\Myc.Ast.TypeChecker.pas"/>
<DCCReference Include="..\Src\AST\Myc.Ast.Lowering.pas"/> <DCCReference Include="..\Src\AST\Myc.Ast.Lowering.pas"/>
+3 -2
View File
@@ -27,6 +27,7 @@ uses
Myc.Ast.Nodes, Myc.Ast.Nodes,
Myc.Ast.Scope, Myc.Ast.Scope,
Myc.Ast, Myc.Ast,
Myc.Ast.Visitor,
Myc.Ast.Evaluator, Myc.Ast.Evaluator,
Myc.Ast.Dumper, Myc.Ast.Dumper,
Myc.Data.Decimal, Myc.Data.Decimal,
@@ -262,7 +263,7 @@ begin
FWorkspace.Align := TAlignLayout.Client; FWorkspace.Align := TAlignLayout.Client;
FWorkspace.ClipChildren := true; FWorkspace.ClipChildren := true;
FWorkspace.OnMouseDown := WorkspaceMouseDown; FWorkspace.OnMouseDown := WorkspaceMouseDown;
{
TAst.RegisterLibrary( TAst.RegisterLibrary(
procedure(const Scope: IExecutionScope) procedure(const Scope: IExecutionScope)
var var
@@ -356,7 +357,7 @@ begin
); );
end end
); );
}
ClearButtonClick(Self); ClearButtonClick(Self);
end; end;
+76 -83
View File
@@ -49,7 +49,8 @@ type
property Workspace: TAuraWorkspace read GetWorkspace; property Workspace: TAuraWorkspace read GetWorkspace;
end; end;
TAstVisualizer = class(TAstVisitor, IAstVisualizer) // Inherits from the new generic TAstVisitor<TAuraNode>
TAstVisualizer = class(TAstVisitor<TAuraNode>, IAstVisualizer)
private private
FExprDepth: Integer; FExprDepth: Integer;
FParentControl: TControl; FParentControl: TControl;
@@ -63,30 +64,30 @@ type
procedure SetParentControl(const Value: TControl); procedure SetParentControl(const Value: TControl);
protected protected
// Visitor implementations for each AST node type. // Visitor implementations for each AST node type.
function VisitConstant(const Node: IConstantNode): TDataValue; override; function VisitConstant(const Node: IConstantNode): TAuraNode; override;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; function VisitIdentifier(const Node: IIdentifierNode): TAuraNode; override;
function VisitKeyword(const Node: IKeywordNode): TDataValue; override; function VisitKeyword(const Node: IKeywordNode): TAuraNode; override;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; function VisitBinaryExpression(const Node: IBinaryExpressionNode): TAuraNode; override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; function VisitUnaryExpression(const Node: IUnaryExpressionNode): TAuraNode; override;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; function VisitIfExpression(const Node: IIfExpressionNode): TAuraNode; override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; function VisitTernaryExpression(const Node: ITernaryExpressionNode): TAuraNode; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TAuraNode; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): TAuraNode; override;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override; function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TAuraNode; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitBlockExpression(const Node: IBlockExpressionNode): TAuraNode; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAuraNode; override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; function VisitAssignment(const Node: IAssignmentNode): TAuraNode; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; function VisitMacroDefinition(const Node: IMacroDefinitionNode): TAuraNode; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override; function VisitQuasiquote(const Node: IQuasiquoteNode): TAuraNode; override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override; function VisitUnquote(const Node: IUnquoteNode): TAuraNode; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override; function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TAuraNode; override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override; function VisitIndexer(const Node: IIndexerNode): TAuraNode; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; function VisitMemberAccess(const Node: IMemberAccessNode): TAuraNode; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override; function VisitRecordLiteral(const Node: IRecordLiteralNode): TAuraNode; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; function VisitCreateSeries(const Node: ICreateSeriesNode): TAuraNode; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override; function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TAuraNode; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override; function VisitSeriesLength(const Node: ISeriesLengthNode): TAuraNode; override;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override; function VisitRecurNode(const Node: IRecurNode): TAuraNode; override;
public public
constructor Create(AWorkspace: TAuraWorkspace; AParentControl: TControl; AExprDepth: Integer); constructor Create(AWorkspace: TAuraWorkspace; AParentControl: TControl; AExprDepth: Integer);
@@ -593,126 +594,126 @@ begin
FParentControl := Value; FParentControl := Value;
end; end;
function TAstVisualizer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; function TAstVisualizer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraAddSeriesItemNode.Create(Self, Node)); Result := TAuraAddSeriesItemNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitAssignment(const Node: IAssignmentNode): TDataValue; function TAstVisualizer.VisitAssignment(const Node: IAssignmentNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraAssignmentNode.Create(Self, Node)); Result := TAuraAssignmentNode.Create(Self, Node);
end; end;
// --- Visitor Implementations --- // --- Visitor Implementations ---
function TAstVisualizer.VisitConstant(const Node: IConstantNode): TDataValue; function TAstVisualizer.VisitConstant(const Node: IConstantNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraConstantNode.Create(Self, Node)); Result := TAuraConstantNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitIdentifier(const Node: IIdentifierNode): TDataValue; function TAstVisualizer.VisitIdentifier(const Node: IIdentifierNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraIdentifierNode.Create(Self, Node)); Result := TAuraIdentifierNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitKeyword(const Node: IKeywordNode): TDataValue; function TAstVisualizer.VisitKeyword(const Node: IKeywordNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraKeywordNode.Create(Self, Node)); Result := TAuraKeywordNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; function TAstVisualizer.VisitBinaryExpression(const Node: IBinaryExpressionNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraBinaryExpressionNode.Create(Self, Node)); Result := TAuraBinaryExpressionNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; function TAstVisualizer.VisitBlockExpression(const Node: IBlockExpressionNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraBlockExpressionNode.Create(Self, Node)); Result := TAuraBlockExpressionNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; function TAstVisualizer.VisitCreateSeries(const Node: ICreateSeriesNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraCreateSeriesNode.Create(Self, Node)); Result := TAuraCreateSeriesNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; function TAstVisualizer.VisitFunctionCall(const Node: IFunctionCallNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraFunctionCallNode.Create(Self, Node)); Result := TAuraFunctionCallNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; function TAstVisualizer.VisitUnaryExpression(const Node: IUnaryExpressionNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraUnaryExpressionNode.Create(Self, Node)); Result := TAuraUnaryExpressionNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; function TAstVisualizer.VisitIfExpression(const Node: IIfExpressionNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraIfExpressionNode.Create(Self, Node)); Result := TAuraIfExpressionNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitIndexer(const Node: IIndexerNode): TDataValue; function TAstVisualizer.VisitIndexer(const Node: IIndexerNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraIndexerNode.Create(Self, Node)); Result := TAuraIndexerNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; function TAstVisualizer.VisitLambdaExpression(const Node: ILambdaExpressionNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraLambdaExpressionNode.Create(Self, Node)); Result := TAuraLambdaExpressionNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; function TAstVisualizer.VisitMacroDefinition(const Node: IMacroDefinitionNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraMacroDefinitionNode.Create(Self, Node)); Result := TAuraMacroDefinitionNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; function TAstVisualizer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraMacroExpansionNode.Create(Self, Node)); Result := TAuraMacroExpansionNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; function TAstVisualizer.VisitMemberAccess(const Node: IMemberAccessNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraMemberAccessNode.Create(Self, Node)); Result := TAuraMemberAccessNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; function TAstVisualizer.VisitRecordLiteral(const Node: IRecordLiteralNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraRecordLiteralNode.Create(Self, Node)); Result := TAuraRecordLiteralNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; function TAstVisualizer.VisitQuasiquote(const Node: IQuasiquoteNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraQuasiquoteNode.Create(Self, Node)); Result := TAuraQuasiquoteNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitRecurNode(const Node: IRecurNode): TDataValue; function TAstVisualizer.VisitRecurNode(const Node: IRecurNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraRecurNode.Create(Self, Node)); Result := TAuraRecurNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; function TAstVisualizer.VisitSeriesLength(const Node: ISeriesLengthNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraSeriesLengthNode.Create(Self, Node)); Result := TAuraSeriesLengthNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; function TAstVisualizer.VisitTernaryExpression(const Node: ITernaryExpressionNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraTernaryExpressionNode.Create(Self, Node)); Result := TAuraTernaryExpressionNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitUnquote(const Node: IUnquoteNode): TDataValue; function TAstVisualizer.VisitUnquote(const Node: IUnquoteNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraUnquoteNode.Create(Self, Node)); Result := TAuraUnquoteNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; function TAstVisualizer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraUnquoteSplicingNode.Create(Self, Node)); Result := TAuraUnquoteSplicingNode.Create(Self, Node);
end; end;
function TAstVisualizer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; function TAstVisualizer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAuraNode;
begin begin
Result := TDataValue.FromGeneric<TAuraNode>(TAuraVariableDeclarationNode.Create(Self, Node)); Result := TAuraVariableDeclarationNode.Create(Self, Node);
end; end;
{ TAutoFitControl } { TAutoFitControl }
@@ -1517,16 +1518,8 @@ begin
end; end;
function TAuraLambdaExpressionNode.CreateAst: IAstNode; function TAuraLambdaExpressionNode.CreateAst: IAstNode;
var
params: TArray<IIdentifierNode>;
i: Integer;
begin begin
// Re-use FNode.Parameters which is the source of truth Result := TAst.LambdaExpr(FNode.Parameters, FBodyNode.CreateAst);
SetLength(params, Length(FNode.Parameters));
for i := 0 to High(FNode.Parameters) do
params[i] := TAst.Identifier(FNode.Parameters[i].Name);
Result := TAst.LambdaExpr(params, FBodyNode.CreateAst);
end; end;
procedure TAuraLambdaExpressionNode.SetupNode; procedure TAuraLambdaExpressionNode.SetupNode;
@@ -1966,7 +1959,7 @@ end;
function TAuraUnquoteSplicingNode.CreateAst: IAstNode; function TAuraUnquoteSplicingNode.CreateAst: IAstNode;
begin begin
Result := TAst.UnquoteSplicing(FExpressionNode.CreateAst); Result := TAst.UnquoteSplicing(FExpressionNode.CreateAst.AsQuasiquote);
end; end;
procedure TAuraUnquoteSplicingNode.SetupNode; procedure TAuraUnquoteSplicingNode.SetupNode;
+86 -69
View File
@@ -11,82 +11,89 @@ uses
type type
// This visitor converts an AST expression subtree into a single string. // This visitor converts an AST expression subtree into a single string.
TAstToTextVisitor = class(TAstVisitor) // It inherits from the generic visitor, returning a 'string' for each node.
public TAstToTextVisitor = class(TAstVisitor<string>)
function VisitConstant(const Node: IConstantNode): TDataValue; override; protected
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; function VisitConstant(const Node: IConstantNode): string; override;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; function VisitIdentifier(const Node: IIdentifierNode): string; override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; function VisitKeyword(const Node: IKeywordNode): string; override;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; function VisitBinaryExpression(const Node: IBinaryExpressionNode): string; override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; function VisitUnaryExpression(const Node: IUnaryExpressionNode): string; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; function VisitIfExpression(const Node: IIfExpressionNode): string; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; function VisitTernaryExpression(const Node: ITernaryExpressionNode): string; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): string; override;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override; function VisitMacroDefinition(const Node: IMacroDefinitionNode): string; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): string; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; function VisitMacroExpansionNode(const Node: IMacroExpansionNode): string; override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; function VisitBlockExpression(const Node: IBlockExpressionNode): string; override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): string; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; function VisitAssignment(const Node: IAssignmentNode): string; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override; function VisitIndexer(const Node: IIndexerNode): string; override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override; function VisitMemberAccess(const Node: IMemberAccessNode): string; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override; function VisitQuasiquote(const Node: IQuasiquoteNode): string; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; function VisitUnquote(const Node: IUnquoteNode): string; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override; function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): string; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override; function VisitCreateSeries(const Node: ICreateSeriesNode): string; override;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override; function VisitAddSeriesItem(const Node: IAddSeriesItemNode): string; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): string; override;
function VisitRecurNode(const Node: IRecurNode): string; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): string; override;
end; end;
implementation implementation
uses
System.Classes, // For TStringBuilder
Myc.Data.Keyword;
{ TAstToTextVisitor } { TAstToTextVisitor }
function TAstToTextVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; function TAstToTextVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): string;
var var
seriesStr, valueStr, lookbackStr: string; seriesStr, valueStr, lookbackStr: string;
begin begin
seriesStr := Node.Series.Accept(Self).AsText; seriesStr := Accept(Node.Series);
valueStr := Node.Value.Accept(Self).AsText; valueStr := Accept(Node.Value);
lookbackStr := ''; lookbackStr := '';
if Assigned(Node.Lookback) then if Assigned(Node.Lookback) then
lookbackStr := ', ' + Node.Lookback.Accept(Self).AsText; lookbackStr := ', ' + Accept(Node.Lookback);
Result := Format('%s.add(%s%s)', [seriesStr, valueStr, lookbackStr]); Result := Format('%s.add(%s%s)', [seriesStr, valueStr, lookbackStr]);
end; end;
function TAstToTextVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue; function TAstToTextVisitor.VisitAssignment(const Node: IAssignmentNode): string;
begin begin
Result := Node.Identifier.Name + ' := ' + Node.Value.Accept(Self).AsText; Result := Node.Identifier.Name + ' := ' + Accept(Node.Value);
end; end;
function TAstToTextVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; function TAstToTextVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): string;
begin begin
var leftStr := Node.Left.Accept(Self).AsText; var leftStr := Accept(Node.Left);
var rightStr := Node.Right.Accept(Self).AsText; var rightStr := Accept(Node.Right);
Result := '(' + leftStr + ' ' + Node.Operator.ToString + ' ' + rightStr + ')'; Result := '(' + leftStr + ' ' + Node.Operator.ToString + ' ' + rightStr + ')';
end; end;
function TAstToTextVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; function TAstToTextVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): string;
begin begin
Result := '{...}'; Result := '{...}';
end; end;
function TAstToTextVisitor.VisitConstant(const Node: IConstantNode): TDataValue; function TAstToTextVisitor.VisitConstant(const Node: IConstantNode): string;
begin begin
Result := Node.Value.ToString; Result := Node.Value.ToString;
end; end;
function TAstToTextVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; function TAstToTextVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): string;
begin begin
Result := 'new series(' + Node.Definition + ')'; Result := 'new series(' + Node.Definition + ')';
end; end;
function TAstToTextVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; function TAstToTextVisitor.VisitFunctionCall(const Node: IFunctionCallNode): string;
var var
i: Integer; i: Integer;
sb: TStringBuilder; sb: TStringBuilder;
calleeStr: string; calleeStr: string;
begin begin
calleeStr := Node.Callee.Accept(Self).AsText; calleeStr := Accept(Node.Callee);
sb := TStringBuilder.Create; sb := TStringBuilder.Create;
try try
@@ -94,7 +101,7 @@ begin
sb.Append('('); sb.Append('(');
for i := 0 to High(Node.Arguments) do for i := 0 to High(Node.Arguments) do
begin begin
sb.Append(Node.Arguments[i].Accept(Self).AsText); sb.Append(Accept(Node.Arguments[i]));
if i < High(Node.Arguments) then if i < High(Node.Arguments) then
sb.Append(', '); sb.Append(', ');
end; end;
@@ -105,7 +112,7 @@ begin
end; end;
end; end;
function TAstToTextVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; function TAstToTextVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): string;
begin begin
// To represent a macro expansion as a single line of text, the most // To represent a macro expansion as a single line of text, the most
// informative representation is the original call itself. We can simply // informative representation is the original call itself. We can simply
@@ -113,7 +120,7 @@ begin
Result := VisitFunctionCall(Node); Result := VisitFunctionCall(Node);
end; end;
function TAstToTextVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue; function TAstToTextVisitor.VisitRecurNode(const Node: IRecurNode): string;
var var
i: Integer; i: Integer;
sb: TStringBuilder; sb: TStringBuilder;
@@ -123,7 +130,7 @@ begin
sb.Append('recur('); sb.Append('recur(');
for i := 0 to High(Node.Arguments) do for i := 0 to High(Node.Arguments) do
begin begin
sb.Append(Node.Arguments[i].Accept(Self).AsText); sb.Append(Accept(Node.Arguments[i]));
if i < High(Node.Arguments) then if i < High(Node.Arguments) then
sb.Append(', '); sb.Append(', ');
end; end;
@@ -134,33 +141,38 @@ begin
end; end;
end; end;
function TAstToTextVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue; function TAstToTextVisitor.VisitIdentifier(const Node: IIdentifierNode): string;
begin begin
Result := Node.Name; Result := Node.Name;
end; end;
function TAstToTextVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; function TAstToTextVisitor.VisitIfExpression(const Node: IIfExpressionNode): string;
begin begin
Result := Node.Condition.Accept(Self).AsText; Result := Accept(Node.Condition);
end; end;
function TAstToTextVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue; function TAstToTextVisitor.VisitIndexer(const Node: IIndexerNode): string;
var var
baseStr, indexStr: string; baseStr, indexStr: string;
begin begin
baseStr := Node.Base.Accept(Self).AsText; baseStr := Accept(Node.Base);
indexStr := Node.Index.Accept(Self).AsText; indexStr := Accept(Node.Index);
Result := Format('%s[%s]', [baseStr, indexStr]); Result := Format('%s[%s]', [baseStr, indexStr]);
end; end;
function TAstToTextVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; function TAstToTextVisitor.VisitKeyword(const Node: IKeywordNode): string;
begin
Result := ':' + Node.Value.Name;
end;
function TAstToTextVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): string;
var var
i: Integer; i: Integer;
sb: TStringBuilder; sb: TStringBuilder;
begin begin
sb := TStringBuilder.Create; sb := TStringBuilder.Create;
try try
sb.Append(#$03BB + '('); sb.Append(WideChar($03BB) + '('); // Lambda char
if Length(Node.Parameters) > 0 then if Length(Node.Parameters) > 0 then
begin begin
for i := 0 to High(Node.Parameters) do for i := 0 to High(Node.Parameters) do
@@ -177,7 +189,7 @@ begin
end; end;
end; end;
function TAstToTextVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; function TAstToTextVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): string;
var var
i: Integer; i: Integer;
sb: TStringBuilder; sb: TStringBuilder;
@@ -204,56 +216,61 @@ begin
end; end;
end; end;
function TAstToTextVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; function TAstToTextVisitor.VisitMemberAccess(const Node: IMemberAccessNode): string;
var var
baseStr: string; baseStr: string;
begin begin
baseStr := Node.Base.Accept(Self).AsText; baseStr := Accept(Node.Base);
Result := Format('%s.%s', [baseStr, Node.Member.Value.Name]); Result := Format('%s.%s', [baseStr, Node.Member.Value.Name]);
end; end;
function TAstToTextVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; function TAstToTextVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): string;
begin begin
Result := '`' + Node.Expression.Accept(Self).AsText; Result := '`' + Accept(Node.Expression);
end; end;
function TAstToTextVisitor.VisitUnquote(const Node: IUnquoteNode): TDataValue; function TAstToTextVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): string;
begin begin
Result := '~' + Node.Expression.Accept(Self).AsText; Result := '{...}';
end; end;
function TAstToTextVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; function TAstToTextVisitor.VisitUnquote(const Node: IUnquoteNode): string;
begin begin
Result := '~@' + Node.Expression.Accept(Self).AsText; Result := '~' + Accept(Node.Expression);
end; end;
function TAstToTextVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; function TAstToTextVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): string;
begin
Result := '~@' + Accept(Node.Expression);
end;
function TAstToTextVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): string;
var var
seriesStr: string; seriesStr: string;
begin begin
seriesStr := Node.Series.Accept(Self).AsText; seriesStr := Accept(Node.Series);
Result := Format('length(%s)', [seriesStr]); Result := Format('length(%s)', [seriesStr]);
end; end;
function TAstToTextVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; function TAstToTextVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): string;
begin begin
var condStr := Node.Condition.Accept(Self).AsText; var condStr := Accept(Node.Condition);
var thenStr := Node.ThenBranch.Accept(Self).AsText; var thenStr := Accept(Node.ThenBranch);
var elseStr := Node.ElseBranch.Accept(Self).AsText; var elseStr := Accept(Node.ElseBranch);
Result := Format('(%s ? %s : %s)', [condStr, thenStr, elseStr]); Result := Format('(%s ? %s : %s)', [condStr, thenStr, elseStr]);
end; end;
function TAstToTextVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; function TAstToTextVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): string;
begin begin
Result := Node.Operator.ToString + ' ' + Node.Right.Accept(Self).AsText; Result := Node.Operator.ToString + ' ' + Accept(Node.Right);
end; end;
function TAstToTextVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; function TAstToTextVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): string;
var var
initStr: string; initStr: string;
begin begin
if Assigned(Node.Initializer) then if Assigned(Node.Initializer) then
initStr := ' := ' + Node.Initializer.Accept(Self).AsText initStr := ' := ' + Accept(Node.Initializer)
else else
initStr := ''; initStr := '';
Result := 'var ' + Node.Identifier.Name + initStr; Result := 'var ' + Node.Identifier.Name + initStr;
+54 -34
View File
@@ -9,7 +9,8 @@ uses
Myc.Ast.Nodes, Myc.Ast.Nodes,
Myc.Ast.Visitor, Myc.Ast.Visitor,
Myc.Ast.Scope, Myc.Ast.Scope,
Myc.Data.Value; Myc.Data.Value,
Myc.Ast;
type type
// This visitor analyzes the AST to find all variables that need to be "lifted" or "boxed" // This visitor analyzes the AST to find all variables that need to be "lifted" or "boxed"
@@ -17,23 +18,28 @@ type
TUpvalueAnalyzer = class(TAstTransformer) TUpvalueAnalyzer = class(TAstTransformer)
private private
FBoxedDeclarations: THashSet<IVariableDeclarationNode>; FBoxedDeclarations: THashSet<IVariableDeclarationNode>;
FCurrentScope: IScopeDescriptor; FCurrentDescriptor: IScopeDescriptor;
FDeclarationMap: TDictionary<IScopeDescriptor, TDictionary<string, IVariableDeclarationNode>>; FDeclarationMap: TDictionary<IScopeDescriptor, TDictionary<string, IVariableDeclarationNode>>;
procedure MarkDeclarationForBoxing(const AName: string); procedure MarkDeclarationForBoxing(const AName: string);
protected protected
// Overridden Visit methods to perform analysis during traversal. // Overridden Visit methods to perform analysis during traversal.
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
public public
constructor Create(const AParent: IScopeDescriptor); constructor Create(const AParent: IScopeDescriptor);
destructor Destroy; override; destructor Destroy; override;
// Added Execute method
function Execute(const ARootNode: IAstNode): IAstNode;
class function Analyze(const ARootNode: IAstNode; const AParent: IScopeDescriptor): THashSet<IVariableDeclarationNode>; static; class function Analyze(const ARootNode: IAstNode; const AParent: IScopeDescriptor): THashSet<IVariableDeclarationNode>; static;
end; end;
implementation implementation
uses uses
System.Generics.Defaults,
Myc.Ast.Types; Myc.Ast.Types;
{ TUpvalueAnalyzer } { TUpvalueAnalyzer }
@@ -42,22 +48,28 @@ constructor TUpvalueAnalyzer.Create(const AParent: IScopeDescriptor);
begin begin
inherited Create; inherited Create;
FBoxedDeclarations := THashSet<IVariableDeclarationNode>.Create; FBoxedDeclarations := THashSet<IVariableDeclarationNode>.Create;
FDeclarationMap := TDictionary<IScopeDescriptor, TDictionary<string, IVariableDeclarationNode>>.Create; FDeclarationMap :=
FCurrentScope := TScope.CreateDescriptor(AParent); TObjectDictionary<IScopeDescriptor, TDictionary<string, IVariableDeclarationNode>>
.Create([doOwnsValues], TEqualityComparer<IScopeDescriptor>.Default);
FCurrentDescriptor := TScope.CreateDescriptor(AParent);
end; end;
destructor TUpvalueAnalyzer.Destroy; destructor TUpvalueAnalyzer.Destroy;
begin begin
for var dict in FDeclarationMap.Values do
dict.Free;
FDeclarationMap.Free; FDeclarationMap.Free;
FBoxedDeclarations.Free; FBoxedDeclarations.Free;
inherited Destroy; inherited Destroy;
end; end;
function TUpvalueAnalyzer.Execute(const ARootNode: IAstNode): IAstNode;
begin
// Accept will call the Visit... methods and traverse the tree
Result := Accept(ARootNode);
end;
class function TUpvalueAnalyzer.Analyze(const ARootNode: IAstNode; const AParent: IScopeDescriptor): THashSet<IVariableDeclarationNode>; class function TUpvalueAnalyzer.Analyze(const ARootNode: IAstNode; const AParent: IScopeDescriptor): THashSet<IVariableDeclarationNode>;
var var
analyzer: TUpvalueAnalyzer; analyzer: TUpvalueAnalyzer; // Changed to concrete type
begin begin
if not Assigned(ARootNode) then if not Assigned(ARootNode) then
exit(THashSet<IVariableDeclarationNode>.Create); exit(THashSet<IVariableDeclarationNode>.Create);
@@ -78,12 +90,12 @@ var
declarationScope: IScopeDescriptor; declarationScope: IScopeDescriptor;
i: Integer; i: Integer;
begin begin
symbol := FCurrentScope.FindSymbol(AName); symbol := FCurrentDescriptor.FindSymbol(AName);
if symbol.Address.Kind <> akLocalOrParent then if symbol.Address.Kind <> akLocalOrParent then
exit; exit;
// Walk up the scope chain to find the scope where the variable was declared. // Walk up the scope chain to find the scope where the variable was declared.
declarationScope := FCurrentScope; declarationScope := FCurrentDescriptor;
for i := 1 to symbol.Address.ScopeDepth do for i := 1 to symbol.Address.ScopeDepth do
begin begin
if not Assigned(declarationScope.Parent) then if not Assigned(declarationScope.Parent) then
@@ -101,13 +113,13 @@ begin
end; end;
end; end;
function TUpvalueAnalyzer.VisitIdentifier(const Node: IIdentifierNode): TDataValue; function TUpvalueAnalyzer.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
var var
symbol: TResolvedSymbol; symbol: TResolvedSymbol;
begin begin
if Assigned(FCurrentScope) then if Assigned(FCurrentDescriptor) then
begin begin
symbol := FCurrentScope.FindSymbol(Node.Name); symbol := FCurrentDescriptor.FindSymbol(Node.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
@@ -116,54 +128,62 @@ begin
end; end;
end; end;
// As a traverser, return the original node wrapped in a TDataValue. // This is a leaf node, do not call inherited.
Result := TDataValue.FromIntf<IIdentifierNode>(Node); Result := Node;
end; end;
function TUpvalueAnalyzer.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; function TUpvalueAnalyzer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var
N: TLambdaExpressionNode;
begin begin
N := (Node as TLambdaExpressionNode);
// A lambda creates a new lexical scope, inheriting from the current one. // A lambda creates a new lexical scope, inheriting from the current one.
FCurrentScope := TScope.CreateDescriptor(FCurrentScope); FCurrentDescriptor := TScope.CreateDescriptor(FCurrentDescriptor);
try try
// Define the lambda's parameters within its new scope. // Define the lambda's parameters within its new scope.
// We use TTypes.Unknown as type inference hasn't run yet. // We use TTypes.Unknown as type inference hasn't run yet.
for var param in Node.Parameters do for var param in N.Parameters do
FCurrentScope.Define(param.Name, TTypes.Unknown); FCurrentDescriptor.Define(Accept(param).AsIdentifier.Name, TTypes.Unknown);
// Traverse the lambda body within the new scope context. // Traverse the lambda body within the new scope context.
Node.Body.Accept(Self); N.Body := Accept(N.Body); // Manual traversal
// We do not transform, just analyze. Return the original node wrapped. Result := N;
Result := TDataValue.FromIntf<ILambdaExpressionNode>(Node);
finally finally
// Restore the parent scope after leaving the lambda. // Restore the parent scope after leaving the lambda.
FCurrentScope := FCurrentScope.Parent; FCurrentDescriptor := FCurrentDescriptor.Parent;
end; end;
end; end;
function TUpvalueAnalyzer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; function TUpvalueAnalyzer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
var var
scopeDeclarations: TDictionary<string, IVariableDeclarationNode>; scopeDeclarations: TDictionary<string, IVariableDeclarationNode>;
N: TVariableDeclarationNode;
begin begin
N := (Node as TVariableDeclarationNode);
// Traverse the initializer first. It's evaluated in the current scope // Traverse the initializer first. It's evaluated in the current scope
// before the new variable is defined. // before the new variable is defined.
if Assigned(Node.Initializer) then if Assigned(N.Initializer) then
Node.Initializer.Accept(Self); N.Initializer := Accept(N.Initializer);
// Traverse the identifier
Accept(N.Identifier);
// After processing the initializer, define the variable in the current scope. // After processing the initializer, define the variable in the current scope.
// We use TTypes.Unknown as type inference hasn't run yet. // We use TTypes.Unknown as type inference hasn't run yet.
FCurrentScope.Define(Node.Identifier.Name, TTypes.Unknown); FCurrentDescriptor.Define(N.Identifier.Name, TTypes.Unknown);
// Map this declaration node to its scope and name for later lookup. // Map this declaration node to its scope and name for later lookup.
if not FDeclarationMap.TryGetValue(FCurrentScope, scopeDeclarations) then if not FDeclarationMap.TryGetValue(FCurrentDescriptor, scopeDeclarations) then
begin begin
scopeDeclarations := TDictionary<string, IVariableDeclarationNode>.Create; scopeDeclarations := TDictionary<string, IVariableDeclarationNode>.Create;
FDeclarationMap.Add(FCurrentScope, scopeDeclarations); FDeclarationMap.Add(FCurrentDescriptor, scopeDeclarations);
end; end;
scopeDeclarations.Add(Node.Identifier.Name, Node); scopeDeclarations.Add(N.Identifier.Name, N);
// As a traverser, return the original node wrapped in a TDataValue. Result := N;
Result := TDataValue.FromIntf<IVariableDeclarationNode>(Node);
end; end;
end. end.
-144
View File
@@ -1,144 +0,0 @@
unit Myc.Ast.Binding.Nodes;
interface
uses
System.SysUtils,
System.Classes,
System.Generics.Collections,
Myc.Data.Scalar,
Myc.Data.Value,
Myc.Ast.Nodes,
Myc.Ast.Visitor,
Myc.Ast.Scope,
Myc.Ast.Types,
Myc.Ast;
type
TBoundIdentifierNode = class(TIdentifierNode)
private
FAddress: TResolvedAddress;
public
constructor Create(const AUnboundNode: IIdentifierNode; const AAddress: TResolvedAddress);
property Address: TResolvedAddress read FAddress;
end;
TBoundVariableDeclarationNode = class(TVariableDeclarationNode)
private
FIsBoxed: Boolean;
public
constructor Create(const AIdentifier: IIdentifierNode; AInitializer: IAstNode; AIsBoxed: Boolean);
property IsBoxed: Boolean read FIsBoxed;
end;
TBoundLambdaExpressionNode = class(TLambdaExpressionNode)
private
FScopeDescriptor: IScopeDescriptor;
FUpvalues: TArray<TResolvedAddress>;
FHasNestedLambdas: Boolean;
public
constructor Create(
const AUnboundNode: ILambdaExpressionNode;
const ABody: IAstNode;
const AParameters: TArray<IIdentifierNode>;
const AScopeDescriptor: IScopeDescriptor;
const AUpvalues: TArray<TResolvedAddress>;
AHasNestedLambdas: Boolean
);
property ScopeDescriptor: IScopeDescriptor read FScopeDescriptor;
property Upvalues: TArray<TResolvedAddress> read FUpvalues;
property HasNestedLambdas: Boolean read FHasNestedLambdas;
end;
TBoundFunctionCallNode = class(TFunctionCallNode)
private
FIsTailCall: Boolean;
public
constructor Create(
const AUnboundNode: IFunctionCallNode;
const ACallee: IAstNode;
const AArguments: TArray<IAstNode>;
AIsTailCall: Boolean
);
property IsTailCall: Boolean read FIsTailCall;
end;
TBoundRecordLiteralNode = class(TRecordLiteralNode)
private
FDefinition: IScalarRecordDefinition;
public
constructor Create(const AFields: TArray<TRecordFieldLiteral>; const ADef: IScalarRecordDefinition);
property Definition: IScalarRecordDefinition read FDefinition write FDefinition;
end;
// Represents a bound record literal that maps to a generic (non-scalar) record
TBoundGenericRecordLiteralNode = class(TRecordLiteralNode)
private
FDefinition: IGenericRecordDefinition;
public
constructor Create(const AFields: TArray<TRecordFieldLiteral>; const ADef: IGenericRecordDefinition);
property Definition: IGenericRecordDefinition read FDefinition write FDefinition;
end;
implementation
uses
Myc.Data.Keyword;
{ TBoundIdentifierNode }
constructor TBoundIdentifierNode.Create(const AUnboundNode: IIdentifierNode; const AAddress: TResolvedAddress);
begin
inherited Create(AUnboundNode.Name);
FAddress := AAddress;
end;
{ TBoundVariableDeclarationNode }
constructor TBoundVariableDeclarationNode.Create(const AIdentifier: IIdentifierNode; AInitializer: IAstNode; AIsBoxed: Boolean);
begin
inherited Create(AIdentifier, AInitializer);
FIsBoxed := AIsBoxed;
end;
{ TBoundLambdaExpressionNode }
constructor TBoundLambdaExpressionNode.Create(
const AUnboundNode: ILambdaExpressionNode;
const ABody: IAstNode;
const AParameters: TArray<IIdentifierNode>;
const AScopeDescriptor: IScopeDescriptor;
const AUpvalues: TArray<TResolvedAddress>;
AHasNestedLambdas: Boolean
);
begin
inherited Create(AParameters, ABody);
FScopeDescriptor := AScopeDescriptor;
FUpvalues := AUpvalues;
FHasNestedLambdas := AHasNestedLambdas;
end;
{ TBoundFunctionCallNode }
constructor TBoundFunctionCallNode.Create(
const AUnboundNode: IFunctionCallNode;
const ACallee: IAstNode;
const AArguments: TArray<IAstNode>;
AIsTailCall: Boolean
);
begin
inherited Create(ACallee, AArguments);
FIsTailCall := AIsTailCall;
end;
{ TBoundGenericRecordLiteralNode }
constructor TBoundGenericRecordLiteralNode.Create(const AFields: TArray<TRecordFieldLiteral>; const ADef: IGenericRecordDefinition);
begin
inherited Create(AFields);
FDefinition := ADef;
end;
{ TBoundRecordLiteralNode }
constructor TBoundRecordLiteralNode.Create(const AFields: TArray<TRecordFieldLiteral>; const ADef: IScalarRecordDefinition);
begin
inherited Create(AFields);
FDefinition := ADef;
end;
end.
+137 -295
View File
@@ -36,30 +36,24 @@ type
procedure EnterScope; procedure EnterScope;
procedure ExitScope; procedure ExitScope;
function IsValidIdentifier(const Name: string): Boolean; function IsValidIdentifier(const Name: string): Boolean;
function SetType(const NodeData: TDataValue; const AType: IStaticType): TDataValue; overload;
protected protected
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; // --- Core Binding Logic (Mutators) ---
function VisitKeyword(const Node: IKeywordNode): TDataValue; override; function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; // --- Transformation Logic ---
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override; // --- Standard Traversal (Use inherited) ---
function VisitRecurNode(const Node: IRecurNode): TDataValue; override; function VisitKeyword(const Node: IKeywordNode): IAstNode; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; function VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; function VisitRecurNode(const Node: IRecurNode): IAstNode; override;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; function VisitConstant(const Node: IConstantNode): IAstNode; override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; function VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode; override;
function VisitConstant(const Node: IConstantNode): TDataValue; override; function VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; function VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode; override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override;
public public
constructor Create(const AInitialScope: IExecutionScope); constructor Create(const AInitialScope: IExecutionScope);
@@ -78,8 +72,7 @@ implementation
uses uses
System.Generics.Defaults, System.Generics.Defaults,
System.Character, System.Character,
Myc.Data.Keyword, Myc.Data.Keyword;
Myc.Ast.Binding.Nodes;
type type
TResolvedAddressComparer = class(TEqualityComparer<TResolvedAddress>) TResolvedAddressComparer = class(TEqualityComparer<TResolvedAddress>)
@@ -111,7 +104,7 @@ begin
FInitialScope := AInitialScope; FInitialScope := AInitialScope;
FCurrentDescriptor := AInitialScope.CreateDescriptor; FCurrentDescriptor := AInitialScope.CreateDescriptor;
FUpvalueStack := TObjectStack<TUpvalueMapping>.Create(True); FUpvalueStack := TObjectStack<TUpvalueMapping>.Create(True); // Use Comparer
FNestedLambdaCount := 0; FNestedLambdaCount := 0;
FBoxedDeclarations := nil; FBoxedDeclarations := nil;
end; end;
@@ -123,13 +116,6 @@ begin
inherited; inherited;
end; end;
function TAstBinder.SetType(const NodeData: TDataValue; const AType: IStaticType): TDataValue;
begin
if (not NodeData.IsVoid) and (NodeData.Kind = vkInterface) then
(NodeData.AsIntf<IAstNode> as TAstNode).StaticType := AType;
Result := NodeData;
end;
class function TAstBinder.Bind(const InitialScope: IExecutionScope; const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode; class function TAstBinder.Bind(const InitialScope: IExecutionScope; const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
begin begin
var binder := TAstBinder.Create(InitialScope) as IAstBinder; var binder := TAstBinder.Create(InitialScope) as IAstBinder;
@@ -165,40 +151,42 @@ end;
function TAstBinder.Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode; function TAstBinder.Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
begin begin
// Pre-pass: Find all variables that need boxing
FBoxedDeclarations := TUpvalueAnalyzer.Analyze(RootNode, FCurrentDescriptor.Parent); FBoxedDeclarations := TUpvalueAnalyzer.Analyze(RootNode, FCurrentDescriptor.Parent);
try try
EnterScope; EnterScope;
try try
var transformedValue := Accept(RootNode); // Main pass: Run the mutator
if transformedValue.IsVoid then Result := Accept(RootNode); // Accept returns IAstNode
Result := TAst.Block([]) if not Assigned(Result) then
else Result := TAst.Block([]);
Result := transformedValue.AsIntf<IAstNode>;
// Set the type of the root expression (e.g., the final 'do' block)
(Result as TAstNode).StaticType := TTypes.Unknown; (Result as TAstNode).StaticType := TTypes.Unknown;
Descriptor := FCurrentDescriptor; Descriptor := FCurrentDescriptor;
finally finally
ExitScope; ExitScope;
end; end;
finally finally
FBoxedDeclarations.Free; // Free the set
FBoxedDeclarations := nil;
end; end;
end; end;
function TAstBinder.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; function TAstBinder.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode;
begin begin
// Macros are compile-time only. The Binder (Phase 2) should not see them.
raise Exception.Create('TMyAstBinder: MacroDefinition node encountered.'); raise Exception.Create('TMyAstBinder: MacroDefinition node encountered.');
end; end;
function TAstBinder.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; function TAstBinder.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode;
begin begin
// The MacroExpander (Phase 1) should have unwrapped this.
// We only visit the *expanded* body.
Result := Accept(Node.ExpandedBody); Result := Accept(Node.ExpandedBody);
end; end;
function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
var
boundCall: TBoundFunctionCallNode;
callee: IAstNode;
args: TArray<IAstNode>;
begin begin
// --- Transformation: Keyword-as-Function --- // --- Transformation: Keyword-as-Function ---
if (Node.Callee is TKeywordNode) then if (Node.Callee is TKeywordNode) then
@@ -209,105 +197,44 @@ begin
'Keyword :%s expects exactly one argument (the record/map), but got %d', 'Keyword :%s expects exactly one argument (the record/map), but got %d',
[keywordNode.Value.Name, Length(Node.Arguments)]); [keywordNode.Value.Name, Length(Node.Arguments)]);
var baseNode := Accept(Node.Arguments[0]).AsIntf<IAstNode>; // Manually visit the argument, as we are replacing this node
var baseNode := Accept(Node.Arguments[0]);
var memberAccessNode := TAst.MemberAccess(baseNode, keywordNode); var memberAccessNode := TAst.MemberAccess(baseNode, keywordNode);
// Visit the *new* node to bind it
Result := Accept(memberAccessNode); Result := Accept(memberAccessNode);
exit; exit;
end; end;
// --- Default: Bind as a standard function call --- // --- Default: Bind as a standard function call ---
callee := Accept(Node.Callee).AsIntf<IAstNode>; // Use the inherited implementation to visit children (Callee, Arguments)
args := AcceptNodes<IAstNode>(Node.Arguments); // and mutate their properties in place.
Result := inherited VisitFunctionCall(Node);
// Create the node, always marking IsTailCall as false. // Set metadata for *this* node
// The Lowerer (Phase 4) will set this flag correctly. (Node as TFunctionCallNode).IsTailCall := False; // Default, TCO (Phase 5) will set this
boundCall := TBoundFunctionCallNode.Create(Node, callee, args, False);
Result := SetType(TDataValue.FromIntf<IFunctionCallNode>(boundCall), TTypes.Unknown);
end; end;
function TAstBinder.VisitAssignment(const Node: IAssignmentNode): TDataValue; function TAstBinder.VisitConstant(const Node: IConstantNode): IAstNode;
var
boundIdentifier, boundValue: IAstNode;
boundNode: IAssignmentNode;
begin
boundIdentifier := Accept(Node.Identifier).AsIntf<IAstNode>;
boundValue := Accept(Node.Value).AsIntf<IAstNode>;
boundNode := TAst.Assign(boundIdentifier as TBoundIdentifierNode, boundValue);
Result := SetType(TDataValue.FromIntf<IAssignmentNode>(boundNode), TTypes.Unknown);
end;
function TAstBinder.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
var
left, right: IAstNode;
boundNode: IBinaryExpressionNode;
begin
left := Accept(Node.Left).AsIntf<IAstNode>;
right := Accept(Node.Right).AsIntf<IAstNode>;
boundNode := TAst.BinaryExpr(left, Node.Operator, right);
Result := SetType(TDataValue.FromIntf<IBinaryExpressionNode>(boundNode), TTypes.Unknown);
end;
function TAstBinder.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
var
exprs: TArray<IAstNode>;
i: Integer;
transformedValue: TDataValue;
exprList: TList<IAstNode>;
boundNode: IBlockExpressionNode;
begin
exprList := TList<IAstNode>.Create;
try
for i := 0 to High(Node.Expressions) do
begin
transformedValue := Accept(Node.Expressions[i]);
if not transformedValue.IsVoid then
exprList.Add(transformedValue.AsIntf<IAstNode>);
end;
exprs := exprList.ToArray;
finally
exprList.Free;
end;
if (Length(exprs) = Length(Node.Expressions)) then
begin
var same := True;
for i := 0 to High(exprs) do
if exprs[i] <> Node.Expressions[i] then
begin
same := False;
break;
end;
if same then
boundNode := Node
else
boundNode := TAst.Block(exprs);
end
else
boundNode := TAst.Block(exprs);
Result := SetType(TDataValue.FromIntf<IBlockExpressionNode>(boundNode), TTypes.Unknown);
end;
function TAstBinder.VisitConstant(const Node: IConstantNode): TDataValue;
begin begin
// Set type (Phase 3)
case Node.Value.Kind of case Node.Value.Kind of
TDataValueKind.vkScalar: TDataValueKind.vkScalar: (Node as TAstNode).StaticType := TTypes.FromScalarKind(Node.Value.AsScalar.Kind);
Result := SetType(TDataValue.FromIntf<IConstantNode>(Node), TTypes.FromScalarKind(Node.Value.AsScalar.Kind)); TDataValueKind.vkText: (Node as TAstNode).StaticType := TTypes.Text;
TDataValueKind.vkText: Result := SetType(TDataValue.FromIntf<IConstantNode>(Node), TTypes.Text); TDataValueKind.vkVoid: (Node as TAstNode).StaticType := TTypes.Void;
TDataValueKind.vkVoid: Result := SetType(TDataValue.FromIntf<IConstantNode>(Node), TTypes.Void);
else else
Result := SetType(TDataValue.FromIntf<IConstantNode>(Node), TTypes.Unknown); (Node as TAstNode).StaticType := TTypes.Unknown;
end; end;
Result := Node;
end; end;
function TAstBinder.VisitKeyword(const Node: IKeywordNode): TDataValue; function TAstBinder.VisitKeyword(const Node: IKeywordNode): IAstNode;
begin begin
Result := SetType(TDataValue.FromIntf<IKeywordNode>(Node), TTypes.Keyword); (Node as TAstNode).StaticType := TTypes.Keyword;
Result := Node;
end; end;
function TAstBinder.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; function TAstBinder.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode;
var var
elemType: IStaticType; elemType: IStaticType;
begin begin
@@ -317,197 +244,100 @@ begin
on E: Exception do on E: Exception do
elemType := TTypes.Unknown; elemType := TTypes.Unknown;
end; end;
Result := SetType(TDataValue.FromIntf<ICreateSeriesNode>(Node), TTypes.CreateSeries(elemType)); (Node as TAstNode).StaticType := TTypes.CreateSeries(elemType);
Result := Node;
end; end;
function TAstBinder.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; function TAstBinder.VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode;
var
seriesNode, valueNode, lookbackNode: IAstNode;
begin begin
seriesNode := Accept(Node.Series).AsIntf<IAstNode>; // Visit children
valueNode := Accept(Node.Value).AsIntf<IAstNode>; Result := inherited VisitAddSeriesItem(Node);
if Node.Lookback <> nil then (Node as TAstNode).StaticType := TTypes.Void;
lookbackNode := Accept(Node.Lookback).AsIntf<IAstNode>
else
lookbackNode := nil;
var boundNode := TAst.AddSeriesItem(seriesNode as TIdentifierNode, valueNode, lookbackNode);
Result := SetType(TDataValue.FromIntf<IAddSeriesItemNode>(boundNode), TTypes.Void);
end; end;
function TAstBinder.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; function TAstBinder.VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode;
begin begin
Accept(Node.Series); // Visit children
Result := SetType(TDataValue.FromIntf<ISeriesLengthNode>(Node), TTypes.Ordinal); Result := inherited VisitSeriesLength(Node);
(Node as TAstNode).StaticType := TTypes.Ordinal;
end; end;
function TAstBinder.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; function TAstBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var
condition, thenBranch, elseBranch: IAstNode;
boundNode: IIfExpressionNode;
begin
condition := Accept(Node.Condition).AsIntf<IAstNode>;
thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>;
if Assigned(Node.ElseBranch) then
elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>
else
elseBranch := nil;
if (condition <> Node.Condition) or (thenBranch <> Node.ThenBranch) or (elseBranch <> Node.ElseBranch) then
boundNode := TAst.IfExpr(condition, thenBranch, elseBranch)
else
boundNode := Node;
Result := SetType(TDataValue.FromIntf<IIfExpressionNode>(boundNode), TTypes.Unknown);
end;
function TAstBinder.VisitIndexer(const Node: IIndexerNode): TDataValue;
var
baseNode, indexNode: IAstNode;
boundNode: IIndexerNode;
begin
baseNode := Accept(Node.Base).AsIntf<IAstNode>;
indexNode := Accept(Node.Index).AsIntf<IAstNode>;
boundNode := TAst.Indexer(baseNode, indexNode);
Result := SetType(TDataValue.FromIntf<IIndexerNode>(boundNode), TTypes.Unknown);
end;
function TAstBinder.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
var
baseNode: IAstNode;
boundNode: IMemberAccessNode;
begin
baseNode := Accept(Node.Base).AsIntf<IAstNode>;
boundNode := TAst.MemberAccess(baseNode, Node.Member);
Result := SetType(TDataValue.FromIntf<IMemberAccessNode>(boundNode), TTypes.Unknown);
end;
function TAstBinder.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
var
i: Integer;
boundFields: TArray<TRecordFieldLiteral>;
valNode: IAstNode;
valType: IStaticType;
allScalar: Boolean;
begin
SetLength(boundFields, Length(Node.Fields));
allScalar := True;
for i := 0 to High(Node.Fields) do
begin
valNode := Accept(Node.Fields[i].Value).AsIntf<IAstNode>;
valType := (valNode as TAstNode).StaticType;
if not (valType.Kind in [stOrdinal, stFloat, stKeyword, stUnknown]) then
allScalar := False;
boundFields[i] := TRecordFieldLiteral.Create(Node.Fields[i].Key, valNode);
end;
if allScalar then
begin
var boundNode := TBoundRecordLiteralNode.Create(boundFields, nil);
Result := SetType(TDataValue.FromIntf<IRecordLiteralNode>(boundNode), TTypes.Unknown);
end
else
begin
var genBoundNode := TBoundGenericRecordLiteralNode.Create(boundFields, nil);
Result := SetType(TDataValue.FromIntf<IRecordLiteralNode>(genBoundNode), TTypes.Unknown);
end;
end;
function TAstBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
var var
i: integer; i: integer;
boundParams: TArray<IIdentifierNode>; N: TLambdaExpressionNode;
boundBody: IAstNode; adr: TResolvedAddress;
lambdaScope: IScopeDescriptor;
upvalues: TArray<TResolvedAddress>;
hasNestedLambdas: Boolean;
lastNestedLambdaCount: Integer;
boundLambda: ILambdaExpressionNode;
begin begin
N := (Node as TLambdaExpressionNode);
// We do *not* call inherited, as we must manage the scope manually.
FUpvalueStack.Push(TUpvalueMapping.Create(TResolvedAddressComparer.Create)); FUpvalueStack.Push(TUpvalueMapping.Create(TResolvedAddressComparer.Create));
try try
EnterScope; EnterScope;
try try
// Define <self> (slot 0)
FCurrentDescriptor.Define('<self>', TTypes.Unknown); FCurrentDescriptor.Define('<self>', TTypes.Unknown);
SetLength(boundParams, Length(Node.Parameters)); // Define parameters
for i := 0 to High(Node.Parameters) do for i := 0 to High(N.Parameters) do
begin begin
var paramNode := Node.Parameters[i]; var paramNode := N.Parameters[i];
var slotIndex := FCurrentDescriptor.Define(paramNode.Name, TTypes.Unknown); var slotIndex := FCurrentDescriptor.Define(paramNode.Name, TTypes.Unknown);
var address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex); adr := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
boundParams[i] := TBoundIdentifierNode.Create(paramNode, address);
(boundParams[i] as TAstNode).StaticType := TTypes.Unknown; // Mutate the parameter node with its address
(paramNode as TIdentifierNode).Address := adr;
(paramNode as TAstNode).StaticType := TTypes.Unknown;
end; end;
lastNestedLambdaCount := FNestedLambdaCount; // Visit the body *within the new scope*
boundBody := Accept(Node.Body).AsIntf<IAstNode>; var lastNestedLambdaCount := FNestedLambdaCount;
hasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount; N.Body := Accept(N.Body);
lambdaScope := FCurrentDescriptor; N.HasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount;
// Save the descriptor for the evaluator
N.ScopeDescriptor := FCurrentDescriptor;
finally finally
ExitScope; ExitScope;
end; end;
// --- Extract Upvalues ---
var upvalueMapping := FUpvalueStack.Peek; var upvalueMapping := FUpvalueStack.Peek;
var sortedPairs := upvalueMapping.ToArray; var sortedPairs := upvalueMapping.ToArray;
// Sort by index (Value)
TArray.Sort<TPair<TResolvedAddress, Integer>>( TArray.Sort<TPair<TResolvedAddress, Integer>>(
sortedPairs, sortedPairs,
TComparer<TPair<TResolvedAddress, Integer>>.Construct( TComparer<TPair<TResolvedAddress, Integer>>.Construct(
function(const Left, Right: TPair<TResolvedAddress, Integer>): Integer begin Result := Left.Value - Right.Value; end function(const Left, Right: TPair<TResolvedAddress, Integer>): Integer begin Result := Left.Value - Right.Value; end
) )
); );
SetLength(upvalues, Length(sortedPairs));
for i := 0 to High(sortedPairs) do var uvArr: TArray<TResolvedAddress>;
upvalues[i] := sortedPairs[i].Key; SetLength(uvArr, Length(sortedPairs));
for i := 0 to High(uvArr) do
uvArr[i] := sortedPairs[i].Key;
N.Upvalues := uvArr;
finally finally
FUpvalueStack.Pop; FUpvalueStack.Pop;
end; end;
inc(FNestedLambdaCount); inc(FNestedLambdaCount);
boundLambda := TBoundLambdaExpressionNode.Create(Node, boundBody, boundParams, lambdaScope, upvalues, hasNestedLambdas); // Type will be set by TypeChecker
Result := SetType(TDataValue.FromIntf<ILambdaExpressionNode>(boundLambda), TTypes.Unknown); Result := Node;
end; end;
function TAstBinder.VisitRecurNode(const Node: IRecurNode): TDataValue; function TAstBinder.VisitRecurNode(const Node: IRecurNode): IAstNode;
begin begin
var boundNode := TAst.Recur(AcceptNodes<IAstNode>(Node.Arguments)); // Visit children
Result := SetType(TDataValue.FromIntf<IRecurNode>(boundNode), TTypes.Void); Result := inherited VisitRecurNode(Node);
(Node as TAstNode).StaticType := TTypes.Void; // Recur never returns a value
end; end;
function TAstBinder.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; function TAstBinder.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
var
condition, thenBranch, elseBranch: IAstNode;
boundNode: ITernaryExpressionNode;
begin
condition := Accept(Node.Condition).AsIntf<IAstNode>;
thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>;
elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>;
if (condition <> Node.Condition) or (thenBranch <> Node.ThenBranch) or (elseBranch <> Node.ElseBranch) then
boundNode := TAst.TernaryExpr(condition, thenBranch, elseBranch)
else
boundNode := Node;
Result := SetType(TDataValue.FromIntf<ITernaryExpressionNode>(boundNode), TTypes.Unknown);
end;
function TAstBinder.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
var
right: IAstNode;
boundNode: IUnaryExpressionNode;
begin
right := Accept(Node.Right).AsIntf<IAstNode>;
boundNode := TAst.UnaryExpr(Node.Operator, right);
Result := SetType(TDataValue.FromIntf<IUnaryExpressionNode>(boundNode), TTypes.Unknown);
end;
function TAstBinder.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
var var
symbol: TResolvedSymbol; symbol: TResolvedSymbol;
boundNode: IIdentifierNode;
adr: TResolvedAddress; adr: TResolvedAddress;
begin begin
symbol := FCurrentDescriptor.FindSymbol(Node.Name); symbol := FCurrentDescriptor.FindSymbol(Node.Name);
@@ -517,52 +347,64 @@ begin
begin begin
if (adr.ScopeDepth > 0) and (FUpvalueStack.Count > 0) then if (adr.ScopeDepth > 0) and (FUpvalueStack.Count > 0) then
begin begin
// Handle Upvalue // --- Handle Upvalue ---
var upvalue := FUpvalueStack.Peek; var upvalueMap := FUpvalueStack.Peek;
// Adjust address to be relative to the captured scope
dec(adr.ScopeDepth); dec(adr.ScopeDepth);
var upvalueIndex: Integer; var upvalueIndex: Integer;
if not upvalue.TryGetValue(adr, upvalueIndex) then if not upvalueMap.TryGetValue(adr, upvalueIndex) then
begin begin
upvalueIndex := upvalue.Count; // This is a new upvalue for this lambda
upvalue.Add(adr, upvalueIndex); upvalueIndex := upvalueMap.Count;
upvalueMap.Add(adr, upvalueIndex);
end; end;
boundNode := TBoundIdentifierNode.Create(Node, TResolvedAddress.Create(akUpvalue, 0, upvalueIndex));
// Mutate the node to point to the Upvalue slot
(Node as TIdentifierNode).Address := TResolvedAddress.Create(akUpvalue, 0, upvalueIndex);
end end
else else
// Handle LocalOrParent begin
boundNode := TBoundIdentifierNode.Create(Node, adr); // --- Handle LocalOrParent ---
// Mutate the node to point to the Local/Parent slot
(Node as TIdentifierNode).Address := adr;
end;
Result := SetType(TDataValue.FromIntf<IIdentifierNode>(boundNode), symbol.StaticType); (Node as TAstNode).StaticType := symbol.StaticType; // Set type from scope
end end
else else
raise Exception.CreateFmt('Undefined identifier: "%s"', [Node.Name]); raise Exception.CreateFmt('Undefined identifier: "%s"', [Node.Name]);
Result := Node;
end; end;
function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
var var
initializer: IAstNode;
slotIndex: Integer; slotIndex: Integer;
address: TResolvedAddress; address: TResolvedAddress;
boundIdentifier: IIdentifierNode; N: TVariableDeclarationNode;
isBoxed: Boolean;
boundDecl: IVariableDeclarationNode;
begin begin
if not IsValidIdentifier(Node.Identifier.Name) then N := (Node as TVariableDeclarationNode);
raise Exception.CreateFmt('Invalid identifier name: "%s".', [Node.Identifier.Name]);
initializer := nil; if not IsValidIdentifier(N.Identifier.Name) then
if Node.Initializer <> nil then raise Exception.CreateFmt('Invalid identifier name: "%s".', [N.Identifier.Name]);
initializer := Accept(Node.Initializer).AsIntf<IAstNode>;
slotIndex := FCurrentDescriptor.Define(Node.Identifier.Name, TTypes.Unknown); // 1. Visit initializer *first*
if Assigned(N.Initializer) then
N.Initializer := Accept(N.Initializer);
// 2. Define variable in *current* scope
slotIndex := FCurrentDescriptor.Define(N.Identifier.Name, TTypes.Unknown);
address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex); address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
boundIdentifier := TBoundIdentifierNode.Create(Node.Identifier, address);
(boundIdentifier as TAstNode).StaticType := TTypes.Unknown;
isBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node); // 3. Mutate the Identifier node (which is NOT visited by inherited call)
boundDecl := TBoundVariableDeclarationNode.Create(boundIdentifier, initializer, isBoxed); (N.Identifier as TIdentifierNode).Address := address;
(N.Identifier as TAstNode).StaticType := TTypes.Unknown; // TypeChecker will set this
Result := SetType(TDataValue.FromIntf<IVariableDeclarationNode>(boundDecl), TTypes.Unknown); // 4. Mutate this declaration node
N.IsBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node);
Result := Node;
end; end;
end. end.
+96 -63
View File
@@ -11,8 +11,7 @@ uses
Myc.Ast.Visitor, Myc.Ast.Visitor,
Myc.Ast.Scope, Myc.Ast.Scope,
Myc.Ast.Types, Myc.Ast.Types,
Myc.Ast, Myc.Ast;
Myc.Ast.Binding.Nodes;
type type
IAstTCO = interface(IAstVisitor) IAstTCO = interface(IAstVisitor)
@@ -21,22 +20,27 @@ type
// This transformer runs *after* the Lowerer (Phase 4). // This transformer runs *after* the Lowerer (Phase 4).
// Its sole responsibility is to identify tail calls (TCO) // Its sole responsibility is to identify tail calls (TCO)
// and set the `IsTailCall` flag on TBoundFunctionCallNode. // and set the `IsTailCall` flag on TFunctionCallNode.
TAstTCO = class(TAstTransformer, IAstTCO) TAstTCO = class(TAstTransformer, IAstTCO)
private private
FIsTailStack: TStack<Boolean>; FIsTailStack: TStack<Boolean>;
FNextIsTail: Boolean; FNextIsTail: Boolean;
protected protected
function Accept(const Node: IAstNode): TDataValue; override; function Accept(const Node: IAstNode): IAstNode; override;
// Overrides for TCO propagation // Overrides for TCO propagation
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; function VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override; function VisitRecurNode(const Node: IRecurNode): IAstNode; override;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override; function VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; override;
// Operands are never in tail position.
function VisitBinaryExpression(const Node: IBinaryExpressionNode): IAstNode; override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): IAstNode; override;
// The core TCO logic // The core TCO logic
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
public public
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
@@ -70,140 +74,169 @@ end;
function TAstTCO.Execute(const RootNode: IAstNode): IAstNode; function TAstTCO.Execute(const RootNode: IAstNode): IAstNode;
begin begin
var transformedValue := Accept(RootNode); Result := Accept(RootNode); // Use IAstNode-returning Accept
if transformedValue.IsVoid then if not Assigned(Result) then
Result := TAst.Block([]) Result := TAst.Block([]);
else
Result := transformedValue.AsIntf<IAstNode>;
end; end;
function TAstTCO.Accept(const Node: IAstNode): TDataValue; function TAstTCO.Accept(const Node: IAstNode): IAstNode;
begin begin
if (not Assigned(Node)) or Done then if (not Assigned(Node)) then
begin
Result := nil;
exit; exit;
end;
FIsTailStack.Push(FNextIsTail); FIsTailStack.Push(FNextIsTail);
try try
// Call inherited Accept, which handles the data unwrapping
Result := inherited Accept(Node); Result := inherited Accept(Node);
finally finally
FNextIsTail := FIsTailStack.Pop; FNextIsTail := FIsTailStack.Pop;
end; end;
end; end;
function TAstTCO.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; function TAstTCO.VisitBinaryExpression(const Node: IBinaryExpressionNode): IAstNode;
begin
// Operands are never in tail position.
FNextIsTail := False;
// Call inherited, which will visit Left and Right with FNextIsTail = False
Result := inherited VisitBinaryExpression(Node);
end;
function TAstTCO.VisitUnaryExpression(const Node: IUnaryExpressionNode): IAstNode;
begin
// Operand is never in tail position.
FNextIsTail := False;
// Call inherited, which will visit Right with FNextIsTail = False
Result := inherited VisitUnaryExpression(Node);
end;
function TAstTCO.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
var var
i: Integer; i: Integer;
isContextTail: Boolean; isContextTail: Boolean;
N: TBlockExpressionNode;
begin begin
N := (Node as TBlockExpressionNode);
isContextTail := FIsTailStack.Peek; isContextTail := FIsTailStack.Peek;
for i := 0 to High(Node.Expressions) do
// We must manually iterate here to set FNextIsTail for each child
for i := 0 to High(N.Expressions) do
begin begin
// Only the last expression in a block is in tail position // Only the last expression in a block is in tail position
FNextIsTail := isContextTail and (i = High(Node.Expressions)); FNextIsTail := isContextTail and (i = High(N.Expressions));
Accept(Node.Expressions[i]); N.Expressions[i] := Accept(N.Expressions[i]);
end; end;
Result := TDataValue.FromIntf<IBlockExpressionNode>(Node); Result := N;
end; end;
function TAstTCO.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; function TAstTCO.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
var var
isContextTail: Boolean; isContextTail: Boolean;
N: TIfExpressionNode;
begin begin
N := (Node as TIfExpressionNode);
isContextTail := FIsTailStack.Peek; isContextTail := FIsTailStack.Peek;
// Condition is never in tail position // Condition is never in tail position
FNextIsTail := False; FNextIsTail := False;
Accept(Node.Condition); N.Condition := Accept(N.Condition);
// Then/Else branches ARE in tail position if the IfExpr is // Then/Else branches ARE in tail position if the IfExpr is
FNextIsTail := isContextTail; FNextIsTail := isContextTail;
Accept(Node.ThenBranch); N.ThenBranch := Accept(N.ThenBranch);
if Assigned(Node.ElseBranch) then N.ElseBranch := Accept(N.ElseBranch);
Accept(Node.ElseBranch);
Result := TDataValue.FromIntf<IIfExpressionNode>(Node); Result := N;
end; end;
function TAstTCO.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; function TAstTCO.VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode;
var var
isContextTail: Boolean; isContextTail: Boolean;
N: TTernaryExpressionNode;
begin begin
N := (Node as TTernaryExpressionNode);
isContextTail := FIsTailStack.Peek; isContextTail := FIsTailStack.Peek;
// Condition is never in tail position // Condition is never in tail position
FNextIsTail := False; FNextIsTail := False;
Accept(Node.Condition); N.Condition := Accept(N.Condition);
// Then/Else branches ARE in tail position if the TernaryExpr is // Then/Else branches ARE in tail position if the TernaryExpr is
FNextIsTail := isContextTail; FNextIsTail := isContextTail;
Accept(Node.ThenBranch); N.ThenBranch := Accept(N.ThenBranch);
Accept(Node.ElseBranch); N.ElseBranch := Accept(N.ElseBranch);
Result := TDataValue.FromIntf<ITernaryExpressionNode>(Node); Result := N;
end; end;
function TAstTCO.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; function TAstTCO.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var
N: TLambdaExpressionNode;
begin begin
// Parameters are never in tail position N := (Node as TLambdaExpressionNode);
FNextIsTail := False;
AcceptNodes<IIdentifierNode>(Node.Parameters);
// The body of a lambda is *always* a tail position (relative to the lambda) // The body of a lambda is *always* a tail position (relative to the lambda)
FNextIsTail := True; FNextIsTail := True;
Accept(Node.Body); N.Body := Accept(N.Body);
Result := TDataValue.FromIntf<ILambdaExpressionNode>(Node); Result := N;
end; end;
function TAstTCO.VisitRecurNode(const Node: IRecurNode): TDataValue; function TAstTCO.VisitRecurNode(const Node: IRecurNode): IAstNode;
var
N: TRecurNode;
begin begin
if not FIsTailStack.Peek then if not FIsTailStack.Peek then
raise Exception.Create('''recur'' can only be used in a tail position.'); raise Exception.Create('''recur'' can only be used in a tail position.');
N := (Node as TRecurNode);
// Arguments are not in tail position // Arguments are not in tail position
FNextIsTail := False; FNextIsTail := False;
AcceptNodes<IAstNode>(Node.Arguments); N.Arguments := AcceptNodes<IAstNode>(N.Arguments, function(Node: IAstNode): IAstNode begin exit(Node) end);
Result := TDataValue.FromIntf<IRecurNode>(Node); Result := N;
end; end;
function TAstTCO.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; function TAstTCO.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode;
var
N: TMacroExpansionNode;
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;
Accept(Node.ExpandedBody); N.ExpandedBody := Accept(N.ExpandedBody);
// Also visit the original call nodes, though they are not in tail pos // Also visit the original call nodes, though they are not in tail pos
FNextIsTail := False; FNextIsTail := False;
Accept(Node.Callee); N.Callee := Accept(N.Callee);
AcceptNodes<IAstNode>(Node.Arguments); N.Arguments := AcceptNodes<IAstNode>(N.Arguments, function(Node: IAstNode): IAstNode begin exit(Node) end);
Result := TDataValue.FromIntf<IMacroExpansionNode>(Node); Result := N;
end; end;
function TAstTCO.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; function TAstTCO.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
var var
callee: IAstNode;
args: TArray<IAstNode>;
isTailCall: Boolean; isTailCall: Boolean;
N: TFunctionCallNode;
begin begin
isTailCall := FIsTailStack.Peek; isTailCall := FIsTailStack.Peek;
N := (Node as TFunctionCallNode);
// Arguments are not in tail position // Arguments are not in tail position
FNextIsTail := False; FNextIsTail := False;
callee := Accept(Node.Callee).AsIntf<IAstNode>; N.Callee := Accept(N.Callee);
args := AcceptNodes<IAstNode>(Node.Arguments); N.Arguments := AcceptNodes<IAstNode>(N.Arguments, function(Node: IAstNode): IAstNode begin exit(Node) end);
// If TCO status changed, we MUST rebuild the node. // Mutate the node with the TCO status
var boundNode := (Node as TBoundFunctionCallNode); N.IsTailCall := isTailCall;
if (boundNode.IsTailCall <> isTailCall) or (callee <> Node.Callee) or (args <> Node.Arguments) then
begin Result := N;
var newBoundCall := TBoundFunctionCallNode.Create(Node, callee, args, isTailCall);
(newBoundCall as TAstNode).StaticType := (Node as TAstNode).StaticType;
Result := TDataValue.FromIntf<IFunctionCallNode>(newBoundCall);
end
else
Result := TDataValue.FromIntf<IFunctionCallNode>(Node); // No change
end; end;
end. end.
+81 -102
View File
@@ -9,7 +9,8 @@ uses
Myc.Ast.Nodes, Myc.Ast.Nodes,
Myc.Ast.Visitor, Myc.Ast.Visitor,
Myc.Data.Value, Myc.Data.Value,
Myc.Data.Scalar; Myc.Data.Scalar,
Myc.Ast;
type type
// Dumps a bound AST into a human-readable format for debugging purposes. // Dumps a bound AST into a human-readable format for debugging purposes.
@@ -18,7 +19,8 @@ type
procedure Execute(const RootNode: IAstNode); procedure Execute(const RootNode: IAstNode);
end; end;
TAstDumper = class(TAstVisitor) // Inherits from the new non-generic TAstVisitor base class
TAstDumper = class(TAstVisitor, IAstDumper)
private private
FOutput: TStrings; FOutput: TStrings;
FIndent: Integer; FIndent: Integer;
@@ -29,30 +31,31 @@ type
function FormatAddress(const Addr: TResolvedAddress): string; function FormatAddress(const Addr: TResolvedAddress): string;
protected protected
function VisitConstant(const Node: IConstantNode): TDataValue; override; // Override abstract procedures from TAstVisitor
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; procedure VisitConstant(const Node: IConstantNode); override;
function VisitKeyword(const Node: IKeywordNode): TDataValue; override; procedure VisitIdentifier(const Node: IIdentifierNode); override;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; procedure VisitKeyword(const Node: IKeywordNode); override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; procedure VisitBinaryExpression(const Node: IBinaryExpressionNode); override;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; procedure VisitUnaryExpression(const Node: IUnaryExpressionNode); override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; procedure VisitIfExpression(const Node: IIfExpressionNode); override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; procedure VisitTernaryExpression(const Node: ITernaryExpressionNode); override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; procedure VisitLambdaExpression(const Node: ILambdaExpressionNode); override;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override; procedure VisitFunctionCall(const Node: IFunctionCallNode); override;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override; procedure VisitMacroExpansionNode(const Node: IMacroExpansionNode); override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; procedure VisitRecurNode(const Node: IRecurNode); override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; procedure VisitBlockExpression(const Node: IBlockExpressionNode); override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; procedure VisitVariableDeclaration(const Node: IVariableDeclarationNode); override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; procedure VisitAssignment(const Node: IAssignmentNode); override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override; procedure VisitMacroDefinition(const Node: IMacroDefinitionNode); override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override; procedure VisitQuasiquote(const Node: IQuasiquoteNode); override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override; procedure VisitUnquote(const Node: IUnquoteNode); override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override; procedure VisitUnquoteSplicing(const Node: IUnquoteSplicingNode); override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; procedure VisitIndexer(const Node: IIndexerNode); override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override; procedure VisitMemberAccess(const Node: IMemberAccessNode); override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; procedure VisitRecordLiteral(const Node: IRecordLiteralNode); override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override; procedure VisitCreateSeries(const Node: ICreateSeriesNode); override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override; procedure VisitAddSeriesItem(const Node: IAddSeriesItemNode); override;
procedure VisitSeriesLength(const Node: ISeriesLengthNode); override;
public public
// Creates a new instance of the AST dumper. // Creates a new instance of the AST dumper.
@@ -67,8 +70,8 @@ type
implementation implementation
uses uses
Myc.Data.Keyword, Myc.Data.Keyword;
Myc.Ast.Binding.Nodes; // Myc.Ast.Binding.Nodes; // Removed
{ TAstDumper } { TAstDumper }
@@ -132,47 +135,45 @@ begin
end; end;
end; end;
function TAstDumper.VisitConstant(const Node: IConstantNode): TDataValue; procedure TAstDumper.VisitConstant(const Node: IConstantNode);
begin begin
LogFmt('Constant: %s', [Node.Value.ToString]); LogFmt('Constant: %s', [Node.Value.ToString]);
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitIdentifier(const Node: IIdentifierNode): TDataValue; procedure TAstDumper.VisitIdentifier(const Node: IIdentifierNode);
var
N: TIdentifierNode;
begin begin
if Node is TBoundIdentifierNode then N := (Node as TIdentifierNode);
LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress((Node as TBoundIdentifierNode).Address)]) if N.Address.Kind <> akUnresolved then
LogFmt('Identifier: %s -> %s', [N.Name, FormatAddress(N.Address)])
else else
LogFmt('Identifier: %s (unbound)', [Node.Name]); LogFmt('Identifier: %s (unbound)', [N.Name]);
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitKeyword(const Node: IKeywordNode): TDataValue; procedure TAstDumper.VisitKeyword(const Node: IKeywordNode);
begin begin
LogFmt('Keyword: :%s', [Node.Value.Name]); LogFmt('Keyword: :%s', [Node.Value.Name]);
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; procedure TAstDumper.VisitBinaryExpression(const Node: IBinaryExpressionNode);
begin begin
LogFmt('BinaryExpression: %s', [Node.Operator.ToString]); LogFmt('BinaryExpression: %s', [Node.Operator.ToString]);
Indent; Indent;
Node.Left.Accept(Self); Node.Left.Accept(Self);
Node.Right.Accept(Self); Node.Right.Accept(Self);
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; procedure TAstDumper.VisitUnaryExpression(const Node: IUnaryExpressionNode);
begin begin
LogFmt('UnaryExpression: %s', [Node.Operator.ToString]); LogFmt('UnaryExpression: %s', [Node.Operator.ToString]);
Indent; Indent;
Node.Right.Accept(Self); Node.Right.Accept(Self);
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; procedure TAstDumper.VisitIfExpression(const Node: IIfExpressionNode);
begin begin
Log('IfExpression'); Log('IfExpression');
Indent; Indent;
@@ -186,10 +187,9 @@ begin
Node.ElseBranch.Accept(Self); Node.ElseBranch.Accept(Self);
end; end;
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; procedure TAstDumper.VisitTernaryExpression(const Node: ITernaryExpressionNode);
begin begin
Log('TernaryExpression'); Log('TernaryExpression');
Indent; Indent;
@@ -200,25 +200,24 @@ begin
Log('Else:'); Log('Else:');
Node.ElseBranch.Accept(Self); Node.ElseBranch.Accept(Self);
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; procedure TAstDumper.VisitLambdaExpression(const Node: ILambdaExpressionNode);
var var
param: IIdentifierNode;
upvalueAddr: TResolvedAddress; upvalueAddr: TResolvedAddress;
pair: TPair<string, Integer>; pair: TPair<string, Integer>;
boundNode: TBoundLambdaExpressionNode; boundNode: TLambdaExpressionNode;
begin begin
if Node is TBoundLambdaExpressionNode then boundNode := Node as TLambdaExpressionNode;
if Assigned(boundNode.ScopeDescriptor) then
begin begin
boundNode := Node as TBoundLambdaExpressionNode;
LogFmt('LambdaExpression (HasNested: %s)', [boundNode.HasNestedLambdas.ToString(TUseBoolStrs.True)]); LogFmt('LambdaExpression (HasNested: %s)', [boundNode.HasNestedLambdas.ToString(TUseBoolStrs.True)]);
Indent; Indent;
Log('Parameters:'); Log('Parameters:');
Indent; Indent;
for param in boundNode.Parameters do for var param in boundNode.Parameters do
param.Accept(Self); param.Accept(Self);
Unindent; Unindent;
@@ -248,38 +247,35 @@ begin
Indent; Indent;
Log('Parameters:'); Log('Parameters:');
Indent; Indent;
for param in Node.Parameters do for var param in Node.Parameters do
param.Accept(Self); param.Accept(Self);
Unindent; Unindent;
Log('Body:'); Log('Body:');
Node.Body.Accept(Self); Node.Body.Accept(Self);
Unindent; Unindent;
end; end;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; procedure TAstDumper.VisitFunctionCall(const Node: IFunctionCallNode);
var var
arg: IAstNode; arg: IAstNode;
N: TFunctionCallNode;
begin begin
if Node is TBoundFunctionCallNode then N := (Node as TFunctionCallNode);
LogFmt('FunctionCall (IsTailCall: %s)', [(Node as TBoundFunctionCallNode).IsTailCall.ToString(TUseBoolStrs.True)]) LogFmt('FunctionCall (IsTailCall: %s)', [N.IsTailCall.ToString(TUseBoolStrs.True)]);
else
Log('FunctionCall (unbound)');
Indent; Indent;
Log('Callee:'); Log('Callee:');
Node.Callee.Accept(Self); N.Callee.Accept(Self);
LogFmt('Arguments (%d):', [Length(Node.Arguments)]); LogFmt('Arguments (%d):', [Length(N.Arguments)]);
Indent; Indent;
for arg in Node.Arguments do for arg in N.Arguments do
arg.Accept(Self); arg.Accept(Self);
Unindent; Unindent;
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; procedure TAstDumper.VisitMacroExpansionNode(const Node: IMacroExpansionNode);
var var
arg: IAstNode; arg: IAstNode;
begin begin
@@ -303,10 +299,9 @@ begin
Unindent; Unindent;
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitRecurNode(const Node: IRecurNode): TDataValue; procedure TAstDumper.VisitRecurNode(const Node: IRecurNode);
var var
arg: IAstNode; arg: IAstNode;
begin begin
@@ -319,10 +314,9 @@ begin
arg.Accept(Self); arg.Accept(Self);
Unindent; Unindent;
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; procedure TAstDumper.VisitBlockExpression(const Node: IBlockExpressionNode);
var var
expr: IAstNode; expr: IAstNode;
begin begin
@@ -331,28 +325,26 @@ begin
for expr in Node.Expressions do for expr in Node.Expressions do
expr.Accept(Self); expr.Accept(Self);
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; procedure TAstDumper.VisitVariableDeclaration(const Node: IVariableDeclarationNode);
var
N: TVariableDeclarationNode;
begin begin
if Node is TBoundVariableDeclarationNode then N := (Node as TVariableDeclarationNode);
LogFmt('VariableDeclaration (IsBoxed: %s)', [(Node as TBoundVariableDeclarationNode).IsBoxed.ToString(TUseBoolStrs.True)]) LogFmt('VariableDeclaration (IsBoxed: %s)', [N.IsBoxed.ToString(TUseBoolStrs.True)]);
else
Log('VariableDeclaration (unbound)');
Indent; Indent;
Node.Identifier.Accept(Self); N.Identifier.Accept(Self);
if Assigned(Node.Initializer) then if Assigned(N.Initializer) then
begin begin
Log('Initializer:'); Log('Initializer:');
Node.Initializer.Accept(Self); N.Initializer.Accept(Self);
end; end;
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitAssignment(const Node: IAssignmentNode): TDataValue; procedure TAstDumper.VisitAssignment(const Node: IAssignmentNode);
begin begin
Log('Assignment'); Log('Assignment');
Indent; Indent;
@@ -360,12 +352,9 @@ begin
Log('Value:'); Log('Value:');
Node.Value.Accept(Self); Node.Value.Accept(Self);
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; procedure TAstDumper.VisitMacroDefinition(const Node: IMacroDefinitionNode);
var
param: IIdentifierNode;
begin begin
Log('MacroDefinition'); Log('MacroDefinition');
Indent; Indent;
@@ -377,7 +366,7 @@ begin
Log('Parameters:'); Log('Parameters:');
Indent; Indent;
for param in Node.Parameters do for var param in Node.Parameters do
param.Accept(Self); param.Accept(Self);
Unindent; Unindent;
@@ -387,37 +376,33 @@ begin
Unindent; Unindent;
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; procedure TAstDumper.VisitQuasiquote(const Node: IQuasiquoteNode);
begin begin
Log('Quasiquote'); Log('Quasiquote');
Indent; Indent;
Node.Expression.Accept(Self); Node.Expression.Accept(Self);
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitUnquote(const Node: IUnquoteNode): TDataValue; procedure TAstDumper.VisitUnquote(const Node: IUnquoteNode);
begin begin
Log('Unquote'); Log('Unquote');
Indent; Indent;
Node.Expression.Accept(Self); Node.Expression.Accept(Self);
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; procedure TAstDumper.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode);
begin begin
Log('UnquoteSplicing'); Log('UnquoteSplicing');
Indent; Indent;
Node.Expression.Accept(Self); Node.Expression.Accept(Self);
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitIndexer(const Node: IIndexerNode): TDataValue; procedure TAstDumper.VisitIndexer(const Node: IIndexerNode);
begin begin
Log('Indexer'); Log('Indexer');
Indent; Indent;
@@ -426,10 +411,9 @@ begin
Log('Index:'); Log('Index:');
Node.Index.Accept(Self); Node.Index.Accept(Self);
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; procedure TAstDumper.VisitMemberAccess(const Node: IMemberAccessNode);
begin begin
Log('MemberAccess'); Log('MemberAccess');
Indent; Indent;
@@ -438,10 +422,9 @@ begin
Log('Member:'); Log('Member:');
Node.Member.Accept(Self); Node.Member.Accept(Self);
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; procedure TAstDumper.VisitRecordLiteral(const Node: IRecordLiteralNode);
var var
field: TRecordFieldLiteral; field: TRecordFieldLiteral;
begin begin
@@ -455,16 +438,14 @@ begin
Unindent; Unindent;
end; end;
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; procedure TAstDumper.VisitCreateSeries(const Node: ICreateSeriesNode);
begin begin
LogFmt('CreateSeries: %s', [Node.Definition]); LogFmt('CreateSeries: %s', [Node.Definition]);
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; procedure TAstDumper.VisitAddSeriesItem(const Node: IAddSeriesItemNode);
begin begin
Log('AddSeriesItem'); Log('AddSeriesItem');
Indent; Indent;
@@ -478,17 +459,15 @@ begin
Node.Lookback.Accept(Self); Node.Lookback.Accept(Self);
end; end;
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
function TAstDumper.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; procedure TAstDumper.VisitSeriesLength(const Node: ISeriesLengthNode);
begin begin
Log('SeriesLength'); Log('SeriesLength');
Indent; Indent;
Log('Series:'); Log('Series:');
Node.Series.Accept(Self); Node.Series.Accept(Self);
Unindent; Unindent;
Result := TDataValue.Void;
end; end;
end. end.
+56 -54
View File
@@ -9,43 +9,44 @@ uses
Myc.Data.Scalar, Myc.Data.Scalar,
Myc.Data.Value, Myc.Data.Value,
Myc.Ast.Nodes, Myc.Ast.Nodes,
Myc.Ast.Visitor,
Myc.Ast.Binding,
Myc.Ast.Scope; Myc.Ast.Scope;
type type
// The standard AST evaluator for production use. // The standard AST evaluator for production use.
TEvaluatorVisitor = class(TAstVisitor, IEvaluatorVisitor) // This class inherits directly from TInterfacedObject as it is an
// Interpreter (AST -> TDataValue), not a Transformer (AST -> IAstNode).
TEvaluatorVisitor = class(TInterfacedObject, IAstVisitor, IEvaluatorVisitor)
private private
FScope: IExecutionScope; FScope: IExecutionScope;
class var class var
procedure HandleTCO(var ResultValue: TDataValue); procedure HandleTCO(var ResultValue: TDataValue);
protected protected
function VisitConstant(const Node: IConstantNode): TDataValue; override; // IAstVisitor methods made virtual for TDebugEvaluatorVisitor to override
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; function VisitConstant(const Node: IConstantNode): TDataValue; virtual;
function VisitKeyword(const Node: IKeywordNode): TDataValue; override; function VisitIdentifier(const Node: IIdentifierNode): TDataValue; virtual;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; function VisitKeyword(const Node: IKeywordNode): TDataValue; virtual;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; virtual;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; virtual;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; virtual;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; virtual;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; virtual;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override; function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; virtual;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override; function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; virtual;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override; function VisitUnquote(const Node: IUnquoteNode): TDataValue; virtual;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; virtual;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; virtual;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; virtual;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; virtual;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; virtual;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override; function VisitAssignment(const Node: IAssignmentNode): TDataValue; virtual;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; function VisitIndexer(const Node: IIndexerNode): TDataValue; virtual;
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override; function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; virtual;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; virtual;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override; function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; virtual;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override; function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; virtual;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override; function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; virtual;
function VisitRecurNode(const Node: IRecurNode): TDataValue; virtual;
function IsTruthy(const AValue: TDataValue): Boolean; inline; function IsTruthy(const AValue: TDataValue): Boolean; inline;
@@ -68,11 +69,12 @@ implementation
uses uses
System.TypInfo, System.TypInfo,
System.Generics.Defaults, System.Generics.Defaults,
Myc.Ast, // Added
Myc.Data.Keyword, Myc.Data.Keyword,
Myc.Data.Decimal, Myc.Data.Decimal,
Myc.Data.Series, Myc.Data.Series,
Myc.Data.Scalar.JSON, Myc.Data.Scalar.JSON;
Myc.Ast.Binding.Nodes; // Myc.Ast.Binding.Nodes; // Removed
// Helper type for TCO via trampolining. // Helper type for TCO via trampolining.
type type
@@ -206,7 +208,7 @@ end;
function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
var var
boundNode: TBoundLambdaExpressionNode; boundNode: TLambdaExpressionNode; // Changed
capturedCells: TArray<IValueCell>; capturedCells: TArray<IValueCell>;
i: Integer; i: Integer;
sourceAddresses: TArray<TResolvedAddress>; sourceAddresses: TArray<TResolvedAddress>;
@@ -214,7 +216,7 @@ var
visitorFactory: TEvaluatorFactory; visitorFactory: TEvaluatorFactory;
begin begin
// Cast to the bound node to access binder-specific information. // Cast to the bound node to access binder-specific information.
boundNode := Node as TBoundLambdaExpressionNode; boundNode := Node as TLambdaExpressionNode; // Changed
// Create the closure by capturing the value cells of all upvalues from the current scope. // Create the closure by capturing the value cells of all upvalues from the current scope.
sourceAddresses := boundNode.Upvalues; sourceAddresses := boundNode.Upvalues;
@@ -257,13 +259,13 @@ begin
// Capture the closure itself in slot 0 for 'recur' to find it. // Capture the closure itself in slot 0 for 'recur' to find it.
adr.SlotIndex := 0; adr.SlotIndex := 0;
lambdaScope[adr] := closure; lambdaScope[adr] := TDataValue(closure); // Explicit cast
// Populate the scope with the actual parameters passed to the function. // Populate the scope with the actual parameters passed to the function.
for i := 0 to High(ArgValues) do for i := 0 to High(ArgValues) do
begin begin
// Parameters in a bound lambda must be bound identifiers. // Parameters in a bound lambda must be bound identifiers.
adr.SlotIndex := (params[i] as TBoundIdentifierNode).Address.SlotIndex; adr.SlotIndex := (params[i] as TIdentifierNode).Address.SlotIndex; // Changed
lambdaScope[adr] := ArgValues[i]; lambdaScope[adr] := ArgValues[i];
end; end;
@@ -273,7 +275,7 @@ begin
end; end;
// The result of visiting a lambda node is the callable closure itself. // The result of visiting a lambda node is the callable closure itself.
Result := closure; Result := TDataValue(closure); // Explicit cast
end; end;
function TEvaluatorVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; function TEvaluatorVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
@@ -303,7 +305,7 @@ function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDa
var var
calleeValue: TDataValue; calleeValue: TDataValue;
argValues: TArray<TDataValue>; argValues: TArray<TDataValue>;
boundNode: TBoundFunctionCallNode; boundNode: TFunctionCallNode; // Changed
begin begin
calleeValue := Node.Callee.Accept(Self); calleeValue := Node.Callee.Accept(Self);
if calleeValue.Kind <> vkMethod then if calleeValue.Kind <> vkMethod then
@@ -314,7 +316,7 @@ begin
for var i := 0 to High(argNodes) do for var i := 0 to High(argNodes) do
argValues[i] := argNodes[i].Accept(Self); argValues[i] := argNodes[i].Accept(Self);
boundNode := Node as TBoundFunctionCallNode; boundNode := Node as TFunctionCallNode; // Changed
if boundNode.IsTailCall then if boundNode.IsTailCall then
begin begin
// This is a tail call. Return a thunk to be processed by the trampoline. // This is a tail call. Return a thunk to be processed by the trampoline.
@@ -363,7 +365,7 @@ var
itemValue, lookbackValue, seriesVar: TDataValue; itemValue, lookbackValue, seriesVar: TDataValue;
lookback: Int64; lookback: Int64;
begin begin
seriesVar := FScope[(Node.Series as TBoundIdentifierNode).Address]; seriesVar := FScope[(Node.Series as TIdentifierNode).Address]; // Changed
itemValue := Node.Value.Accept(Self); itemValue := Node.Value.Accept(Self);
lookback := -1; lookback := -1;
@@ -397,7 +399,7 @@ begin
// Evaluate the new value. // Evaluate the new value.
Result := Node.Value.Accept(Self); Result := Node.Value.Accept(Self);
// Assign it. The scope's SetValues implementation now handles boxing correctly. // Assign it. The scope's SetValues implementation now handles boxing correctly.
FScope[(Node.Identifier as TBoundIdentifierNode).Address] := Result; FScope[(Node.Identifier as TIdentifierNode).Address] := Result; // Changed
end; end;
function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue; function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
@@ -408,7 +410,7 @@ end;
function TEvaluatorVisitor.VisitKeyword(const Node: IKeywordNode): TDataValue; function TEvaluatorVisitor.VisitKeyword(const Node: IKeywordNode): TDataValue;
begin begin
// Return the keyword as a TScalar value // Return the keyword as a TScalar value
Result := TDataValue(TScalar.FromKeyword(Node.Value)); Result := TDataValue(TScalar.FromKeyword(Node.Value)); // Explicit cast
end; end;
function TEvaluatorVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; function TEvaluatorVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
@@ -434,7 +436,7 @@ end;
function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue; function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
begin begin
// The scope's GetValues implementation now handles unboxing automatically. // The scope's GetValues implementation now handles unboxing automatically.
Result := FScope[(Node as TBoundIdentifierNode).Address]; Result := FScope[(Node as TIdentifierNode).Address]; // Changed
end; end;
function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue; function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
@@ -458,7 +460,7 @@ begin
series := baseValue.AsSeries; series := baseValue.AsSeries;
if (index < 0) or (index >= series.TotalCount) then if (index < 0) or (index >= series.TotalCount) then
raise EArgumentException.CreateFmt('Index %d is out of bounds for series with %d elements.', [index, series.TotalCount]); raise EArgumentException.CreateFmt('Index %d is out of bounds for series with %d elements.', [index, series.TotalCount]);
Result := series.Items[Integer(index)]; Result := TDataValue(series.Items[Integer(index)]); // Explicit cast
end; end;
vkRecordSeries: vkRecordSeries:
begin begin
@@ -487,7 +489,7 @@ begin
case baseValue.Kind of case baseValue.Kind of
vkRecordSeries: Result := TDataValue.FromSeries(baseValue.AsRecordSeries[Node.Member.Value]); vkRecordSeries: Result := TDataValue.FromSeries(baseValue.AsRecordSeries[Node.Member.Value]);
vkRecord: Result := baseValue.AsRecord[Node.Member.Value]; vkRecord: Result := TDataValue(baseValue.AsRecord[Node.Member.Value]); // Explicit cast
// --- NEW GENERIC PATH --- // --- NEW GENERIC PATH ---
vkGenericRecord: vkGenericRecord:
@@ -509,10 +511,10 @@ var
i: Integer; i: Integer;
begin begin
// Check which type the binder created // Check which type the binder created
if Node is TBoundGenericRecordLiteralNode then if Node is TGenericRecordLiteralNode then // Changed
begin begin
// --- NEW GENERIC PATH --- // --- NEW GENERIC PATH ---
var boundNode := Node as TBoundGenericRecordLiteralNode; var boundNode := Node as TGenericRecordLiteralNode; // Changed
var genFields: TArray<TPair<IKeyword, TDataValue>>; var genFields: TArray<TPair<IKeyword, TDataValue>>;
SetLength(genFields, Length(boundNode.Fields)); SetLength(genFields, Length(boundNode.Fields));
@@ -530,10 +532,10 @@ begin
var dynRec := TDynamicRecord.Create(genFields); var dynRec := TDynamicRecord.Create(genFields);
Result := TDataValue.FromGenericRecord(dynRec); Result := TDataValue.FromGenericRecord(dynRec);
end end
else if Node is TBoundRecordLiteralNode then else if Node is TRecordLiteralNode then // Changed
begin begin
// --- EXISTING SCALAR PATH --- // --- EXISTING SCALAR PATH ---
var boundNode := Node as TBoundRecordLiteralNode; var boundNode := Node as TRecordLiteralNode; // Changed
var values: TArray<TScalar.TValue>; var values: TArray<TScalar.TValue>;
SetLength(values, Length(boundNode.Fields)); SetLength(values, Length(boundNode.Fields));
@@ -554,7 +556,7 @@ end;
function TEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; function TEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
var var
address: TResolvedAddress; address: TResolvedAddress;
boundNode: TBoundVariableDeclarationNode; boundNode: TVariableDeclarationNode; // Changed
begin begin
// First, evaluate the initializer to get the variable's initial value. // First, evaluate the initializer to get the variable's initial value.
if Assigned(Node.Initializer) then if Assigned(Node.Initializer) then
@@ -562,9 +564,9 @@ begin
else else
Result := TDataValue.Void; Result := TDataValue.Void;
// After binding, all declaration nodes are TBoundVariableDeclarationNode // After binding, all declaration nodes are TVariableDeclarationNode
boundNode := Node as TBoundVariableDeclarationNode; boundNode := Node as TVariableDeclarationNode; // Changed
address := (boundNode.Identifier as TBoundIdentifierNode).Address; address := (boundNode.Identifier as TIdentifierNode).Address; // Changed
// Check the IsBoxed flag set by the binder. // Check the IsBoxed flag set by the binder.
if boundNode.IsBoxed then if boundNode.IsBoxed then
@@ -599,7 +601,7 @@ begin
+ ' and ' + ' and '
+ rightValue.AsScalar.Kind.ToString + rightValue.AsScalar.Kind.ToString
+ ' .'); + ' .');
Result := resScalar; Result := TDataValue(resScalar); // Explicit cast
end; end;
function TEvaluatorVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; function TEvaluatorVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
@@ -614,7 +616,7 @@ begin
var res: TScalar; var res: TScalar;
if not TScalar.TryUnaryOperation(Node.Operator, rightValue.AsScalar, res) then if not TScalar.TryUnaryOperation(Node.Operator, rightValue.AsScalar, res) then
raise ENotSupportedException.Create('Unary operation not supported for scalar type' + rightValue.AsScalar.Kind.ToString + '.'); raise ENotSupportedException.Create('Unary operation not supported for scalar type' + rightValue.AsScalar.Kind.ToString + '.');
Result := res; Result := TDataValue(res); // Explicit cast
end; end;
function TEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; function TEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
@@ -649,7 +651,7 @@ var
seriesValue: TDataValue; seriesValue: TDataValue;
len: Int64; len: Int64;
begin begin
seriesValue := FScope[(Node.Series as TBoundIdentifierNode).Address]; seriesValue := FScope[(Node.Series as TIdentifierNode).Address]; // Changed
case seriesValue.Kind of case seriesValue.Kind of
vkSeries: len := seriesValue.AsSeries.Count; vkSeries: len := seriesValue.AsSeries.Count;
@@ -658,7 +660,7 @@ begin
raise EArgumentException.CreateFmt('Cannot get length of type %s.', [GetEnumName(TypeInfo(TDataValueKind), Ord(seriesValue.Kind))]); raise EArgumentException.CreateFmt('Cannot get length of type %s.', [GetEnumName(TypeInfo(TDataValueKind), Ord(seriesValue.Kind))]);
end; end;
Result := TScalar.FromInt64(len); Result := TDataValue(TScalar.FromInt64(len)); // Explicit cast
end; end;
end. end.
+213 -408
View File
@@ -20,9 +20,9 @@ type
// TJsonAstConverter implements the visitor pattern for serialization // TJsonAstConverter implements the visitor pattern for serialization
// and uses factory functions for deserialization. // and uses factory functions for deserialization.
TJsonAstConverter = class(TAstVisitor, IJsonAstConverter) // Inherits from the new generic visitor base class
TJsonAstConverter = class(TAstVisitor<TJSONObject>, IJsonAstConverter)
private private
FJsonObjectStack: TStack<TJSONObject>;
procedure DataValueToJson(const AValue: TDataValue; const AParent: TJSONObject; const AName: string); procedure DataValueToJson(const AValue: TDataValue; const AParent: TJSONObject; const AName: string);
function JsonToNode(const AJson: TJSONValue; const ExpectedType: String = ''): IAstNode; function JsonToNode(const AJson: TJSONValue; const ExpectedType: String = ''): IAstNode;
function JsonToDataValue(const AObj: TJSONObject; const AName: string): TDataValue; function JsonToDataValue(const AObj: TJSONObject; const AName: string): TDataValue;
@@ -52,37 +52,36 @@ type
function JsonToAddSeriesItemNode(const AObj: TJSONObject): IAddSeriesItemNode; function JsonToAddSeriesItemNode(const AObj: TJSONObject): IAddSeriesItemNode;
function JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode; function JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode;
protected protected
// IAstVisitor implementation for serialization // IAstVisitor implementation for serialization (T = TJSONObject)
function VisitConstant(const Node: IConstantNode): TDataValue; override; function VisitConstant(const Node: IConstantNode): TJSONObject; override;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; function VisitIdentifier(const Node: IIdentifierNode): TJSONObject; override;
function VisitKeyword(const Node: IKeywordNode): TDataValue; override; function VisitKeyword(const Node: IKeywordNode): TJSONObject; override;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; function VisitBinaryExpression(const Node: IBinaryExpressionNode): TJSONObject; override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; function VisitUnaryExpression(const Node: IUnaryExpressionNode): TJSONObject; override;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; function VisitIfExpression(const Node: IIfExpressionNode): TJSONObject; override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; function VisitTernaryExpression(const Node: ITernaryExpressionNode): TJSONObject; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TJSONObject; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; function VisitMacroDefinition(const Node: IMacroDefinitionNode): TJSONObject; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override; function VisitQuasiquote(const Node: IQuasiquoteNode): TJSONObject; override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override; function VisitUnquote(const Node: IUnquoteNode): TJSONObject; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override; function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TJSONObject; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): TJSONObject; override;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override; function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TJSONObject; override;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override; function VisitRecurNode(const Node: IRecurNode): TJSONObject; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitBlockExpression(const Node: IBlockExpressionNode): TJSONObject; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TJSONObject; override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; function VisitAssignment(const Node: IAssignmentNode): TJSONObject; override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override; function VisitIndexer(const Node: IIndexerNode): TJSONObject; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; function VisitMemberAccess(const Node: IMemberAccessNode): TJSONObject; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override; function VisitRecordLiteral(const Node: IRecordLiteralNode): TJSONObject; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; function VisitCreateSeries(const Node: ICreateSeriesNode): TJSONObject; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override; function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TJSONObject; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override; function VisitSeriesLength(const Node: ISeriesLengthNode): TJSONObject; override;
function Serialize(const RootNode: IAstNode): TJSONObject; function Serialize(const RootNode: IAstNode): TJSONObject;
function Deserialize(const AJson: TJSONObject): IAstNode; function Deserialize(const AJson: TJSONObject): IAstNode;
public public
constructor Create; constructor Create;
destructor Destroy; override;
end; end;
implementation implementation
@@ -95,13 +94,7 @@ uses
constructor TJsonAstConverter.Create; constructor TJsonAstConverter.Create;
begin begin
inherited; inherited;
FJsonObjectStack := TStack<TJSONObject>.Create; // FJsonObjectStack removed
end;
destructor TJsonAstConverter.Destroy;
begin
FJsonObjectStack.Free;
inherited;
end; end;
function TJsonAstConverter.Deserialize(const AJson: TJSONObject): IAstNode; function TJsonAstConverter.Deserialize(const AJson: TJSONObject): IAstNode;
@@ -145,506 +138,322 @@ begin
AParent.AddPair(AName, valObj); AParent.AddPair(AName, valObj);
end; end;
function TJsonAstConverter.VisitConstant(const Node: IConstantNode): TDataValue; function TJsonAstConverter.VisitConstant(const Node: IConstantNode): TJSONObject;
var
obj: TJSONObject;
begin begin
obj := TJSONObject.Create; Result := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('Constant')); Result.AddPair('NodeType', TJSONString.Create('Constant'));
DataValueToJson(Node.Value, obj, 'Value'); DataValueToJson(Node.Value, Result, 'Value');
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitIdentifier(const Node: IIdentifierNode): TDataValue; function TJsonAstConverter.VisitIdentifier(const Node: IIdentifierNode): TJSONObject;
var
obj: TJSONObject;
begin begin
obj := TJSONObject.Create; Result := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('Identifier')); Result.AddPair('NodeType', TJSONString.Create('Identifier'));
obj.AddPair('Name', TJSONString.Create(Node.Name)); Result.AddPair('Name', TJSONString.Create(Node.Name));
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitKeyword(const Node: IKeywordNode): TDataValue; function TJsonAstConverter.VisitKeyword(const Node: IKeywordNode): TJSONObject;
var
obj: TJSONObject;
begin begin
obj := TJSONObject.Create; Result := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('Keyword')); Result.AddPair('NodeType', TJSONString.Create('Keyword'));
obj.AddPair('Name', TJSONString.Create(Node.Value.Name)); Result.AddPair('Name', TJSONString.Create(Node.Value.Name));
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; function TJsonAstConverter.VisitBinaryExpression(const Node: IBinaryExpressionNode): TJSONObject;
var var
obj, leftObj, rightObj: TJSONObject; leftObj, rightObj: TJSONObject;
begin begin
Node.Left.Accept(Self); leftObj := Accept(Node.Left);
Node.Right.Accept(Self); rightObj := Accept(Node.Right);
rightObj := FJsonObjectStack.Pop; Result := TJSONObject.Create;
leftObj := FJsonObjectStack.Pop; Result.AddPair('NodeType', TJSONString.Create('BinaryExpr'));
Result.AddPair('Operator', TJSONString.Create(Node.Operator.ToString));
obj := TJSONObject.Create; Result.AddPair('Left', leftObj);
obj.AddPair('NodeType', TJSONString.Create('BinaryExpr')); Result.AddPair('Right', rightObj);
obj.AddPair('Operator', TJSONString.Create(Node.Operator.ToString));
obj.AddPair('Left', leftObj);
obj.AddPair('Right', rightObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; function TJsonAstConverter.VisitUnaryExpression(const Node: IUnaryExpressionNode): TJSONObject;
var var
obj, rightObj: TJSONObject; rightObj: TJSONObject;
begin begin
Node.Right.Accept(Self); rightObj := Accept(Node.Right);
rightObj := FJsonObjectStack.Pop;
obj := TJSONObject.Create; Result := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('UnaryExpr')); Result.AddPair('NodeType', TJSONString.Create('UnaryExpr'));
obj.AddPair('Operator', TJSONString.Create(Node.Operator.ToString)); Result.AddPair('Operator', TJSONString.Create(Node.Operator.ToString));
obj.AddPair('Right', rightObj); Result.AddPair('Right', rightObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; function TJsonAstConverter.VisitIfExpression(const Node: IIfExpressionNode): TJSONObject;
var var
obj, condObj, thenObj, elseObj: TJSONObject; condObj, thenObj, elseObj: TJSONObject;
begin begin
Node.Condition.Accept(Self); condObj := Accept(Node.Condition);
Node.ThenBranch.Accept(Self); thenObj := Accept(Node.ThenBranch);
if Assigned(Node.ElseBranch) then elseObj := Accept(Node.ElseBranch); // Accept handles nil
Node.ElseBranch.Accept(Self);
if Assigned(Node.ElseBranch) then Result := TJSONObject.Create;
elseObj := FJsonObjectStack.Pop Result.AddPair('NodeType', TJSONString.Create('IfExpr'));
else Result.AddPair('Condition', condObj);
elseObj := nil; Result.AddPair('ThenBranch', thenObj);
thenObj := FJsonObjectStack.Pop;
condObj := FJsonObjectStack.Pop;
obj := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('IfExpr'));
obj.AddPair('Condition', condObj);
obj.AddPair('ThenBranch', thenObj);
if Assigned(elseObj) then if Assigned(elseObj) then
obj.AddPair('ElseBranch', elseObj) Result.AddPair('ElseBranch', elseObj)
else else
obj.AddPair('ElseBranch', TJSONNull.Create); Result.AddPair('ElseBranch', TJSONNull.Create);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; function TJsonAstConverter.VisitTernaryExpression(const Node: ITernaryExpressionNode): TJSONObject;
var var
obj, condObj, thenObj, elseObj: TJSONObject; condObj, thenObj, elseObj: TJSONObject;
begin begin
Node.Condition.Accept(Self); condObj := Accept(Node.Condition);
Node.ThenBranch.Accept(Self); thenObj := Accept(Node.ThenBranch);
Node.ElseBranch.Accept(Self); elseObj := Accept(Node.ElseBranch);
elseObj := FJsonObjectStack.Pop; Result := TJSONObject.Create;
thenObj := FJsonObjectStack.Pop; Result.AddPair('NodeType', TJSONString.Create('TernaryExpr'));
condObj := FJsonObjectStack.Pop; Result.AddPair('Condition', condObj);
Result.AddPair('ThenBranch', thenObj);
obj := TJSONObject.Create; Result.AddPair('ElseBranch', elseObj);
obj.AddPair('NodeType', TJSONString.Create('TernaryExpr'));
obj.AddPair('Condition', condObj);
obj.AddPair('ThenBranch', thenObj);
obj.AddPair('ElseBranch', elseObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; function TJsonAstConverter.VisitLambdaExpression(const Node: ILambdaExpressionNode): TJSONObject;
var var
obj, bodyObj: TJSONObject; bodyObj: TJSONObject;
paramsArray: TJSONArray; paramsArray: TJSONArray;
tempParams: TArray<TJSONObject>;
param: IIdentifierNode;
i: Integer;
begin begin
for param in Node.Parameters do bodyObj := Accept(Node.Body);
param.Accept(Self);
Node.Body.Accept(Self);
bodyObj := FJsonObjectStack.Pop;
SetLength(tempParams, Length(Node.Parameters));
for i := High(tempParams) downto 0 do
tempParams[i] := FJsonObjectStack.Pop;
paramsArray := TJSONArray.Create; paramsArray := TJSONArray.Create;
for i := 0 to High(tempParams) do for var param in Node.Parameters do
paramsArray.Add(tempParams[i]); paramsArray.Add(Accept(param));
obj := TJSONObject.Create; Result := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('LambdaExpr')); Result.AddPair('NodeType', TJSONString.Create('LambdaExpr'));
obj.AddPair('Parameters', paramsArray); Result.AddPair('Parameters', paramsArray);
obj.AddPair('Body', bodyObj); Result.AddPair('Body', bodyObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; function TJsonAstConverter.VisitMacroDefinition(const Node: IMacroDefinitionNode): TJSONObject;
var var
obj, nameObj, bodyObj: TJSONObject; nameObj, bodyObj: TJSONObject;
paramsArray: TJSONArray; paramsArray: TJSONArray;
tempParams: TArray<TJSONObject>;
param: IIdentifierNode;
i: Integer;
begin begin
Node.Name.Accept(Self); nameObj := Accept(Node.Name);
for param in Node.Parameters do bodyObj := Accept(Node.Body);
param.Accept(Self);
Node.Body.Accept(Self);
bodyObj := FJsonObjectStack.Pop;
SetLength(tempParams, Length(Node.Parameters));
for i := High(tempParams) downto 0 do
tempParams[i] := FJsonObjectStack.Pop;
nameObj := FJsonObjectStack.Pop;
paramsArray := TJSONArray.Create; paramsArray := TJSONArray.Create;
for i := 0 to High(tempParams) do for var param in Node.Parameters do
paramsArray.Add(tempParams[i]); paramsArray.Add(Accept(param));
obj := TJSONObject.Create; Result := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('MacroDef')); Result.AddPair('NodeType', TJSONString.Create('MacroDef'));
obj.AddPair('Name', nameObj); Result.AddPair('Name', nameObj);
obj.AddPair('Parameters', paramsArray); Result.AddPair('Parameters', paramsArray);
obj.AddPair('Body', bodyObj); Result.AddPair('Body', bodyObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; function TJsonAstConverter.VisitQuasiquote(const Node: IQuasiquoteNode): TJSONObject;
var
obj, exprObj: TJSONObject;
begin begin
Node.Expression.Accept(Self); Result := TJSONObject.Create;
exprObj := FJsonObjectStack.Pop; Result.AddPair('NodeType', TJSONString.Create('Quasiquote'));
Result.AddPair('Expression', Accept(Node.Expression));
obj := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('Quasiquote'));
obj.AddPair('Expression', exprObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitUnquote(const Node: IUnquoteNode): TDataValue; function TJsonAstConverter.VisitUnquote(const Node: IUnquoteNode): TJSONObject;
var
obj, exprObj: TJSONObject;
begin begin
Node.Expression.Accept(Self); Result := TJSONObject.Create;
exprObj := FJsonObjectStack.Pop; Result.AddPair('NodeType', TJSONString.Create('Unquote'));
Result.AddPair('Expression', Accept(Node.Expression));
obj := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('Unquote'));
obj.AddPair('Expression', exprObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; function TJsonAstConverter.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TJSONObject;
var
obj, exprObj: TJSONObject;
begin begin
Node.Expression.Accept(Self); Result := TJSONObject.Create;
exprObj := FJsonObjectStack.Pop; Result.AddPair('NodeType', TJSONString.Create('UnquoteSplicing'));
Result.AddPair('Expression', Accept(Node.Expression));
obj := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('UnquoteSplicing'));
obj.AddPair('Expression', exprObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; function TJsonAstConverter.VisitFunctionCall(const Node: IFunctionCallNode): TJSONObject;
var var
obj, calleeObj: TJSONObject; calleeObj: TJSONObject;
argsArray: TJSONArray; argsArray: TJSONArray;
tempArgs: TArray<TJSONObject>;
arg: IAstNode; arg: IAstNode;
i: Integer;
begin begin
Node.Callee.Accept(Self); calleeObj := Accept(Node.Callee);
for arg in Node.Arguments do
arg.Accept(Self);
SetLength(tempArgs, Length(Node.Arguments));
for i := High(tempArgs) downto 0 do
tempArgs[i] := FJsonObjectStack.Pop;
argsArray := TJSONArray.Create; argsArray := TJSONArray.Create;
for i := 0 to High(tempArgs) do for arg in Node.Arguments do
argsArray.Add(tempArgs[i]); argsArray.Add(Accept(arg));
calleeObj := FJsonObjectStack.Pop; Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('FunctionCall'));
obj := TJSONObject.Create; Result.AddPair('Callee', calleeObj);
obj.AddPair('NodeType', TJSONString.Create('FunctionCall')); Result.AddPair('Arguments', argsArray);
obj.AddPair('Callee', calleeObj);
obj.AddPair('Arguments', argsArray);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; function TJsonAstConverter.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TJSONObject;
var var
obj, calleeObj, bodyObj: TJSONObject; calleeObj, bodyObj: TJSONObject;
argsArray: TJSONArray; argsArray: TJSONArray;
tempArgs: TArray<TJSONObject>;
arg: IAstNode; arg: IAstNode;
i: Integer;
begin begin
// Serialize all children first: Callee, Arguments, and the new ExpandedBody calleeObj := Accept(Node.Callee);
Node.Callee.Accept(Self); bodyObj := Accept(Node.ExpandedBody);
for arg in Node.Arguments do
arg.Accept(Self);
Node.ExpandedBody.Accept(Self);
// Pop children's JSON from stack in reverse order of visitation
bodyObj := FJsonObjectStack.Pop;
SetLength(tempArgs, Length(Node.Arguments));
for i := High(tempArgs) downto 0 do
tempArgs[i] := FJsonObjectStack.Pop;
calleeObj := FJsonObjectStack.Pop;
// Build JSON array for arguments
argsArray := TJSONArray.Create;
for var argObj in tempArgs do
argsArray.Add(argObj);
// Create the final JSON object for this node
obj := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('MacroExpansion'));
obj.AddPair('Callee', calleeObj);
obj.AddPair('Arguments', argsArray);
obj.AddPair('ExpandedBody', bodyObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end;
function TJsonAstConverter.VisitRecurNode(const Node: IRecurNode): TDataValue;
var
obj: TJSONObject;
argsArray: TJSONArray;
tempArgs: TArray<TJSONObject>;
arg: IAstNode;
i: Integer;
begin
for arg in Node.Arguments do
arg.Accept(Self);
SetLength(tempArgs, Length(Node.Arguments));
for i := High(tempArgs) downto 0 do
tempArgs[i] := FJsonObjectStack.Pop;
argsArray := TJSONArray.Create; argsArray := TJSONArray.Create;
for i := 0 to High(tempArgs) do for arg in Node.Arguments do
argsArray.Add(tempArgs[i]); argsArray.Add(Accept(arg));
obj := TJSONObject.Create; Result := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('Recur')); Result.AddPair('NodeType', TJSONString.Create('MacroExpansion'));
obj.AddPair('Arguments', argsArray); Result.AddPair('Callee', calleeObj);
FJsonObjectStack.Push(obj); Result.AddPair('Arguments', argsArray);
Result := TDataValue.Void; Result.AddPair('ExpandedBody', bodyObj);
end; end;
function TJsonAstConverter.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; function TJsonAstConverter.VisitRecurNode(const Node: IRecurNode): TJSONObject;
var
argsArray: TJSONArray;
arg: IAstNode;
begin
argsArray := TJSONArray.Create;
for arg in Node.Arguments do
argsArray.Add(Accept(arg));
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Recur'));
Result.AddPair('Arguments', argsArray);
end;
function TJsonAstConverter.VisitBlockExpression(const Node: IBlockExpressionNode): TJSONObject;
var var
obj: TJSONObject;
exprsArray: TJSONArray; exprsArray: TJSONArray;
tempExprs: TArray<TJSONObject>;
expr: IAstNode; expr: IAstNode;
i: Integer;
begin begin
for expr in Node.Expressions do
expr.Accept(Self);
SetLength(tempExprs, Length(Node.Expressions));
for i := High(tempExprs) downto 0 do
tempExprs[i] := FJsonObjectStack.Pop;
exprsArray := TJSONArray.Create; exprsArray := TJSONArray.Create;
for i := 0 to High(tempExprs) do for expr in Node.Expressions do
exprsArray.Add(tempExprs[i]); exprsArray.Add(Accept(expr));
obj := TJSONObject.Create; Result := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('Block')); Result.AddPair('NodeType', TJSONString.Create('Block'));
obj.AddPair('Expressions', exprsArray); Result.AddPair('Expressions', exprsArray);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; function TJsonAstConverter.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TJSONObject;
var var
obj, identObj, initObj: TJSONObject; identObj, initObj: TJSONObject;
begin begin
Node.Identifier.Accept(Self); identObj := Accept(Node.Identifier);
if Assigned(Node.Initializer) then initObj := Accept(Node.Initializer); // Accept handles nil
Node.Initializer.Accept(Self);
if Assigned(Node.Initializer) then Result := TJSONObject.Create;
initObj := FJsonObjectStack.Pop Result.AddPair('NodeType', TJSONString.Create('VarDecl'));
else Result.AddPair('Identifier', identObj);
initObj := nil;
identObj := FJsonObjectStack.Pop;
obj := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('VarDecl'));
obj.AddPair('Identifier', identObj);
if Assigned(initObj) then if Assigned(initObj) then
obj.AddPair('Initializer', initObj) Result.AddPair('Initializer', initObj)
else else
obj.AddPair('Initializer', TJSONNull.Create); Result.AddPair('Initializer', TJSONNull.Create);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitAssignment(const Node: IAssignmentNode): TDataValue; function TJsonAstConverter.VisitAssignment(const Node: IAssignmentNode): TJSONObject;
var var
obj, identObj, valueObj: TJSONObject; identObj, valueObj: TJSONObject;
begin begin
Node.Identifier.Accept(Self); identObj := Accept(Node.Identifier);
Node.Value.Accept(Self); valueObj := Accept(Node.Value);
valueObj := FJsonObjectStack.Pop; Result := TJSONObject.Create;
identObj := FJsonObjectStack.Pop; Result.AddPair('NodeType', TJSONString.Create('Assignment'));
Result.AddPair('Identifier', identObj);
obj := TJSONObject.Create; Result.AddPair('Value', valueObj);
obj.AddPair('NodeType', TJSONString.Create('Assignment'));
obj.AddPair('Identifier', identObj);
obj.AddPair('Value', valueObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitIndexer(const Node: IIndexerNode): TDataValue; function TJsonAstConverter.VisitIndexer(const Node: IIndexerNode): TJSONObject;
var var
obj, baseObj, indexObj: TJSONObject; baseObj, indexObj: TJSONObject;
begin begin
Node.Base.Accept(Self); baseObj := Accept(Node.Base);
Node.Index.Accept(Self); indexObj := Accept(Node.Index);
indexObj := FJsonObjectStack.Pop; Result := TJSONObject.Create;
baseObj := FJsonObjectStack.Pop; Result.AddPair('NodeType', TJSONString.Create('Indexer'));
Result.AddPair('Base', baseObj);
obj := TJSONObject.Create; Result.AddPair('Index', indexObj);
obj.AddPair('NodeType', TJSONString.Create('Indexer'));
obj.AddPair('Base', baseObj);
obj.AddPair('Index', indexObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; function TJsonAstConverter.VisitMemberAccess(const Node: IMemberAccessNode): TJSONObject;
var var
obj, baseObj, memberObj: TJSONObject; baseObj, memberObj: TJSONObject;
begin begin
Node.Base.Accept(Self); baseObj := Accept(Node.Base);
Node.Member.Accept(Self); memberObj := Accept(Node.Member);
memberObj := FJsonObjectStack.Pop; Result := TJSONObject.Create;
baseObj := FJsonObjectStack.Pop; Result.AddPair('NodeType', TJSONString.Create('MemberAccess'));
Result.AddPair('Base', baseObj);
obj := TJSONObject.Create; Result.AddPair('Member', memberObj);
obj.AddPair('NodeType', TJSONString.Create('MemberAccess'));
obj.AddPair('Base', baseObj);
obj.AddPair('Member', memberObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; function TJsonAstConverter.VisitRecordLiteral(const Node: IRecordLiteralNode): TJSONObject;
var var
obj, fieldObj: TJSONObject;
fieldsArray: TJSONArray; fieldsArray: TJSONArray;
field: TRecordFieldLiteral; field: TRecordFieldLiteral;
fieldObj: TJSONObject;
begin begin
obj := TJSONObject.Create; Result := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('RecordLiteral')); Result.AddPair('NodeType', TJSONString.Create('RecordLiteral'));
fieldsArray := TJSONArray.Create; fieldsArray := TJSONArray.Create;
for field in Node.Fields do for field in Node.Fields do
begin begin
field.Value.Accept(Self); // This pushes the value JSON onto the stack
fieldObj := TJSONObject.Create; fieldObj := TJSONObject.Create;
fieldObj.AddPair('Name', TJSONString.Create(field.Key.Value.Name)); fieldObj.AddPair('Name', TJSONString.Create(field.Key.Value.Name));
fieldObj.AddPair('Value', FJsonObjectStack.Pop); fieldObj.AddPair('Value', Accept(field.Value)); // Get TJSONObject
fieldsArray.Add(fieldObj); fieldsArray.Add(fieldObj);
end; end;
obj.AddPair('Fields', fieldsArray); Result.AddPair('Fields', fieldsArray);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; function TJsonAstConverter.VisitCreateSeries(const Node: ICreateSeriesNode): TJSONObject;
var
obj: TJSONObject;
begin begin
obj := TJSONObject.Create; Result := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('CreateSeries')); Result.AddPair('NodeType', TJSONString.Create('CreateSeries'));
obj.AddPair('Definition', TJSONString.Create(Node.Definition)); Result.AddPair('Definition', TJSONString.Create(Node.Definition));
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; function TJsonAstConverter.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TJSONObject;
var var
obj, seriesObj, valueObj, lookbackObj: TJSONObject; seriesObj, valueObj, lookbackObj: TJSONObject;
begin begin
Node.Series.Accept(Self); seriesObj := Accept(Node.Series);
Node.Value.Accept(Self); valueObj := Accept(Node.Value);
if Assigned(Node.Lookback) then lookbackObj := Accept(Node.Lookback); // Accept handles nil
Node.Lookback.Accept(Self);
if Assigned(Node.Lookback) then Result := TJSONObject.Create;
lookbackObj := FJsonObjectStack.Pop Result.AddPair('NodeType', TJSONString.Create('AddSeriesItem'));
else Result.AddPair('Series', seriesObj);
lookbackObj := nil; Result.AddPair('Value', valueObj);
valueObj := FJsonObjectStack.Pop;
seriesObj := FJsonObjectStack.Pop;
obj := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('AddSeriesItem'));
obj.AddPair('Series', seriesObj);
obj.AddPair('Value', valueObj);
if Assigned(lookbackObj) then if Assigned(lookbackObj) then
obj.AddPair('Lookback', lookbackObj) Result.AddPair('Lookback', lookbackObj)
else else
obj.AddPair('Lookback', TJSONNull.Create); Result.AddPair('Lookback', TJSONNull.Create);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
function TJsonAstConverter.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; function TJsonAstConverter.VisitSeriesLength(const Node: ISeriesLengthNode): TJSONObject;
var var
obj, seriesObj: TJSONObject; seriesObj: TJSONObject;
begin begin
Node.Series.Accept(Self); seriesObj := Accept(Node.Series);
seriesObj := FJsonObjectStack.Pop;
obj := TJSONObject.Create; Result := TJSONObject.Create;
obj.AddPair('NodeType', TJSONString.Create('SeriesLength')); Result.AddPair('NodeType', TJSONString.Create('SeriesLength'));
obj.AddPair('Series', seriesObj); Result.AddPair('Series', seriesObj);
FJsonObjectStack.Push(obj);
Result := TDataValue.Void;
end; end;
{ TJsonAstConverter - Deserialization } { TJsonAstConverter - Deserialization }
@@ -827,7 +636,7 @@ end;
function TJsonAstConverter.JsonToUnquoteSplicingNode(const AObj: TJSONObject): IUnquoteSplicingNode; function TJsonAstConverter.JsonToUnquoteSplicingNode(const AObj: TJSONObject): IUnquoteSplicingNode;
begin begin
Result := TAst.UnquoteSplicing(JsonToNode(AObj.GetValue('Expression'))); Result := TAst.UnquoteSplicing(JsonToNode(AObj.GetValue('Expression')).AsQuasiquote);
end; end;
function TJsonAstConverter.JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode; function TJsonAstConverter.JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode;
@@ -1020,16 +829,12 @@ end;
function TJsonAstConverter.Serialize(const RootNode: IAstNode): TJSONObject; function TJsonAstConverter.Serialize(const RootNode: IAstNode): TJSONObject;
begin begin
FJsonObjectStack.Clear;
if not Assigned(RootNode) then if not Assigned(RootNode) then
exit(nil); exit(nil);
RootNode.Accept(Self); // Call Accept, which returns TDataValue(vkGeneric(TJSONObject))
// and unwrap it.
if FJsonObjectStack.Count <> 1 then Result := RootNode.Accept(Self).AsGeneric<TJSONObject>;
raise EInvalidOpException.Create('JSON serialization stack is corrupt.');
Result := FJsonObjectStack.Pop;
end; end;
end. end.
+23 -27
View File
@@ -12,8 +12,7 @@ uses
Myc.Ast.Visitor, Myc.Ast.Visitor,
Myc.Ast.Scope, Myc.Ast.Scope,
Myc.Ast.Types, Myc.Ast.Types,
Myc.Ast, Myc.Ast;
Myc.Ast.Binding.Nodes;
type type
IAstLowerer = interface(IAstVisitor) IAstLowerer = interface(IAstVisitor)
@@ -30,7 +29,7 @@ type
function SetType(const Node: IAstNode; const AType: IStaticType): IAstNode; function SetType(const Node: IAstNode; const AType: IStaticType): IAstNode;
protected protected
// Override to find nodes to lower // Override to find nodes to lower
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
public public
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
@@ -50,6 +49,7 @@ uses
constructor TAstLowerer.Create; constructor TAstLowerer.Create;
var var
op: TScalar.TBinaryOp; op: TScalar.TBinaryOp;
uOp: TScalar.TUnaryOp; // Added for unary
begin begin
inherited Create; inherited Create;
@@ -59,7 +59,8 @@ begin
FBinaryOperators.Add(op.ToString, op); FBinaryOperators.Add(op.ToString, op);
FUnaryOperators := TDictionary<string, TScalar.TUnaryOp>.Create; FUnaryOperators := TDictionary<string, TScalar.TUnaryOp>.Create;
FUnaryOperators.Add('not', TScalar.TUnaryOp.Not); for uOp := Low(TScalar.TUnaryOp) to High(TScalar.TUnaryOp) do // Changed
FUnaryOperators.Add(uOp.ToString, uOp);
// Note: '-' is handled as a special case in VisitFunctionCall // Note: '-' is handled as a special case in VisitFunctionCall
end; end;
@@ -78,11 +79,9 @@ end;
function TAstLowerer.Execute(const RootNode: IAstNode): IAstNode; function TAstLowerer.Execute(const RootNode: IAstNode): IAstNode;
begin begin
var transformedValue := Accept(RootNode); Result := Accept(RootNode); // Use IAstNode-returning Accept
if transformedValue.IsVoid then if not Assigned(Result) then
Result := TAst.Block([]) Result := TAst.Block([]);
else
Result := transformedValue.AsIntf<IAstNode>;
if Assigned(Result) then if Assigned(Result) then
(Result as TAstNode).StaticType := (Result as TAstNode).StaticType; (Result as TAstNode).StaticType := (Result as TAstNode).StaticType;
@@ -90,25 +89,18 @@ end;
function TAstLowerer.SetType(const Node: IAstNode; const AType: IStaticType): IAstNode; function TAstLowerer.SetType(const Node: IAstNode; const AType: IStaticType): IAstNode;
begin begin
(Node as TAstNode).StaticType := AType; if Assigned(Node) then // Add nil check for safety
(Node as TAstNode).StaticType := AType;
Result := Node; Result := Node;
end; end;
function TAstLowerer.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; function TAstLowerer.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
var var
calleeIdentifier: TIdentifierNode; calleeIdentifier: TIdentifierNode;
binaryOp: TScalar.TBinaryOp; binaryOp: TScalar.TBinaryOp;
unaryOp: TScalar.TUnaryOp; unaryOp: TScalar.TUnaryOp;
left, right: IAstNode; left, right: IAstNode;
begin begin
// --- Transformation: Keyword-as-Function ---
// (This logic is correctly handled in TAstBinder)
if (Node.Callee is TKeywordNode) then
begin
// This node should have been transformed by the TAstBinder.
exit(inherited VisitFunctionCall(Node));
end;
// --- Optimization: Operator Folding --- // --- Optimization: Operator Folding ---
if (Node.Callee is TIdentifierNode) then if (Node.Callee is TIdentifierNode) then
begin begin
@@ -118,10 +110,12 @@ begin
begin begin
if FBinaryOperators.TryGetValue(calleeIdentifier.Name, binaryOp) then if FBinaryOperators.TryGetValue(calleeIdentifier.Name, binaryOp) then
begin begin
left := Accept(Node.Arguments[0]).AsIntf<IAstNode>; // Note: We MUST visit children *before* creating the new node
right := Accept(Node.Arguments[1]).AsIntf<IAstNode>; left := Accept(Node.Arguments[0]);
right := Accept(Node.Arguments[1]);
var binExpr := TAst.BinaryExpr(left, binaryOp, right); var binExpr := TAst.BinaryExpr(left, binaryOp, right);
Result := TDataValue.FromIntf<IAstNode>(SetType(binExpr, (Node as TAstNode).StaticType)); // Copy metadata (StaticType) from old node to new node
Result := SetType(binExpr, (Node as TAstNode).StaticType);
exit; exit;
end; end;
end; end;
@@ -130,23 +124,25 @@ begin
begin begin
if FUnaryOperators.TryGetValue(calleeIdentifier.Name, unaryOp) then if FUnaryOperators.TryGetValue(calleeIdentifier.Name, unaryOp) then
begin begin
right := Accept(Node.Arguments[0]).AsIntf<IAstNode>; right := Accept(Node.Arguments[0]);
var unExpr := TAst.UnaryExpr(unaryOp, right); var unExpr := TAst.UnaryExpr(unaryOp, right);
Result := TDataValue.FromIntf<IAstNode>(SetType(unExpr, (Node as TAstNode).StaticType)); // Copy metadata
Result := SetType(unExpr, (Node as TAstNode).StaticType);
exit; exit;
end; end;
if (calleeIdentifier.Name = '-') then if (calleeIdentifier.Name = '-') then
begin begin
right := Accept(Node.Arguments[0]).AsIntf<IAstNode>; right := Accept(Node.Arguments[0]);
var unExpr := TAst.UnaryExpr(TScalar.TUnaryOp.Negate, right); var unExpr := TAst.UnaryExpr(TScalar.TUnaryOp.Negate, right);
Result := TDataValue.FromIntf<IAstNode>(SetType(unExpr, (Node as TAstNode).StaticType)); // Copy metadata
Result := SetType(unExpr, (Node as TAstNode).StaticType);
exit; exit;
end; end;
end; end;
end; end;
// If no rewrite matched, continue traversal // If no rewrite matched, continue traversal using the base implementation
Result := inherited VisitFunctionCall(Node); Result := inherited VisitFunctionCall(Node);
end; end;
+54 -51
View File
@@ -12,8 +12,7 @@ uses
Myc.Ast.Visitor, Myc.Ast.Visitor,
Myc.Ast.Scope, Myc.Ast.Scope,
Myc.Ast.Types, Myc.Ast.Types,
Myc.Ast, Myc.Ast;
Myc.Ast.Binding; // Required for TAstBinder.Bind
type type
IAstMacroExpander = interface(IAstVisitor) IAstMacroExpander = interface(IAstVisitor)
@@ -31,11 +30,12 @@ type
FMacroScope: IExecutionScope; FMacroScope: IExecutionScope;
function TransformAndSpliceNodes(const ANodes: TArray<IAstNode>): TArray<IAstNode>; function TransformAndSpliceNodes(const ANodes: TArray<IAstNode>): TArray<IAstNode>;
protected protected
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override; // Override the new IAstNode-returning methods
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override; function VisitUnquote(const Node: IUnquoteNode): IAstNode; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override; function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; override;
public public
constructor Create(const AMacroScope: IExecutionScope; const AEvaluate: TEvaluateProc); constructor Create(const AMacroScope: IExecutionScope; const AEvaluate: TEvaluateProc);
class function Expand(const MacroScope: IExecutionScope; const RootNode: IAstNode; const AEvaluate: TEvaluateProc): IAstNode; class function Expand(const MacroScope: IExecutionScope; const RootNode: IAstNode; const AEvaluate: TEvaluateProc): IAstNode;
@@ -50,11 +50,10 @@ type
FCurrentDescriptor: IScopeDescriptor; FCurrentDescriptor: IScopeDescriptor;
FEvaluatorFactory: TEvaluatorFactory; FEvaluatorFactory: TEvaluatorFactory;
protected protected
function Accept(const Node: IAstNode): TDataValue; override;
// Finds macro definitions // Finds macro definitions
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override;
// Finds and expands macro calls // Finds and expands macro calls
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
public public
constructor Create(const AInitialScope: IExecutionScope; const AEvaluatorFactory: TEvaluatorFactory); constructor Create(const AInitialScope: IExecutionScope; const AEvaluatorFactory: TEvaluatorFactory);
function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode; function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
@@ -71,6 +70,7 @@ implementation
uses uses
System.Generics.Defaults, System.Generics.Defaults,
Myc.Ast.Binding, // Required for TAstBinder.Bind
Myc.Ast.Evaluator, // Required for TEvaluatorVisitor.Execute Myc.Ast.Evaluator, // Required for TEvaluatorVisitor.Execute
Myc.Data.Keyword; Myc.Data.Keyword;
@@ -89,14 +89,19 @@ class function TExpansionVisitor.Expand(
const AEvaluate: TEvaluateProc const AEvaluate: TEvaluateProc
): IAstNode; ): IAstNode;
begin begin
var expander := TExpansionVisitor.Create(MacroScope, AEvaluate) as IAstTransformer; var expander := TExpansionVisitor.Create(MacroScope, AEvaluate);
Result := expander.Execute(RootNode); try
Result := expander.Accept(RootNode); // Use IAstNode-returning Accept
finally
expander.Free;
end;
end; end;
function TExpansionVisitor.TransformAndSpliceNodes(const ANodes: TArray<IAstNode>): TArray<IAstNode>; function TExpansionVisitor.TransformAndSpliceNodes(const ANodes: TArray<IAstNode>): TArray<IAstNode>;
var var
newList: TList<IAstNode>; newList: TList<IAstNode>;
nodeToSplice: IAstNode; nodeToSplice: IAstNode;
transformedNode: IAstNode;
begin begin
newList := TList<IAstNode>.Create; newList := TList<IAstNode>.Create;
try try
@@ -126,9 +131,10 @@ begin
end end
else else
begin begin
var transformedValue := node.Accept(self); // Use the IAstNode-returning Accept helper
if not transformedValue.IsVoid then transformedNode := Self.Accept(node);
newList.Add(transformedValue.AsIntf<IAstNode>); if Assigned(transformedNode) then
newList.Add(transformedNode);
end; end;
end; end;
Result := newList.ToArray; Result := newList.ToArray;
@@ -137,7 +143,7 @@ begin
end; end;
end; end;
function TExpansionVisitor.VisitUnquote(const Node: IUnquoteNode): TDataValue; function TExpansionVisitor.VisitUnquote(const Node: IUnquoteNode): IAstNode;
var var
value: TDataValue; value: TDataValue;
expr: IAstNode; expr: IAstNode;
@@ -155,7 +161,7 @@ begin
var argValue := FMacroScope.Values[symbol.Address]; var argValue := FMacroScope.Values[symbol.Address];
if argValue.Kind = vkInterface then if argValue.Kind = vkInterface then
begin begin
Result := argValue; Result := argValue.AsIntf<IAstNode>; // Return the node directly
exit; exit;
end; end;
end; end;
@@ -167,44 +173,44 @@ begin
// If the result is an AST node (e.g. from a helper function), return it directly. // If the result is an AST node (e.g. from a helper function), return it directly.
if value.Kind = vkInterface then if value.Kind = vkInterface then
begin begin
Result := value; Result := value.AsIntf<IAstNode>;
exit; exit;
end; end;
// If the result is a scalar, text, or void, wrap it in a TConstantNode. // If the result is a scalar, text, or void, wrap it in a TConstantNode.
if value.Kind in [vkScalar, vkText, vkVoid] then if value.Kind in [vkScalar, vkText, vkVoid] then
begin begin
Result := TDataValue.FromIntf<IAstNode>(TAst.Constant(value)); Result := TAst.Constant(value);
end end
else else
raise Exception.CreateFmt('Cannot unquote complex runtime value of type %s at compile time.', [value.Kind.ToString]); raise Exception.CreateFmt('Cannot unquote complex runtime value of type %s at compile time.', [value.Kind.ToString]);
end; end;
function TExpansionVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; function TExpansionVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
begin begin
// This should be handled by the caller (VisitFunctionCall / VisitBlockExpression) // This should be handled by the caller (VisitFunctionCall / VisitBlockExpression)
raise Exception.Create('Unquote-splicing (`~@`) can only appear inside a list form (e.g., a function call or a `do` block).'); raise Exception.Create('Unquote-splicing (`~@`) can only appear inside a list form (e.g., a function call or a `do` block).');
end; end;
function TExpansionVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; function TExpansionVisitor.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
var var
newArgs: TArray<IAstNode>; newArgs: TArray<IAstNode>;
transformedCallee: IAstNode; transformedCallee: IAstNode;
begin begin
transformedCallee := Self.Accept(Node.Callee).AsIntf<IAstNode>; transformedCallee := Self.Accept(Node.Callee); // Calls IAstNode helper
newArgs := TransformAndSpliceNodes(Node.Arguments); newArgs := TransformAndSpliceNodes(Node.Arguments);
Result := TDataValue.FromIntf<IFunctionCallNode>(TAst.FunctionCall(transformedCallee, newArgs)); Result := TAst.FunctionCall(transformedCallee, newArgs);
end; end;
function TExpansionVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; function TExpansionVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
var var
newExprs: TArray<IAstNode>; newExprs: TArray<IAstNode>;
begin begin
newExprs := TransformAndSpliceNodes(Node.Expressions); newExprs := TransformAndSpliceNodes(Node.Expressions);
Result := TDataValue.FromIntf<IBlockExpressionNode>(TAst.Block(newExprs)); Result := TAst.Block(newExprs);
end; end;
function TExpansionVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; function TExpansionVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
begin begin
// Record literals do not support splicing. // Record literals do not support splicing.
// We just use the default TAstTransformer implementation which visits child values. // We just use the default TAstTransformer implementation which visits child values.
@@ -238,40 +244,30 @@ end;
function TMacroExpander.Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode; function TMacroExpander.Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
begin begin
// This executes the transformation (Accept) // This executes the transformation (Accept)
var transformedValue := Accept(RootNode); Result := Accept(RootNode); // Calls the IAstNode-returning helper
if transformedValue.IsVoid then if not Assigned(Result) then
Result := TAst.Block([]) Result := TAst.Block([]); // e.g. if root was (defmacro ...)
else
Result := transformedValue.AsIntf<IAstNode>;
Descriptor := FCurrentDescriptor; Descriptor := FCurrentDescriptor;
end; end;
function TMacroExpander.Accept(const Node: IAstNode): TDataValue; function TMacroExpander.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode;
begin
// Standard transformer Accept
if (not Assigned(Node)) or Done then
exit;
Result := Node.Accept(Self);
end;
function TMacroExpander.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
begin begin
// Register the macro in the current descriptor // Register the macro in the current descriptor
FCurrentDescriptor.DefineMacro(Node.Name.Name, Node); FCurrentDescriptor.DefineMacro(Node.Name.Name, Node);
// Remove the (defmacro ...) node from the AST; it's compile-time only. // Remove the (defmacro ...) node from the AST; it's compile-time only.
Result := TDataValue.Void; Result := nil;
end; end;
function TMacroExpander.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; function TMacroExpander.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
var var
calleeIdentifier: TIdentifierNode; calleeIdentifier: TIdentifierNode;
macroDef: IMacroDefinitionNode; macroDef: IMacroDefinitionNode;
i: Integer; i: Integer;
begin begin
// Check if this is an identifier // Check if this is an identifier
if not (Node.Callee is TIdentifierNode) then if Node.Callee.Kind <> akIdentifier then
exit(inherited VisitFunctionCall(Node)); exit(inherited VisitFunctionCall(Node));
calleeIdentifier := Node.Callee as TIdentifierNode; calleeIdentifier := Node.Callee as TIdentifierNode;
@@ -292,7 +288,7 @@ begin
// 2. Populate scope: Map parameter names to the *un-evaluated* AST nodes // 2. Populate scope: Map parameter names to the *un-evaluated* AST nodes
for i := 0 to High(params) do for i := 0 to High(params) do
expansionScope.Define(params[i].Name, TDataValue.FromIntf<IAstNode>(Node.Arguments[i])); expansionScope.Define(params[i].Name, TDataValue.FromIntf<IAstNode>(Node.Arguments[i])); // Use FromIntf
// 3. Create the evaluation callback (TEvaluateProc) // 3. Create the evaluation callback (TEvaluateProc)
var evaluatorProc: TEvaluateProc; var evaluatorProc: TEvaluateProc;
@@ -300,26 +296,33 @@ begin
function(const ANodeToEvaluate: IAstNode): TDataValue function(const ANodeToEvaluate: IAstNode): TDataValue
var var
subDescriptor: IScopeDescriptor; subDescriptor: IScopeDescriptor;
evalScope: IExecutionScope;
evaluator: IEvaluatorVisitor;
boundSubAst: IAstNode;
begin begin
// This is the compile-time evaluation (Binder + Evaluator) // This is the compile-time evaluation (Binder + Evaluator)
// It runs in the *current* context (FInitialScope + FCurrentDescriptor) // It runs in the *current* context (FInitialScope + FCurrentDescriptor)
// not the expansionScope.
var tempInitScope := TScope.CreateScope(FInitialScope.Parent, FCurrentDescriptor, nil); var tempInitScope := TScope.CreateScope(FInitialScope.Parent, FCurrentDescriptor, nil);
var boundSubAst := TAstBinder.Bind(tempInitScope, ANodeToEvaluate, subDescriptor);
var evalScope := subDescriptor.CreateScope(tempInitScope); // Bind (mutates ANodeToEvaluate) and get its descriptor
var evaluator := FEvaluatorFactory(evalScope); boundSubAst := TAstBinder.Bind(tempInitScope, ANodeToEvaluate, subDescriptor);
// Create eval scope from the new descriptor
evalScope := subDescriptor.CreateScope(tempInitScope);
evaluator := FEvaluatorFactory(evalScope);
Result := evaluator.Execute(boundSubAst); Result := evaluator.Execute(boundSubAst);
end; end;
// 4. Expand the macro body using the TExpansionVisitor // 4. Expand the macro body using the TExpansionVisitor
var expandedBody := TExpansionVisitor.Expand(expansionScope, macroDef.Body.Expression, evaluatorProc); var expandedBody := TExpansionVisitor.Expand(expansionScope, macroDef.Body.AsUnquoteSplicing.Expression, evaluatorProc);
// 5. Wrap the result in a MacroExpansionNode (for debugging/tracing) // 5. Wrap the result in a MacroExpansionNode (for debugging/tracing)
var macroNode := TMacroExpansionNode.Create(Node, expandedBody); var macroNode := TAst.MacroExpansionNode(Node, expandedBody);
// 6. IMPORTANT: Re-run the expander on the *new* AST fragment // 6. IMPORTANT: Re-run the expander on the *new* AST fragment
// to handle macros that expand into other macros. // to handle macros that expand into other macros.
Result := Self.Accept(macroNode); Result := Self.Accept(macroNode); // Calls the IAstNode helper, returns IAstNode
end; end;
end. end.
+62 -4
View File
@@ -38,6 +38,34 @@ type
ISeriesLengthNode = interface; ISeriesLengthNode = interface;
IRecurNode = interface; IRecurNode = interface;
// Defines the concrete kinds of AST nodes
TAstNodeKind = (
akConstant,
akIdentifier,
akKeyword,
akBinaryExpression,
akUnaryExpression,
akIfExpression,
akTernaryExpression,
akLambdaExpression,
akFunctionCall,
akMacroExpansion,
akBlockExpression,
akVariableDeclaration,
akAssignment,
akMacroDefinition,
akQuasiquote,
akUnquote,
akUnquoteSplicing,
akIndexer,
akMemberAccess,
akRecordLiteral,
akCreateSeries,
akAddSeriesItem,
akSeriesLength,
akRecur
);
// --- Concrete Type Definitions --- // --- Concrete Type Definitions ---
// Defines how an identifier's address was resolved. // Defines how an identifier's address was resolved.
@@ -88,7 +116,37 @@ type
end; end;
IAstNode = interface(IInterface) IAstNode = interface(IInterface)
{$region 'private'}
function GetKind: TAstNodeKind;
{$endregion}
function Accept(const Visitor: IAstVisitor): TDataValue; function Accept(const Visitor: IAstVisitor): TDataValue;
function AsConstant: IConstantNode;
function AsIdentifier: IIdentifierNode;
function AsKeyword: IKeywordNode;
function AsBinaryExpression: IBinaryExpressionNode;
function AsUnaryExpression: IUnaryExpressionNode;
function AsIfExpression: IIfExpressionNode;
function AsTernaryExpression: ITernaryExpressionNode;
function AsLambdaExpression: ILambdaExpressionNode;
function AsFunctionCall: IFunctionCallNode;
function AsMacroExpansion: IMacroExpansionNode;
function AsBlockExpression: IBlockExpressionNode;
function AsVariableDeclaration: IVariableDeclarationNode;
function AsAssignment: IAssignmentNode;
function AsMacroDefinition: IMacroDefinitionNode;
function AsQuasiquote: IQuasiquoteNode;
function AsUnquote: IUnquoteNode;
function AsUnquoteSplicing: IUnquoteSplicingNode;
function AsIndexer: IIndexerNode;
function AsMemberAccess: IMemberAccessNode;
function AsRecordLiteral: IRecordLiteralNode;
function AsCreateSeries: ICreateSeriesNode;
function AsAddSeriesItem: IAddSeriesItemNode;
function AsSeriesLength: ISeriesLengthNode;
function AsRecur: IRecurNode;
property Kind: TAstNodeKind read GetKind;
end; end;
IConstantNode = interface(IAstNode) IConstantNode = interface(IAstNode)
@@ -221,11 +279,11 @@ type
{$region 'private'} {$region 'private'}
function GetName: IIdentifierNode; function GetName: IIdentifierNode;
function GetParameters: TArray<IIdentifierNode>; function GetParameters: TArray<IIdentifierNode>;
function GetBody: IQuasiquoteNode; function GetBody: IAstNode;
{$endregion} {$endregion}
property Name: IIdentifierNode read GetName; property Name: IIdentifierNode read GetName;
property Parameters: TArray<IIdentifierNode> read GetParameters; property Parameters: TArray<IIdentifierNode> read GetParameters;
property Body: IQuasiquoteNode read GetBody; property Body: IAstNode read GetBody;
end; end;
// Represents a quasiquoted expression, e.g., `(a b) // Represents a quasiquoted expression, e.g., `(a b)
@@ -247,9 +305,9 @@ type
// Represents an unquote-splicing expression, e.g., ~@y // Represents an unquote-splicing expression, e.g., ~@y
IUnquoteSplicingNode = interface(IAstNode) IUnquoteSplicingNode = interface(IAstNode)
{$region 'private'} {$region 'private'}
function GetExpression: IAstNode; function GetExpression: IQuasiquoteNode;
{$endregion} {$endregion}
property Expression: IAstNode read GetExpression; property Expression: IQuasiquoteNode read GetExpression;
end; end;
IIndexerNode = interface(IAstNode) IIndexerNode = interface(IAstNode)
+2 -1
View File
@@ -278,7 +278,8 @@ begin
raise EArgumentException.Create('The argument to Memoize must be a function.'); raise EArgumentException.Create('The argument to Memoize must be a function.');
// create a managed dictionary // create a managed dictionary
var cCache: TDataValue := TDictionary<Int64, TDataValue>.Create; var cCache: TDataValue;
cCache.FromObj(TDictionary<Int64, TDataValue>.Create);
funcToMemoize := Args[0].AsMethod(); funcToMemoize := Args[0].AsMethod();
+61 -84
View File
@@ -29,7 +29,7 @@ var
atom ::= number | string | identifier | keyword atom ::= number | string | identifier | keyword
(* ---------------------------------------------------------------------- *) (* ---------------------------------------------------------------------- *)
(* ---- Reader-Macros (Syntactic Sugar) ---- *) (* ---- Reader-Macros (Syntactic Sugar) ---- *)
(* ---------------------------------------------------------------------- *) (* ---------------------------------------------------------------------- *)
reader_macro ::= "'" expression (* (quote ...) *) reader_macro ::= "'" expression (* (quote ...) *)
@@ -38,7 +38,7 @@ var
| "~@" expression (* (unquote-splicing ...) *) | "~@" expression (* (unquote-splicing ...) *)
(* ---------------------------------------------------------------------- *) (* ---------------------------------------------------------------------- *)
(* ---- Lists (S-Expressions) ---- *) (* ---- Lists (S-Expressions) ---- *)
(* ---------------------------------------------------------------------- *) (* ---------------------------------------------------------------------- *)
(* A list is the primary structure for code. Empty lists () are invalid. *) (* A list is the primary structure for code. Empty lists () are invalid. *)
@@ -64,7 +64,7 @@ var
function_call ::= expression expression* function_call ::= expression expression*
(* ---------------------------------------------------------------------- *) (* ---------------------------------------------------------------------- *)
(* ---- Parameter Lists and Records ---- *) (* ---- Parameter Lists and Records ---- *)
(* ---------------------------------------------------------------------- *) (* ---------------------------------------------------------------------- *)
(* A parameter list is *only* valid inside 'fn' and 'defmacro' *) (* A parameter list is *only* valid inside 'fn' and 'defmacro' *)
@@ -181,6 +181,7 @@ type
// --- Internal Printer Implementation --- // --- Internal Printer Implementation ---
// Inherits from the new non-generic TAstVisitor
TPrettyPrintVisitor = class(TAstVisitor) TPrettyPrintVisitor = class(TAstVisitor)
private private
FBuilder: TStringBuilder; FBuilder: TStringBuilder;
@@ -193,32 +194,33 @@ type
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
function GetResult: string; function GetResult: string;
// IAstVisitor // Helper
function Execute(const RootNode: IAstNode): TDataValue; function Execute(const RootNode: IAstNode): TDataValue;
function VisitConstant(const Node: IConstantNode): TDataValue; override; // Override abstract procedures from TAstVisitor
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; procedure VisitConstant(const Node: IConstantNode); override;
function VisitKeyword(const Node: IKeywordNode): TDataValue; override; procedure VisitIdentifier(const Node: IIdentifierNode); override;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; procedure VisitKeyword(const Node: IKeywordNode); override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; procedure VisitBinaryExpression(const Node: IBinaryExpressionNode); override;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; procedure VisitUnaryExpression(const Node: IUnaryExpressionNode); override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; procedure VisitIfExpression(const Node: IIfExpressionNode); override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; procedure VisitTernaryExpression(const Node: ITernaryExpressionNode); override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; procedure VisitLambdaExpression(const Node: ILambdaExpressionNode); override;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; override; procedure VisitMacroDefinition(const Node: IMacroDefinitionNode); override;
function VisitUnquote(const Node: IUnquoteNode): TDataValue; override; procedure VisitQuasiquote(const Node: IQuasiquoteNode); override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override; procedure VisitUnquote(const Node: IUnquoteNode); override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; procedure VisitUnquoteSplicing(const Node: IUnquoteSplicingNode); override;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override; procedure VisitFunctionCall(const Node: IFunctionCallNode); override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; procedure VisitMacroExpansionNode(const Node: IMacroExpansionNode); override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; procedure VisitBlockExpression(const Node: IBlockExpressionNode); override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; procedure VisitVariableDeclaration(const Node: IVariableDeclarationNode); override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override; procedure VisitAssignment(const Node: IAssignmentNode); override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; procedure VisitIndexer(const Node: IIndexerNode); override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override; procedure VisitMemberAccess(const Node: IMemberAccessNode); override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; procedure VisitRecordLiteral(const Node: IRecordLiteralNode); override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override; procedure VisitCreateSeries(const Node: ICreateSeriesNode); override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override; procedure VisitAddSeriesItem(const Node: IAddSeriesItemNode); override;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override; procedure VisitSeriesLength(const Node: ISeriesLengthNode); override;
procedure VisitRecurNode(const Node: IRecurNode); override;
end; end;
{ TLexer } { TLexer }
@@ -592,7 +594,7 @@ begin
if (Length(tailNodes) <> 3) or (tailTokens[0].Kind <> tkIdentifier) then if (Length(tailNodes) <> 3) or (tailTokens[0].Kind <> tkIdentifier) then
raise Exception.Create('Syntax Error: ''defmacro'' requires a name, a parameter list, and a body.'); raise Exception.Create('Syntax Error: ''defmacro'' requires a name, a parameter list, and a body.');
var macroName := IIdentifierNode(tailNodes[0]); var macroName := tailNodes[0].AsIdentifier;
var macroParams := elements[2].Params; var macroParams := elements[2].Params;
if tailTokens[2].Kind <> tkBacktick then if tailTokens[2].Kind <> tkBacktick then
@@ -670,7 +672,7 @@ begin
begin begin
NextToken; NextToken;
expr := ParseExpression; expr := ParseExpression;
Result.Node := TAst.UnquoteSplicing(expr.Node); Result.Node := TAst.UnquoteSplicing(expr.Node.AsQuasiquote);
end end
else else
begin begin
@@ -781,7 +783,7 @@ begin
Result := TDataValue.Void; Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitConstant(const Node: IConstantNode): TDataValue; procedure TPrettyPrintVisitor.VisitConstant(const Node: IConstantNode);
var var
val: TDataValue; val: TDataValue;
begin begin
@@ -790,22 +792,19 @@ begin
Append('"' + val.AsText + '"') Append('"' + val.AsText + '"')
else else
Append(val.ToString); Append(val.ToString);
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue; procedure TPrettyPrintVisitor.VisitIdentifier(const Node: IIdentifierNode);
begin begin
Append(Node.Name); Append(Node.Name);
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitKeyword(const Node: IKeywordNode): TDataValue; procedure TPrettyPrintVisitor.VisitKeyword(const Node: IKeywordNode);
begin begin
Append(':' + Node.Value.Name); Append(':' + Node.Value.Name);
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; procedure TPrettyPrintVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode);
begin begin
Append('(' + Node.Operator.ToString); Append('(' + Node.Operator.ToString);
Append(' '); Append(' ');
@@ -813,19 +812,17 @@ begin
Append(' '); Append(' ');
Node.Right.Accept(Self); Node.Right.Accept(Self);
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; procedure TPrettyPrintVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode);
begin begin
Append('(' + Node.Operator.ToString); Append('(' + Node.Operator.ToString);
Append(' '); Append(' ');
Node.Right.Accept(Self); Node.Right.Accept(Self);
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; procedure TPrettyPrintVisitor.VisitIfExpression(const Node: IIfExpressionNode);
begin begin
Append('(if '); Append('(if ');
Node.Condition.Accept(Self); Node.Condition.Accept(Self);
@@ -840,10 +837,9 @@ begin
Unindent; Unindent;
NewLine; NewLine;
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; procedure TPrettyPrintVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode);
begin begin
Append('(? '); Append('(? ');
Node.Condition.Accept(Self); Node.Condition.Accept(Self);
@@ -855,17 +851,15 @@ begin
Unindent; Unindent;
NewLine; NewLine;
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; procedure TPrettyPrintVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode);
var var
param: IIdentifierNode;
sb: TStringBuilder; sb: TStringBuilder;
begin begin
sb := TStringBuilder.Create; sb := TStringBuilder.Create;
try try
for param in Node.Parameters do for var param in Node.Parameters do
sb.Append(param.Name + ' '); sb.Append(param.Name + ' ');
if sb.Length > 0 then if sb.Length > 0 then
sb.Remove(sb.Length - 1, 1); // remove trailing space sb.Remove(sb.Length - 1, 1); // remove trailing space
@@ -881,17 +875,15 @@ begin
Unindent; Unindent;
NewLine; NewLine;
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; procedure TPrettyPrintVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode);
var var
param: IIdentifierNode;
sb: TStringBuilder; sb: TStringBuilder;
begin begin
sb := TStringBuilder.Create; sb := TStringBuilder.Create;
try try
for param in Node.Parameters do for var param in Node.Parameters do
sb.Append(param.Name + ' '); sb.Append(param.Name + ' ');
if sb.Length > 0 then if sb.Length > 0 then
sb.Remove(sb.Length - 1, 1); sb.Remove(sb.Length - 1, 1);
@@ -907,33 +899,29 @@ begin
Unindent; Unindent;
NewLine; NewLine;
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; procedure TPrettyPrintVisitor.VisitQuasiquote(const Node: IQuasiquoteNode);
begin begin
Append('`'); Append('`');
Node.Expression.Accept(Self); Node.Expression.Accept(Self);
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitUnquote(const Node: IUnquoteNode): TDataValue; procedure TPrettyPrintVisitor.VisitUnquote(const Node: IUnquoteNode);
begin begin
Append('~'); Append('~');
Node.Expression.Accept(Self); Node.Expression.Accept(Self);
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; procedure TPrettyPrintVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode);
begin begin
// To handle '~@', we need to check if the expression is an identifier '@'. // To handle '~@', we need to check if the expression is an identifier '@'.
// However, the parser logic now handles this, so we just print '~@'. // However, the parser logic now handles this, so we just print '~@'.
Append('~@'); Append('~@');
Node.Expression.Accept(Self); Node.Expression.Accept(Self);
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; procedure TPrettyPrintVisitor.VisitFunctionCall(const Node: IFunctionCallNode);
var var
arg: IAstNode; arg: IAstNode;
begin begin
@@ -942,7 +930,7 @@ begin
begin begin
Append(''''); Append('''');
Node.Arguments[0].Accept(Self); Node.Arguments[0].Accept(Self);
exit(TDataValue.Void); exit;
end; end;
Append('('); Append('(');
@@ -957,17 +945,16 @@ begin
Unindent; Unindent;
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; procedure TPrettyPrintVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode);
begin begin
// For pretty-printing, show the original macro call, not the expanded body. // For pretty-printing, show the original macro call, not the expanded body.
// Delegate to the regular VisitFunctionCall to print it as such. // Delegate to the regular VisitFunctionCall to print it as such.
Result := VisitFunctionCall(Node); VisitFunctionCall(Node);
end; end;
function TPrettyPrintVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue; procedure TPrettyPrintVisitor.VisitRecurNode(const Node: IRecurNode);
var var
arg: IAstNode; arg: IAstNode;
begin begin
@@ -981,10 +968,9 @@ begin
Unindent; Unindent;
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; procedure TPrettyPrintVisitor.VisitBlockExpression(const Node: IBlockExpressionNode);
var var
expr: IAstNode; expr: IAstNode;
begin begin
@@ -998,10 +984,9 @@ begin
Unindent; Unindent;
NewLine; NewLine;
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; procedure TPrettyPrintVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode);
begin begin
Append('(def '); Append('(def ');
Node.Identifier.Accept(Self); Node.Identifier.Accept(Self);
@@ -1011,47 +996,43 @@ begin
Node.Initializer.Accept(Self); Node.Initializer.Accept(Self);
end; end;
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue; procedure TPrettyPrintVisitor.VisitAssignment(const Node: IAssignmentNode);
begin begin
Append('(assign '); Append('(assign ');
Node.Identifier.Accept(Self); Node.Identifier.Accept(Self);
Append(' '); Append(' ');
Node.Value.Accept(Self); Node.Value.Accept(Self);
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue; procedure TPrettyPrintVisitor.VisitIndexer(const Node: IIndexerNode);
begin begin
Append('(get '); Append('(get ');
Node.Base.Accept(Self); Node.Base.Accept(Self);
Append(' '); Append(' ');
Node.Index.Accept(Self); Node.Index.Accept(Self);
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; procedure TPrettyPrintVisitor.VisitMemberAccess(const Node: IMemberAccessNode);
begin begin
Append('(.'); Append('(.');
Node.Member.Accept(Self); Node.Member.Accept(Self);
Append(' '); Append(' ');
Node.Base.Accept(Self); Node.Base.Accept(Self);
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; procedure TPrettyPrintVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode);
var var
field: TRecordFieldLiteral; field: TRecordFieldLiteral;
begin begin
if Length(Node.Fields) = 0 then if Length(Node.Fields) = 0 then
begin begin
Append('{}'); Append('{}');
exit(TDataValue.Void); exit;
end; end;
Append('{'); Append('{');
@@ -1066,16 +1047,14 @@ begin
Unindent; Unindent;
NewLine; NewLine;
Append('}'); Append('}');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; procedure TPrettyPrintVisitor.VisitCreateSeries(const Node: ICreateSeriesNode);
begin begin
Append(Format('(new-series "%s")', [Node.Definition])); Append(Format('(new-series "%s")', [Node.Definition]));
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; procedure TPrettyPrintVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode);
begin begin
Append('(add-item '); Append('(add-item ');
Node.Series.Accept(Self); Node.Series.Accept(Self);
@@ -1087,15 +1066,13 @@ begin
Node.Lookback.Accept(Self); Node.Lookback.Accept(Self);
end; end;
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
function TPrettyPrintVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; procedure TPrettyPrintVisitor.VisitSeriesLength(const Node: ISeriesLengthNode);
begin begin
Append('(count '); Append('(count ');
Node.Series.Accept(Self); Node.Series.Accept(Self);
Append(')'); Append(')');
Result := TDataValue.Void;
end; end;
{ TAstScript } { TAstScript }
+196 -176
View File
@@ -12,8 +12,7 @@ uses
Myc.Ast.Visitor, Myc.Ast.Visitor,
Myc.Ast.Scope, Myc.Ast.Scope,
Myc.Ast.Types, Myc.Ast.Types,
Myc.Ast, Myc.Ast;
Myc.Ast.Binding.Nodes;
type type
IAstTypeChecker = interface(IAstVisitor) IAstTypeChecker = interface(IAstVisitor)
@@ -27,33 +26,34 @@ type
TTypeChecker = class(TAstTransformer, IAstTypeChecker) TTypeChecker = class(TAstTransformer, IAstTypeChecker)
private private
FCurrentDescriptor: IScopeDescriptor; FCurrentDescriptor: IScopeDescriptor;
function SetType(const NodeData: TDataValue; const AType: IStaticType): TDataValue; function SetType(const Node: IAstNode; const AType: IStaticType): IAstNode;
protected protected
// Override all visit methods to perform type checking // Override all visit methods to perform type checking
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; function VisitAssignment(const Node: IAssignmentNode): IAstNode; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; function VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode; override;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; function VisitBinaryExpression(const Node: IBinaryExpressionNode): IAstNode; override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; function VisitUnaryExpression(const Node: IUnaryExpressionNode): IAstNode; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; function VisitMemberAccess(const Node: IMemberAccessNode): IAstNode; override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override; function VisitIndexer(const Node: IIndexerNode): IAstNode; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override; function VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; function VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override; function VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override; function VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode; override;
function VisitRecurNode(const Node: IRecurNode): TDataValue; override; function VisitRecurNode(const Node: IRecurNode): IAstNode; override;
// Base cases (types are already set by Binder)
function VisitConstant(const Node: IConstantNode): IAstNode; override;
function VisitKeyword(const Node: IKeywordNode): IAstNode; override;
// Base cases (types are already known from binder)
function VisitConstant(const Node: IConstantNode): TDataValue; override;
function VisitKeyword(const Node: IKeywordNode): TDataValue; override;
// Compile-time nodes (should not be present) // Compile-time nodes (should not be present)
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; override; function VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override;
public public
constructor Create(const ADescriptor: IScopeDescriptor); constructor Create(const ADescriptor: IScopeDescriptor);
function Execute(const RootNode: IAstNode; const ADescriptor: IScopeDescriptor): IAstNode; function Execute(const RootNode: IAstNode; const ADescriptor: IScopeDescriptor): IAstNode;
@@ -85,165 +85,166 @@ end;
function TTypeChecker.Execute(const RootNode: IAstNode; const ADescriptor: IScopeDescriptor): IAstNode; function TTypeChecker.Execute(const RootNode: IAstNode; const ADescriptor: IScopeDescriptor): IAstNode;
begin begin
FCurrentDescriptor := ADescriptor; FCurrentDescriptor := ADescriptor;
var transformedValue := Accept(RootNode); Result := Accept(RootNode); // Use IAstNode-returning Accept
if transformedValue.IsVoid then if not Assigned(Result) then
Result := TAst.Block([]) Result := TAst.Block([]);
else
Result := transformedValue.AsIntf<IAstNode>; // (Result as TAstNode).StaticType is set by the last Visit call
(Result as TAstNode).StaticType := (Result as TAstNode).StaticType;
end; end;
function TTypeChecker.SetType(const NodeData: TDataValue; const AType: IStaticType): TDataValue; function TTypeChecker.SetType(const Node: IAstNode; const AType: IStaticType): IAstNode;
begin begin
if (not NodeData.IsVoid) and (NodeData.Kind = vkInterface) then if Assigned(Node) then
(NodeData.AsIntf<IAstNode> as TAstNode).StaticType := AType; (Node as TAstNode).StaticType := AType;
Result := NodeData; Result := Node;
end; end;
function TTypeChecker.VisitConstant(const Node: IConstantNode): TDataValue; function TTypeChecker.VisitConstant(const Node: IConstantNode): IAstNode;
begin begin
// Type was set by Binder, just propagate it up. // Type was set by Binder, just propagate it up.
Result := TDataValue.FromIntf<IConstantNode>(Node); Result := inherited VisitConstant(Node);
end; end;
function TTypeChecker.VisitKeyword(const Node: IKeywordNode): TDataValue; function TTypeChecker.VisitKeyword(const Node: IKeywordNode): IAstNode;
begin begin
// Type was set by Binder, just propagate it up. // Type was set by Binder, just propagate it up.
Result := TDataValue.FromIntf<IKeywordNode>(Node); Result := inherited VisitKeyword(Node);
end; end;
function TTypeChecker.VisitIdentifier(const Node: IIdentifierNode): TDataValue; function TTypeChecker.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
begin begin
// Type was set by Binder (read from scope), just propagate it up. // Type was set by Binder (read from scope), just propagate it up.
Result := TDataValue.FromIntf<IIdentifierNode>(Node); Result := inherited VisitIdentifier(Node);
end; end;
function TTypeChecker.VisitRecurNode(const Node: IRecurNode): TDataValue; function TTypeChecker.VisitRecurNode(const Node: IRecurNode): IAstNode;
begin begin
// Type was set by Binder (TTypes.Void), just propagate it up. // Type was set by Binder (TTypes.Void), just propagate it up.
Result := TDataValue.FromIntf<IRecurNode>(Node); Result := inherited VisitRecurNode(Node);
end; end;
function TTypeChecker.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; function TTypeChecker.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode;
begin begin
raise Exception.Create('TTypeChecker: MacroDefinition node encountered.'); raise Exception.Create('TTypeChecker: MacroDefinition node encountered.');
end; end;
function TTypeChecker.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; function TTypeChecker.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode;
begin begin
raise Exception.Create('TTypeChecker: MacroExpansionNode node encountered.'); // TypeChecker runs *after* expansion, so we just visit the body.
Result := Accept(Node.ExpandedBody);
end; end;
function TTypeChecker.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; function TTypeChecker.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
var var
initNode: IAstNode;
initType: IStaticType; initType: IStaticType;
boundIdent: TBoundIdentifierNode; boundIdent: TIdentifierNode;
adr: TResolvedAddress; adr: TResolvedAddress;
begin begin
// 1. Visit the initializer (if it exists) to get its (now-inferred) type. // 1. Visit children first (Identifier is leaf, Initializer is traversed)
inherited;
// 2. Get initializer type
if Assigned(Node.Initializer) then if Assigned(Node.Initializer) then
begin initType := (Node.Initializer as TAstNode).StaticType
initNode := Accept(Node.Initializer).AsIntf<IAstNode>;
initType := (initNode as TAstNode).StaticType;
end
else else
initType := TTypes.Void; initType := TTypes.Void;
// 2. Get the address from the bound identifier. // 3. Get the address from the bound identifier.
boundIdent := (Node.Identifier as TBoundIdentifierNode); boundIdent := (Node.Identifier as TIdentifierNode);
adr := boundIdent.Address; adr := boundIdent.Address;
// 3. Update the type in the scope descriptor (which was set to Unknown by the binder). // 4. Update the type in the scope descriptor (which was set to Unknown by the binder).
FCurrentDescriptor.UpdateType(adr.SlotIndex, initType); FCurrentDescriptor.UpdateType(adr.SlotIndex, initType);
// 4. Update the static types of the nodes themselves. // 5. Update the static types of the nodes themselves.
(boundIdent as TAstNode).StaticType := initType; (boundIdent as TAstNode).StaticType := initType;
Result := SetType(TDataValue.FromIntf(Node), initType); Result := SetType(Node, initType);
end; end;
function TTypeChecker.VisitAssignment(const Node: IAssignmentNode): TDataValue; function TTypeChecker.VisitAssignment(const Node: IAssignmentNode): IAstNode;
var var
boundIdentifier, boundValue: IAstNode;
targetType, sourceType: IStaticType; targetType, sourceType: IStaticType;
begin begin
boundIdentifier := Accept(Node.Identifier).AsIntf<IAstNode>; // 1. Visit children first (Identifier, Value)
boundValue := Accept(Node.Value).AsIntf<IAstNode>; inherited;
targetType := (boundIdentifier as TAstNode).StaticType; // 2. Get types
sourceType := (boundValue as TAstNode).StaticType; targetType := (Node.Identifier as TAstNode).StaticType;
sourceType := (Node.Value as TAstNode).StaticType;
// 3. Check assignment
if not TTypeRules.CanAssign(targetType, sourceType) then if not TTypeRules.CanAssign(targetType, sourceType) then
raise ETypeException.CreateFmt('Cannot assign type %s to %s', [sourceType.ToString, targetType.ToString]); raise ETypeException.CreateFmt('Cannot assign type %s to %s', [sourceType.ToString, targetType.ToString]);
Result := SetType(TDataValue.FromIntf<IAssignmentNode>(Node), targetType); Result := SetType(Node, targetType);
end; end;
function TTypeChecker.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; function TTypeChecker.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var var
boundNode: TBoundLambdaExpressionNode; boundNode: TLambdaExpressionNode;
boundBody: IAstNode;
bodyType, methodType: IStaticType; bodyType, methodType: IStaticType;
paramTypes: TArray<IStaticType>; paramTypes: TArray<IStaticType>;
i: Integer; i: Integer;
savedDescriptor: IScopeDescriptor;
begin begin
boundNode := (Node as TBoundLambdaExpressionNode); boundNode := (Node as TLambdaExpressionNode);
// 1. Enter the lambda's scope // 1. Enter the lambda's scope (which Binder already created)
savedDescriptor := FCurrentDescriptor;
FCurrentDescriptor := boundNode.ScopeDescriptor; FCurrentDescriptor := boundNode.ScopeDescriptor;
try try
// 2. Set parameter types (currently Unknown, but required for signature) // 2. Parameters are leaves, so we don't try to evaluate them
// 3. Get parameter types (currently Unknown, but required for signature)
SetLength(paramTypes, Length(boundNode.Parameters)); SetLength(paramTypes, Length(boundNode.Parameters));
for i := 0 to High(boundNode.Parameters) do for i := 0 to High(boundNode.Parameters) do
paramTypes[i] := (boundNode.Parameters[i] as TAstNode).StaticType; // Propagates Unknown paramTypes[i] := (boundNode.Parameters[i] as TAstNode).StaticType; // Propagates Unknown
// 3. Visit the body to infer its return type // 4. Visit the body to infer its return type
boundBody := Accept(boundNode.Body).AsIntf<IAstNode>; Accept(boundNode.Body);
bodyType := (boundBody as TAstNode).StaticType; bodyType := (boundNode.Body as TAstNode).StaticType;
// 4. Create the final method type // 5. Create the final method type
methodType := TTypes.CreateMethod(paramTypes, bodyType); methodType := TTypes.CreateMethod(paramTypes, bodyType);
// 5. Update the type for <self> (Slot 0) in the descriptor // 6. Update the type for <self> (Slot 0) in the descriptor
FCurrentDescriptor.UpdateType(0, methodType); FCurrentDescriptor.UpdateType(0, methodType);
finally finally
// 6. Restore parent descriptor // 7. Restore parent descriptor
FCurrentDescriptor := FCurrentDescriptor.Parent; FCurrentDescriptor := savedDescriptor;
end; end;
// 7. Set the type of the lambda node itself // 8. Set the type of the lambda node itself
Result := SetType(TDataValue.FromIntf<ILambdaExpressionNode>(boundNode), methodType); Result := SetType(boundNode, methodType);
end; end;
function TTypeChecker.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; function TTypeChecker.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
var var
callee: IAstNode;
args: TArray<IAstNode>;
calleeType, retType: IStaticType; calleeType, retType: IStaticType;
i: Integer; i: Integer;
begin begin
// 1. Visit children first (bottom-up) // 1. Visit children first (bottom-up)
callee := Accept(Node.Callee).AsIntf<IAstNode>; inherited;
args := AcceptNodes<IAstNode>(Node.Arguments);
// 2. Get callee type (now inferred) // 2. Get callee type (now inferred)
calleeType := (callee as TAstNode).StaticType; calleeType := (Node.Callee as TAstNode).StaticType;
retType := TTypes.Unknown; // Default if not a method retType := TTypes.Unknown; // Default if not a method
// 3. Perform type checking // 3. Perform type checking
if calleeType.Kind = TStaticTypeKind.stMethod then if calleeType.Kind = TStaticTypeKind.stMethod then
begin begin
var signature := calleeType.Signature; var signature := calleeType.Signature;
if Length(args) <> Length(signature.ParamTypes) then if Length(Node.Arguments) <> Length(signature.ParamTypes) then
raise ETypeException.CreateFmt('Function expects %d arguments, but got %d', [Length(signature.ParamTypes), Length(args)]); raise ETypeException
.CreateFmt('Function expects %d arguments, but got %d', [Length(signature.ParamTypes), Length(Node.Arguments)]);
retType := signature.ReturnType; retType := signature.ReturnType;
// Check argument types // Check argument types
for i := 0 to High(args) do for i := 0 to High(Node.Arguments) do
begin begin
var argType := (args[i] as TAstNode).StaticType; var argType := (Node.Arguments[i] as TAstNode).StaticType;
var paramType := signature.ParamTypes[i]; var paramType := signature.ParamTypes[i];
if not TTypeRules.CanAssign(paramType, argType) then if not TTypeRules.CanAssign(paramType, argType) then
raise ETypeException raise ETypeException
@@ -254,99 +255,111 @@ begin
raise ETypeException.CreateFmt('Cannot invoke type %s as a function.', [calleeType.ToString]); raise ETypeException.CreateFmt('Cannot invoke type %s as a function.', [calleeType.ToString]);
// 4. Set the type for this call node // 4. Set the type for this call node
Result := SetType(TDataValue.FromIntf<IFunctionCallNode>(Node), retType); Result := SetType(Node, retType);
end; end;
function TTypeChecker.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; function TTypeChecker.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
var var
blockType: IStaticType; blockType: IStaticType;
exprs: TArray<IAstNode>;
begin begin
exprs := AcceptNodes<IAstNode>(Node.Expressions); // 1. Visit children
inherited;
if Length(exprs) > 0 then // 2. Type is type of last expression
blockType := (exprs[High(exprs)] as TAstNode).StaticType if Length(Node.Expressions) > 0 then
blockType := (Node.Expressions[High(Node.Expressions)] as TAstNode).StaticType
else else
blockType := TTypes.Void; blockType := TTypes.Void;
Result := SetType(TDataValue.FromIntf<IBlockExpressionNode>(Node), blockType); Result := SetType(Node, blockType);
end; end;
function TTypeChecker.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; function TTypeChecker.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
var var
condition, thenBranch, elseBranch: IAstNode;
conditionType, thenType, elseType, resultType: IStaticType; conditionType, thenType, elseType, resultType: IStaticType;
begin begin
condition := Accept(Node.Condition).AsIntf<IAstNode>; // 1. Visit children
thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>; inherited;
elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>; // Accept(nil) returns void
conditionType := (condition as TAstNode).StaticType; // 2. Check condition
conditionType := (Node.Condition as TAstNode).StaticType;
if (conditionType.Kind <> stUnknown) and not TTypeRules.CanAssign(TTypes.Ordinal, conditionType) then if (conditionType.Kind <> stUnknown) and not TTypeRules.CanAssign(TTypes.Ordinal, conditionType) then
raise ETypeException.CreateFmt('If condition must be Ordinal, but got %s', [conditionType.ToString]); raise ETypeException.CreateFmt('If condition must be Ordinal, but got %s', [conditionType.ToString]);
thenType := (thenBranch as TAstNode).StaticType; // 3. Promote branch types
thenType := (Node.ThenBranch as TAstNode).StaticType;
elseType := elseType :=
if elseBranch <> nil then (elseBranch as TAstNode).StaticType if Node.ElseBranch <> nil then (Node.ElseBranch as TAstNode).StaticType
else TTypes.Void; else TTypes.Void;
resultType := TTypeRules.Promote(thenType, elseType); resultType := TTypeRules.Promote(thenType, elseType);
Result := SetType(TDataValue.FromIntf<IIfExpressionNode>(Node), resultType); Result := SetType(Node, resultType);
end; end;
function TTypeChecker.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; function TTypeChecker.VisitTernaryExpression(const Node: ITernaryExpressionNode): IAstNode;
var var
condition, thenBranch, elseBranch: IAstNode;
conditionType, thenType, elseType, resultType: IStaticType; conditionType, thenType, elseType, resultType: IStaticType;
begin begin
condition := Accept(Node.Condition).AsIntf<IAstNode>; // 1. Visit children
thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>; inherited;
elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>;
conditionType := (condition as TAstNode).StaticType; // 2. Check condition
conditionType := (Node.Condition as TAstNode).StaticType;
if (conditionType.Kind <> stUnknown) and not TTypeRules.CanAssign(TTypes.Ordinal, conditionType) then if (conditionType.Kind <> stUnknown) and not TTypeRules.CanAssign(TTypes.Ordinal, conditionType) then
raise ETypeException.CreateFmt('Ternary condition must be Ordinal, but got %s', [conditionType.ToString]); raise ETypeException.CreateFmt('Ternary condition must be Ordinal, but got %s', [conditionType.ToString]);
thenType := (thenBranch as TAstNode).StaticType; // 3. Promote branch types
elseType := (elseBranch as TAstNode).StaticType; thenType := (Node.ThenBranch as TAstNode).StaticType;
elseType := (Node.ElseBranch as TAstNode).StaticType;
resultType := TTypeRules.Promote(thenType, elseType); resultType := TTypeRules.Promote(thenType, elseType);
Result := SetType(TDataValue.FromIntf<ITernaryExpressionNode>(Node), resultType); Result := SetType(Node, resultType);
end; end;
function TTypeChecker.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; function TTypeChecker.VisitBinaryExpression(const Node: IBinaryExpressionNode): IAstNode;
var var
left, right: IAstNode;
leftType, rightType, resultType: IStaticType; leftType, rightType, resultType: IStaticType;
begin begin
left := Accept(Node.Left).AsIntf<IAstNode>; // 1. Visit children
right := Accept(Node.Right).AsIntf<IAstNode>; inherited;
leftType := (left as TAstNode).StaticType;
rightType := (right as TAstNode).StaticType; // 2. Get types
leftType := (Node.Left as TAstNode).StaticType;
rightType := (Node.Right as TAstNode).StaticType;
// 3. Resolve
resultType := TTypeRules.ResolveBinaryOp(Node.Operator, leftType, rightType); resultType := TTypeRules.ResolveBinaryOp(Node.Operator, leftType, rightType);
Result := SetType(TDataValue.FromIntf<IBinaryExpressionNode>(Node), resultType); Result := SetType(Node, resultType);
end; end;
function TTypeChecker.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; function TTypeChecker.VisitUnaryExpression(const Node: IUnaryExpressionNode): IAstNode;
var var
right: IAstNode;
rightType, resultType: IStaticType; rightType, resultType: IStaticType;
begin begin
right := Accept(Node.Right).AsIntf<IAstNode>; // 1. Visit children
rightType := (right as TAstNode).StaticType; inherited;
// 2. Get types
rightType := (Node.Right as TAstNode).StaticType;
// 3. Resolve
resultType := TTypeRules.ResolveUnaryOp(Node.Operator, rightType); resultType := TTypeRules.ResolveUnaryOp(Node.Operator, rightType);
Result := SetType(TDataValue.FromIntf<IUnaryExpressionNode>(Node), resultType); Result := SetType(Node, resultType);
end; end;
function TTypeChecker.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; function TTypeChecker.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode;
var var
baseNode: IAstNode;
baseType, elemType: IStaticType; baseType, elemType: IStaticType;
fieldIndex: Integer; fieldIndex: Integer;
begin begin
baseNode := Accept(Node.Base).AsIntf<IAstNode>; // 1. Visit children
baseType := (baseNode as TAstNode).StaticType; inherited;
// 2. Get types
baseType := (Node.Base as TAstNode).StaticType;
elemType := TTypes.Unknown; elemType := TTypes.Unknown;
// 3. Resolve
if (baseType.Kind <> TStaticTypeKind.stUnknown) then if (baseType.Kind <> TStaticTypeKind.stUnknown) then
begin begin
if (baseType.Kind = TStaticTypeKind.stRecord) or (baseType.Kind = TStaticTypeKind.stRecordSeries) then if (baseType.Kind = TStaticTypeKind.stRecord) or (baseType.Kind = TStaticTypeKind.stRecordSeries) then
@@ -376,20 +389,22 @@ begin
end; end;
end; end;
Result := SetType(TDataValue.FromIntf<IMemberAccessNode>(Node), elemType); Result := SetType(Node, elemType);
end; end;
function TTypeChecker.VisitIndexer(const Node: IIndexerNode): TDataValue; function TTypeChecker.VisitIndexer(const Node: IIndexerNode): IAstNode;
var var
baseNode, indexNode: IAstNode;
baseType, indexType, elemType: IStaticType; baseType, indexType, elemType: IStaticType;
begin begin
baseNode := Accept(Node.Base).AsIntf<IAstNode>; // 1. Visit children
indexNode := Accept(Node.Index).AsIntf<IAstNode>; inherited;
baseType := (baseNode as TAstNode).StaticType;
indexType := (indexNode as TAstNode).StaticType;
// 2. Get types
baseType := (Node.Base as TAstNode).StaticType;
indexType := (Node.Index as TAstNode).StaticType;
elemType := TTypes.Unknown; elemType := TTypes.Unknown;
// 3. Resolve
if (baseType.Kind <> TStaticTypeKind.stUnknown) then if (baseType.Kind <> TStaticTypeKind.stUnknown) then
begin begin
if (baseType.Kind <> TStaticTypeKind.stSeries) and (baseType.Kind <> TStaticTypeKind.stRecordSeries) then if (baseType.Kind <> TStaticTypeKind.stSeries) and (baseType.Kind <> TStaticTypeKind.stRecordSeries) then
@@ -404,13 +419,12 @@ begin
elemType := TTypes.CreateRecord(baseType.Definition); elemType := TTypes.CreateRecord(baseType.Definition);
end; end;
Result := SetType(TDataValue.FromIntf<IIndexerNode>(Node), elemType); Result := SetType(Node, elemType);
end; end;
function TTypeChecker.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; function TTypeChecker.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
var var
i: Integer; i: Integer;
boundFields: TArray<TRecordFieldLiteral>;
scalarDefFields: TArray<TScalarRecordField>; scalarDefFields: TArray<TScalarRecordField>;
def: IScalarRecordDefinition; def: IScalarRecordDefinition;
staticType: IStaticType; staticType: IStaticType;
@@ -418,19 +432,22 @@ var
valType: IStaticType; valType: IStaticType;
scalarKind: TScalar.TKind; scalarKind: TScalar.TKind;
allScalar: Boolean; allScalar: Boolean;
N: TGenericRecordLiteralNode; // Parser creates this type
begin begin
SetLength(boundFields, Length(Node.Fields)); // 1. Visit all child nodes first to infer their types
SetLength(scalarDefFields, Length(Node.Fields)); inherited;
N := (Node as TGenericRecordLiteralNode);
SetLength(scalarDefFields, Length(N.Fields));
allScalar := True; allScalar := True;
// 1. Visit all child nodes first to infer their types // 2. Check if this record literal can be a TScalarRecord
for i := 0 to High(Node.Fields) do for i := 0 to High(N.Fields) do
begin begin
valNode := Accept(Node.Fields[i].Value).AsIntf<IAstNode>; valNode := N.Fields[i].Value;
valType := (valNode as TAstNode).StaticType; valType := (valNode as TAstNode).StaticType;
boundFields[i] := TRecordFieldLiteral.Create(Node.Fields[i].Key, valNode);
// 2. Check if this field fits the scalar path
if (valType.Kind = stOrdinal) then if (valType.Kind = stOrdinal) then
scalarKind := TScalar.TKind.Ordinal scalarKind := TScalar.TKind.Ordinal
else if (valType.Kind = stFloat) then else if (valType.Kind = stFloat) then
@@ -444,51 +461,51 @@ begin
end; end;
if allScalar then if allScalar then
scalarDefFields[i] := TScalarRecordField.Create(Node.Fields[i].Key.Value, scalarKind); scalarDefFields[i] := TScalarRecordField.Create(N.Fields[i].Key.Value, scalarKind);
end; end;
// 3. Create the appropriate record type (Scalar or Generic) // 3. Create the appropriate record type (Scalar or Generic) and mutate the node
if allScalar then if allScalar then
begin begin
def := TScalarRecordRegistry.Intern(scalarDefFields); def := TScalarRecordRegistry.Intern(scalarDefFields);
staticType := TTypes.CreateRecord(def); staticType := TTypes.CreateRecord(def);
// We can re-use the TBoundRecordLiteralNode from the binder N.Definition := def; // Mutate
(Node as TBoundRecordLiteralNode).Definition := def; N.GenericDefinition := nil; // Mutate
end end
else else
begin begin
var genDefFields: TArray<TPair<IKeyword, IStaticType>>; var genDefFields: TArray<TPair<IKeyword, IStaticType>>;
SetLength(genDefFields, Length(boundFields)); SetLength(genDefFields, Length(N.Fields));
for i := 0 to High(boundFields) do for i := 0 to High(N.Fields) do
genDefFields[i] := TPair<IKeyword, IStaticType>.Create(boundFields[i].Key.Value, (boundFields[i].Value as TAstNode).StaticType); genDefFields[i] := TPair<IKeyword, IStaticType>.Create(N.Fields[i].Key.Value, (N.Fields[i].Value as TAstNode).StaticType);
var genDef := TGenericRecordRegistry.Intern(genDefFields); var genDef := TGenericRecordRegistry.Intern(genDefFields);
staticType := TTypes.CreateGenericRecord(genDef); staticType := TTypes.CreateGenericRecord(genDef);
// Re-use the TBoundGenericRecordLiteralNode from the binder N.GenericDefinition := genDef; // Mutate
(Node as TBoundGenericRecordLiteralNode).Definition := genDef; N.Definition := nil; // Mutate
end; end;
Result := SetType(TDataValue.FromIntf<IRecordLiteralNode>(Node), staticType); Result := SetType(N, staticType);
end; end;
function TTypeChecker.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; function TTypeChecker.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode;
begin begin
// Type was set by Binder, just propagate it up. // Type was set by Binder, just propagate it up.
Result := TDataValue.FromIntf<ICreateSeriesNode>(Node); Result := inherited VisitCreateSeries(Node);
end; end;
function TTypeChecker.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; function TTypeChecker.VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode;
var var
seriesNode, valueNode, lookbackNode: IAstNode;
seriesType, valueType: IStaticType; seriesType, valueType: IStaticType;
begin begin
seriesNode := Accept(Node.Series).AsIntf<IAstNode>; // 1. Visit children
valueNode := Accept(Node.Value).AsIntf<IAstNode>; inherited;
lookbackNode := Accept(Node.Lookback).AsIntf<IAstNode>;
seriesType := (seriesNode as TAstNode).StaticType; // 2. Get types
valueType := (valueNode as TAstNode).StaticType; seriesType := (Node.Series as TAstNode).StaticType;
valueType := (Node.Value as TAstNode).StaticType;
// 3. Check types
if (seriesType.Kind <> stUnknown) then if (seriesType.Kind <> stUnknown) then
begin begin
if (seriesType.Kind <> TStaticTypeKind.stSeries) then if (seriesType.Kind <> TStaticTypeKind.stSeries) then
@@ -499,30 +516,33 @@ begin
.CreateFmt('Cannot add item of type %s to series of type %s', [valueType.ToString, seriesType.ElementType.ToString]); .CreateFmt('Cannot add item of type %s to series of type %s', [valueType.ToString, seriesType.ElementType.ToString]);
end; end;
if (lookbackNode <> nil) then if (Node.Lookback <> nil) then
begin begin
var lookbackType := (lookbackNode as TAstNode).StaticType; var lookbackType := (Node.Lookback as TAstNode).StaticType;
if (lookbackType.Kind <> stUnknown) and not (lookbackType.Kind = TStaticTypeKind.stOrdinal) then if (lookbackType.Kind <> stUnknown) and not (lookbackType.Kind = TStaticTypeKind.stOrdinal) then
raise ETypeException.Create('Lookback parameter for "add" must be an ordinal value.'); raise ETypeException.Create('Lookback parameter for "add" must be an ordinal value.');
end; end;
Result := SetType(TDataValue.FromIntf<IAddSeriesItemNode>(Node), TTypes.Void); Result := SetType(Node, TTypes.Void);
end; end;
function TTypeChecker.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; function TTypeChecker.VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode;
var var
seriesNode: IAstNode;
seriesType: IStaticType; seriesType: IStaticType;
begin begin
seriesNode := Accept(Node.Series).AsIntf<IAstNode>; // 1. Visit children
seriesType := (seriesNode as TAstNode).StaticType; inherited;
// 2. Get type
seriesType := (Node.Series as TAstNode).StaticType;
// 3. Check type
if (seriesType.Kind <> stUnknown) if (seriesType.Kind <> stUnknown)
and (seriesType.Kind <> TStaticTypeKind.stSeries) and (seriesType.Kind <> TStaticTypeKind.stSeries)
and (seriesType.Kind <> TStaticTypeKind.stRecordSeries) then and (seriesType.Kind <> TStaticTypeKind.stRecordSeries) then
raise ETypeException.CreateFmt('"length" requires a series, but got %s', [seriesType.ToString]); raise ETypeException.CreateFmt('"length" requires a series, but got %s', [seriesType.ToString]);
Result := SetType(TDataValue.FromIntf<ISeriesLengthNode>(Node), TTypes.Ordinal); Result := SetType(Node, TTypes.Ordinal);
end; end;
end. end.
File diff suppressed because it is too large Load Diff
+1163 -655
View File
File diff suppressed because it is too large Load Diff
+19 -9
View File
@@ -62,7 +62,6 @@ type
class operator Implicit(const AValue: TDataValue): TScalar; overload; inline; class operator Implicit(const AValue: TDataValue): TScalar; overload; inline;
class operator Implicit(const AValue: String): TDataValue; overload; inline; class operator Implicit(const AValue: String): TDataValue; overload; inline;
class operator Implicit(const AValue: TDataValue.TFunc): TDataValue; overload; inline; class operator Implicit(const AValue: TDataValue.TFunc): TDataValue; overload; inline;
class operator Implicit(const AValue: TObject): TDataValue; overload; inline;
// atomic operations // atomic operations
function CompareAndSet(const Expected, NewValue: TDataValue): Boolean; function CompareAndSet(const Expected, NewValue: TDataValue): Boolean;
@@ -77,6 +76,9 @@ type
class function FromPtr(const AValue: Pointer): TDataValue; static; inline; class function FromPtr(const AValue: Pointer): TDataValue; static; inline;
function AsPtr: Pointer; inline; function AsPtr: Pointer; inline;
// This take ownership of the object!
procedure FromObj(const AValue: TObject); inline;
class function Map(const SourceSeries: ISeries; const MapperFunc: TFunc): ISeries; static; class function Map(const SourceSeries: ISeries; const MapperFunc: TFunc): ISeries; static;
class function FromRecordSeries(const AValue: IRecordSeries): TDataValue; static; inline; class function FromRecordSeries(const AValue: IRecordSeries): TDataValue; static; inline;
@@ -158,12 +160,17 @@ function TDataValue.AsGeneric<T>: T;
begin begin
if FKind <> vkGeneric then if FKind <> vkGeneric then
raise EInvalidCast.Create('Cannot read value as generic of ' + String(PTypeInfo(TypeInfo(T)).Name) + '.'); raise EInvalidCast.Create('Cannot read value as generic of ' + String(PTypeInfo(TypeInfo(T)).Name) + '.');
if PTypeInfo(TypeInfo(T)).Kind <> tkInterface then
begin
{$ifdef DEBUG} {$ifdef DEBUG}
var val := TVal<T>(FInterface); var val := TVal<T>(FInterface);
if val.TypeHandle <> TypeInfo(T) then if val.TypeHandle <> TypeInfo(T) then
raise EInvalidCast.Create('Generic type is not a ' + String(PTypeInfo(TypeInfo(T)).Name) + '.'); raise EInvalidCast.Create('Generic type is not a ' + String(PTypeInfo(TypeInfo(T)).Name) + '.');
{$endif} {$endif}
Result := TVal<T>(FInterface).Value; Result := TVal<T>(FInterface).Value;
end
else
IInterface(Pointer(@Result)^) := FInterface
end; end;
function TDataValue.AsIntf<T>: T; function TDataValue.AsIntf<T>: T;
@@ -278,7 +285,10 @@ end;
class function TDataValue.FromGeneric<T>(const [ref] AValue: T): TDataValue; class function TDataValue.FromGeneric<T>(const [ref] AValue: T): TDataValue;
begin begin
Result.FKind := vkGeneric; Result.FKind := vkGeneric;
Result.FInterface := TVal<T>.Create(AValue); if PTypeInfo(TypeInfo(T)).Kind = tkInterface then
Result.FInterface := IInterface(Pointer(@AValue)^)
else
Result.FInterface := TVal<T>.Create(AValue);
end; end;
class function TDataValue.FromPtr(const AValue: Pointer): TDataValue; class function TDataValue.FromPtr(const AValue: Pointer): TDataValue;
@@ -473,10 +483,10 @@ begin
TFunc(Result.FInterface) := AValue; TFunc(Result.FInterface) := AValue;
end; end;
class operator TDataValue.Implicit(const AValue: TObject): TDataValue; procedure TDataValue.FromObj(const AValue: TObject);
begin begin
Result.FKind := vkManagedObject; FKind := vkManagedObject;
Result.FInterface := TObjVal.Create(AValue); FInterface := TObjVal.Create(AValue);
end; end;
class operator TDataValue.Implicit(const AValue: TDataValue): TScalar; class operator TDataValue.Implicit(const AValue: TDataValue): TScalar;