Major refactoring, split Bound Ast from source Ast
This commit is contained in:
@@ -5,13 +5,12 @@ uses
|
||||
FMX.Forms,
|
||||
MainForm in 'MainForm.pas' {Form1},
|
||||
Myc.Ast.Evaluator in '..\Src\AST\Myc.Ast.Evaluator.pas',
|
||||
Myc.Ast.Printer in '..\Src\AST\Myc.Ast.Printer.pas',
|
||||
Myc.Ast.Nodes in '..\Src\AST\Myc.Ast.Nodes.pas',
|
||||
Myc.Ast.Scope in '..\Src\AST\Myc.Ast.Scope.pas',
|
||||
Myc.Fmx.AstEditor in 'Myc.Fmx.AstEditor.pas',
|
||||
Myc.Data.Value in 'Myc.Data.Value.pas',
|
||||
Myc.Ast.Debugger in '..\Src\AST\Myc.Ast.Debugger.pas',
|
||||
Myc.Ast.Traverser in '..\Src\AST\Myc.Ast.Traverser.pas',
|
||||
Myc.Ast.Transformer in '..\Src\AST\Myc.Ast.Transformer.pas',
|
||||
Myc.Ast.Binding in '..\Src\AST\Myc.Ast.Binding.pas',
|
||||
Myc.Ast.RTL in '..\Src\AST\Myc.Ast.RTL.pas',
|
||||
Myc.Ast.Dumper in '..\Src\AST\Myc.Ast.Dumper.pas',
|
||||
|
||||
@@ -136,13 +136,12 @@
|
||||
<FormType>fmx</FormType>
|
||||
</DCCReference>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.Evaluator.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.Printer.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.Nodes.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.Scope.pas"/>
|
||||
<DCCReference Include="Myc.Fmx.AstEditor.pas"/>
|
||||
<DCCReference Include="Myc.Data.Value.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.Debugger.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.Traverser.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.Transformer.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.Binding.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.RTL.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.Dumper.pas"/>
|
||||
|
||||
+100
-128
@@ -26,7 +26,6 @@ uses
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast,
|
||||
Myc.Ast.Evaluator,
|
||||
Myc.Ast.Printer,
|
||||
Myc.Ast.Dumper,
|
||||
Myc.Data.Decimal,
|
||||
Myc.Ast.Binding,
|
||||
@@ -100,14 +99,14 @@ type
|
||||
procedure TailCallButtenClick(Sender: TObject);
|
||||
procedure ToJSONButtonClick(Sender: TObject);
|
||||
private
|
||||
// Stores the last AST generated by Test1 or Test2 button clicks.
|
||||
FLastAst: IAstNode;
|
||||
FCurrAst: IAstNode;
|
||||
FCurrDesc: IScopeDescriptor;
|
||||
FGScope: IExecutionScope;
|
||||
FWorkspace: TAuraWorkspace;
|
||||
FTriggerScope: IExecutionScope;
|
||||
FScriptUpdate: Boolean;
|
||||
procedure WorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
|
||||
function CreateVisitor(Scope: IExecutionScope): IAstVisitor;
|
||||
function CreateEvaluator(Scope: IExecutionScope): IEvaluatorVisitor;
|
||||
// Helper function to encapsulate the Bind -> Evaluate pattern
|
||||
function ExecuteAst(const ANode: IAstNode; const AParentScope: IExecutionScope): TDataValue;
|
||||
procedure UpdateScript;
|
||||
@@ -131,65 +130,50 @@ uses
|
||||
|
||||
procedure TForm1.InnerLambdaButtonClick(Sender: TObject);
|
||||
var
|
||||
// Only the root node is needed as a local variable.
|
||||
mainBlock: IAstNode;
|
||||
|
||||
// Execution and result variables
|
||||
resultValue: TDataValue;
|
||||
begin
|
||||
// Build the entire AST inline to ensure no node instances are shared.
|
||||
mainBlock :=
|
||||
TAst.Block(
|
||||
[
|
||||
// var outer = lambda() { ... };
|
||||
TAst.VarDecl(
|
||||
TAst.Identifier('outer'),
|
||||
TAst.LambdaExpr(
|
||||
[],
|
||||
TAst.Block(
|
||||
[
|
||||
// var x = 10;
|
||||
TAst.VarDecl(TAst.Identifier('x'), TAst.Constant(10)),
|
||||
// var inner = lambda() { ... };
|
||||
TAst.VarDecl(
|
||||
TAst.Identifier('inner'),
|
||||
TAst.LambdaExpr(
|
||||
[],
|
||||
TAst.Block(
|
||||
[
|
||||
// var innermost = lambda() { ... };
|
||||
TAst.VarDecl(
|
||||
TAst.Identifier('innermost'),
|
||||
TAst.LambdaExpr(
|
||||
[],
|
||||
// x = x + 5;
|
||||
TAst.Assign(
|
||||
TAst.Identifier('x'),
|
||||
TAst.BinaryExpr(TAst.Identifier('x'), TScalar.TBinaryOp.Add, TAst.Constant(5))
|
||||
)
|
||||
)
|
||||
),
|
||||
// innermost();
|
||||
TAst.FunctionCall(TAst.Identifier('innermost'), [])
|
||||
]
|
||||
)
|
||||
)
|
||||
),
|
||||
// inner();
|
||||
TAst.FunctionCall(TAst.Identifier('inner'), []),
|
||||
// return x;
|
||||
TAst.Identifier('x')
|
||||
]
|
||||
)
|
||||
)
|
||||
),
|
||||
// var finalResult = outer();
|
||||
TAst.VarDecl(TAst.Identifier('finalResult'), TAst.FunctionCall(TAst.Identifier('outer'), []))
|
||||
]
|
||||
);
|
||||
|
||||
FLastAst := mainBlock;
|
||||
|
||||
resultValue := ExecuteAst(mainBlock, FGScope);
|
||||
|
||||
Assert(TScalar.FromInt64(15) = resultValue.AsScalar, 'The final result should be 15, but is ' + resultValue.AsScalar.ToString);
|
||||
@@ -201,18 +185,22 @@ begin
|
||||
FWorkspace.DeleteChildren;
|
||||
FWorkspace.Repaint;
|
||||
|
||||
// Create and prepare the global scope once
|
||||
FGScope := TAst.CreateScope(nil);
|
||||
RegisterNativeFunctions(FGScope);
|
||||
end;
|
||||
|
||||
function TForm1.ExecuteAst(const ANode: IAstNode; const AParentScope: IExecutionScope): TDataValue;
|
||||
var
|
||||
binder: IAstBinder;
|
||||
scriptScope: IExecutionScope;
|
||||
visitor: IEvaluatorVisitor;
|
||||
begin
|
||||
// This helper function handles simple, one-off script executions.
|
||||
// It binds the AST and then decides whether to run a debug session or a standard evaluation.
|
||||
var scriptScope := TAstBinder.Bind(ANode, AParentScope).CreateScope(AParentScope);
|
||||
var visitor := CreateVisitor(scriptScope);
|
||||
Result := visitor.Execute(ANode);
|
||||
binder := TAstBinder.Create(AParentScope);
|
||||
FCurrAst := binder.Execute(ANode, FCurrDesc);
|
||||
|
||||
scriptScope := FCurrDesc.CreateScope(AParentScope);
|
||||
visitor := CreateEvaluator(scriptScope);
|
||||
Result := visitor.Execute(FCurrAst);
|
||||
end;
|
||||
|
||||
procedure TForm1.FormCreate(Sender: TObject);
|
||||
@@ -221,13 +209,18 @@ begin
|
||||
FWorkspace.Parent := Panel2;
|
||||
FWorkspace.Align := TAlignLayout.Client;
|
||||
FWorkspace.ClipChildren := true;
|
||||
|
||||
FWorkspace.OnMouseDown := WorkspaceMouseDown;
|
||||
|
||||
Tast.RegisterLibrary(
|
||||
procedure(const Scope: IExecutionScope)
|
||||
var
|
||||
smaAst, boundSmaAst: IAstNode;
|
||||
binder: IAstBinder;
|
||||
smaDescriptor: IScopeDescriptor;
|
||||
smaScope: IExecutionScope;
|
||||
smaVisitor: IEvaluatorVisitor;
|
||||
begin
|
||||
var smaAst :=
|
||||
smaAst :=
|
||||
TAst.LambdaExpr(
|
||||
[TAst.Identifier('len')],
|
||||
TAst.Block(
|
||||
@@ -278,28 +271,32 @@ begin
|
||||
)
|
||||
);
|
||||
|
||||
Scope.Define('CreateSMA', CreateVisitor(TAstBinder.Bind(smaAst, FGScope).CreateScope(FGScope)).Execute(smaAst));
|
||||
binder := TAstBinder.Create(FGScope);
|
||||
boundSmaAst := binder.Execute(smaAst, smaDescriptor);
|
||||
|
||||
smaScope := smaDescriptor.CreateScope(FGScope);
|
||||
smaVisitor := CreateEvaluator(smaScope);
|
||||
|
||||
Scope.Define('CreateSMA', smaVisitor.Execute(boundSmaAst));
|
||||
end
|
||||
);
|
||||
|
||||
// Setup global scope
|
||||
ClearButtonClick(Self);
|
||||
end;
|
||||
|
||||
procedure TForm1.FibonacciButtonClick(Sender: TObject);
|
||||
var
|
||||
root: IAstNode;
|
||||
root, fibAst, boundFibAst: IAstNode;
|
||||
result: TDataValue;
|
||||
sw: TStopwatch;
|
||||
fibScope: IExecutionScope;
|
||||
visitor: IEvaluatorVisitor;
|
||||
binder: IAstBinder;
|
||||
begin
|
||||
// This script defines a naive, slow recursive 'fib' function (on purpose!).
|
||||
// The lambda captures its own name ('fib') from the parent scope to perform recursion.
|
||||
var fibAst :=
|
||||
fibAst :=
|
||||
TAst.Block(
|
||||
[
|
||||
// 1. var fib; (Declare the name so it can be captured by the lambda)
|
||||
TAst.VarDecl(TAst.Identifier('fib')),
|
||||
// 2. fib = lambda(n) { ... fib(n-1) + fib(n-2) ... };
|
||||
TAst.Assign(
|
||||
TAst.Identifier('fib'),
|
||||
TAst.LambdaExpr(
|
||||
@@ -308,13 +305,11 @@ begin
|
||||
TAst.BinaryExpr(TAst.Identifier('n'), TScalar.TBinaryOp.Less, TAst.Constant(2)),
|
||||
TAst.Identifier('n'),
|
||||
TAst.BinaryExpr(
|
||||
// Naive recursive call using the variable name 'fib'
|
||||
TAst.FunctionCall(
|
||||
TAst.Identifier('fib'),
|
||||
[TAst.BinaryExpr(TAst.Identifier('n'), TScalar.TBinaryOp.Subtract, TAst.Constant(1))]
|
||||
),
|
||||
TScalar.TBinaryOp.Add,
|
||||
// Second naive recursive call
|
||||
TAst.FunctionCall(
|
||||
TAst.Identifier('fib'),
|
||||
[TAst.BinaryExpr(TAst.Identifier('n'), TScalar.TBinaryOp.Subtract, TAst.Constant(2))]
|
||||
@@ -326,25 +321,22 @@ begin
|
||||
]
|
||||
);
|
||||
|
||||
// Create a new scope and define the 'fib' function within it.
|
||||
var fibScope := TAstBinder.Bind(fibAst, FGScope).CreateScope(FGScope);
|
||||
var visitor := CreateVisitor(fibScope);
|
||||
visitor.Execute(fibAst);
|
||||
binder := TAstBinder.Create(FGScope);
|
||||
var desc: IScopeDescriptor;
|
||||
boundFibAst := binder.Execute(fibAst, desc);
|
||||
fibScope := desc.CreateScope(FGScope);
|
||||
|
||||
visitor := CreateEvaluator(fibScope);
|
||||
visitor.Execute(boundFibAst);
|
||||
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add('--- Naive recursive fib with AST---');
|
||||
sw := TStopwatch.StartNew;
|
||||
|
||||
// The script to execute is just the call to the function.
|
||||
root := TAst.FunctionCall(TAst.Identifier('fib'), [TAst.Constant(25)]);
|
||||
|
||||
FLastAst := root;
|
||||
|
||||
// Execute within the scope where 'fib' is defined.
|
||||
result := ExecuteAst(root, fibScope);
|
||||
|
||||
sw.Stop;
|
||||
Memo1.Lines.Add(Format('Result: fib(30) %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
||||
Memo1.Lines.Add(Format('Result: fib(25) %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
||||
|
||||
Memo1.Lines.Add('');
|
||||
Memo1.Lines.Add('--- Memoized naive fib with AST (using global fib)---');
|
||||
@@ -353,37 +345,29 @@ begin
|
||||
root :=
|
||||
TAst.Block(
|
||||
[
|
||||
// Create a memoized version by calling the RTL function on 'fib'
|
||||
TAst.Assign(TAst.Identifier('fib'), TAst.FunctionCall(TAst.Identifier('Memoize'), [TAst.Identifier('fib')])),
|
||||
// Call the new, memoized function
|
||||
TAst.FunctionCall(TAst.Identifier('fib'), [TAst.Constant(25)])
|
||||
]
|
||||
);
|
||||
|
||||
FLastAst := root;
|
||||
result := ExecuteAst(root, fibScope);
|
||||
|
||||
sw.Stop;
|
||||
Memo1.Lines.Add(Format('Result: fib(30) %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
||||
Memo1.Lines.Add(Format('Result: fib(25) %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
||||
UpdateScript;
|
||||
end;
|
||||
|
||||
procedure TForm1.PrettyPrintButtonClick(Sender: TObject);
|
||||
var
|
||||
visitor: TPrettyPrintVisitor;
|
||||
begin
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add('--- AST Pretty Print ---');
|
||||
|
||||
if not Assigned(FLastAst) then
|
||||
if not Assigned(FCurrAst) then
|
||||
begin
|
||||
Memo1.Lines.Add('No AST has been generated yet.');
|
||||
exit;
|
||||
end;
|
||||
|
||||
visitor := TPrettyPrintVisitor.Create;
|
||||
visitor.Execute(FLastAst);
|
||||
Memo1.Lines.Add(visitor.GetResult);
|
||||
Memo1.Lines.Add(TAstScript.Print(FCurrAst));
|
||||
end;
|
||||
|
||||
procedure TForm1.RecursionButtonClick(Sender: TObject);
|
||||
@@ -430,7 +414,6 @@ begin
|
||||
]
|
||||
);
|
||||
|
||||
FLastAst := root;
|
||||
// Execute with FGScope as parent.
|
||||
result := ExecuteAst(root, FGScope);
|
||||
|
||||
@@ -459,14 +442,12 @@ begin
|
||||
|
||||
for i := 0 to 4 do
|
||||
begin
|
||||
// TScalar can no longer hold TDateTime, convert to Int64 for the test.
|
||||
values[0].AsInt64 := Round((Now + i) * 24 * 60 * 60 * 1000);
|
||||
values[1].AsDouble := 100.0 + i;
|
||||
values[2].AsDouble := 105.0 + i;
|
||||
values[3].AsDouble := 98.0 + i;
|
||||
values[4].AsDouble := 102.0 + i;
|
||||
values[5].AsInt64 := 10000 * (i + 1);
|
||||
|
||||
series.Add(TScalarRecord.Create(recordDef, values));
|
||||
end;
|
||||
scope.Define('ohlcvSeries', TDataValue.FromRecordSeries(series));
|
||||
@@ -485,7 +466,6 @@ begin
|
||||
)
|
||||
);
|
||||
|
||||
FLastAst := ast;
|
||||
callAst := TAst.FunctionCall(ast, []);
|
||||
resultValue := ExecuteAst(callAst, scope);
|
||||
|
||||
@@ -515,7 +495,6 @@ begin
|
||||
)
|
||||
);
|
||||
|
||||
FLastAst := main;
|
||||
callAst := TAst.FunctionCall(main, []);
|
||||
result := ExecuteAst(callAst, FGScope);
|
||||
|
||||
@@ -554,7 +533,6 @@ begin
|
||||
]
|
||||
);
|
||||
|
||||
FLastAst := root;
|
||||
result := ExecuteAst(root, FGScope);
|
||||
|
||||
sw.Stop;
|
||||
@@ -566,7 +544,6 @@ procedure TForm1.WorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift
|
||||
begin
|
||||
if Button <> TMouseButton.mbMiddle then
|
||||
exit;
|
||||
|
||||
ShowVizualization(X, Y);
|
||||
end;
|
||||
|
||||
@@ -580,11 +557,15 @@ var
|
||||
scope: IExecutionScope;
|
||||
values: TArray<TScalar.TValue>;
|
||||
recordValue: TScalarRecord;
|
||||
binder: IAstBinder;
|
||||
setupAst, boundSetupAst, callAst, boundCallAst: IAstNode;
|
||||
setupDescriptor, callDescriptor: IScopeDescriptor;
|
||||
seriesAddress: TResolvedAddress;
|
||||
begin
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add(Format('--- Simulating O(1) SMA Crossover Strategy for %d ticks ---', [numRecs]));
|
||||
var sw := TStopwatch.StartNew;
|
||||
var setupAst :=
|
||||
setupAst :=
|
||||
TAst.Block(
|
||||
[
|
||||
TAst.VarDecl(TAst.Identifier('smaFast'), TAst.FunctionCall(TAst.Identifier('CreateSMA'), [TAst.Constant(smaFastLength)])),
|
||||
@@ -633,27 +614,34 @@ begin
|
||||
]
|
||||
);
|
||||
|
||||
FLastAst := setupAst;
|
||||
|
||||
// 1. Bind and execute the setup script.
|
||||
scope := TAstBinder.Bind(setupAst, FGScope).CreateScope(FGScope);
|
||||
binder := TAstBinder.Create(FGScope);
|
||||
boundSetupAst := binder.Execute(setupAst, setupDescriptor);
|
||||
|
||||
// This is a temporary visitor just for the setup execution.
|
||||
var setupVisitor := CreateVisitor(scope);
|
||||
setupVisitor.Execute(setupAst);
|
||||
scope := setupDescriptor.CreateScope(FGScope);
|
||||
var setupVisitor := CreateEvaluator(scope);
|
||||
setupVisitor.Execute(boundSetupAst);
|
||||
|
||||
// 2. Prepare for the simulation loop by modifying the now-populated scope.
|
||||
// 2. Prepare for the simulation loop
|
||||
scope.Define('current_series', TDataValue.Void);
|
||||
var currentSeriesIdent := TAst.Identifier('current_series');
|
||||
var callAst := TAst.FunctionCall(TAst.Identifier('maCrossStrategy'), [currentSeriesIdent]);
|
||||
callAst := TAst.FunctionCall(TAst.Identifier('maCrossStrategy'), [currentSeriesIdent]);
|
||||
|
||||
// 3. Re-bind the scope with the new AST. This creates the FINAL scope for the loop.
|
||||
scope := TAstBinder.Bind(callAst, scope).CreateScope(scope);
|
||||
var seriesAddress := currentSeriesIdent.Address;
|
||||
// 3. Re-bind the call AST within the now-populated scope to resolve the new variable.
|
||||
binder := TAstBinder.Create(scope);
|
||||
boundCallAst := binder.Execute(callAst, callDescriptor);
|
||||
|
||||
var visitor := CreateVisitor(scope);
|
||||
// 4. Get the address of 'current_series' from the new descriptor.
|
||||
var depth, index: Integer;
|
||||
if not callDescriptor.FindSymbol('current_series', depth, index) then
|
||||
raise Exception.Create('Could not resolve current_series address.');
|
||||
seriesAddress := TResolvedAddress.Create(akLocalOrParent, depth, index);
|
||||
|
||||
// 5. Simulation Loop
|
||||
// 5. Create the final scope and visitor for the simulation loop.
|
||||
var loopScope := callDescriptor.CreateScope(scope);
|
||||
var visitor := CreateEvaluator(loopScope);
|
||||
|
||||
// 6. Simulation Loop
|
||||
Memo1.Lines.Add('Starting simulation...');
|
||||
Application.ProcessMessages;
|
||||
|
||||
@@ -686,8 +674,8 @@ begin
|
||||
|
||||
if series.AsRecordSeries.TotalCount >= smaSlowLength then
|
||||
begin
|
||||
scope[seriesAddress] := series;
|
||||
var resultValue := visitor.Execute(callAst);
|
||||
loopScope[seriesAddress] := series;
|
||||
var resultValue := visitor.Execute(boundCallAst);
|
||||
if i mod 50 = 0 then
|
||||
begin
|
||||
Memo1.Lines.Add(Format('Tick %d/%d: Close = %.2f, Signal = %s', [i, numRecs, ohlcvRec.Close, resultValue.ToString]));
|
||||
@@ -704,7 +692,6 @@ end;
|
||||
|
||||
procedure TForm1.TailCallButtenClick(Sender: TObject);
|
||||
const
|
||||
// A large number to prove TCO prevents stack overflow
|
||||
RecursionDepth = 1000000;
|
||||
var
|
||||
root: IAstNode;
|
||||
@@ -719,27 +706,21 @@ begin
|
||||
root :=
|
||||
TAst.Block(
|
||||
[
|
||||
// var countDown = lambda(n) { ... };
|
||||
TAst.VarDecl(
|
||||
TAst.Identifier('countDown'),
|
||||
TAst.LambdaExpr(
|
||||
[TAst.Identifier('n')],
|
||||
// if (n > 0) then recur(n-1) else 0
|
||||
TAst.IfExpr(
|
||||
TAst.BinaryExpr(TAst.Identifier('n'), TScalar.TBinaryOp.Greater, TAst.Constant(0)),
|
||||
// This is the tail call position, now using recur.
|
||||
TAst.Recur([TAst.BinaryExpr(TAst.Identifier('n'), TScalar.TBinaryOp.Subtract, TAst.Constant(1))]),
|
||||
// Base case of the recursion returns 0
|
||||
TAst.Constant(0)
|
||||
)
|
||||
)
|
||||
),
|
||||
// Initial call to start the recursion
|
||||
TAst.FunctionCall(TAst.Identifier('countDown'), [TAst.Constant(RecursionDepth)])
|
||||
]
|
||||
);
|
||||
|
||||
FLastAst := root;
|
||||
result := ExecuteAst(root, FGScope);
|
||||
sw.Stop;
|
||||
|
||||
@@ -751,6 +732,8 @@ end;
|
||||
procedure TForm1.CreateTriggerExampleButtonClick(Sender: TObject);
|
||||
var
|
||||
blk: IAstNode;
|
||||
binder: IAstBinder;
|
||||
visitor: IEvaluatorVisitor;
|
||||
begin
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add('--- Creating Trigger Blueprint ---');
|
||||
@@ -771,20 +754,19 @@ begin
|
||||
]
|
||||
);
|
||||
|
||||
FTriggerScope := TAstBinder.Bind(blk, FGScope).CreateScope(FGScope);
|
||||
binder := TAstBinder.Create(FGScope);
|
||||
FCurrAst := binder.Execute(blk, FCurrDesc);
|
||||
FTriggerScope := FCurrDesc.CreateScope(FGScope);
|
||||
|
||||
// This case is simple enough to just inline the logic from ExecuteAst
|
||||
var visitor := CreateVisitor(FTriggerScope);
|
||||
visitor.Execute(blk);
|
||||
|
||||
FLastAst := blk;
|
||||
visitor := CreateEvaluator(FTriggerScope);
|
||||
visitor.Execute(FCurrAst);
|
||||
|
||||
Memo1.Lines.Add('Variable "X" and function "tickHandler" defined in persistent scope.');
|
||||
Memo1.Lines.Add('Click "Do Trigger" to execute.');
|
||||
UpdateScript;
|
||||
end;
|
||||
|
||||
function TForm1.CreateVisitor(Scope: IExecutionScope): IAstVisitor;
|
||||
function TForm1.CreateEvaluator(Scope: IExecutionScope): IEvaluatorVisitor;
|
||||
begin
|
||||
if DebugBox.IsChecked then
|
||||
Result := TDebugEvaluatorVisitor.Create(Scope, Memo1.Lines, ShowScopeBox.IsChecked)
|
||||
@@ -797,7 +779,6 @@ var
|
||||
callAst: IFunctionCallNode;
|
||||
begin
|
||||
callAst := TAst.FunctionCall(TAst.Identifier('tickHandler'), [TAst.Constant(1)]);
|
||||
FLastAst := callAst;
|
||||
|
||||
var X := ExecuteAst(callAst, FTriggerScope);
|
||||
|
||||
@@ -810,7 +791,6 @@ var
|
||||
callAst: IFunctionCallNode;
|
||||
begin
|
||||
callAst := TAst.FunctionCall(TAst.Identifier('tickHandler'), [TAst.Constant(2)]);
|
||||
FLastAst := callAst;
|
||||
|
||||
var X := ExecuteAst(callAst, FTriggerScope);
|
||||
|
||||
@@ -819,18 +799,21 @@ begin
|
||||
end;
|
||||
|
||||
procedure TForm1.DumpButtonClick(Sender: TObject);
|
||||
var
|
||||
binder: IAstBinder;
|
||||
boundNode: IAstNode;
|
||||
begin
|
||||
// Call the dumper for the last AST.
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add('--- AST Dump ---');
|
||||
|
||||
if not Assigned(FLastAst) then
|
||||
if not Assigned(FCurrAst) then
|
||||
begin
|
||||
Memo1.Lines.Add('No AST has been generated yet. Click a test button first.');
|
||||
exit;
|
||||
end;
|
||||
|
||||
TAstDumper.Dump(FLastAst, Memo1.Lines);
|
||||
boundNode := binder.Execute(FCurrAst, FCurrDesc);
|
||||
TAstDumper.Dump(boundNode, Memo1.Lines);
|
||||
end;
|
||||
|
||||
procedure TForm1.ExternalFuncButtonClick(Sender: TObject);
|
||||
@@ -860,7 +843,6 @@ begin
|
||||
|
||||
callAst := TAst.FunctionCall(TAst.Identifier('delphiAdd'), [TAst.Constant(100), TAst.Constant(123)]);
|
||||
|
||||
FLastAst := callAst;
|
||||
resultValue := ExecuteAst(callAst, scope);
|
||||
Memo1.Lines.Add(Format('Result from delphiAdd(100, 123): %s', [resultValue.ToString]));
|
||||
UpdateScript;
|
||||
@@ -874,10 +856,7 @@ begin
|
||||
mainBlock :=
|
||||
TAst.Block(
|
||||
[
|
||||
// 1. Define the shared variable.
|
||||
TAst.VarDecl(TAst.Identifier('a'), TAst.Constant(10)),
|
||||
// 2. Define a function that returns a closure that MODIFIES 'a'.
|
||||
// This creates the multi-level capture scenario.
|
||||
TAst.VarDecl(
|
||||
TAst.Identifier('modifier'),
|
||||
TAst.LambdaExpr(
|
||||
@@ -891,34 +870,23 @@ begin
|
||||
)
|
||||
)
|
||||
),
|
||||
// 3. Define a simple closure that READS 'a'.
|
||||
TAst.VarDecl(TAst.Identifier('reader'), TAst.LambdaExpr([], TAst.Identifier('a'))),
|
||||
// --- Execute the test ---
|
||||
// 4. Get the inner modifier closure.
|
||||
TAst.VarDecl(TAst.Identifier('innermost_closure'), TAst.FunctionCall(TAst.Identifier('modifier'), [])),
|
||||
// 5. Call the inner closure to modify 'a'.
|
||||
TAst.FunctionCall(TAst.Identifier('innermost_closure'), []),
|
||||
// 6. Call the reader to get the final value.
|
||||
TAst.FunctionCall(TAst.Identifier('reader'), [])
|
||||
]
|
||||
);
|
||||
|
||||
FLastAst := mainBlock;
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add('--- Executing Corrected Upvalue Test ---');
|
||||
|
||||
resultValue := ExecuteAst(mainBlock, FGScope);
|
||||
|
||||
// With a correct binder and evaluator, the result must be 15.
|
||||
var res := resultValue.AsScalar.Value.AsInt64;
|
||||
if res = 15 then
|
||||
begin
|
||||
Memo1.Lines.Add('SUCCESS: The result is 15.');
|
||||
end
|
||||
Memo1.Lines.Add('SUCCESS: The result is 15.')
|
||||
else
|
||||
begin
|
||||
Memo1.Lines.Add(Format('FAILURE: Expected 15, but got %s.', [resultValue.ToString]));
|
||||
end;
|
||||
|
||||
Assert(TScalar.FromInt64(15) = resultValue.AsScalar, 'The final result should be 15.');
|
||||
Memo1.Lines.Add('Please check the new dump.');
|
||||
@@ -946,13 +914,15 @@ begin
|
||||
end;
|
||||
|
||||
try
|
||||
FLastAst := TAstJson.Deserialize(jsonString);
|
||||
var binder := TAstBinder.Create(FGScope);
|
||||
FCurrAst := binder.Execute(TAstJson.Deserialize(jsonString), FCurrDesc);
|
||||
Memo1.Lines.Add('AST deserialized successfully from JSON.');
|
||||
Memo1.Lines.Add('You can now visualize it (Middle Mouse Click) or pretty-print it.');
|
||||
except
|
||||
on E: Exception do
|
||||
begin
|
||||
FLastAst := nil;
|
||||
FCurrAst := nil;
|
||||
FCurrDesc := nil;
|
||||
Memo1.Lines.Add('Error deserializing AST from JSON:');
|
||||
Memo1.Lines.Add(E.Message);
|
||||
Memo1.Lines.Add('--- Original JSON ---');
|
||||
@@ -970,7 +940,10 @@ begin
|
||||
try
|
||||
FScriptUpdate := true;
|
||||
try
|
||||
ScriptMemo.Lines.Text := ScriptMemo.Lines.Text + #10#13 + TAstScript.Print(Node);
|
||||
if Assigned(Node) then
|
||||
ScriptMemo.Lines.Text := TAstScript.Print(Node)
|
||||
else
|
||||
ScriptMemo.Lines.Clear;
|
||||
finally
|
||||
FScriptUpdate := false;
|
||||
end;
|
||||
@@ -987,7 +960,8 @@ begin
|
||||
|
||||
Memo1.Lines.Clear;
|
||||
try
|
||||
FLastAst := TAstScript.Parse(ScriptMemo.Lines.Text);
|
||||
var binder := TAstBinder.Create(FGScope);
|
||||
FCurrAst := binder.Execute(TAstScript.Parse(ScriptMemo.Lines.Text), FCurrDesc);
|
||||
|
||||
FWorkspace.DeleteChildren;
|
||||
ShowVizualization(14, 14);
|
||||
@@ -1003,14 +977,14 @@ var
|
||||
begin
|
||||
Memo1.Lines.Clear;
|
||||
|
||||
if not Assigned(FLastAst) then
|
||||
if not Assigned(FCurrAst) then
|
||||
begin
|
||||
Memo1.Lines.Add('No AST available to serialize. Please generate one first.');
|
||||
exit;
|
||||
end;
|
||||
|
||||
try
|
||||
jsonString := TAstJson.Serialize(FLastAst);
|
||||
jsonString := TAstJson.Serialize(FCurrAst);
|
||||
Memo1.Lines.Text := jsonString;
|
||||
except
|
||||
on E: Exception do
|
||||
@@ -1023,8 +997,7 @@ end;
|
||||
|
||||
procedure TForm1.UpdateScript;
|
||||
begin
|
||||
Memo1.Lines.Clear;
|
||||
PrintScript(FLastAst);
|
||||
PrintScript(FCurrAst);
|
||||
|
||||
FWorkspace.DeleteChildren;
|
||||
ShowVizualization(14, 14);
|
||||
@@ -1032,14 +1005,13 @@ end;
|
||||
|
||||
procedure TForm1.ShowVizualization(X, Y: Single);
|
||||
begin
|
||||
if FLastAst <> nil then
|
||||
if FCurrAst <> nil then
|
||||
begin
|
||||
var visu := TVisualizationMode.vmDetailed;
|
||||
if FlowOnlyBox.IsChecked then
|
||||
visu := TVisualizationMode.vmControlFlow;
|
||||
|
||||
var descr := TAstBinder.Bind(FLastAst, FGScope);
|
||||
FWorkspace.BuildTree(FLastAst, descr.CreateScope(FGScope), TPointF.Create(X, Y), visu);
|
||||
FWorkspace.BuildTree(FCurrAst, FCurrDesc, TPointF.Create(X, Y), visu);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
@@ -6,40 +6,34 @@ uses
|
||||
System.SysUtils,
|
||||
Myc.Data.Value,
|
||||
Myc.Data.Scalar,
|
||||
Myc.Ast.Transformer,
|
||||
Myc.Ast.Nodes;
|
||||
|
||||
type
|
||||
// This visitor converts an AST expression subtree into a single string.
|
||||
TAstToTextVisitor = class(TInterfacedObject, IAstVisitor)
|
||||
TAstToTextVisitor = class(TAstVisitor)
|
||||
public
|
||||
{ IAstVisitor }
|
||||
function Execute(const RootNode: IAstNode): TDataValue;
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
function VisitIndexer(const Node: IIndexerNode): TDataValue;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
|
||||
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue; override;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
|
||||
function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
|
||||
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
function TAstToTextVisitor.Execute(const RootNode: IAstNode): TDataValue;
|
||||
begin
|
||||
Result := RootNode.Accept(Self);
|
||||
end;
|
||||
|
||||
{ TAstToTextVisitor }
|
||||
|
||||
function TAstToTextVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
||||
|
||||
@@ -44,7 +44,12 @@ type
|
||||
procedure DblClick; override;
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
procedure BuildTree(const RootNode: IAstNode; const RootScope: IExecutionScope; const Position: TPointF; AMode: TVisualizationMode);
|
||||
procedure BuildTree(
|
||||
const RootNode: IAstNode;
|
||||
const RootDescriptor: IScopeDescriptor;
|
||||
const Position: TPointF;
|
||||
AMode: TVisualizationMode
|
||||
);
|
||||
function GetChildrenMatrix(var Matrix: TMatrix; var Simple: Boolean): Boolean; override;
|
||||
function Zoom(Factor: Single): Boolean;
|
||||
|
||||
@@ -109,19 +114,17 @@ end;
|
||||
|
||||
procedure TAuraWorkspace.BuildTree(
|
||||
const RootNode: IAstNode;
|
||||
const RootScope: IExecutionScope;
|
||||
const RootDescriptor: IScopeDescriptor;
|
||||
const Position: TPointF;
|
||||
AMode: TVisualizationMode
|
||||
);
|
||||
var
|
||||
connections: TList<TPinConnection>;
|
||||
rootDescriptor: IScopeDescriptor;
|
||||
begin
|
||||
connections := TList<TPinConnection>.Create;
|
||||
try
|
||||
// Create the scope descriptor from the execution scope provided by the binder.
|
||||
rootDescriptor := TAstBinder.CreateDescriptor(RootScope);
|
||||
RootNode.Accept(TAstToAuraNodeVisitor.Create(Self, Self, Position, connections, nil, AMode, nil, nil, rootDescriptor));
|
||||
RootNode.Accept(TAstToAuraNodeVisitor.Create(Self, Self, Position, connections, nil, AMode, nil, nil, RootDescriptor));
|
||||
FConnections := FConnections + connections.ToArray;
|
||||
finally
|
||||
connections.Free;
|
||||
|
||||
@@ -17,6 +17,7 @@ uses
|
||||
Myc.Data.Value,
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Scope,
|
||||
Myc.Ast.Transformer, // Für TAstVisitor
|
||||
Myc.Fmx.AstEditor.Node,
|
||||
Myc.Fmx.AstEditor.Workspace;
|
||||
|
||||
@@ -30,7 +31,7 @@ type
|
||||
TPinShape = (psCircle, psTriangle);
|
||||
TPinAlignment = (paLeft, paRight);
|
||||
|
||||
TAstToAuraNodeVisitor = class(TInterfacedObject, IAstVisitor)
|
||||
TAstToAuraNodeVisitor = class(TAstVisitor)
|
||||
public
|
||||
type
|
||||
TAuraNodeResult = record
|
||||
@@ -101,24 +102,24 @@ type
|
||||
property Connections: TList<TPinConnection> read FConnections;
|
||||
|
||||
{ IAstVisitor }
|
||||
function Execute(const RootNode: IAstNode): TDataValue;
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
function VisitIndexer(const Node: IIndexerNode): TDataValue;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
|
||||
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
procedure Execute(const RootNode: IAstNode);
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue; override;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
|
||||
function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
|
||||
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -128,6 +129,7 @@ uses
|
||||
System.StrUtils,
|
||||
FMX.Platform,
|
||||
Myc.Data.Scalar,
|
||||
Myc.Ast.Binding, // For TBound...Node classes
|
||||
Myc.Fmx.AstEditor.Text;
|
||||
|
||||
{ TAstToAuraNodeVisitor }
|
||||
@@ -339,9 +341,9 @@ begin
|
||||
Result.TagString := Tag;
|
||||
end;
|
||||
|
||||
function TAstToAuraNodeVisitor.Execute(const RootNode: IAstNode): TDataValue;
|
||||
procedure TAstToAuraNodeVisitor.Execute(const RootNode: IAstNode);
|
||||
begin
|
||||
Result := RootNode.Accept(Self);
|
||||
RootNode.Accept(Self)
|
||||
end;
|
||||
|
||||
function TAstToAuraNodeVisitor.TryGetDescr(const Node: IAstNode; out Text: String): Boolean;
|
||||
@@ -349,8 +351,7 @@ begin
|
||||
if FMode <> vmControlFlow then
|
||||
exit(false);
|
||||
|
||||
var textVisitor: IAstVisitor := TAstToTextVisitor.Create;
|
||||
Text := Node.Accept(textVisitor).AsText;
|
||||
Text := Node.Accept(TAstToTextVisitor.Create).AsText;
|
||||
Result := Pos('{', Text) = 0;
|
||||
end;
|
||||
|
||||
@@ -612,11 +613,13 @@ function TAstToAuraNodeVisitor.VisitIdentifier(const Node: IIdentifierNode): TDa
|
||||
var
|
||||
existingResult: TAuraNodeResult;
|
||||
identifierNode: TAuraNode;
|
||||
boundNode: TBoundIdentifierNode;
|
||||
begin
|
||||
if Node.Address.Kind <> akUnresolved then
|
||||
if Node is TBoundIdentifierNode then
|
||||
begin
|
||||
// FindNodeForAddress now handles all cases: local cache, on-demand parameter creation, and recursive parent/upvalue lookup.
|
||||
if FindNodeForAddress(Node.Address, existingResult) then
|
||||
boundNode := Node as TBoundIdentifierNode;
|
||||
// FindNodeForAddress handles all cases: local cache, on-demand parameter creation, and recursive parent/upvalue lookup.
|
||||
if FindNodeForAddress(boundNode.Address, existingResult) then
|
||||
begin
|
||||
FLastResult := existingResult;
|
||||
end
|
||||
@@ -632,7 +635,7 @@ begin
|
||||
end
|
||||
else
|
||||
begin
|
||||
identifierNode := CreateNodeControl('Identifier', Node.Name);
|
||||
identifierNode := CreateNodeControl('Identifier (unbound)', Node.Name);
|
||||
FLastResult.LayoutNode := identifierNode;
|
||||
FLastResult.OutputPin := CreateOutput(identifierNode);
|
||||
FinalizeNodeLayout(identifierNode);
|
||||
@@ -756,12 +759,14 @@ var
|
||||
originalAddress: TResolvedAddress;
|
||||
targetVisitor: TAstToAuraNodeVisitor;
|
||||
i: Integer;
|
||||
boundLambda: TBoundLambdaExpressionNode;
|
||||
begin
|
||||
if (Address.Kind = akUpvalue) and Assigned(FCurrentLambda) then
|
||||
if (Address.Kind = akUpvalue) and Assigned(FCurrentLambda) and (FCurrentLambda is TBoundLambdaExpressionNode) then
|
||||
begin
|
||||
if Address.SlotIndex < Length(FCurrentLambda.Upvalues) then
|
||||
boundLambda := FCurrentLambda as TBoundLambdaExpressionNode;
|
||||
if Address.SlotIndex < Length(boundLambda.Upvalues) then
|
||||
begin
|
||||
originalAddress := FCurrentLambda.Upvalues[Address.SlotIndex];
|
||||
originalAddress := boundLambda.Upvalues[Address.SlotIndex];
|
||||
if Assigned(FParentVisitor) then
|
||||
exit(FParentVisitor.FindNodeForAddress(originalAddress, NodeResult));
|
||||
end;
|
||||
@@ -799,14 +804,26 @@ var
|
||||
control: TControl;
|
||||
childLastResult: TAuraNodeResult;
|
||||
entryNode, exitNode: TAuraNode;
|
||||
boundNode: TBoundLambdaExpressionNode;
|
||||
begin
|
||||
// This visitor must operate on a bound AST.
|
||||
if not (Node is TBoundLambdaExpressionNode) then
|
||||
begin
|
||||
lambdaNode := CreateNodeControl(#$03BB, '(unbound)');
|
||||
FinalizeNodeLayout(lambdaNode);
|
||||
FLastResult.LayoutNode := lambdaNode;
|
||||
FLastResult.OutputPin := CreateOutput(lambdaNode);
|
||||
exit(TDataValue.Void);
|
||||
end;
|
||||
boundNode := Node as TBoundLambdaExpressionNode;
|
||||
|
||||
// Parameter string for the title
|
||||
paramStr := '(';
|
||||
if Length(Node.Parameters) > 0 then
|
||||
if Length(boundNode.Parameters) > 0 then
|
||||
begin
|
||||
paramStr := paramStr + Node.Parameters[0].Name;
|
||||
for i := 1 to High(Node.Parameters) do
|
||||
paramStr := paramStr + ', ' + Node.Parameters[i].Name;
|
||||
paramStr := paramStr + boundNode.Parameters[0].Name;
|
||||
for i := 1 to High(boundNode.Parameters) do
|
||||
paramStr := paramStr + ', ' + boundNode.Parameters[i].Name;
|
||||
end;
|
||||
paramStr := paramStr + ')';
|
||||
|
||||
@@ -822,8 +839,6 @@ begin
|
||||
entryNode.Parent := lambdaNode;
|
||||
entryNode.Position.Point := TPointF.Create(0, 0);
|
||||
entryNode.Height := pinNodeHeight;
|
||||
// A lambda expression is a value, it doesn't connect to the parent execution flow.
|
||||
// Its internal flow starts with the exit of the 'call' node.
|
||||
var internalExec: TArray<TControl> := [CreateExit(entryNode, '')];
|
||||
FinalizeNodeLayout(entryNode);
|
||||
|
||||
@@ -831,24 +846,25 @@ begin
|
||||
childStartPos := TPointF.Create(FSpacing.X, FSpacing.Y + pinNodeHeight);
|
||||
childVisitor :=
|
||||
TAstToAuraNodeVisitor
|
||||
.Create(FWorkspace, lambdaNode, childStartPos, FConnections, internalExec, FMode, Self, Node, Node.ScopeDescriptor);
|
||||
.Create(FWorkspace, lambdaNode, childStartPos, FConnections, internalExec, FMode, Self, Node, boundNode.ScopeDescriptor);
|
||||
|
||||
FCurrentExec.Clear;
|
||||
|
||||
// 4. Let the child visitor create parameter nodes and visit the lambda body.
|
||||
for var param in Node.Parameters do
|
||||
for var param in boundNode.Parameters do
|
||||
begin
|
||||
if param.Address.Kind <> akUnresolved then
|
||||
if param is TBoundIdentifierNode then
|
||||
with (param as TBoundIdentifierNode) do
|
||||
begin
|
||||
var paramNode := childVisitor.CreateNodeControl('Parameter', param.Name);
|
||||
var paramNode := childVisitor.CreateNodeControl('Parameter', Name);
|
||||
var paramResult: TAuraNodeResult;
|
||||
paramResult.LayoutNode := paramNode;
|
||||
paramResult.OutputPin := childVisitor.CreateOutput(paramNode);
|
||||
childVisitor.FinalizeNodeLayout(paramNode);
|
||||
childVisitor.FSlotCache[param.Address.SlotIndex] := paramResult;
|
||||
childVisitor.FSlotCache[Address.SlotIndex] := paramResult;
|
||||
end;
|
||||
end;
|
||||
Node.Body.Accept(childVisitor);
|
||||
boundNode.Body.Accept(childVisitor);
|
||||
childLastResult := childVisitor.FLastResult;
|
||||
|
||||
// 5. Resize container to fit all internally generated nodes.
|
||||
@@ -1138,6 +1154,7 @@ var
|
||||
oldParentControl: TControl;
|
||||
oldPos: TPointF;
|
||||
control: TControl;
|
||||
boundIdentifier: TBoundIdentifierNode;
|
||||
begin
|
||||
if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
|
||||
begin
|
||||
@@ -1194,8 +1211,11 @@ begin
|
||||
FCurrentPos.Y := varDeclNode.Position.Y + varDeclNode.Height + FSpacing.Y;
|
||||
|
||||
// 8. Die neue Variable im Cache des aktuellen Scopes registrieren.
|
||||
if Node.Identifier.Address.Kind <> akUnresolved then
|
||||
FSlotCache[Node.Identifier.Address.SlotIndex] := FLastResult;
|
||||
if Node.Identifier is TBoundIdentifierNode then
|
||||
begin
|
||||
boundIdentifier := Node.Identifier as TBoundIdentifierNode;
|
||||
FSlotCache[boundIdentifier.Address.SlotIndex] := FLastResult;
|
||||
end;
|
||||
end;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
+311
-310
@@ -8,16 +8,25 @@ uses
|
||||
System.Generics.Collections,
|
||||
Myc.Data.Value,
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Traverser,
|
||||
Myc.Ast.Scope;
|
||||
Myc.Ast.Transformer,
|
||||
Myc.Ast.Scope,
|
||||
Myc.Ast;
|
||||
|
||||
type
|
||||
TAstBinder = class(TAstTraverser)
|
||||
// The binder is a transformer that enriches the AST with semantic information
|
||||
// like resolved addresses, scopes, and tail-call annotations.
|
||||
IAstBinder = interface(IAstVisitor)
|
||||
function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
|
||||
end;
|
||||
|
||||
TAstBinder = class(TAstTransformer, IAstBinder)
|
||||
private
|
||||
type
|
||||
// Helper class to track upvalues for a lambda expression.
|
||||
TUpvalueMapping = class
|
||||
public
|
||||
Map: TDictionary<TResolvedAddress, Integer>;
|
||||
Nodes: TList<IIdentifierNode>;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
@@ -27,60 +36,87 @@ type
|
||||
FNestedLambdaCount: Integer;
|
||||
FIsTailStack: TStack<Boolean>;
|
||||
FNextIsTail: Boolean;
|
||||
|
||||
procedure EnterScope;
|
||||
procedure ExitScope;
|
||||
|
||||
protected
|
||||
function Accept(const Node: IAstNode): TDataValue; override;
|
||||
function GetCurrentDescriptor: IScopeDescriptor;
|
||||
function IsValidIdentifier(const Name: string): Boolean;
|
||||
protected
|
||||
// Stack management for tail-call state is centralized here.
|
||||
function Accept(const Node: IAstNode): TDataValue; override;
|
||||
|
||||
public
|
||||
constructor Create(const AInitialScope: IExecutionScope);
|
||||
destructor Destroy; override;
|
||||
|
||||
class function Bind(const RootNode: IAstNode; const ParentScope: IExecutionScope): IScopeDescriptor;
|
||||
function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
|
||||
|
||||
class function CreateDescriptor(const Scope: IExecutionScope): IScopeDescriptor; static;
|
||||
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
|
||||
// The binder overrides specific transform methods to enrich the AST.
|
||||
function TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode; override;
|
||||
function TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode; override;
|
||||
function TransformAssignment(const Node: IAssignmentNode): IAssignmentNode; override;
|
||||
function TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode; override;
|
||||
function TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode; override;
|
||||
function TransformRecur(const Node: IRecurNode): IRecurNode; override;
|
||||
function TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode; override;
|
||||
function TransformIfExpression(const Node: IIfExpressionNode): IIfExpressionNode; override;
|
||||
function TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode; override;
|
||||
function TransformBinaryExpression(const Node: IBinaryExpressionNode): IBinaryExpressionNode; override;
|
||||
function TransformUnaryExpression(const Node: IUnaryExpressionNode): IUnaryExpressionNode; override;
|
||||
|
||||
property CurrentDescriptor: IScopeDescriptor read FCurrentDescriptor;
|
||||
property CurrentDescriptor: IScopeDescriptor read GetCurrentDescriptor;
|
||||
end;
|
||||
|
||||
TBoundIdentifierNode = class(TIdentifierNode)
|
||||
private
|
||||
FAddress: TResolvedAddress;
|
||||
public
|
||||
constructor Create(const AUnboundNode: IIdentifierNode; const AAddress: TResolvedAddress);
|
||||
property Address: TResolvedAddress read FAddress;
|
||||
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;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.Generics.Defaults,
|
||||
System.Character,
|
||||
Myc.Ast;
|
||||
System.Character;
|
||||
|
||||
type
|
||||
TScopeDescriptor = class(TInterfacedObject, IScopeDescriptor)
|
||||
private
|
||||
FParent: IScopeDescriptor;
|
||||
FSymbols: TDictionary<string, Integer>;
|
||||
function GetParent: IScopeDescriptor;
|
||||
function GetSlotCount: Integer;
|
||||
function GetSymbols: TDictionary<string, Integer>;
|
||||
public
|
||||
constructor Create(const AParent: IScopeDescriptor);
|
||||
destructor Destroy; override;
|
||||
function Define(const Name: string): Integer;
|
||||
function FindSymbol(const Name: string; out Depth, Index: Integer): Boolean;
|
||||
function CreateScope(const Parent: IExecutionScope): IExecutionScope;
|
||||
procedure PopulateFromScope(Scope: TExecutionScope);
|
||||
property Symbols: TDictionary<string, Integer> read FSymbols;
|
||||
end;
|
||||
|
||||
// A custom equality comparer for TResolvedAddress to ensure correct behavior in TDictionary.
|
||||
TResolvedAddressComparer = class(TEqualityComparer<TResolvedAddress>)
|
||||
public
|
||||
@@ -88,73 +124,68 @@ type
|
||||
function GetHashCode(const Value: TResolvedAddress): Integer; override;
|
||||
end;
|
||||
|
||||
{ TResolvedAddressComparer }
|
||||
{ TBoundIdentifierNode }
|
||||
constructor TBoundIdentifierNode.Create(const AUnboundNode: IIdentifierNode; const AAddress: TResolvedAddress);
|
||||
begin
|
||||
inherited Create(AUnboundNode.Name);
|
||||
FAddress := AAddress;
|
||||
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;
|
||||
|
||||
{ TResolvedAddressComparer }
|
||||
function TResolvedAddressComparer.Equals(const Left, Right: TResolvedAddress): Boolean;
|
||||
begin
|
||||
// Use the existing equality operator for the record.
|
||||
Result := (Left = Right);
|
||||
end;
|
||||
|
||||
function TResolvedAddressComparer.GetHashCode(const Value: TResolvedAddress): Integer;
|
||||
begin
|
||||
// Classic hash combining algorithm using prime numbers.
|
||||
Result := 17;
|
||||
Result := Result * 23 + Ord(Value.Kind);
|
||||
Result := Result * 23 + Value.ScopeDepth;
|
||||
Result := Result * 23 + Value.SlotIndex;
|
||||
end;
|
||||
|
||||
{ TAstBinder }
|
||||
|
||||
constructor TAstBinder.Create(const AInitialScope: IExecutionScope);
|
||||
{ TAstBinder.TUpvalueMapping }
|
||||
constructor TAstBinder.TUpvalueMapping.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
FCurrentDescriptor := CreateDescriptor(AInitialScope);
|
||||
FUpvalueStack := TObjectStack<TUpvalueMapping>.Create(true);
|
||||
FNestedLambdaCount := 0;
|
||||
FIsTailStack := TStack<Boolean>.Create;
|
||||
// The content of the root node is in a tail position.
|
||||
FNextIsTail := true;
|
||||
Map := TDictionary<TResolvedAddress, Integer>.Create(TResolvedAddressComparer.Create);
|
||||
Nodes := TList<IIdentifierNode>.Create();
|
||||
end;
|
||||
|
||||
destructor TAstBinder.Destroy;
|
||||
destructor TAstBinder.TUpvalueMapping.Destroy;
|
||||
begin
|
||||
FIsTailStack.Free;
|
||||
FUpvalueStack.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstBinder.Accept(const Node: IAstNode): TDataValue;
|
||||
begin
|
||||
if not Assigned(Node) or Done then
|
||||
exit;
|
||||
|
||||
FIsTailStack.Push(FNextIsTail);
|
||||
try
|
||||
Result := inherited Accept(Node);
|
||||
finally
|
||||
FNextIsTail := FIsTailStack.Pop;
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TAstBinder.Bind(const RootNode: IAstNode; const ParentScope: IExecutionScope): IScopeDescriptor;
|
||||
var
|
||||
binder: TAstBinder;
|
||||
begin
|
||||
binder := TAstBinder.Create(ParentScope);
|
||||
try
|
||||
binder.EnterScope;
|
||||
try
|
||||
// Start the traversal
|
||||
binder.Accept(RootNode);
|
||||
Result := binder.CurrentDescriptor;
|
||||
finally
|
||||
binder.ExitScope;
|
||||
end;
|
||||
finally
|
||||
binder.Free;
|
||||
end;
|
||||
Nodes.Free;
|
||||
Map.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
class function TAstBinder.CreateDescriptor(const Scope: IExecutionScope): IScopeDescriptor;
|
||||
@@ -169,156 +200,190 @@ begin
|
||||
Result := TScopeDescriptor.Create(nil);
|
||||
end;
|
||||
|
||||
constructor TAstBinder.Create(const AInitialScope: IExecutionScope);
|
||||
begin
|
||||
inherited Create;
|
||||
FCurrentDescriptor := CreateDescriptor(AInitialScope);
|
||||
FUpvalueStack := TObjectStack<TUpvalueMapping>.Create(True);
|
||||
FNestedLambdaCount := 0;
|
||||
FIsTailStack := TStack<Boolean>.Create;
|
||||
FNextIsTail := True;
|
||||
end;
|
||||
|
||||
destructor TAstBinder.Destroy;
|
||||
begin
|
||||
FIsTailStack.Free;
|
||||
FUpvalueStack.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstBinder.Accept(const Node: IAstNode): TDataValue;
|
||||
begin
|
||||
if (not Assigned(Node)) or Done then
|
||||
exit;
|
||||
|
||||
FIsTailStack.Push(FNextIsTail);
|
||||
try
|
||||
Result := inherited Accept(Node);
|
||||
finally
|
||||
FNextIsTail := FIsTailStack.Pop;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstBinder.EnterScope;
|
||||
begin
|
||||
FCurrentDescriptor := TScopeDescriptor.Create(FCurrentDescriptor);
|
||||
end;
|
||||
|
||||
function TAstBinder.Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
|
||||
begin
|
||||
EnterScope;
|
||||
try
|
||||
Result := Accept(RootNode).AsIntf<IAstNode>;
|
||||
Descriptor := FCurrentDescriptor;
|
||||
finally
|
||||
ExitScope;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstBinder.ExitScope;
|
||||
begin
|
||||
FCurrentDescriptor := FCurrentDescriptor.Parent;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
function TAstBinder.GetCurrentDescriptor: IScopeDescriptor;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
inherited;
|
||||
Result := FCurrentDescriptor;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||
function TAstBinder.IsValidIdentifier(const Name: string): Boolean;
|
||||
var
|
||||
c: Char;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
inherited;
|
||||
end;
|
||||
if Name.IsEmpty then
|
||||
exit(False);
|
||||
|
||||
function TAstBinder.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
c := Name[1];
|
||||
if not (c.IsLetter or (c = '_')) then
|
||||
exit(False);
|
||||
|
||||
var n := Node.Expressions.Count - 1;
|
||||
for var i := 0 to n do
|
||||
for c in Name do
|
||||
begin
|
||||
// The last expression is in a tail position IF the block itself is.
|
||||
if i = n then
|
||||
FNextIsTail := FIsTailStack.Peek;
|
||||
|
||||
Accept(Node.Expressions[i]);
|
||||
if not (c.IsLetterOrDigit or (c = '_') or (c = '-')) then
|
||||
exit(False);
|
||||
end;
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
begin
|
||||
// Annotate this node based on its context, which is on top of the stack.
|
||||
(Node as TFunctionCallNode).IsTailCall := FIsTailStack.Peek;
|
||||
|
||||
// Let the default traverser visit children (callee, args), but ensure
|
||||
// their context is non-tail.
|
||||
FNextIsTail := False;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
begin
|
||||
// Check if the current context is a tail position.
|
||||
if not FIsTailStack.Peek then
|
||||
raise Exception.Create('''recur'' can only be used in a tail position.');
|
||||
|
||||
// Arguments to recur are not in a tail position.
|
||||
FNextIsTail := False;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
function TAstBinder.TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode;
|
||||
var
|
||||
depth, idx: Integer;
|
||||
identNode: TIdentifierNode;
|
||||
upvalue: TUpvalueMapping;
|
||||
originalAddress: TResolvedAddress;
|
||||
upvalueIndex: Integer;
|
||||
begin
|
||||
identNode := Node as TIdentifierNode;
|
||||
if identNode.Address.Kind <> akUnresolved then
|
||||
exit;
|
||||
|
||||
if FCurrentDescriptor.FindSymbol(identNode.Name, depth, idx) then
|
||||
if FCurrentDescriptor.FindSymbol(Node.Name, depth, idx) then
|
||||
begin
|
||||
if (depth > 0) and (FUpvalueStack.Count > 0) then
|
||||
begin
|
||||
upvalue := FUpvalueStack.Peek;
|
||||
|
||||
// Address is relative to the lambda's parent scope.
|
||||
var upvalue := FUpvalueStack.Peek;
|
||||
dec(depth);
|
||||
originalAddress := TResolvedAddress.Create(akLocalOrParent, depth, idx);
|
||||
var originalAddress := TResolvedAddress.Create(akLocalOrParent, depth, idx);
|
||||
|
||||
var upvalueIndex: Integer;
|
||||
if not upvalue.Map.TryGetValue(originalAddress, upvalueIndex) then
|
||||
begin
|
||||
upvalueIndex := upvalue.Map.Count;
|
||||
upvalue.Map.Add(originalAddress, upvalueIndex);
|
||||
end;
|
||||
|
||||
(Node as TIdentifierNode).Address := TResolvedAddress.Create(akUpvalue, 0, upvalueIndex);
|
||||
var address := TResolvedAddress.Create(akUpvalue, 0, upvalueIndex);
|
||||
Result := TBoundIdentifierNode.Create(Node, address);
|
||||
end
|
||||
else
|
||||
begin
|
||||
// 1. case: depth=0 - this is a local var
|
||||
// 2. case: UpvalueStack is empty - there is no surrounding lambda, we need to reference (and capture) the whole parent scope
|
||||
(Node as TIdentifierNode).Address := TResolvedAddress.Create(akLocalOrParent, depth, idx);
|
||||
end;
|
||||
var address := TResolvedAddress.Create(akLocalOrParent, depth, idx);
|
||||
Result := TBoundIdentifierNode.Create(Node, address);
|
||||
end
|
||||
end
|
||||
else
|
||||
raise Exception.CreateFmt('Undefined identifier: "%s"', [identNode.Name]);
|
||||
raise Exception.CreateFmt('Undefined identifier: "%s"', [Node.Name]);
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
||||
begin
|
||||
// The condition is never in a tail position.
|
||||
FNextIsTail := False;
|
||||
Accept(Node.Condition);
|
||||
|
||||
// The branches are in a tail position if the if-expression itself is.
|
||||
FNextIsTail := FIsTailStack.Peek;
|
||||
Accept(Node.ThenBranch);
|
||||
if Assigned(Node.ElseBranch) then
|
||||
Accept(Node.ElseBranch);
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
function TAstBinder.TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode;
|
||||
var
|
||||
param: IIdentifierNode;
|
||||
sourceAddresses: TArray<TResolvedAddress>;
|
||||
sortedPairs: TArray<TPair<TResolvedAddress, Integer>>;
|
||||
initializer: IAstNode;
|
||||
slotIndex: Integer;
|
||||
address: TResolvedAddress;
|
||||
boundIdentifier: IIdentifierNode;
|
||||
begin
|
||||
if not IsValidIdentifier(Node.Identifier.Name) then
|
||||
raise Exception.CreateFmt('Invalid identifier name: "%s".', [Node.Identifier.Name]);
|
||||
|
||||
FNextIsTail := False;
|
||||
if Node.Initializer <> nil then
|
||||
initializer := Accept(Node.Initializer).AsIntf<IAstNode>;
|
||||
|
||||
slotIndex := FCurrentDescriptor.Define(Node.Identifier.Name);
|
||||
address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
|
||||
|
||||
boundIdentifier := TBoundIdentifierNode.Create(Node.Identifier, address);
|
||||
|
||||
Result := TAst.VarDecl(boundIdentifier, initializer);
|
||||
end;
|
||||
|
||||
function TAstBinder.TransformAssignment(const Node: IAssignmentNode): IAssignmentNode;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
Result := inherited TransformAssignment(Node);
|
||||
end;
|
||||
|
||||
function TAstBinder.TransformBinaryExpression(const Node: IBinaryExpressionNode): IBinaryExpressionNode;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
Result := inherited TransformBinaryExpression(Node);
|
||||
end;
|
||||
|
||||
function TAstBinder.TransformUnaryExpression(const Node: IUnaryExpressionNode): IUnaryExpressionNode;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
Result := inherited TransformUnaryExpression(Node);
|
||||
end;
|
||||
|
||||
function TAstBinder.TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode;
|
||||
var
|
||||
i: integer;
|
||||
boundParams: TArray<IIdentifierNode>;
|
||||
boundBody: IAstNode;
|
||||
lambdaScope: IScopeDescriptor;
|
||||
upvalues: TArray<TResolvedAddress>;
|
||||
hasNestedLambdas: Boolean;
|
||||
lastNestedLambdaCount: Integer;
|
||||
begin
|
||||
FUpvalueStack.Push(TUpvalueMapping.Create);
|
||||
try
|
||||
EnterScope;
|
||||
try
|
||||
// Reserve slot 0 for the closure itself (for 'recur'),
|
||||
// using a name that cannot be accessed from source code.
|
||||
FCurrentDescriptor.Define('<self>');
|
||||
|
||||
for param in Node.Parameters do
|
||||
FCurrentDescriptor.Define(param.Name);
|
||||
SetLength(boundParams, Length(Node.Parameters));
|
||||
for i := 0 to High(Node.Parameters) do
|
||||
begin
|
||||
var paramNode := Node.Parameters[i];
|
||||
var slotIndex := FCurrentDescriptor.Define(paramNode.Name);
|
||||
var address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
|
||||
boundParams[i] := TBoundIdentifierNode.Create(paramNode, address);
|
||||
end;
|
||||
|
||||
var lastNestedLambdaCount := FNestedLambdaCount;
|
||||
lastNestedLambdaCount := FNestedLambdaCount;
|
||||
|
||||
// Parameters are never in a tail position.
|
||||
FNextIsTail := False;
|
||||
for param in Node.Parameters do
|
||||
Accept(param);
|
||||
|
||||
// The body of a lambda is always in a tail position.
|
||||
FNextIsTail := True;
|
||||
Accept(Node.Body);
|
||||
boundBody := Accept(Node.Body).AsIntf<IAstNode>;
|
||||
|
||||
(Node as TLambdaExpressionNode).HasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount;
|
||||
(Node as TLambdaExpressionNode).ScopeDescriptor := FCurrentDescriptor;
|
||||
hasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount;
|
||||
lambdaScope := FCurrentDescriptor;
|
||||
finally
|
||||
ExitScope;
|
||||
end;
|
||||
finally
|
||||
var upvalue := FUpvalueStack.Peek;
|
||||
try
|
||||
sortedPairs := upvalue.Map.ToArray;
|
||||
|
||||
var upvalueMapping := FUpvalueStack.Peek;
|
||||
var sortedPairs := upvalueMapping.Map.ToArray;
|
||||
TArray.Sort<TPair<TResolvedAddress, Integer>>(
|
||||
sortedPairs,
|
||||
TComparer<TPair<TResolvedAddress, Integer>>.Construct(
|
||||
@@ -326,161 +391,97 @@ begin
|
||||
)
|
||||
);
|
||||
|
||||
SetLength(sourceAddresses, Length(sortedPairs));
|
||||
for var i := 0 to High(sortedPairs) do
|
||||
sourceAddresses[i] := sortedPairs[i].Key;
|
||||
SetLength(upvalues, Length(sortedPairs));
|
||||
for i := 0 to High(sortedPairs) do
|
||||
upvalues[i] := sortedPairs[i].Key;
|
||||
|
||||
(Node as TLambdaExpressionNode).Upvalues := sourceAddresses;
|
||||
finally
|
||||
FUpvalueStack.Pop;
|
||||
end;
|
||||
|
||||
inc(FNestedLambdaCount);
|
||||
end;
|
||||
Result := TBoundLambdaExpressionNode.Create(Node, boundBody, boundParams, lambdaScope, upvalues, hasNestedLambdas);
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
begin
|
||||
// The condition is never in a tail position.
|
||||
FNextIsTail := False;
|
||||
Accept(Node.Condition);
|
||||
|
||||
// The branches are in a tail position if the ternary expression itself is.
|
||||
FNextIsTail := FIsTailStack.Peek;
|
||||
Accept(Node.ThenBranch);
|
||||
Accept(Node.ElseBranch);
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
|
||||
begin
|
||||
FNextIsTail := False;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstBinder.IsValidIdentifier(const Name: string): Boolean;
|
||||
function TAstBinder.TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode;
|
||||
var
|
||||
isTailCall: Boolean;
|
||||
callee: IAstNode;
|
||||
args: TArray<IAstNode>;
|
||||
begin
|
||||
isTailCall := FIsTailStack.Peek;
|
||||
|
||||
FNextIsTail := False;
|
||||
callee := Accept(Node.Callee).AsIntf<IAstNode>;
|
||||
args := TransformNodes<IAstNode>(Node.Arguments);
|
||||
|
||||
Result := TBoundFunctionCallNode.Create(Node, callee, args, isTailCall);
|
||||
end;
|
||||
|
||||
function TAstBinder.TransformRecur(const Node: IRecurNode): IRecurNode;
|
||||
begin
|
||||
if not FIsTailStack.Peek then
|
||||
raise Exception.Create('''recur'' can only be used in a tail position.');
|
||||
|
||||
FNextIsTail := False;
|
||||
Result := inherited TransformRecur(Node);
|
||||
end;
|
||||
|
||||
function TAstBinder.TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode;
|
||||
var
|
||||
exprs: TArray<IAstNode>;
|
||||
i: Integer;
|
||||
c: Char;
|
||||
isContextTail: Boolean;
|
||||
begin
|
||||
if Name.IsEmpty then
|
||||
exit(False);
|
||||
isContextTail := FIsTailStack.Peek;
|
||||
|
||||
// First character must be a letter or underscore.
|
||||
c := Name[1];
|
||||
if not (c.IsLetter or (c = '_')) then
|
||||
exit(False);
|
||||
|
||||
// Subsequent characters can be letters, numbers, underscore, or hyphen.
|
||||
for i := 2 to Length(Name) do
|
||||
SetLength(exprs, Node.Expressions.Count);
|
||||
for i := 0 to Node.Expressions.Count - 1 do
|
||||
begin
|
||||
c := Name[i];
|
||||
if not (c.IsLetterOrDigit or (c = '_') or (c = '-')) then
|
||||
exit(False);
|
||||
FNextIsTail := isContextTail and (i = Node.Expressions.Count - 1);
|
||||
exprs[i] := Accept(Node.Expressions[i]).AsIntf<IAstNode>;
|
||||
end;
|
||||
|
||||
Result := True;
|
||||
Result := TAst.Block(exprs);
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
function TAstBinder.TransformIfExpression(const Node: IIfExpressionNode): IIfExpressionNode;
|
||||
var
|
||||
slotIndex: Integer;
|
||||
isContextTail: Boolean;
|
||||
condition, thenBranch, elseBranch: IAstNode;
|
||||
begin
|
||||
// The initializer expression is never in a tail position.
|
||||
FNextIsTail := False;
|
||||
if Assigned(Node.Initializer) then
|
||||
Accept(Node.Initializer);
|
||||
|
||||
// Reject identifiers that contain special characters or reserved operator characters.
|
||||
if not IsValidIdentifier(Node.Identifier.Name) then
|
||||
raise Exception.CreateFmt('Invalid identifier name: "%s".', [Node.Identifier.Name]);
|
||||
|
||||
slotIndex := FCurrentDescriptor.Define(Node.Identifier.Name);
|
||||
(Node.Identifier as TIdentifierNode).Address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
|
||||
isContextTail := FIsTailStack.Peek;
|
||||
|
||||
FNextIsTail := False;
|
||||
Accept(Node.Identifier);
|
||||
condition := Accept(Node.Condition).AsIntf<IAstNode>;
|
||||
|
||||
FNextIsTail := isContextTail;
|
||||
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
|
||||
Result := TAst.IfExpr(condition, thenBranch, elseBranch)
|
||||
else
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
{ TScopeDescriptor }
|
||||
|
||||
constructor TScopeDescriptor.Create(const AParent: IScopeDescriptor);
|
||||
begin
|
||||
inherited Create;
|
||||
FParent := AParent;
|
||||
FSymbols := TDictionary<string, Integer>.Create;
|
||||
end;
|
||||
|
||||
destructor TScopeDescriptor.Destroy;
|
||||
begin
|
||||
FSymbols.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.Define(const Name: string): Integer;
|
||||
begin
|
||||
Result := FSymbols.Count;
|
||||
FSymbols.Add(Name, Result);
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.FindSymbol(const Name: string; out Depth, Index: Integer): Boolean;
|
||||
function TAstBinder.TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode;
|
||||
var
|
||||
currentDescriptor: TScopeDescriptor;
|
||||
isContextTail: Boolean;
|
||||
condition, thenBranch, elseBranch: IAstNode;
|
||||
begin
|
||||
Depth := 0;
|
||||
currentDescriptor := Self;
|
||||
while currentDescriptor <> nil do
|
||||
begin
|
||||
if currentDescriptor.FSymbols.TryGetValue(Name, Index) then
|
||||
exit(true);
|
||||
inc(Depth);
|
||||
currentDescriptor := currentDescriptor.FParent as TScopeDescriptor;
|
||||
end;
|
||||
Result := False;
|
||||
end;
|
||||
isContextTail := FIsTailStack.Peek;
|
||||
|
||||
function TScopeDescriptor.GetParent: IScopeDescriptor;
|
||||
begin
|
||||
Result := FParent;
|
||||
end;
|
||||
FNextIsTail := False;
|
||||
condition := Accept(Node.Condition).AsIntf<IAstNode>;
|
||||
|
||||
function TScopeDescriptor.GetSlotCount: Integer;
|
||||
begin
|
||||
Result := FSymbols.Count;
|
||||
end;
|
||||
FNextIsTail := isContextTail;
|
||||
thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>;
|
||||
elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>;
|
||||
|
||||
function TScopeDescriptor.GetSymbols: TDictionary<string, Integer>;
|
||||
begin
|
||||
Result := FSymbols;
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.CreateScope(const Parent: IExecutionScope): IExecutionScope;
|
||||
begin
|
||||
Result := TExecutionScope.Create(Parent, Self, nil);
|
||||
end;
|
||||
|
||||
procedure TScopeDescriptor.PopulateFromScope(Scope: TExecutionScope);
|
||||
begin
|
||||
for var pair in Scope.NameToIndex do
|
||||
begin
|
||||
var name := Scope.NameStrings[pair.Key];
|
||||
if not FSymbols.ContainsKey(name) then
|
||||
FSymbols.Add(name, pair.Value);
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TAstBinder.TUpvalueMapping.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
// Use the custom equality comparer to ensure correct dictionary behavior.
|
||||
Map := TDictionary<TResolvedAddress, Integer>.Create(TResolvedAddressComparer.Create);
|
||||
Nodes := TList<IIdentifierNode>.Create();
|
||||
end;
|
||||
|
||||
destructor TAstBinder.TUpvalueMapping.Destroy;
|
||||
begin
|
||||
Nodes.Free;
|
||||
Map.Free;
|
||||
inherited Destroy;
|
||||
if (condition <> Node.Condition) or (thenBranch <> Node.ThenBranch) or (elseBranch <> Node.ElseBranch) then
|
||||
Result := TAst.TernaryExpr(condition, thenBranch, elseBranch)
|
||||
else
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -52,8 +52,7 @@ type
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.TypInfo,
|
||||
Myc.Ast.Printer;
|
||||
System.TypInfo;
|
||||
|
||||
{ TDebugEvaluatorVisitor }
|
||||
|
||||
|
||||
+78
-45
@@ -7,13 +7,18 @@ uses
|
||||
System.Classes,
|
||||
System.Generics.Collections,
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Traverser,
|
||||
Myc.Ast.Transformer, // Wird für TAstVisitor benötigt
|
||||
Myc.Data.Value,
|
||||
Myc.Data.Scalar;
|
||||
|
||||
type
|
||||
// Dumps a bound AST into a human-readable format for debugging purposes.
|
||||
TAstDumper = class(TAstTraverser)
|
||||
// It inherits from the abstract TAstVisitor to implement the IAstVisitor interface.
|
||||
IAstDumper = interface(IAstVisitor)
|
||||
procedure Execute(const RootNode: IAstNode);
|
||||
end;
|
||||
|
||||
TAstDumper = class(TAstVisitor)
|
||||
private
|
||||
FOutput: TStrings;
|
||||
FIndent: Integer;
|
||||
@@ -22,8 +27,6 @@ type
|
||||
procedure Log(const Text: string); overload;
|
||||
procedure LogFmt(const Fmt: string; const Args: array of const); overload;
|
||||
function FormatAddress(const Addr: TResolvedAddress): string;
|
||||
protected
|
||||
function Accept(const Node: IAstNode): TDataValue; override;
|
||||
public
|
||||
// Creates a new instance of the AST dumper.
|
||||
constructor Create(const AOutput: TStrings);
|
||||
@@ -31,6 +34,9 @@ type
|
||||
// Traverses the given root node and writes the structural information into the output.
|
||||
class procedure Dump(const RootNode: IAstNode; const Output: TStrings);
|
||||
|
||||
procedure Execute(const RootNode: IAstNode);
|
||||
|
||||
// IAstVisitor implementation
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue; override;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override;
|
||||
@@ -52,19 +58,22 @@ type
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Myc.Ast.Binding; // For TBound...Node classes
|
||||
|
||||
{ TAstDumper }
|
||||
|
||||
class procedure TAstDumper.Dump(const RootNode: IAstNode; const Output: TStrings);
|
||||
var
|
||||
dumper: TAstDumper;
|
||||
begin
|
||||
if not Assigned(Output) then
|
||||
if (not Assigned(Output)) or (not Assigned(RootNode)) then
|
||||
exit;
|
||||
|
||||
Output.Clear;
|
||||
dumper := TAstDumper.Create(Output);
|
||||
try
|
||||
dumper.Accept(RootNode);
|
||||
dumper.Execute(RootNode);
|
||||
finally
|
||||
dumper.Free;
|
||||
end;
|
||||
@@ -77,11 +86,10 @@ begin
|
||||
FIndent := 0;
|
||||
end;
|
||||
|
||||
function TAstDumper.Accept(const Node: IAstNode): TDataValue;
|
||||
procedure TAstDumper.Execute(const RootNode: IAstNode);
|
||||
begin
|
||||
Result := TDataValue.Void;
|
||||
if Assigned(Node) then
|
||||
Result := inherited Accept(Node);
|
||||
if Assigned(RootNode) then
|
||||
RootNode.Accept(Self);
|
||||
end;
|
||||
|
||||
procedure TAstDumper.Indent;
|
||||
@@ -123,7 +131,10 @@ end;
|
||||
|
||||
function TAstDumper.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
begin
|
||||
LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress(Node.Address)]);
|
||||
if Node is TBoundIdentifierNode then
|
||||
LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress((Node as TBoundIdentifierNode).Address)])
|
||||
else
|
||||
LogFmt('Identifier: %s (unbound)', [Node.Name]);
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
@@ -131,8 +142,8 @@ function TAstDumper.VisitBinaryExpression(const Node: IBinaryExpressionNode): TD
|
||||
begin
|
||||
LogFmt('BinaryExpression: %s', [Node.Operator.ToString]);
|
||||
Indent;
|
||||
Accept(Node.Left);
|
||||
Accept(Node.Right);
|
||||
Node.Left.Accept(Self);
|
||||
Node.Right.Accept(Self);
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
@@ -141,7 +152,7 @@ function TAstDumper.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDat
|
||||
begin
|
||||
LogFmt('UnaryExpression: %s', [Node.Operator.ToString]);
|
||||
Indent;
|
||||
Accept(Node.Right);
|
||||
Node.Right.Accept(Self);
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
@@ -151,13 +162,13 @@ begin
|
||||
Log('IfExpression');
|
||||
Indent;
|
||||
Log('Condition:');
|
||||
Accept(Node.Condition);
|
||||
Node.Condition.Accept(Self);
|
||||
Log('Then:');
|
||||
Accept(Node.ThenBranch);
|
||||
Node.ThenBranch.Accept(Self);
|
||||
if Assigned(Node.ElseBranch) then
|
||||
begin
|
||||
Log('Else:');
|
||||
Accept(Node.ElseBranch);
|
||||
Node.ElseBranch.Accept(Self);
|
||||
end;
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
@@ -168,11 +179,11 @@ begin
|
||||
Log('TernaryExpression');
|
||||
Indent;
|
||||
Log('Condition:');
|
||||
Accept(Node.Condition);
|
||||
Node.Condition.Accept(Self);
|
||||
Log('Then:');
|
||||
Accept(Node.ThenBranch);
|
||||
Node.ThenBranch.Accept(Self);
|
||||
Log('Else:');
|
||||
Accept(Node.ElseBranch);
|
||||
Node.ElseBranch.Accept(Self);
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
@@ -182,35 +193,53 @@ var
|
||||
param: IIdentifierNode;
|
||||
upvalueAddr: TResolvedAddress;
|
||||
pair: TPair<string, Integer>;
|
||||
boundNode: TBoundLambdaExpressionNode;
|
||||
begin
|
||||
LogFmt('LambdaExpression (HasNested: %s)', [Node.HasNestedLambdas.ToString(TUseBoolStrs.True)]);
|
||||
if Node is TBoundLambdaExpressionNode then
|
||||
begin
|
||||
boundNode := Node as TBoundLambdaExpressionNode;
|
||||
LogFmt('LambdaExpression (HasNested: %s)', [boundNode.HasNestedLambdas.ToString(TUseBoolStrs.True)]);
|
||||
Indent;
|
||||
|
||||
Log('Parameters:');
|
||||
Indent;
|
||||
for param in Node.Parameters do
|
||||
Accept(param);
|
||||
for param in boundNode.Parameters do
|
||||
param.Accept(Self);
|
||||
Unindent;
|
||||
|
||||
LogFmt('Upvalues (%d):', [Length(Node.Upvalues)]);
|
||||
LogFmt('Upvalues (%d):', [Length(boundNode.Upvalues)]);
|
||||
Indent;
|
||||
for upvalueAddr in Node.Upvalues do
|
||||
for upvalueAddr in boundNode.Upvalues do
|
||||
Log(FormatAddress(upvalueAddr));
|
||||
Unindent;
|
||||
|
||||
Log('Scope Descriptor:');
|
||||
Indent;
|
||||
if Assigned(Node.ScopeDescriptor) then
|
||||
for pair in Node.ScopeDescriptor.Symbols do
|
||||
if Assigned(boundNode.ScopeDescriptor) then
|
||||
for pair in boundNode.ScopeDescriptor.Symbols do
|
||||
LogFmt('"%s" -> Slot %d', [pair.Key, pair.Value])
|
||||
else
|
||||
Log('(none)');
|
||||
Unindent;
|
||||
|
||||
Log('Body:');
|
||||
Accept(Node.Body);
|
||||
boundNode.Body.Accept(Self);
|
||||
|
||||
Unindent;
|
||||
end
|
||||
else
|
||||
begin
|
||||
Log('LambdaExpression (unbound)');
|
||||
Indent;
|
||||
Log('Parameters:');
|
||||
Indent;
|
||||
for param in Node.Parameters do
|
||||
param.Accept(Self);
|
||||
Unindent;
|
||||
Log('Body:');
|
||||
Node.Body.Accept(Self);
|
||||
Unindent;
|
||||
end;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
@@ -218,14 +247,18 @@ function TAstDumper.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue
|
||||
var
|
||||
arg: IAstNode;
|
||||
begin
|
||||
LogFmt('FunctionCall (IsTailCall: %s)', [Node.IsTailCall.ToString(TUseBoolStrs.True)]);
|
||||
if Node is TBoundFunctionCallNode then
|
||||
LogFmt('FunctionCall (IsTailCall: %s)', [(Node as TBoundFunctionCallNode).IsTailCall.ToString(TUseBoolStrs.True)])
|
||||
else
|
||||
Log('FunctionCall (unbound)');
|
||||
|
||||
Indent;
|
||||
Log('Callee:');
|
||||
Accept(Node.Callee);
|
||||
Node.Callee.Accept(Self);
|
||||
LogFmt('Arguments (%d):', [Length(Node.Arguments)]);
|
||||
Indent;
|
||||
for arg in Node.Arguments do
|
||||
Accept(arg);
|
||||
arg.Accept(Self);
|
||||
Unindent;
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
@@ -240,7 +273,7 @@ begin
|
||||
LogFmt('Arguments (%d):', [Length(Node.Arguments)]);
|
||||
Indent;
|
||||
for arg in Node.Arguments do
|
||||
Accept(arg);
|
||||
arg.Accept(Self);
|
||||
Unindent;
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
@@ -253,7 +286,7 @@ begin
|
||||
Log('BlockExpression');
|
||||
Indent;
|
||||
for expr in Node.Expressions do
|
||||
Accept(expr);
|
||||
expr.Accept(Self);
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
@@ -262,11 +295,11 @@ function TAstDumper.VisitVariableDeclaration(const Node: IVariableDeclarationNod
|
||||
begin
|
||||
Log('VariableDeclaration');
|
||||
Indent;
|
||||
Accept(Node.Identifier);
|
||||
Node.Identifier.Accept(Self);
|
||||
if Assigned(Node.Initializer) then
|
||||
begin
|
||||
Log('Initializer:');
|
||||
Accept(Node.Initializer);
|
||||
Node.Initializer.Accept(Self);
|
||||
end;
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
@@ -276,9 +309,9 @@ function TAstDumper.VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
begin
|
||||
Log('Assignment');
|
||||
Indent;
|
||||
Accept(Node.Identifier);
|
||||
Node.Identifier.Accept(Self);
|
||||
Log('Value:');
|
||||
Accept(Node.Value);
|
||||
Node.Value.Accept(Self);
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
@@ -288,9 +321,9 @@ begin
|
||||
Log('Indexer');
|
||||
Indent;
|
||||
Log('Base:');
|
||||
Accept(Node.Base);
|
||||
Node.Base.Accept(Self);
|
||||
Log('Index:');
|
||||
Accept(Node.Index);
|
||||
Node.Index.Accept(Self);
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
@@ -300,9 +333,9 @@ begin
|
||||
Log('MemberAccess');
|
||||
Indent;
|
||||
Log('Base:');
|
||||
Accept(Node.Base);
|
||||
Node.Base.Accept(Self);
|
||||
Log('Member:');
|
||||
Accept(Node.Member);
|
||||
Node.Member.Accept(Self);
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
@@ -318,13 +351,13 @@ begin
|
||||
Log('AddSeriesItem');
|
||||
Indent;
|
||||
Log('Series:');
|
||||
Accept(Node.Series);
|
||||
Node.Series.Accept(Self);
|
||||
Log('Value:');
|
||||
Accept(Node.Value);
|
||||
Node.Value.Accept(Self);
|
||||
if Assigned(Node.Lookback) then
|
||||
begin
|
||||
Log('Lookback:');
|
||||
Accept(Node.Lookback);
|
||||
Node.Lookback.Accept(Self);
|
||||
end;
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
@@ -335,7 +368,7 @@ begin
|
||||
Log('SeriesLength');
|
||||
Indent;
|
||||
Log('Series:');
|
||||
Accept(Node.Series);
|
||||
Node.Series.Accept(Self);
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
@@ -9,14 +9,20 @@ uses
|
||||
Myc.Data.Scalar,
|
||||
Myc.Data.Value,
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Transformer, // Neu
|
||||
Myc.Ast.Binding,
|
||||
Myc.Ast.Scope;
|
||||
|
||||
type
|
||||
// A factory for creating visitors, primarily used by the debugger subsystem.
|
||||
TVisitorFactory = reference to function(const Scope: IExecutionScope): IAstVisitor;
|
||||
|
||||
IEvaluatorVisitor = interface(IAstVisitor)
|
||||
function Execute(const RootNode: IAstNode): TDataValue;
|
||||
end;
|
||||
|
||||
// The standard AST evaluator for production use.
|
||||
TEvaluatorVisitor = class(TInterfacedObject, IAstVisitor)
|
||||
TEvaluatorVisitor = class(TAstVisitor, IEvaluatorVisitor)
|
||||
private
|
||||
FScope: IExecutionScope;
|
||||
protected
|
||||
@@ -34,23 +40,23 @@ type
|
||||
// Executes an AST with proper TCO handling. This is the main entry point.
|
||||
function Execute(const RootNode: IAstNode): TDataValue;
|
||||
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue; virtual;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; virtual;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; virtual;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; virtual;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; virtual;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; virtual;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; virtual;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; virtual;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; virtual;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; virtual;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; virtual;
|
||||
function VisitIndexer(const Node: IIndexerNode): TDataValue; virtual;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; virtual;
|
||||
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; virtual;
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; virtual;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; virtual;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; virtual;
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue; override;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
|
||||
function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
|
||||
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
|
||||
end;
|
||||
|
||||
// Registers native Delphi functions into a scope.
|
||||
@@ -161,30 +167,33 @@ end;
|
||||
|
||||
function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
var
|
||||
boundNode: TBoundLambdaExpressionNode;
|
||||
capturedCells: TArray<IValueCell>;
|
||||
i: Integer;
|
||||
sourceAddresses: TArray<TResolvedAddress>;
|
||||
closureScope: IExecutionScope;
|
||||
visitorFactory: TVisitorFactory;
|
||||
begin
|
||||
sourceAddresses := Node.Upvalues;
|
||||
// Cast to the bound node to access binder-specific information.
|
||||
boundNode := Node as TBoundLambdaExpressionNode;
|
||||
|
||||
sourceAddresses := boundNode.Upvalues;
|
||||
SetLength(capturedCells, Length(sourceAddresses));
|
||||
for i := 0 to High(sourceAddresses) do
|
||||
capturedCells[i] := FScope.Capture(sourceAddresses[i]);
|
||||
|
||||
if Node.HasNestedLambdas then
|
||||
if boundNode.HasNestedLambdas then
|
||||
closureScope := FScope
|
||||
else
|
||||
closureScope := nil;
|
||||
|
||||
// Get the appropriate factory via virtual dispatch.
|
||||
visitorFactory := CreateVisitorFactory();
|
||||
|
||||
var scopeDescriptor := Node.ScopeDescriptor;
|
||||
var params := Node.Parameters;
|
||||
var scopeDescriptor := boundNode.ScopeDescriptor;
|
||||
var params := boundNode.Parameters;
|
||||
|
||||
// declare closure as weak to break cyclic referencing when it is captured by itself
|
||||
var [weak] closure: TDataValue.TFunc;
|
||||
var cNode: ILambdaExpressionNode := Node;
|
||||
|
||||
closure :=
|
||||
TDataValue.TFunc(
|
||||
@@ -204,21 +213,19 @@ begin
|
||||
adr.ScopeDepth := 0;
|
||||
|
||||
// Capture the closure itself in slot 0 for 'recur' to find it.
|
||||
// The name 'Self' is no longer exposed to the user by the binder.
|
||||
adr.SlotIndex := 0;
|
||||
lambdaScope[adr] := TDataValue(closure);
|
||||
|
||||
// Populate the actual parameters.
|
||||
for i := 0 to High(ArgValues) do
|
||||
begin
|
||||
adr.SlotIndex := params[i].Address.SlotIndex;
|
||||
// Parameters in a bound lambda must be bound identifiers.
|
||||
adr.SlotIndex := (params[i] as TBoundIdentifierNode).Address.SlotIndex;
|
||||
lambdaScope[adr] := ArgValues[i];
|
||||
end;
|
||||
|
||||
// Use the injected factory to create the visitor for the lambda's body.
|
||||
bodyVisitor := visitorFactory(lambdaScope);
|
||||
|
||||
Result := Node.Body.Accept(bodyVisitor);
|
||||
Result := cNode.Body.Accept(bodyVisitor);
|
||||
end
|
||||
);
|
||||
|
||||
@@ -229,6 +236,7 @@ function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDa
|
||||
var
|
||||
calleeValue: TDataValue;
|
||||
argValues: TArray<TDataValue>;
|
||||
boundNode: TBoundFunctionCallNode;
|
||||
begin
|
||||
calleeValue := Node.Callee.Accept(Self);
|
||||
if calleeValue.Kind <> vkMethod then
|
||||
@@ -239,19 +247,16 @@ begin
|
||||
for var i := 0 to High(argNodes) do
|
||||
argValues[i] := argNodes[i].Accept(Self);
|
||||
|
||||
if Node.IsTailCall then
|
||||
boundNode := Node as TBoundFunctionCallNode;
|
||||
if boundNode.IsTailCall then
|
||||
begin
|
||||
// This is a tail call. Return a thunk to be processed
|
||||
// by an outer trampoline loop (either in Execute or a parent non-tail-call).
|
||||
// This is a tail call. Return a thunk to be processed by the trampoline.
|
||||
Result := TDataValue.FromGeneric<TThunk>(TThunk.Create(calleeValue, argValues));
|
||||
end
|
||||
else
|
||||
begin
|
||||
// This is a non-tail call. It must execute the call and act as
|
||||
// the trampoline for any subsequent tail calls it may trigger.
|
||||
|
||||
// This is a non-tail call. It must execute the call and act as the trampoline.
|
||||
Result := (calleeValue.AsMethod)(argValues);
|
||||
|
||||
HandleTCO(Result);
|
||||
end;
|
||||
end;
|
||||
@@ -263,23 +268,21 @@ var
|
||||
calleeValue: TDataValue;
|
||||
i: Integer;
|
||||
begin
|
||||
// The binder ensures this is only in a tail position.
|
||||
if not Node.IsTailCall then
|
||||
raise EInvalidOperation.Create('Recur has to be a tail call');
|
||||
|
||||
// Evaluate all arguments for the recursive call.
|
||||
SetLength(argValues, Length(Node.Arguments));
|
||||
for i := 0 to High(Node.Arguments) do
|
||||
argValues[i] := Node.Arguments[i].Accept(Self);
|
||||
|
||||
// The callee is the current function, which is stored by the lambda
|
||||
// expression visitor in slot 0 of the current scope.
|
||||
// The callee is the current function, stored in slot 0 of the current scope.
|
||||
calleeAddress.Kind := akLocalOrParent;
|
||||
calleeAddress.ScopeDepth := 0;
|
||||
calleeAddress.SlotIndex := 0;
|
||||
calleeValue := FScope[calleeAddress];
|
||||
|
||||
// Recur must be in a tail position, so we always return a thunk.
|
||||
// The binder is responsible for enforcing the tail position rule.
|
||||
// Recur always returns a thunk for the trampoline.
|
||||
Result := TDataValue.FromGeneric<TThunk>(TThunk.Create(calleeValue, argValues));
|
||||
end;
|
||||
|
||||
@@ -288,7 +291,7 @@ var
|
||||
itemValue, lookbackValue, seriesVar: TDataValue;
|
||||
lookback: Int64;
|
||||
begin
|
||||
seriesVar := FScope[Node.Series.Address];
|
||||
seriesVar := FScope[(Node.Series as TBoundIdentifierNode).Address];
|
||||
itemValue := Node.Value.Accept(Self);
|
||||
lookback := -1;
|
||||
|
||||
@@ -297,7 +300,6 @@ begin
|
||||
lookbackValue := Node.Lookback.Accept(Self);
|
||||
if (lookbackValue.Kind <> vkScalar) or (lookbackValue.AsScalar.Kind <> TScalar.TKind.Ordinal) then
|
||||
raise EArgumentException.Create('Lookback parameter must be an integer.');
|
||||
|
||||
lookback := lookbackValue.AsScalar.Value.AsInt64;
|
||||
end;
|
||||
|
||||
@@ -321,7 +323,7 @@ end;
|
||||
function TEvaluatorVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
begin
|
||||
Result := Node.Value.Accept(Self);
|
||||
FScope[Node.Identifier.Address] := Result;
|
||||
FScope[(Node.Identifier as TBoundIdentifierNode).Address] := Result;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
|
||||
@@ -334,13 +336,11 @@ var
|
||||
def: string;
|
||||
begin
|
||||
def := Node.Definition.Trim;
|
||||
|
||||
if def.StartsWith('[') then
|
||||
begin
|
||||
var recordDef := TRttiAstHelper.JsonToRecordDefinition(def);
|
||||
if Length(recordDef.Fields) = 0 then
|
||||
raise EArgumentException.Create('Failed to parse record definition from JSON array.');
|
||||
|
||||
var recordSeries := TScalarRecordSeries.Create(recordDef);
|
||||
Result := TDataValue.FromRecordSeries(recordSeries);
|
||||
end
|
||||
@@ -353,7 +353,7 @@ end;
|
||||
|
||||
function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
begin
|
||||
Result := FScope[Node.Address];
|
||||
Result := FScope[(Node as TBoundIdentifierNode).Address];
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
|
||||
@@ -411,9 +411,11 @@ end;
|
||||
function TEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
begin
|
||||
if Assigned(Node.Initializer) then
|
||||
Result := Node.Initializer.Accept(Self);
|
||||
Result := Node.Initializer.Accept(Self)
|
||||
else
|
||||
Result := TDataValue.Void;
|
||||
|
||||
FScope[Node.Identifier.Address] := Result;
|
||||
FScope[(Node.Identifier as TBoundIdentifierNode).Address] := Result;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||
@@ -457,7 +459,9 @@ begin
|
||||
if IsTruthy(Node.Condition.Accept(Self)) then
|
||||
Result := Node.ThenBranch.Accept(Self)
|
||||
else if Assigned(Node.ElseBranch) then
|
||||
Result := Node.ElseBranch.Accept(Self);
|
||||
Result := Node.ElseBranch.Accept(Self)
|
||||
else
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
@@ -482,7 +486,7 @@ var
|
||||
seriesValue: TDataValue;
|
||||
len: Int64;
|
||||
begin
|
||||
seriesValue := FScope[Node.Series.Address];
|
||||
seriesValue := FScope[(Node.Series as TBoundIdentifierNode).Address];
|
||||
|
||||
case seriesValue.Kind of
|
||||
vkSeries: len := seriesValue.AsSeries.Count;
|
||||
|
||||
@@ -86,8 +86,6 @@ type
|
||||
end;
|
||||
|
||||
IAstVisitor = interface
|
||||
function Execute(const RootNode: IAstNode): TDataValue;
|
||||
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||
@@ -121,11 +119,9 @@ type
|
||||
|
||||
IIdentifierNode = interface(IAstNode)
|
||||
{$region 'private'}
|
||||
function GetAddress: TResolvedAddress;
|
||||
function GetName: string;
|
||||
{$endregion}
|
||||
property Name: string read GetName;
|
||||
property Address: TResolvedAddress read GetAddress;
|
||||
end;
|
||||
|
||||
IBinaryExpressionNode = interface(IAstNode)
|
||||
@@ -174,26 +170,18 @@ type
|
||||
{$region 'private'}
|
||||
function GetParameters: TArray<IIdentifierNode>;
|
||||
function GetBody: IAstNode;
|
||||
function GetScopeDescriptor: IScopeDescriptor;
|
||||
function GetUpvalues: TArray<TResolvedAddress>;
|
||||
function GetHasNestedLambdas: Boolean;
|
||||
{$endregion}
|
||||
property Parameters: TArray<IIdentifierNode> read GetParameters;
|
||||
property Body: IAstNode read GetBody;
|
||||
property ScopeDescriptor: IScopeDescriptor read GetScopeDescriptor;
|
||||
property Upvalues: TArray<TResolvedAddress> read GetUpvalues;
|
||||
property HasNestedLambdas: Boolean read GetHasNestedLambdas;
|
||||
end;
|
||||
|
||||
IFunctionCallNode = interface(IAstNode)
|
||||
{$region 'private'}
|
||||
function GetCallee: IAstNode;
|
||||
function GetArguments: TArray<IAstNode>;
|
||||
function GetIsTailCall: Boolean;
|
||||
{$endregion}
|
||||
property Callee: IAstNode read GetCallee;
|
||||
property Arguments: TArray<IAstNode> read GetArguments;
|
||||
property IsTailCall: Boolean read GetIsTailCall;
|
||||
end;
|
||||
|
||||
// A node representing a tail-recursive call.
|
||||
|
||||
@@ -1,327 +0,0 @@
|
||||
unit Myc.Ast.Printer;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
System.Classes,
|
||||
System.Generics.Collections,
|
||||
Myc.Data.Value,
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast;
|
||||
|
||||
type
|
||||
// A visitor that converts an AST into a LISP-like (Clojure-style) string representation.
|
||||
TPrettyPrintVisitor = class(TInterfacedObject, IAstVisitor)
|
||||
private
|
||||
FBuilder: TStringBuilder;
|
||||
FIndentLevel: Integer;
|
||||
procedure Indent;
|
||||
procedure Unindent;
|
||||
procedure Append(const S: string);
|
||||
procedure NewLine;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
function GetResult: string;
|
||||
// IAstVisitor
|
||||
function Execute(const RootNode: IAstNode): TDataValue;
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
function VisitIndexer(const Node: IIndexerNode): TDataValue;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
|
||||
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Myc.Data.Scalar;
|
||||
|
||||
{ TPrettyPrintVisitor }
|
||||
|
||||
constructor TPrettyPrintVisitor.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
FBuilder := TStringBuilder.Create;
|
||||
FIndentLevel := 0;
|
||||
end;
|
||||
|
||||
destructor TPrettyPrintVisitor.Destroy;
|
||||
begin
|
||||
FBuilder.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.GetResult: string;
|
||||
begin
|
||||
Result := FBuilder.ToString;
|
||||
end;
|
||||
|
||||
procedure TPrettyPrintVisitor.Indent;
|
||||
begin
|
||||
inc(FIndentLevel, 2);
|
||||
end;
|
||||
|
||||
procedure TPrettyPrintVisitor.Unindent;
|
||||
begin
|
||||
dec(FIndentLevel, 2);
|
||||
end;
|
||||
|
||||
procedure TPrettyPrintVisitor.Append(const S: string);
|
||||
begin
|
||||
FBuilder.Append(S);
|
||||
end;
|
||||
|
||||
procedure TPrettyPrintVisitor.NewLine;
|
||||
begin
|
||||
FBuilder.AppendLine;
|
||||
FBuilder.Append(''.PadLeft(FIndentLevel));
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.Execute(const RootNode: IAstNode): TDataValue;
|
||||
begin
|
||||
if Assigned(RootNode) then
|
||||
RootNode.Accept(Self);
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
|
||||
var
|
||||
val: TDataValue;
|
||||
begin
|
||||
val := Node.Value;
|
||||
if val.Kind = vkText then
|
||||
Append('"' + val.AsText + '"')
|
||||
else
|
||||
Append(val.ToString);
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
begin
|
||||
Append(Node.Name);
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||
begin
|
||||
Append('(' + Node.Operator.ToString);
|
||||
Append(' ');
|
||||
Node.Left.Accept(Self);
|
||||
Append(' ');
|
||||
Node.Right.Accept(Self);
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
|
||||
begin
|
||||
Append('(' + Node.Operator.ToString);
|
||||
Append(' ');
|
||||
Node.Right.Accept(Self);
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
||||
begin
|
||||
Append('(if ');
|
||||
Node.Condition.Accept(Self);
|
||||
Indent;
|
||||
NewLine;
|
||||
Node.ThenBranch.Accept(Self);
|
||||
if Assigned(Node.ElseBranch) then
|
||||
begin
|
||||
NewLine;
|
||||
Node.ElseBranch.Accept(Self);
|
||||
end;
|
||||
Unindent;
|
||||
NewLine;
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
begin
|
||||
Append('(if ');
|
||||
Node.Condition.Accept(Self);
|
||||
Indent;
|
||||
NewLine;
|
||||
Node.ThenBranch.Accept(Self);
|
||||
NewLine;
|
||||
Node.ElseBranch.Accept(Self);
|
||||
Unindent;
|
||||
NewLine;
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
var
|
||||
param: IIdentifierNode;
|
||||
sb: TStringBuilder;
|
||||
begin
|
||||
sb := TStringBuilder.Create;
|
||||
try
|
||||
for param in Node.Parameters do
|
||||
sb.Append(param.Name + ' ');
|
||||
if sb.Length > 0 then
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
|
||||
Append('(fn [' + sb.ToString + ']');
|
||||
finally
|
||||
sb.Free;
|
||||
end;
|
||||
|
||||
Indent;
|
||||
NewLine;
|
||||
Node.Body.Accept(Self);
|
||||
Unindent;
|
||||
NewLine;
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
var
|
||||
arg: IAstNode;
|
||||
begin
|
||||
Append('(');
|
||||
Node.Callee.Accept(Self);
|
||||
|
||||
Indent;
|
||||
for arg in Node.Arguments do
|
||||
begin
|
||||
NewLine;
|
||||
arg.Accept(Self);
|
||||
end;
|
||||
Unindent;
|
||||
|
||||
if Length(Node.Arguments) > 0 then
|
||||
NewLine;
|
||||
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
var
|
||||
arg: IAstNode;
|
||||
begin
|
||||
Append('(recur');
|
||||
Indent;
|
||||
for arg in Node.Arguments do
|
||||
begin
|
||||
NewLine;
|
||||
arg.Accept(Self);
|
||||
end;
|
||||
Unindent;
|
||||
if Length(Node.Arguments) > 0 then
|
||||
NewLine;
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
var
|
||||
expr: IAstNode;
|
||||
begin
|
||||
Append('(do');
|
||||
Indent;
|
||||
for expr in Node.Expressions do
|
||||
begin
|
||||
NewLine;
|
||||
expr.Accept(Self);
|
||||
end;
|
||||
Unindent;
|
||||
NewLine;
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
begin
|
||||
Append('(def ');
|
||||
Node.Identifier.Accept(Self);
|
||||
if Assigned(Node.Initializer) then
|
||||
begin
|
||||
Append(' ');
|
||||
Node.Initializer.Accept(Self);
|
||||
end;
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
begin
|
||||
Append('(assign ');
|
||||
Node.Identifier.Accept(Self);
|
||||
Append(' ');
|
||||
Node.Value.Accept(Self);
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
|
||||
begin
|
||||
Append('(get ');
|
||||
Node.Base.Accept(Self);
|
||||
Append(' ');
|
||||
Node.Index.Accept(Self);
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
|
||||
begin
|
||||
Append('(.');
|
||||
Node.Member.Accept(Self);
|
||||
Append(' ');
|
||||
Node.Base.Accept(Self);
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
||||
begin
|
||||
Append(Format('(new-series "%s")', [Node.Definition]));
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
||||
begin
|
||||
Append('(add-item ');
|
||||
Node.Series.Accept(Self);
|
||||
Append(' ');
|
||||
Node.Value.Accept(Self);
|
||||
if Assigned(Node.Lookback) then
|
||||
begin
|
||||
Append(' ');
|
||||
Node.Lookback.Accept(Self);
|
||||
end;
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
||||
begin
|
||||
Append('(count ');
|
||||
Node.Series.Accept(Self);
|
||||
Append(')');
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
end.
|
||||
@@ -60,6 +60,25 @@ type
|
||||
property Parent: IExecutionScope read FParent;
|
||||
end;
|
||||
|
||||
// Describes the layout of a scope: variable names and their slot indices.
|
||||
// This is generated by the binder and used to create TExecutionScope instances at runtime.
|
||||
TScopeDescriptor = class(TInterfacedObject, IScopeDescriptor)
|
||||
private
|
||||
FParent: IScopeDescriptor;
|
||||
FSymbols: TDictionary<string, Integer>;
|
||||
function GetParent: IScopeDescriptor;
|
||||
function GetSlotCount: Integer;
|
||||
function GetSymbols: TDictionary<string, Integer>;
|
||||
public
|
||||
constructor Create(const AParent: IScopeDescriptor);
|
||||
destructor Destroy; override;
|
||||
function Define(const Name: string): Integer;
|
||||
function FindSymbol(const Name: string; out Depth, Index: Integer): Boolean;
|
||||
function CreateScope(const Parent: IExecutionScope): IExecutionScope;
|
||||
procedure PopulateFromScope(Scope: TExecutionScope);
|
||||
property Symbols: TDictionary<string, Integer> read FSymbols;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
@@ -339,4 +358,74 @@ begin
|
||||
FValues[FIdx] := AValue;
|
||||
end;
|
||||
|
||||
{ TScopeDescriptor }
|
||||
|
||||
constructor TScopeDescriptor.Create(const AParent: IScopeDescriptor);
|
||||
begin
|
||||
inherited Create;
|
||||
FParent := AParent;
|
||||
FSymbols := TDictionary<string, Integer>.Create;
|
||||
end;
|
||||
|
||||
destructor TScopeDescriptor.Destroy;
|
||||
begin
|
||||
FSymbols.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.CreateScope(const Parent: IExecutionScope): IExecutionScope;
|
||||
begin
|
||||
// Creates a runtime scope instance based on this descriptor's layout.
|
||||
Result := TExecutionScope.Create(Parent, Self, nil);
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.Define(const Name: string): Integer;
|
||||
begin
|
||||
Result := FSymbols.Count;
|
||||
FSymbols.Add(Name, Result);
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.FindSymbol(const Name: string; out Depth, Index: Integer): Boolean;
|
||||
var
|
||||
currentDescriptor: TScopeDescriptor;
|
||||
begin
|
||||
Depth := 0;
|
||||
currentDescriptor := Self;
|
||||
while currentDescriptor <> nil do
|
||||
begin
|
||||
if currentDescriptor.FSymbols.TryGetValue(Name, Index) then
|
||||
exit(True);
|
||||
inc(Depth);
|
||||
currentDescriptor := currentDescriptor.FParent as TScopeDescriptor;
|
||||
end;
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.GetParent: IScopeDescriptor;
|
||||
begin
|
||||
Result := FParent;
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.GetSlotCount: Integer;
|
||||
begin
|
||||
Result := FSymbols.Count;
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.GetSymbols: TDictionary<string, Integer>;
|
||||
begin
|
||||
Result := FSymbols;
|
||||
end;
|
||||
|
||||
procedure TScopeDescriptor.PopulateFromScope(Scope: TExecutionScope);
|
||||
begin
|
||||
// This method is likely used to create a descriptor from an existing, dynamically populated scope.
|
||||
// Note: This relies on internal details of TExecutionScope (NameToIndex, NameStrings).
|
||||
for var pair in Scope.NameToIndex do
|
||||
begin
|
||||
var name := Scope.NameStrings[pair.Key];
|
||||
if not FSymbols.ContainsKey(name) then
|
||||
FSymbols.Add(name, pair.Value);
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,427 @@
|
||||
unit Myc.Ast.Transformer;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
System.Generics.Collections,
|
||||
Myc.Data.Value,
|
||||
Myc.Ast,
|
||||
Myc.Ast.Nodes;
|
||||
|
||||
type
|
||||
// A fully abstract base class for any visitor of the AST.
|
||||
TAstVisitor = class abstract(TInterfacedObject, IAstVisitor)
|
||||
public
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue; virtual; abstract;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; virtual; abstract;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; virtual; abstract;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; virtual; abstract;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; virtual; abstract;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; virtual; abstract;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; virtual; abstract;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; virtual; abstract;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; virtual; abstract;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; virtual; abstract;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; virtual; abstract;
|
||||
function VisitIndexer(const Node: IIndexerNode): TDataValue; virtual; abstract;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; virtual; abstract;
|
||||
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; virtual; abstract;
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; virtual; abstract;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; virtual; abstract;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; virtual; abstract;
|
||||
end;
|
||||
|
||||
IAstTransformer = interface(IAstVisitor)
|
||||
function Execute(const RootNode: IAstNode): IAstNode;
|
||||
end;
|
||||
|
||||
// A base visitor that walks the AST and rebuilds it node by node.
|
||||
// This class uses the Template Method pattern:
|
||||
// - The Visit... methods are the template and should not be overridden.
|
||||
// - The virtual Transform... methods are the hooks for derived classes to customize behavior.
|
||||
TAstTransformer = class abstract(TAstVisitor, IAstTransformer)
|
||||
private
|
||||
FDone: Boolean;
|
||||
|
||||
protected
|
||||
// Dispatch methods for each node type. Derived classes override these to change the transformation.
|
||||
function TransformConstant(const Node: IConstantNode): IConstantNode; virtual;
|
||||
function TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode; virtual;
|
||||
function TransformBinaryExpression(const Node: IBinaryExpressionNode): IBinaryExpressionNode; virtual;
|
||||
function TransformUnaryExpression(const Node: IUnaryExpressionNode): IUnaryExpressionNode; virtual;
|
||||
function TransformIfExpression(const Node: IIfExpressionNode): IIfExpressionNode; virtual;
|
||||
function TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode; virtual;
|
||||
function TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode; virtual;
|
||||
function TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode; virtual;
|
||||
function TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode; virtual;
|
||||
function TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode; virtual;
|
||||
function TransformAssignment(const Node: IAssignmentNode): IAssignmentNode; virtual;
|
||||
function TransformIndexer(const Node: IIndexerNode): IIndexerNode; virtual;
|
||||
function TransformMemberAccess(const Node: IMemberAccessNode): IMemberAccessNode; virtual;
|
||||
function TransformCreateSeries(const Node: ICreateSeriesNode): ICreateSeriesNode; virtual;
|
||||
function TransformAddSeriesItem(const Node: IAddSeriesItemNode): IAddSeriesItemNode; virtual;
|
||||
function TransformSeriesLength(const Node: ISeriesLengthNode): ISeriesLengthNode; virtual;
|
||||
function TransformRecur(const Node: IRecurNode): IRecurNode; virtual;
|
||||
|
||||
// Helper to transform an array of nodes.
|
||||
function TransformNodes<T: IAstNode>(const Nodes: TArray<T>): TArray<T>;
|
||||
|
||||
function Accept(const Node: IAstNode): TDataValue; virtual;
|
||||
|
||||
property Done: Boolean read FDone write FDone;
|
||||
public
|
||||
// This is the main entry point for the transformer, returning a transformed node.
|
||||
function Execute(const RootNode: IAstNode): IAstNode;
|
||||
|
||||
// Final Visit methods - these should not be overridden. They call the virtual Transform methods.
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue; override; final;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; final;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; final;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; final;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; final;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; final;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; final;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; final;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; final;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; final;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; final;
|
||||
function VisitIndexer(const Node: IIndexerNode): TDataValue; override; final;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; final;
|
||||
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; final;
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override; final;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override; final;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; override; final;
|
||||
end;
|
||||
|
||||
// TAstTraverser simply inherits the identity-transform (cloning) behavior from TAstTransformer.
|
||||
TAstTraverser = class(TAstTransformer);
|
||||
|
||||
// Generic traverser for managing state during AST walks.
|
||||
TAstTraverser<T> = class(TAstTraverser)
|
||||
private
|
||||
FData: TStack<T>;
|
||||
protected
|
||||
function Accept(const Node: IAstNode): TDataValue; override;
|
||||
// Called before a node is visited. The returned value is pushed onto the state stack.
|
||||
function EnterNode(const Node: IAstNode): T; virtual; abstract;
|
||||
// Called after a node has been visited.
|
||||
procedure ExitNode(const Node: IAstNode; const Data: T); virtual;
|
||||
property Data: TStack<T> read FData;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TAstTransformer }
|
||||
|
||||
function TAstTransformer.Accept(const Node: IAstNode): TDataValue;
|
||||
begin
|
||||
if (not Assigned(Node)) or FDone then
|
||||
exit;
|
||||
Result := Node.Accept(Self);
|
||||
end;
|
||||
|
||||
function TAstTransformer.Execute(const RootNode: IAstNode): IAstNode;
|
||||
begin
|
||||
Result := Accept(RootNode).AsIntf<IAstNode>;
|
||||
end;
|
||||
|
||||
// --- Default implementations for Transform... methods (Identity Transformation / Cloning) ---
|
||||
|
||||
function TAstTransformer.TransformConstant(const Node: IConstantNode): IConstantNode;
|
||||
begin
|
||||
// The node itself is immutable, return it directly.
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformIdentifier(const Node: IIdentifierNode): IIdentifierNode;
|
||||
begin
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformBinaryExpression(const Node: IBinaryExpressionNode): IBinaryExpressionNode;
|
||||
begin
|
||||
var left := Accept(Node.Left).AsIntf<IAstNode>;
|
||||
var right := Accept(Node.Right).AsIntf<IAstNode>;
|
||||
if (left = Node.Left) and (right = Node.Right) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.BinaryExpr(left, Node.Operator, right);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformUnaryExpression(const Node: IUnaryExpressionNode): IUnaryExpressionNode;
|
||||
begin
|
||||
var right := Accept(Node.Right).AsIntf<IAstNode>;
|
||||
if right = Node.Right then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.UnaryExpr(Node.Operator, right);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformIfExpression(const Node: IIfExpressionNode): IIfExpressionNode;
|
||||
begin
|
||||
var condition := Accept(Node.Condition).AsIntf<IAstNode>;
|
||||
var thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>;
|
||||
var elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>;
|
||||
if (condition = Node.Condition) and (thenBranch = Node.ThenBranch) and (elseBranch = Node.ElseBranch) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.IfExpr(condition, thenBranch, elseBranch);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode;
|
||||
begin
|
||||
var condition := Accept(Node.Condition).AsIntf<IAstNode>;
|
||||
var thenBranch := Accept(Node.ThenBranch).AsIntf<IAstNode>;
|
||||
var elseBranch := Accept(Node.ElseBranch).AsIntf<IAstNode>;
|
||||
if (condition = Node.Condition) and (thenBranch = Node.ThenBranch) and (elseBranch = Node.ElseBranch) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.TernaryExpr(condition, thenBranch, elseBranch);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode;
|
||||
begin
|
||||
var parameters := TransformNodes<IIdentifierNode>(Node.Parameters);
|
||||
var body := Accept(Node.Body).AsIntf<IAstNode>;
|
||||
if (parameters = Node.Parameters) and (body = Node.Body) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.LambdaExpr(parameters, body);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode;
|
||||
begin
|
||||
var callee := Accept(Node.Callee).AsIntf<IAstNode>;
|
||||
var args := TransformNodes<IAstNode>(Node.Arguments);
|
||||
if (callee = Node.Callee) and (args = Node.Arguments) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.FunctionCall(callee, args);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode;
|
||||
begin
|
||||
// TList is mutable, so we always transform.
|
||||
var exprs: TArray<IAstNode>;
|
||||
var i: Integer;
|
||||
SetLength(exprs, Node.Expressions.Count);
|
||||
for i := 0 to Node.Expressions.Count - 1 do
|
||||
exprs[i] := Accept(Node.Expressions[i]).AsIntf<IAstNode>;
|
||||
Result := TAst.Block(exprs);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode;
|
||||
begin
|
||||
var identifier := Accept(Node.Identifier).AsIntf<IIdentifierNode>;
|
||||
var initializer := Accept(Node.Initializer).AsIntf<IAstNode>;
|
||||
if (identifier = Node.Identifier) and (initializer = Node.Initializer) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.VarDecl(identifier, initializer);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformAssignment(const Node: IAssignmentNode): IAssignmentNode;
|
||||
begin
|
||||
var identifier := Accept(Node.Identifier).AsIntf<IIdentifierNode>;
|
||||
var value := Accept(Node.Value).AsIntf<IAstNode>;
|
||||
if (identifier = Node.Identifier) and (value = Node.Value) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.Assign(identifier, value);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformIndexer(const Node: IIndexerNode): IIndexerNode;
|
||||
begin
|
||||
var base := Accept(Node.Base).AsIntf<IAstNode>;
|
||||
var index := Accept(Node.Index).AsIntf<IAstNode>;
|
||||
if (base = Node.Base) and (index = Node.Index) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.Indexer(base, index);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformMemberAccess(const Node: IMemberAccessNode): IMemberAccessNode;
|
||||
begin
|
||||
var base := Accept(Node.Base).AsIntf<IAstNode>;
|
||||
var member := Node.Member;
|
||||
if (base = Node.Base) and (member = Node.Member) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.MemberAccess(base, member);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformCreateSeries(const Node: ICreateSeriesNode): ICreateSeriesNode;
|
||||
begin
|
||||
Result := Node;
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformAddSeriesItem(const Node: IAddSeriesItemNode): IAddSeriesItemNode;
|
||||
begin
|
||||
var series := Accept(Node.Series).AsIntf<IIdentifierNode>;
|
||||
var value := Accept(Node.Value).AsIntf<IAstNode>;
|
||||
var lookback := Accept(Node.Lookback).AsIntf<IAstNode>;
|
||||
if (series = Node.Series) and (value = Node.Value) and (lookback = Node.Lookback) then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.AddSeriesItem(series, value, lookback);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformSeriesLength(const Node: ISeriesLengthNode): ISeriesLengthNode;
|
||||
begin
|
||||
var series := Accept(Node.Series).AsIntf<IIdentifierNode>;
|
||||
if series = Node.Series then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.SeriesLength(series);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformRecur(const Node: IRecurNode): IRecurNode;
|
||||
begin
|
||||
var args := TransformNodes<IAstNode>(Node.Arguments);
|
||||
if args = Node.Arguments then
|
||||
Result := Node
|
||||
else
|
||||
Result := TAst.Recur(args);
|
||||
end;
|
||||
|
||||
function TAstTransformer.TransformNodes<T>(const Nodes: TArray<T>): TArray<T>;
|
||||
var
|
||||
i: Integer;
|
||||
hasChanged: boolean;
|
||||
begin
|
||||
hasChanged := False;
|
||||
SetLength(Result, Length(Nodes));
|
||||
for i := 0 to High(Nodes) do
|
||||
begin
|
||||
Result[i] := Accept(Nodes[i]).AsIntf<T>;
|
||||
if Result[i] <> Nodes[i] then
|
||||
hasChanged := True;
|
||||
end;
|
||||
if not hasChanged then
|
||||
Result := Nodes;
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitConstant(const Node: IConstantNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IConstantNode>(TransformConstant(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IIdentifierNode>(TransformIdentifier(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IBinaryExpressionNode>(TransformBinaryExpression(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IUnaryExpressionNode>(TransformUnaryExpression(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IIfExpressionNode>(TransformIfExpression(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<ITernaryExpressionNode>(TransformTernaryExpression(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<ILambdaExpressionNode>(TransformLambdaExpression(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IFunctionCallNode>(TransformFunctionCall(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IBlockExpressionNode>(TransformBlockExpression(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IVariableDeclarationNode>(TransformVariableDeclaration(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IAssignmentNode>(TransformAssignment(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitIndexer(const Node: IIndexerNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IIndexerNode>(TransformIndexer(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IMemberAccessNode>(TransformMemberAccess(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<ICreateSeriesNode>(TransformCreateSeries(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IAddSeriesItemNode>(TransformAddSeriesItem(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<ISeriesLengthNode>(TransformSeriesLength(Node));
|
||||
end;
|
||||
|
||||
function TAstTransformer.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
begin
|
||||
Result := TDataValue.FromIntf<IRecurNode>(TransformRecur(Node));
|
||||
end;
|
||||
|
||||
{ TAstTraverser<T> }
|
||||
|
||||
constructor TAstTraverser<T>.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
FData := TStack<T>.Create;
|
||||
end;
|
||||
|
||||
destructor TAstTraverser<T>.Destroy;
|
||||
begin
|
||||
FData.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstTraverser<T>.Accept(const Node: IAstNode): TDataValue;
|
||||
begin
|
||||
if not Assigned(Node) then
|
||||
exit;
|
||||
|
||||
FData.Push(EnterNode(Node));
|
||||
try
|
||||
// Call the inherited Accept, which will dispatch to the virtual Visit... methods
|
||||
// in the base TAstTransformer, performing the transformation/cloning.
|
||||
Result := inherited Accept(Node);
|
||||
finally
|
||||
var data := FData.Pop;
|
||||
ExitNode(Node, data);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstTraverser<T>.ExitNode(const Node: IAstNode; const Data: T);
|
||||
begin
|
||||
end;
|
||||
|
||||
end.
|
||||
@@ -1,233 +0,0 @@
|
||||
unit Myc.Ast.Traverser;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.Classes,
|
||||
System.Generics.Collections,
|
||||
Myc.Data.Value,
|
||||
Myc.Ast.Nodes;
|
||||
|
||||
type
|
||||
// TAstTraverser provides a default AST traversal implementation.
|
||||
TAstTraverser = class abstract(TInterfacedObject, IAstVisitor)
|
||||
private
|
||||
FDone: Boolean;
|
||||
protected
|
||||
function Accept(const Node: IAstNode): TDataValue; virtual;
|
||||
property Done: Boolean read FDone write FDone;
|
||||
public
|
||||
function Execute(const RootNode: IAstNode): TDataValue;
|
||||
|
||||
function VisitConstant(const Node: IConstantNode): TDataValue; virtual;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; virtual;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; virtual;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; virtual;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; virtual;
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; virtual;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; virtual;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; virtual;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; virtual;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; virtual;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; virtual;
|
||||
function VisitIndexer(const Node: IIndexerNode): TDataValue; virtual;
|
||||
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; virtual;
|
||||
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; virtual;
|
||||
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; virtual;
|
||||
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; virtual;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; virtual;
|
||||
end;
|
||||
|
||||
// Generic traverser for managing state during AST walks.
|
||||
TAstTraverser<T> = class abstract(TAstTraverser)
|
||||
private
|
||||
FData: TStack<T>;
|
||||
protected
|
||||
function Accept(const Node: IAstNode): TDataValue; override;
|
||||
// Called before a node is visited. The returned value is pushed onto the state stack.
|
||||
function EnterNode(const Node: IAstNode): T; virtual; abstract;
|
||||
// Called after a node has been visited.
|
||||
procedure ExitNode(const Node: IAstNode; const Data: T); virtual;
|
||||
property Data: TStack<T> read FData;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TAstTraverser }
|
||||
|
||||
function TAstTraverser.Accept(const Node: IAstNode): TDataValue;
|
||||
begin
|
||||
if not Assigned(Node) or FDone then
|
||||
exit;
|
||||
Result := Node.Accept(Self);
|
||||
end;
|
||||
|
||||
function TAstTraverser.Execute(const RootNode: IAstNode): TDataValue;
|
||||
begin
|
||||
Result := RootNode.Accept(Self);
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
||||
begin
|
||||
Accept(Node.Series);
|
||||
Accept(Node.Value);
|
||||
if Assigned(Node.Lookback) then
|
||||
Accept(Node.Lookback);
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
begin
|
||||
Accept(Node.Value);
|
||||
Accept(Node.Identifier);
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||
begin
|
||||
Accept(Node.Left);
|
||||
Accept(Node.Right);
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
var
|
||||
expr: IAstNode;
|
||||
begin
|
||||
for expr in Node.Expressions do
|
||||
begin
|
||||
if FDone then
|
||||
break;
|
||||
Accept(expr);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitConstant(const Node: IConstantNode): TDataValue;
|
||||
begin
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
||||
begin
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
var
|
||||
arg: IAstNode;
|
||||
begin
|
||||
Accept(Node.Callee);
|
||||
|
||||
for arg in Node.Arguments do
|
||||
begin
|
||||
if FDone then
|
||||
break;
|
||||
Accept(arg);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
var
|
||||
arg: IAstNode;
|
||||
begin
|
||||
for arg in Node.Arguments do
|
||||
begin
|
||||
if FDone then
|
||||
break;
|
||||
Accept(arg);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
begin
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
||||
begin
|
||||
Accept(Node.Condition);
|
||||
Accept(Node.ThenBranch);
|
||||
if Assigned(Node.ElseBranch) then
|
||||
Accept(Node.ElseBranch);
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitIndexer(const Node: IIndexerNode): TDataValue;
|
||||
begin
|
||||
Accept(Node.Base);
|
||||
Accept(Node.Index);
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
var
|
||||
param: IIdentifierNode;
|
||||
begin
|
||||
for param in Node.Parameters do
|
||||
begin
|
||||
if FDone then
|
||||
break;
|
||||
Accept(param);
|
||||
end;
|
||||
Accept(Node.Body);
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
|
||||
begin
|
||||
// Do not visit the member identifier, as it's not a variable in the current scope.
|
||||
Accept(Node.Base);
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
||||
begin
|
||||
Accept(Node.Series);
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
begin
|
||||
Accept(Node.Condition);
|
||||
Accept(Node.ThenBranch);
|
||||
Accept(Node.ElseBranch);
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
|
||||
begin
|
||||
Accept(Node.Right);
|
||||
end;
|
||||
|
||||
function TAstTraverser.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
begin
|
||||
if Assigned(Node.Initializer) then
|
||||
Accept(Node.Initializer);
|
||||
Accept(Node.Identifier);
|
||||
end;
|
||||
|
||||
{ TAstTraverser<T> }
|
||||
|
||||
constructor TAstTraverser<T>.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
FData := TStack<T>.Create;
|
||||
end;
|
||||
|
||||
destructor TAstTraverser<T>.Destroy;
|
||||
begin
|
||||
FData.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstTraverser<T>.Accept(const Node: IAstNode): TDataValue;
|
||||
begin
|
||||
if not Assigned(Node) or Done then
|
||||
exit;
|
||||
|
||||
FData.Push(EnterNode(Node));
|
||||
try
|
||||
Result := inherited Accept(Node);
|
||||
finally
|
||||
var data := FData.Pop;
|
||||
ExitNode(Node, data);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstTraverser<T>.ExitNode(const Node: IAstNode; const Data: T);
|
||||
begin
|
||||
|
||||
end;
|
||||
|
||||
end.
|
||||
+5
-51
@@ -27,7 +27,7 @@ type
|
||||
class procedure RegisterLibrary(const AProc: TRegisterLibraryProc); static;
|
||||
class function CreateScope(Parent: IExecutionScope; const Descriptor: IScopeDescriptor = nil): IExecutionScope; static;
|
||||
|
||||
// --- Existing factory functions ---
|
||||
// --- Factory functions ---
|
||||
class function Constant(const AValue: TDataValue): IConstantNode; overload; static;
|
||||
class function Constant(const AValue: TScalar): IConstantNode; overload; static;
|
||||
class function Constant(const AValue: String): IConstantNode; overload; static;
|
||||
@@ -72,15 +72,11 @@ type
|
||||
TIdentifierNode = class(TAstNode, IIdentifierNode)
|
||||
private
|
||||
FName: string;
|
||||
// --- Annotation fields added for the binder ---
|
||||
FResolvedAddress: TResolvedAddress;
|
||||
function GetName: string;
|
||||
function GetAddress: TResolvedAddress; inline;
|
||||
public
|
||||
constructor Create(AName: string);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
property Name: string read FName;
|
||||
property Address: TResolvedAddress read FResolvedAddress write FResolvedAddress;
|
||||
end;
|
||||
|
||||
TBinaryExpressionNode = class(TAstNode, IBinaryExpressionNode)
|
||||
@@ -137,46 +133,34 @@ type
|
||||
private
|
||||
FParameters: TArray<IIdentifierNode>;
|
||||
FBody: IAstNode;
|
||||
FScopeDescriptor: IScopeDescriptor;
|
||||
FUpvalues: TArray<TResolvedAddress>;
|
||||
FHasNestedLambdas: Boolean;
|
||||
function GetParameters: TArray<IIdentifierNode>;
|
||||
function GetBody: IAstNode;
|
||||
function GetScopeDescriptor: IScopeDescriptor;
|
||||
function GetUpvalues: TArray<TResolvedAddress>;
|
||||
function GetHasNestedLambdas: Boolean;
|
||||
public
|
||||
constructor Create(const AParameters: TArray<IIdentifierNode>; const ABody: IAstNode);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
property HasNestedLambdas: Boolean read FHasNestedLambdas write FHasNestedLambdas;
|
||||
property ScopeDescriptor: IScopeDescriptor read FScopeDescriptor write FScopeDescriptor;
|
||||
property Upvalues: TArray<TResolvedAddress> read FUpvalues write FUpvalues;
|
||||
property Body: IAstNode read FBody;
|
||||
property Parameters: TArray<IIdentifierNode> read FParameters;
|
||||
end;
|
||||
|
||||
TFunctionCallNode = class(TAstNode, IFunctionCallNode)
|
||||
private
|
||||
FCallee: IAstNode;
|
||||
FArguments: TArray<IAstNode>;
|
||||
FIsTailCall: Boolean;
|
||||
function GetCallee: IAstNode;
|
||||
function GetArguments: TArray<IAstNode>;
|
||||
function GetIsTailCall: Boolean;
|
||||
public
|
||||
constructor Create(const ACallee: IAstNode; const AArguments: TArray<IAstNode>);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
property IsTailCall: Boolean read FIsTailCall write FIsTailCall;
|
||||
end;
|
||||
|
||||
TRecurNode = class(TAstNode, IRecurNode)
|
||||
private
|
||||
FArguments: TArray<IAstNode>;
|
||||
FIsTailCall: Boolean;
|
||||
function GetArguments: TArray<IAstNode>;
|
||||
function GetIsTailCall: Boolean;
|
||||
public
|
||||
constructor Create(const AArguments: TArray<IAstNode>);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
property IsTailCall: Boolean read FIsTailCall write FIsTailCall;
|
||||
end;
|
||||
|
||||
TBlockExpressionNode = class(TAstNode, IBlockExpressionNode)
|
||||
@@ -266,9 +250,6 @@ type
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Myc.Ast.Traverser;
|
||||
|
||||
{ TConstantNode }
|
||||
|
||||
constructor TConstantNode.Create(const AValue: TDataValue);
|
||||
@@ -308,11 +289,6 @@ begin
|
||||
Result := FName;
|
||||
end;
|
||||
|
||||
function TIdentifierNode.GetAddress: TResolvedAddress;
|
||||
begin
|
||||
Result := FResolvedAddress;
|
||||
end;
|
||||
|
||||
{ TBinaryExpressionNode }
|
||||
|
||||
constructor TBinaryExpressionNode.Create(ALeft: IAstNode; AOperator: TScalar.TBinaryOp; ARight: IAstNode);
|
||||
@@ -434,12 +410,6 @@ begin
|
||||
inherited Create;
|
||||
FBody := ABody;
|
||||
FParameters := AParameters;
|
||||
FScopeDescriptor := nil;
|
||||
end;
|
||||
|
||||
function TLambdaExpressionNode.GetUpvalues: TArray<TResolvedAddress>;
|
||||
begin
|
||||
Result := FUpvalues;
|
||||
end;
|
||||
|
||||
function TLambdaExpressionNode.Accept(const Visitor: IAstVisitor): TDataValue;
|
||||
@@ -452,21 +422,11 @@ begin
|
||||
Result := FBody;
|
||||
end;
|
||||
|
||||
function TLambdaExpressionNode.GetHasNestedLambdas: Boolean;
|
||||
begin
|
||||
Result := FHasNestedLambdas;
|
||||
end;
|
||||
|
||||
function TLambdaExpressionNode.GetParameters: TArray<IIdentifierNode>;
|
||||
begin
|
||||
Result := FParameters;
|
||||
end;
|
||||
|
||||
function TLambdaExpressionNode.GetScopeDescriptor: IScopeDescriptor;
|
||||
begin
|
||||
Result := FScopeDescriptor;
|
||||
end;
|
||||
|
||||
{ TFunctionCallNode }
|
||||
|
||||
constructor TFunctionCallNode.Create(const ACallee: IAstNode; const AArguments: TArray<IAstNode>);
|
||||
@@ -474,7 +434,6 @@ begin
|
||||
inherited Create;
|
||||
FCallee := ACallee;
|
||||
FArguments := AArguments;
|
||||
FIsTailCall := False;
|
||||
end;
|
||||
|
||||
function TFunctionCallNode.Accept(const Visitor: IAstVisitor): TDataValue;
|
||||
@@ -492,18 +451,12 @@ begin
|
||||
Result := FCallee;
|
||||
end;
|
||||
|
||||
function TFunctionCallNode.GetIsTailCall: Boolean;
|
||||
begin
|
||||
Result := FIsTailCall;
|
||||
end;
|
||||
|
||||
{ TRecurNode }
|
||||
|
||||
constructor TRecurNode.Create(const AArguments: TArray<IAstNode>);
|
||||
begin
|
||||
inherited Create;
|
||||
FArguments := AArguments;
|
||||
FIsTailCall := True;
|
||||
end;
|
||||
|
||||
function TRecurNode.Accept(const Visitor: IAstVisitor): TDataValue;
|
||||
@@ -518,7 +471,8 @@ end;
|
||||
|
||||
function TRecurNode.GetIsTailCall: Boolean;
|
||||
begin
|
||||
Result := FIsTailCall;
|
||||
// 'recur' is always a tail call by definition.
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
{ TBlockExpressionNode }
|
||||
|
||||
@@ -9,7 +9,7 @@ uses
|
||||
Myc.Data.Series;
|
||||
|
||||
type
|
||||
TDataValueKind = (vkVoid, vkScalar, vkText, vkSeries, vkRecordSeries, vkRecord, vkManagedObject, vkMethod, vkGeneric);
|
||||
TDataValueKind = (vkVoid, vkScalar, vkText, vkSeries, vkRecordSeries, vkRecord, vkManagedObject, vkMethod, vkInterface, vkGeneric);
|
||||
|
||||
TDataValue = record
|
||||
type
|
||||
@@ -58,6 +58,9 @@ type
|
||||
class operator Implicit(const AValue: TDataValue.TFunc): TDataValue; overload; inline;
|
||||
class operator Implicit(const AValue: TObject): TDataValue; overload; inline;
|
||||
|
||||
class function FromIntf<T: IInterface>(const AValue: T): TDataValue; static;
|
||||
function AsIntf<T: IInterface>: T; inline;
|
||||
|
||||
class function FromGeneric<T>(const [ref] AValue: T): TDataValue; static; inline;
|
||||
function AsGeneric<T>: T; inline;
|
||||
|
||||
@@ -222,6 +225,15 @@ begin
|
||||
Result := TVal<T>(FInterface).Value;
|
||||
end;
|
||||
|
||||
function TDataValue.AsIntf<T>: T;
|
||||
begin
|
||||
if FKind = Ord(vkLazy) then
|
||||
exit(AsLazy.Value.AsGeneric<T>);
|
||||
if FKind <> Ord(vkInterface) then
|
||||
raise EInvalidCast.Create('Cannot read value as interface.');
|
||||
Result := T(FInterface);
|
||||
end;
|
||||
|
||||
function TDataValue.AsObject: TObject;
|
||||
begin
|
||||
if FKind = Ord(vkLazy) then
|
||||
@@ -296,6 +308,12 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TDataValue.FromIntf<T>(const AValue: T): TDataValue;
|
||||
begin
|
||||
Result.FKind := Ord(vkInterface);
|
||||
Result.FInterface := IInterface(AValue);
|
||||
end;
|
||||
|
||||
class function TDataValue.FromGeneric<T>(const [ref] AValue: T): TDataValue;
|
||||
begin
|
||||
Result.FKind := Ord(vkGeneric);
|
||||
|
||||
Reference in New Issue
Block a user