RECUR keyword added
This commit is contained in:
@@ -12,14 +12,14 @@ uses
|
||||
Myc.Ast.ViewModel in '..\Src\AST\Myc.Ast.ViewModel.pas',
|
||||
Myc.Data.Value in 'Myc.Data.Value.pas',
|
||||
Myc.Ast.Debugger in '..\Src\AST\Myc.Ast.Debugger.pas',
|
||||
Myc.Fmx.AstEditor.Node in 'Myc.Fmx.AstEditor.Node.pas',
|
||||
Myc.Fmx.AstEditor.Workspace in 'Myc.Fmx.AstEditor.Workspace.pas',
|
||||
Myc.Fmx.AstEditor.Text in 'Myc.Fmx.AstEditor.Text.pas',
|
||||
Myc.Ast.Traverser in '..\Src\AST\Myc.Ast.Traverser.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',
|
||||
Myc.Ast.RTL.Core in '..\Src\AST\Myc.Ast.RTL.Core.pas',
|
||||
Myc.Fmx.AstEditor.Node in 'Myc.Fmx.AstEditor.Node.pas',
|
||||
Myc.Fmx.AstEditor.Workspace in 'Myc.Fmx.AstEditor.Workspace.pas',
|
||||
Myc.Fmx.AstEditor.Text in 'Myc.Fmx.AstEditor.Text.pas',
|
||||
Myc.Utils in '..\Src\Myc.Utils.pas';
|
||||
|
||||
{$R *.res}
|
||||
|
||||
@@ -143,14 +143,14 @@
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.ViewModel.pas"/>
|
||||
<DCCReference Include="Myc.Data.Value.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.Debugger.pas"/>
|
||||
<DCCReference Include="Myc.Fmx.AstEditor.Node.pas"/>
|
||||
<DCCReference Include="Myc.Fmx.AstEditor.Workspace.pas"/>
|
||||
<DCCReference Include="Myc.Fmx.AstEditor.Text.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.Traverser.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"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Ast.RTL.Core.pas"/>
|
||||
<DCCReference Include="Myc.Fmx.AstEditor.Node.pas"/>
|
||||
<DCCReference Include="Myc.Fmx.AstEditor.Workspace.pas"/>
|
||||
<DCCReference Include="Myc.Fmx.AstEditor.Text.pas"/>
|
||||
<DCCReference Include="..\Src\Myc.Utils.pas"/>
|
||||
<BuildConfiguration Include="Base">
|
||||
<Key>Base</Key>
|
||||
|
||||
+59
-90
@@ -21,9 +21,6 @@ uses
|
||||
FMX.ScrollBox,
|
||||
FMX.Memo,
|
||||
FMX.Controls.Presentation,
|
||||
Myc.Fmx.AstEditor,
|
||||
Myc.Fmx.AstEditor.Node,
|
||||
Myc.Fmx.AstEditor.Workspace,
|
||||
Myc.Data.Scalar,
|
||||
Myc.Data.Value,
|
||||
Myc.Ast.Nodes,
|
||||
@@ -36,7 +33,10 @@ uses
|
||||
Myc.Ast.RTL,
|
||||
FMX.Layouts,
|
||||
FMX.Objects,
|
||||
Myc.Ast.Debugger;
|
||||
Myc.Ast.Debugger,
|
||||
Myc.Fmx.AstEditor,
|
||||
Myc.Fmx.AstEditor.Node,
|
||||
Myc.Fmx.AstEditor.Workspace;
|
||||
|
||||
type
|
||||
// A test record
|
||||
@@ -281,31 +281,42 @@ var
|
||||
sw: TStopwatch;
|
||||
begin
|
||||
// Create a setup script to define a memoize-compatible 'fib' function globally.
|
||||
// This is rewritten to be tail-recursive using the 'recur' keyword.
|
||||
var fibAst :=
|
||||
TAst.Block(
|
||||
[
|
||||
// 1. var fib; (Declare the name so it can be captured by the lambda. Initializer is nil)
|
||||
// 1. var fib_iter = lambda(n, a, b) { ... }; (The tail-recursive part)
|
||||
TAst.VarDecl(
|
||||
TAst.Identifier('fib_iter'),
|
||||
TAst.LambdaExpr(
|
||||
[TAst.Identifier('n'), TAst.Identifier('a'), TAst.Identifier('b')],
|
||||
TAst.TernaryExpr(
|
||||
TAst.BinaryExpr(TAst.Identifier('n'), boEqual, TAst.Constant(TScalar.FromInt64(0))),
|
||||
TAst.Identifier('a'), // Base case 1
|
||||
TAst.TernaryExpr(
|
||||
TAst.BinaryExpr(TAst.Identifier('n'), boEqual, TAst.Constant(TScalar.FromInt64(1))),
|
||||
TAst.Identifier('b'), // Base case 2
|
||||
TAst.Recur( // Tail-recursive step
|
||||
[
|
||||
TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(1))),
|
||||
TAst.Identifier('b'),
|
||||
TAst.BinaryExpr(TAst.Identifier('a'), boAdd, TAst.Identifier('b'))
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
// 2. var fib;
|
||||
TAst.VarDecl(TAst.Identifier('fib')),
|
||||
// 2. var fib_impl = lambda(n) { ... fib(n-1) + fib(n-2) ... };
|
||||
// 3. fib = lambda(n) { fib_iter(n, 0, 1) }; (The public-facing function)
|
||||
TAst.Assign(
|
||||
TAst.Identifier('fib'),
|
||||
TAst.LambdaExpr(
|
||||
[TAst.Identifier('n')],
|
||||
TAst.TernaryExpr(
|
||||
TAst.BinaryExpr(TAst.Identifier('n'), boLess, TAst.Constant(TScalar.FromInt64(2))),
|
||||
TAst.Identifier('n'),
|
||||
TAst.BinaryExpr(
|
||||
// Recursive calls now use the re-bindable name 'fib' instead of 'Self'
|
||||
TAst.FunctionCall(
|
||||
TAst.Identifier('Self'),
|
||||
[TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(1)))]
|
||||
),
|
||||
boAdd,
|
||||
TAst.FunctionCall(
|
||||
TAst.Identifier('Self'),
|
||||
[TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(2)))]
|
||||
)
|
||||
)
|
||||
TAst.FunctionCall(
|
||||
TAst.Identifier('fib_iter'),
|
||||
[TAst.Identifier('n'), TAst.Constant(TScalar.FromInt64(0)), TAst.Constant(TScalar.FromInt64(1))]
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -314,10 +325,10 @@ begin
|
||||
|
||||
var fibScope := TAstBinder.Bind(fibAst, FGScope).CreateScope(FGScope);
|
||||
var visitor := CreateVisitor(fibScope);
|
||||
Result := visitor.Execute(fibAst);
|
||||
visitor.Execute(fibAst);
|
||||
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add('--- Recursive fib with AST---');
|
||||
Memo1.Lines.Add('--- Tail-Recursive fib with AST---');
|
||||
sw := TStopwatch.StartNew;
|
||||
|
||||
root := TAst.FunctionCall(TAst.Identifier('fib'), [TAst.Constant(TScalar.FromInt64(30))]);
|
||||
@@ -330,7 +341,7 @@ begin
|
||||
Memo1.Lines.Add(Format('Result: fib(30) %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
||||
|
||||
Memo1.Lines.Add('');
|
||||
Memo1.Lines.Add('--- Memoized recursive fib with AST (using global fib)---');
|
||||
Memo1.Lines.Add('--- Memoized tail-recursive fib with AST (using global fib)---');
|
||||
sw := TStopwatch.StartNew;
|
||||
|
||||
root :=
|
||||
@@ -375,30 +386,39 @@ var
|
||||
sw: TStopwatch;
|
||||
begin
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add('--- Recursive factorial(20) ---');
|
||||
Memo1.Lines.Add('--- Tail-Recursive factorial(20) ---');
|
||||
sw := TStopwatch.StartNew;
|
||||
|
||||
// Rewritten to be tail-recursive to use 'recur'
|
||||
root :=
|
||||
TAst.Block(
|
||||
[
|
||||
// Define the tail-recursive helper function
|
||||
TAst.VarDecl(
|
||||
TAst.Identifier('factorial'),
|
||||
TAst.Identifier('fact_iter'),
|
||||
TAst.LambdaExpr(
|
||||
[TAst.Identifier('n')],
|
||||
[TAst.Identifier('n'), TAst.Identifier('acc')],
|
||||
TAst.TernaryExpr(
|
||||
TAst.BinaryExpr(TAst.Identifier('n'), boLess, TAst.Constant(TScalar.FromInt64(2))),
|
||||
TAst.Constant(TScalar.FromInt64(1)),
|
||||
TAst.BinaryExpr(
|
||||
TAst.Identifier('n'),
|
||||
boMultiply,
|
||||
TAst.FunctionCall(
|
||||
TAst.Identifier('Self'),
|
||||
[TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(1)))]
|
||||
)
|
||||
TAst.BinaryExpr(TAst.Identifier('n'), boLessOrEqual, TAst.Constant(TScalar.FromInt64(1))),
|
||||
TAst.Identifier('acc'), // Base case: return the accumulator
|
||||
TAst.Recur( // Tail-recursive step
|
||||
[
|
||||
TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(1))),
|
||||
TAst.BinaryExpr(TAst.Identifier('acc'), boMultiply, TAst.Identifier('n'))
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
// Define the public-facing factorial function
|
||||
TAst.VarDecl(
|
||||
TAst.Identifier('factorial'),
|
||||
TAst.LambdaExpr(
|
||||
[TAst.Identifier('n')],
|
||||
TAst.FunctionCall(TAst.Identifier('fact_iter'), [TAst.Identifier('n'), TAst.Constant(TScalar.FromInt64(1))])
|
||||
)
|
||||
),
|
||||
// Call the main function
|
||||
TAst.FunctionCall(TAst.Identifier('factorial'), [TAst.Constant(TScalar.FromInt64(20))])
|
||||
]
|
||||
);
|
||||
@@ -566,54 +586,6 @@ begin
|
||||
var setupAst :=
|
||||
TAst.Block(
|
||||
[
|
||||
// TAst.VarDecl(
|
||||
// TAst.Identifier('CreateSMA'),
|
||||
// TAst.LambdaExpr(
|
||||
// [TAst.Identifier('len')],
|
||||
// TAst.Block(
|
||||
// [
|
||||
// TAst.VarDecl(TAst.Identifier('sum'), TAst.Constant(TScalar.FromDouble(0.0))),
|
||||
// TAst.VarDecl(TAst.Identifier('count'), TAst.Constant(TScalar.FromInt64(0))),
|
||||
// TAst.LambdaExpr(
|
||||
// [TAst.Identifier('series'), TAst.Identifier('val')],
|
||||
// TAst.Block(
|
||||
// [
|
||||
// TAst.Assign(
|
||||
// TAst.Identifier('sum'),
|
||||
// TAst.BinaryExpr(TAst.Identifier('sum'), boAdd, TAst.Identifier('val'))
|
||||
// ),
|
||||
// TAst.Assign(
|
||||
// TAst.Identifier('count'),
|
||||
// TAst.BinaryExpr(TAst.Identifier('count'), boAdd, TAst.Constant(TScalar.FromInt64(1)))
|
||||
// ),
|
||||
// TAst.IfExpr(
|
||||
// TAst.BinaryExpr(TAst.Identifier('count'), boGreater, TAst.Identifier('len')),
|
||||
// TAst.Assign(
|
||||
// TAst.Identifier('sum'),
|
||||
// TAst.BinaryExpr(
|
||||
// TAst.Identifier('sum'),
|
||||
// boSubtract,
|
||||
// TAst.Indexer(TAst.Identifier('series'), TAst.Identifier('len'))
|
||||
// )
|
||||
// ),
|
||||
// nil
|
||||
// ),
|
||||
// TAst.BinaryExpr(
|
||||
// TAst.Identifier('sum'),
|
||||
// boDivide,
|
||||
// TAst.TernaryExpr(
|
||||
// TAst.BinaryExpr(TAst.Identifier('count'), boLess, TAst.Identifier('len')),
|
||||
// TAst.Identifier('count'),
|
||||
// TAst.Identifier('len')
|
||||
// )
|
||||
// )
|
||||
// ]
|
||||
// )
|
||||
// )
|
||||
// ]
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
TAst.VarDecl(
|
||||
TAst.Identifier('smaFast'),
|
||||
TAst.FunctionCall(TAst.Identifier('CreateSMA'), [TAst.Constant(TScalar.FromInt64(smaFastLength))])
|
||||
@@ -961,14 +933,11 @@ begin
|
||||
TAst.Identifier('countDown'),
|
||||
TAst.LambdaExpr(
|
||||
[TAst.Identifier('n')],
|
||||
// if (n > 0) then Self(n-1) else 'done'
|
||||
// if (n > 0) then recur(n-1) else 'done'
|
||||
TAst.IfExpr(
|
||||
TAst.BinaryExpr(TAst.Identifier('n'), boGreater, TAst.Constant(TScalar.FromInt64(0))),
|
||||
// This is the tail call position.
|
||||
TAst.FunctionCall(
|
||||
TAst.Identifier('Self'),
|
||||
[TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(1)))]
|
||||
),
|
||||
// This is the tail call position, now using recur.
|
||||
TAst.Recur([TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(1)))]),
|
||||
// Base case of the recursion
|
||||
TAst.Constant(TScalar.FromString('done'))
|
||||
)
|
||||
|
||||
@@ -30,6 +30,7 @@ type
|
||||
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
|
||||
@@ -105,6 +106,27 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstToTextVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
var
|
||||
i: Integer;
|
||||
sb: TStringBuilder;
|
||||
begin
|
||||
sb := TStringBuilder.Create;
|
||||
try
|
||||
sb.Append('recur(');
|
||||
for i := 0 to High(Node.Arguments) do
|
||||
begin
|
||||
sb.Append(Node.Arguments[i].Accept(Self).AsText);
|
||||
if i < High(Node.Arguments) then
|
||||
sb.Append(', ');
|
||||
end;
|
||||
sb.Append(')');
|
||||
Result := sb.ToString;
|
||||
finally
|
||||
sb.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstToTextVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
begin
|
||||
Result := Node.Name;
|
||||
|
||||
@@ -118,6 +118,7 @@ type
|
||||
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
|
||||
@@ -567,6 +568,46 @@ begin
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TAstToAuraNodeVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
var
|
||||
details: string;
|
||||
begin
|
||||
if (FMode = vmControlFlow) and TryGetDescr(Node, details) then
|
||||
begin
|
||||
var recurNode := CreateNodeControl('Recur', details);
|
||||
CreateEntry(recurNode);
|
||||
CreateExit(recurNode, '');
|
||||
FinalizeNodeLayout(recurNode);
|
||||
FLastResult.OutputPin := nil;
|
||||
end
|
||||
else
|
||||
begin
|
||||
VisitOperatorNode(
|
||||
Node.Arguments,
|
||||
function(const InputResults: TAuraNodeResultList): TAuraNodeResult
|
||||
var
|
||||
recurNode: TAuraNode;
|
||||
argPin: array of TControl;
|
||||
i: Integer;
|
||||
begin
|
||||
recurNode := BuildNodeControl('Recur', '');
|
||||
CreateEntry(recurNode);
|
||||
SetLength(argPin, Length(Node.Arguments));
|
||||
for i := 0 to High(argPin) do
|
||||
argPin[i] := CreateInput(recurNode, 'Arg' + i.ToString);
|
||||
CreateExit(recurNode, '');
|
||||
for i := 0 to High(Node.Arguments) do
|
||||
if Assigned(InputResults[i].OutputPin) then
|
||||
FConnections.Add(TPinConnection.Create(InputResults[i].OutputPin, argPin[i]));
|
||||
FinalizeNodeLayout(recurNode);
|
||||
Result.LayoutNode := recurNode;
|
||||
Result.OutputPin := nil;
|
||||
end
|
||||
);
|
||||
end;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TAstToAuraNodeVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
var
|
||||
existingResult: TAuraNodeResult;
|
||||
|
||||
@@ -32,6 +32,7 @@ type
|
||||
|
||||
protected
|
||||
function Accept(const Node: IAstNode): TDataValue; override;
|
||||
function IsValidIdentifier(const Name: string): Boolean;
|
||||
|
||||
public
|
||||
constructor Create(const AInitialScope: IExecutionScope);
|
||||
@@ -47,6 +48,7 @@ type
|
||||
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;
|
||||
@@ -58,6 +60,7 @@ implementation
|
||||
|
||||
uses
|
||||
System.Generics.Defaults,
|
||||
System.Character,
|
||||
Myc.Ast;
|
||||
|
||||
type
|
||||
@@ -214,6 +217,17 @@ begin
|
||||
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;
|
||||
var
|
||||
depth, idx: Integer;
|
||||
@@ -278,7 +292,9 @@ begin
|
||||
try
|
||||
EnterScope;
|
||||
try
|
||||
FCurrentDescriptor.Define('Self');
|
||||
// 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);
|
||||
@@ -341,6 +357,30 @@ begin
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TAstBinder.IsValidIdentifier(const Name: string): Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
c: Char;
|
||||
begin
|
||||
if Name.IsEmpty then
|
||||
exit(False);
|
||||
|
||||
// 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
|
||||
begin
|
||||
c := Name[i];
|
||||
if not (c.IsLetterOrDigit or (c = '_') or (c = '-')) then
|
||||
exit(False);
|
||||
end;
|
||||
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
var
|
||||
slotIndex: Integer;
|
||||
@@ -350,6 +390,10 @@ begin
|
||||
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);
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ type
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
|
||||
@@ -230,6 +231,21 @@ begin
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TAstDumper.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
var
|
||||
arg: IAstNode;
|
||||
begin
|
||||
LogFmt('Recur (IsTailCall: %s)', [Node.IsTailCall.ToString(TUseBoolStrs.True)]);
|
||||
Indent;
|
||||
LogFmt('Arguments (%d):', [Length(Node.Arguments)]);
|
||||
Indent;
|
||||
for arg in Node.Arguments do
|
||||
Accept(arg);
|
||||
Unindent;
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TAstDumper.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
var
|
||||
expr: IAstNode;
|
||||
|
||||
@@ -50,6 +50,7 @@ type
|
||||
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;
|
||||
|
||||
// Registers native Delphi functions into a scope.
|
||||
@@ -204,7 +205,8 @@ begin
|
||||
adr.Kind := akLocalOrParent;
|
||||
adr.ScopeDepth := 0;
|
||||
|
||||
// Capture 'Self' (slot 0) for recursion using the captured variable.
|
||||
// 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);
|
||||
|
||||
@@ -256,6 +258,33 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
var
|
||||
argValues: TArray<TDataValue>;
|
||||
calleeAddress: TResolvedAddress;
|
||||
calleeValue: TDataValue;
|
||||
i: Integer;
|
||||
begin
|
||||
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.
|
||||
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.
|
||||
Result := TDataValue.FromGeneric<TThunk>(TThunk.Create(calleeValue, argValues));
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
||||
var
|
||||
itemValue, lookbackValue, seriesVar: TDataValue;
|
||||
|
||||
@@ -41,6 +41,7 @@ type
|
||||
function JsonToTernaryExprNode(const AObj: TJSONObject): ITernaryExpressionNode;
|
||||
function JsonToLambdaExprNode(const AObj: TJSONObject): ILambdaExpressionNode;
|
||||
function JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode;
|
||||
function JsonToRecurNode(const AObj: TJSONObject): IRecurNode;
|
||||
function JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode;
|
||||
function JsonToVarDeclNode(const AObj: TJSONObject): IVariableDeclarationNode;
|
||||
function JsonToAssignmentNode(const AObj: TJSONObject): IAssignmentNode;
|
||||
@@ -59,6 +60,7 @@ type
|
||||
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
||||
function VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
function VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||
@@ -321,6 +323,32 @@ begin
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
var
|
||||
obj: TJSONObject;
|
||||
argsArray: TJSONArray;
|
||||
tempArgs: TArray<TJSONObject>;
|
||||
arg: IAstNode;
|
||||
i: Integer;
|
||||
begin
|
||||
for arg in Node.Arguments do
|
||||
arg.Accept(Self);
|
||||
|
||||
SetLength(tempArgs, Length(Node.Arguments));
|
||||
for i := High(tempArgs) downto 0 do
|
||||
tempArgs[i] := FJsonObjectStack.Pop;
|
||||
|
||||
argsArray := TJSONArray.Create;
|
||||
for i := 0 to High(tempArgs) do
|
||||
argsArray.Add(tempArgs[i]);
|
||||
|
||||
obj := TJSONObject.Create;
|
||||
obj.AddPair('NodeType', TJSONString.Create('Recur'));
|
||||
obj.AddPair('Arguments', argsArray);
|
||||
FJsonObjectStack.Push(obj);
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
var
|
||||
obj: TJSONObject;
|
||||
@@ -604,6 +632,20 @@ begin
|
||||
Result := TAst.FunctionCall(callee, args);
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToRecurNode(const AObj: TJSONObject): IRecurNode;
|
||||
var
|
||||
args: TArray<IAstNode>;
|
||||
argsArray: TJSONArray;
|
||||
i: Integer;
|
||||
begin
|
||||
argsArray := AObj.GetValue<TJSONArray>('Arguments');
|
||||
SetLength(args, argsArray.Count);
|
||||
for i := 0 to argsArray.Count - 1 do
|
||||
args[i] := JsonToNode(argsArray.Items[i]);
|
||||
|
||||
Result := TAst.Recur(args);
|
||||
end;
|
||||
|
||||
function TJsonAstConverter.JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode;
|
||||
var
|
||||
expressions: TArray<IAstNode>;
|
||||
@@ -702,6 +744,8 @@ begin
|
||||
Result := JsonToLambdaExprNode(obj)
|
||||
else if nodeType = 'FunctionCall' then
|
||||
Result := JsonToFunctionCallNode(obj)
|
||||
else if nodeType = 'Recur' then
|
||||
Result := JsonToRecurNode(obj)
|
||||
else if nodeType = 'Block' then
|
||||
Result := JsonToBlockNode(obj)
|
||||
else if nodeType = 'VarDecl' then
|
||||
|
||||
@@ -28,6 +28,7 @@ type
|
||||
ICreateSeriesNode = interface;
|
||||
IAddSeriesItemNode = interface;
|
||||
ISeriesLengthNode = interface;
|
||||
IRecurNode = interface;
|
||||
IExecutionScope = interface;
|
||||
IScopeDescriptor = interface;
|
||||
|
||||
@@ -103,6 +104,7 @@ type
|
||||
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;
|
||||
|
||||
IAstNode = interface(IInterface)
|
||||
@@ -193,6 +195,16 @@ type
|
||||
property IsTailCall: Boolean read GetIsTailCall;
|
||||
end;
|
||||
|
||||
// A node representing a tail-recursive call.
|
||||
IRecurNode = interface(IAstNode)
|
||||
{$region 'private'}
|
||||
function GetArguments: TArray<IAstNode>;
|
||||
function GetIsTailCall: Boolean;
|
||||
{$endregion}
|
||||
property Arguments: TArray<IAstNode> read GetArguments;
|
||||
property IsTailCall: Boolean read GetIsTailCall;
|
||||
end;
|
||||
|
||||
IBlockExpressionNode = interface(IAstNode)
|
||||
{$region 'private'}
|
||||
function GetExpressions: TList<IAstNode>;
|
||||
|
||||
@@ -40,6 +40,7 @@ type
|
||||
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
|
||||
@@ -206,6 +207,21 @@ begin
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
||||
var
|
||||
arg: IAstNode;
|
||||
begin
|
||||
AppendLine('Recur');
|
||||
Indent;
|
||||
AppendLine('Arguments:');
|
||||
Indent;
|
||||
for arg in Node.Arguments do
|
||||
arg.Accept(Self);
|
||||
Unindent;
|
||||
Unindent;
|
||||
Result := TDataValue.Void;
|
||||
end;
|
||||
|
||||
function TPrettyPrintVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
||||
var
|
||||
expr: IAstNode;
|
||||
|
||||
@@ -35,6 +35,7 @@ type
|
||||
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.
|
||||
@@ -123,6 +124,18 @@ begin
|
||||
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;
|
||||
|
||||
@@ -36,6 +36,7 @@ type
|
||||
class function TernaryExpr(const ACondition: IAstNode; const AThenBranch, AElseBranch: IAstNode): ITernaryExpressionNode; static;
|
||||
class function LambdaExpr(const AParameters: TArray<IIdentifierNode>; const ABody: IAstNode): ILambdaExpressionNode; static;
|
||||
class function FunctionCall(const ACallee: IAstNode; const AArguments: TArray<IAstNode>): IFunctionCallNode; static;
|
||||
class function Recur(const AArguments: array of IAstNode): IRecurNode; static;
|
||||
class function Block(const AExpressions: array of IAstNode): IBlockExpressionNode; static;
|
||||
class function VarDecl(const AIdentifier: IIdentifierNode; AInitializer: IAstNode = nil): IVariableDeclarationNode; static;
|
||||
class function Assign(const AIdentifier: IIdentifierNode; const AValue: IAstNode): IAssignmentNode; static;
|
||||
@@ -164,6 +165,18 @@ type
|
||||
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)
|
||||
private
|
||||
FExpressions: TList<IAstNode>;
|
||||
@@ -479,6 +492,30 @@ 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;
|
||||
begin
|
||||
Result := Visitor.VisitRecurNode(Self);
|
||||
end;
|
||||
|
||||
function TRecurNode.GetArguments: TArray<IAstNode>;
|
||||
begin
|
||||
Result := FArguments;
|
||||
end;
|
||||
|
||||
function TRecurNode.GetIsTailCall: Boolean;
|
||||
begin
|
||||
Result := FIsTailCall;
|
||||
end;
|
||||
|
||||
{ TBlockExpressionNode }
|
||||
|
||||
constructor TBlockExpressionNode.Create(const AExpressions: array of IAstNode);
|
||||
@@ -760,6 +797,17 @@ begin
|
||||
Result := TMemberAccessNode.Create(ABase, AMember);
|
||||
end;
|
||||
|
||||
class function TAst.Recur(const AArguments: array of IAstNode): IRecurNode;
|
||||
var
|
||||
args: TArray<IAstNode>;
|
||||
i: Integer;
|
||||
begin
|
||||
SetLength(args, Length(AArguments));
|
||||
for i := 0 to High(AArguments) do
|
||||
args[i] := AArguments[i];
|
||||
Result := TRecurNode.Create(args);
|
||||
end;
|
||||
|
||||
class function TAst.SeriesLength(const ASeries: IIdentifierNode): ISeriesLengthNode;
|
||||
begin
|
||||
Result := TSeriesLengthNode.Create(ASeries);
|
||||
|
||||
Reference in New Issue
Block a user