RECUR keyword added

This commit is contained in:
Michael Schimmel
2025-09-20 12:06:51 +02:00
parent e03155179a
commit 81dd69bf49
13 changed files with 352 additions and 98 deletions
+3 -3
View File
@@ -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}
+3 -3
View File
@@ -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
View File
@@ -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'))
)
+22
View File
@@ -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;
+41
View File
@@ -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;