Global data value refactoring + 1st scripting version
This commit is contained in:
@@ -9,7 +9,6 @@ uses
|
|||||||
Myc.Ast.Nodes in '..\Src\AST\Myc.Ast.Nodes.pas',
|
Myc.Ast.Nodes in '..\Src\AST\Myc.Ast.Nodes.pas',
|
||||||
Myc.Ast.Scope in '..\Src\AST\Myc.Ast.Scope.pas',
|
Myc.Ast.Scope in '..\Src\AST\Myc.Ast.Scope.pas',
|
||||||
Myc.Fmx.AstEditor in 'Myc.Fmx.AstEditor.pas',
|
Myc.Fmx.AstEditor in 'Myc.Fmx.AstEditor.pas',
|
||||||
Myc.Ast.ViewModel in '..\Src\AST\Myc.Ast.ViewModel.pas',
|
|
||||||
Myc.Data.Value in 'Myc.Data.Value.pas',
|
Myc.Data.Value in 'Myc.Data.Value.pas',
|
||||||
Myc.Ast.Debugger in '..\Src\AST\Myc.Ast.Debugger.pas',
|
Myc.Ast.Debugger in '..\Src\AST\Myc.Ast.Debugger.pas',
|
||||||
Myc.Ast.Traverser in '..\Src\AST\Myc.Ast.Traverser.pas',
|
Myc.Ast.Traverser in '..\Src\AST\Myc.Ast.Traverser.pas',
|
||||||
@@ -20,7 +19,8 @@ uses
|
|||||||
Myc.Fmx.AstEditor.Node in 'Myc.Fmx.AstEditor.Node.pas',
|
Myc.Fmx.AstEditor.Node in 'Myc.Fmx.AstEditor.Node.pas',
|
||||||
Myc.Fmx.AstEditor.Workspace in 'Myc.Fmx.AstEditor.Workspace.pas',
|
Myc.Fmx.AstEditor.Workspace in 'Myc.Fmx.AstEditor.Workspace.pas',
|
||||||
Myc.Fmx.AstEditor.Text in 'Myc.Fmx.AstEditor.Text.pas',
|
Myc.Fmx.AstEditor.Text in 'Myc.Fmx.AstEditor.Text.pas',
|
||||||
Myc.Utils in '..\Src\Myc.Utils.pas';
|
Myc.Utils in '..\Src\Myc.Utils.pas',
|
||||||
|
Myc.Ast.Script in '..\Src\AST\Myc.Ast.Script.pas';
|
||||||
|
|
||||||
{$R *.res}
|
{$R *.res}
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,6 @@
|
|||||||
<DCCReference Include="..\Src\AST\Myc.Ast.Nodes.pas"/>
|
<DCCReference Include="..\Src\AST\Myc.Ast.Nodes.pas"/>
|
||||||
<DCCReference Include="..\Src\AST\Myc.Ast.Scope.pas"/>
|
<DCCReference Include="..\Src\AST\Myc.Ast.Scope.pas"/>
|
||||||
<DCCReference Include="Myc.Fmx.AstEditor.pas"/>
|
<DCCReference Include="Myc.Fmx.AstEditor.pas"/>
|
||||||
<DCCReference Include="..\Src\AST\Myc.Ast.ViewModel.pas"/>
|
|
||||||
<DCCReference Include="Myc.Data.Value.pas"/>
|
<DCCReference Include="Myc.Data.Value.pas"/>
|
||||||
<DCCReference Include="..\Src\AST\Myc.Ast.Debugger.pas"/>
|
<DCCReference Include="..\Src\AST\Myc.Ast.Debugger.pas"/>
|
||||||
<DCCReference Include="..\Src\AST\Myc.Ast.Traverser.pas"/>
|
<DCCReference Include="..\Src\AST\Myc.Ast.Traverser.pas"/>
|
||||||
@@ -152,6 +151,7 @@
|
|||||||
<DCCReference Include="Myc.Fmx.AstEditor.Workspace.pas"/>
|
<DCCReference Include="Myc.Fmx.AstEditor.Workspace.pas"/>
|
||||||
<DCCReference Include="Myc.Fmx.AstEditor.Text.pas"/>
|
<DCCReference Include="Myc.Fmx.AstEditor.Text.pas"/>
|
||||||
<DCCReference Include="..\Src\Myc.Utils.pas"/>
|
<DCCReference Include="..\Src\Myc.Utils.pas"/>
|
||||||
|
<DCCReference Include="..\Src\AST\Myc.Ast.Script.pas"/>
|
||||||
<BuildConfiguration Include="Base">
|
<BuildConfiguration Include="Base">
|
||||||
<Key>Base</Key>
|
<Key>Base</Key>
|
||||||
</BuildConfiguration>
|
</BuildConfiguration>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ object Form1: TForm1
|
|||||||
Top = 0
|
Top = 0
|
||||||
Caption = 'Form1'
|
Caption = 'Form1'
|
||||||
ClientHeight = 883
|
ClientHeight = 883
|
||||||
ClientWidth = 925
|
ClientWidth = 1394
|
||||||
FormFactor.Width = 320
|
FormFactor.Width = 320
|
||||||
FormFactor.Height = 480
|
FormFactor.Height = 480
|
||||||
FormFactor.Devices = [Desktop]
|
FormFactor.Devices = [Desktop]
|
||||||
@@ -103,7 +103,7 @@ object Form1: TForm1
|
|||||||
Position.Y = 8.000000000000000000
|
Position.Y = 8.000000000000000000
|
||||||
TabOrder = 11
|
TabOrder = 11
|
||||||
Text = 'Flow only'
|
Text = 'Flow only'
|
||||||
OnChange = ClearButtonClick
|
OnChange = FlowOnlyBoxChange
|
||||||
end
|
end
|
||||||
object SeriesTestButton: TButton
|
object SeriesTestButton: TButton
|
||||||
Position.X = 24.000000000000000000
|
Position.X = 24.000000000000000000
|
||||||
@@ -189,8 +189,8 @@ object Form1: TForm1
|
|||||||
end
|
end
|
||||||
object Panel2: TPanel
|
object Panel2: TPanel
|
||||||
Align = Client
|
Align = Client
|
||||||
Size.Width = 796.000000000000000000
|
Size.Width = 784.000000000000000000
|
||||||
Size.Height = 616.000000000000000000
|
Size.Height = 609.000000000000000000
|
||||||
Size.PlatformDefault = False
|
Size.PlatformDefault = False
|
||||||
TabOrder = 3
|
TabOrder = 3
|
||||||
end
|
end
|
||||||
@@ -202,11 +202,45 @@ object Form1: TForm1
|
|||||||
Align = Bottom
|
Align = Bottom
|
||||||
Position.X = 129.000000000000000000
|
Position.X = 129.000000000000000000
|
||||||
Position.Y = 616.000000000000000000
|
Position.Y = 616.000000000000000000
|
||||||
Size.Width = 796.000000000000000000
|
Size.Width = 1265.000000000000000000
|
||||||
Size.Height = 267.000000000000000000
|
Size.Height = 267.000000000000000000
|
||||||
Size.PlatformDefault = False
|
Size.PlatformDefault = False
|
||||||
TabOrder = 2
|
TabOrder = 2
|
||||||
Viewport.Width = 792.000000000000000000
|
Viewport.Width = 1261.000000000000000000
|
||||||
Viewport.Height = 263.000000000000000000
|
Viewport.Height = 263.000000000000000000
|
||||||
end
|
end
|
||||||
|
object Splitter1: TSplitter
|
||||||
|
Align = Bottom
|
||||||
|
Cursor = crVSplit
|
||||||
|
MinSize = 20.000000000000000000
|
||||||
|
Position.X = 129.000000000000000000
|
||||||
|
Position.Y = 609.000000000000000000
|
||||||
|
Size.Width = 1265.000000000000000000
|
||||||
|
Size.Height = 7.000000000000000000
|
||||||
|
Size.PlatformDefault = False
|
||||||
|
end
|
||||||
|
object Splitter2: TSplitter
|
||||||
|
Align = Right
|
||||||
|
Cursor = crHSplit
|
||||||
|
MinSize = 20.000000000000000000
|
||||||
|
Position.X = 913.000000000000000000
|
||||||
|
Size.Width = 7.000000000000000000
|
||||||
|
Size.Height = 609.000000000000000000
|
||||||
|
Size.PlatformDefault = False
|
||||||
|
end
|
||||||
|
object ScriptMemo: TMemo
|
||||||
|
Touch.InteractiveGestures = [Pan, LongTap, DoubleTap]
|
||||||
|
DataDetectorTypes = []
|
||||||
|
StyledSettings = [Size, Style, FontColor]
|
||||||
|
TextSettings.Font.Family = 'Consolas'
|
||||||
|
OnChangeTracking = ScriptMemoChange
|
||||||
|
Align = Right
|
||||||
|
Position.X = 920.000000000000000000
|
||||||
|
Size.Width = 474.000000000000000000
|
||||||
|
Size.Height = 609.000000000000000000
|
||||||
|
Size.PlatformDefault = False
|
||||||
|
TabOrder = 6
|
||||||
|
Viewport.Width = 470.000000000000000000
|
||||||
|
Viewport.Height = 605.000000000000000000
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+199
-141
@@ -31,6 +31,7 @@ uses
|
|||||||
Myc.Data.Decimal,
|
Myc.Data.Decimal,
|
||||||
Myc.Ast.Binding,
|
Myc.Ast.Binding,
|
||||||
Myc.Ast.RTL,
|
Myc.Ast.RTL,
|
||||||
|
Myc.Ast.Script,
|
||||||
FMX.Layouts,
|
FMX.Layouts,
|
||||||
FMX.Objects,
|
FMX.Objects,
|
||||||
Myc.Ast.Debugger,
|
Myc.Ast.Debugger,
|
||||||
@@ -74,6 +75,9 @@ type
|
|||||||
DumpButton: TButton;
|
DumpButton: TButton;
|
||||||
FailingUpvalueButton: TButton;
|
FailingUpvalueButton: TButton;
|
||||||
TailCallButten: TButton;
|
TailCallButten: TButton;
|
||||||
|
Splitter1: TSplitter;
|
||||||
|
Splitter2: TSplitter;
|
||||||
|
ScriptMemo: TMemo;
|
||||||
procedure InnerLambdaButtonClick(Sender: TObject);
|
procedure InnerLambdaButtonClick(Sender: TObject);
|
||||||
procedure ClearButtonClick(Sender: TObject);
|
procedure ClearButtonClick(Sender: TObject);
|
||||||
procedure FormCreate(Sender: TObject);
|
procedure FormCreate(Sender: TObject);
|
||||||
@@ -84,6 +88,7 @@ type
|
|||||||
procedure ExternalFuncButtonClick(Sender: TObject);
|
procedure ExternalFuncButtonClick(Sender: TObject);
|
||||||
procedure FailingUpvalueButtonClick(Sender: TObject);
|
procedure FailingUpvalueButtonClick(Sender: TObject);
|
||||||
procedure FibonacciButtonClick(Sender: TObject);
|
procedure FibonacciButtonClick(Sender: TObject);
|
||||||
|
procedure FlowOnlyBoxChange(Sender: TObject);
|
||||||
procedure OHLCButtonClick(Sender: TObject);
|
procedure OHLCButtonClick(Sender: TObject);
|
||||||
procedure PrettyPrintButtonClick(Sender: TObject);
|
procedure PrettyPrintButtonClick(Sender: TObject);
|
||||||
procedure RecursionButtonClick(Sender: TObject);
|
procedure RecursionButtonClick(Sender: TObject);
|
||||||
@@ -91,6 +96,7 @@ type
|
|||||||
procedure Test1ButtonClick(Sender: TObject);
|
procedure Test1ButtonClick(Sender: TObject);
|
||||||
procedure Test2ButtonClick(Sender: TObject);
|
procedure Test2ButtonClick(Sender: TObject);
|
||||||
procedure FromJSONButtonClick(Sender: TObject);
|
procedure FromJSONButtonClick(Sender: TObject);
|
||||||
|
procedure ScriptMemoChange(Sender: TObject);
|
||||||
procedure TailCallButtenClick(Sender: TObject);
|
procedure TailCallButtenClick(Sender: TObject);
|
||||||
procedure ToJSONButtonClick(Sender: TObject);
|
procedure ToJSONButtonClick(Sender: TObject);
|
||||||
private
|
private
|
||||||
@@ -98,10 +104,14 @@ type
|
|||||||
FLastAst: IAstNode;
|
FLastAst: IAstNode;
|
||||||
FGScope: IExecutionScope;
|
FGScope: IExecutionScope;
|
||||||
FWorkspace: TAuraWorkspace;
|
FWorkspace: TAuraWorkspace;
|
||||||
|
FTriggerScope: IExecutionScope;
|
||||||
|
FScriptUpdate: Boolean;
|
||||||
procedure WorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
|
procedure WorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
|
||||||
function CreateVisitor(Scope: IExecutionScope): IAstVisitor;
|
function CreateVisitor(Scope: IExecutionScope): IAstVisitor;
|
||||||
// Helper function to encapsulate the Bind -> Evaluate pattern
|
// Helper function to encapsulate the Bind -> Evaluate pattern
|
||||||
function ExecuteAst(const ANode: IAstNode; const AParentScope: IExecutionScope): TDataValue;
|
function ExecuteAst(const ANode: IAstNode; const AParentScope: IExecutionScope): TDataValue;
|
||||||
|
procedure UpdateScript;
|
||||||
|
procedure ShowVizualization(X, Y: Single);
|
||||||
public
|
public
|
||||||
{ Public declarations }
|
{ Public declarations }
|
||||||
end;
|
end;
|
||||||
@@ -138,7 +148,7 @@ begin
|
|||||||
TAst.Block(
|
TAst.Block(
|
||||||
[
|
[
|
||||||
// var x = 10;
|
// var x = 10;
|
||||||
TAst.VarDecl(TAst.Identifier('x'), TAst.Constant(TScalar.FromInteger(10))),
|
TAst.VarDecl(TAst.Identifier('x'), TAst.Constant(10)),
|
||||||
// var inner = lambda() { ... };
|
// var inner = lambda() { ... };
|
||||||
TAst.VarDecl(
|
TAst.VarDecl(
|
||||||
TAst.Identifier('inner'),
|
TAst.Identifier('inner'),
|
||||||
@@ -154,11 +164,7 @@ begin
|
|||||||
// x = x + 5;
|
// x = x + 5;
|
||||||
TAst.Assign(
|
TAst.Assign(
|
||||||
TAst.Identifier('x'),
|
TAst.Identifier('x'),
|
||||||
TAst.BinaryExpr(
|
TAst.BinaryExpr(TAst.Identifier('x'), TScalar.TBinaryOp.Add, TAst.Constant(5))
|
||||||
TAst.Identifier('x'),
|
|
||||||
boAdd,
|
|
||||||
TAst.Constant(TScalar.FromInteger(5))
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -185,7 +191,8 @@ begin
|
|||||||
|
|
||||||
resultValue := ExecuteAst(mainBlock, FGScope);
|
resultValue := ExecuteAst(mainBlock, FGScope);
|
||||||
|
|
||||||
Assert(TScalar.FromInteger(15) = resultValue.AsScalar, 'The final result should be 15, but is ' + resultValue.AsScalar.ToString);
|
Assert(TScalar.FromInt64(15) = resultValue.AsScalar, 'The final result should be 15, but is ' + resultValue.AsScalar.ToString);
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.ClearButtonClick(Sender: TObject);
|
procedure TForm1.ClearButtonClick(Sender: TObject);
|
||||||
@@ -224,27 +231,31 @@ begin
|
|||||||
[TAst.Identifier('len')],
|
[TAst.Identifier('len')],
|
||||||
TAst.Block(
|
TAst.Block(
|
||||||
[
|
[
|
||||||
TAst.VarDecl(TAst.Identifier('sum'), TAst.Constant(TScalar.FromDouble(0.0))),
|
TAst.VarDecl(TAst.Identifier('sum'), TAst.Constant(0.0)),
|
||||||
TAst.VarDecl(TAst.Identifier('count'), TAst.Constant(TScalar.FromInt64(0))),
|
TAst.VarDecl(TAst.Identifier('count'), TAst.Constant(0)),
|
||||||
TAst.LambdaExpr(
|
TAst.LambdaExpr(
|
||||||
[TAst.Identifier('series'), TAst.Identifier('val')],
|
[TAst.Identifier('series'), TAst.Identifier('val')],
|
||||||
TAst.Block(
|
TAst.Block(
|
||||||
[
|
[
|
||||||
TAst.Assign(
|
TAst.Assign(
|
||||||
TAst.Identifier('sum'),
|
TAst.Identifier('sum'),
|
||||||
TAst.BinaryExpr(TAst.Identifier('sum'), boAdd, TAst.Identifier('val'))
|
TAst.BinaryExpr(TAst.Identifier('sum'), TScalar.TBinaryOp.Add, TAst.Identifier('val'))
|
||||||
),
|
),
|
||||||
TAst.Assign(
|
TAst.Assign(
|
||||||
TAst.Identifier('count'),
|
TAst.Identifier('count'),
|
||||||
TAst.BinaryExpr(TAst.Identifier('count'), boAdd, TAst.Constant(TScalar.FromInt64(1)))
|
TAst.BinaryExpr(TAst.Identifier('count'), TScalar.TBinaryOp.Add, TAst.Constant(1))
|
||||||
),
|
),
|
||||||
TAst.Assign(
|
TAst.Assign(
|
||||||
TAst.Identifier('sum'),
|
TAst.Identifier('sum'),
|
||||||
TAst.TernaryExpr(
|
TAst.TernaryExpr(
|
||||||
TAst.BinaryExpr(TAst.Identifier('count'), boGreater, TAst.Identifier('len')),
|
TAst.BinaryExpr(
|
||||||
|
TAst.Identifier('count'),
|
||||||
|
TScalar.TBinaryOp.Greater,
|
||||||
|
TAst.Identifier('len')
|
||||||
|
),
|
||||||
TAst.BinaryExpr(
|
TAst.BinaryExpr(
|
||||||
TAst.Identifier('sum'),
|
TAst.Identifier('sum'),
|
||||||
boSubtract,
|
TScalar.TBinaryOp.Subtract,
|
||||||
TAst.Indexer(TAst.Identifier('series'), TAst.Identifier('len'))
|
TAst.Indexer(TAst.Identifier('series'), TAst.Identifier('len'))
|
||||||
),
|
),
|
||||||
TAst.Identifier('sum')
|
TAst.Identifier('sum')
|
||||||
@@ -252,9 +263,9 @@ begin
|
|||||||
),
|
),
|
||||||
TAst.BinaryExpr(
|
TAst.BinaryExpr(
|
||||||
TAst.Identifier('sum'),
|
TAst.Identifier('sum'),
|
||||||
boDivide,
|
TScalar.TBinaryOp.Divide,
|
||||||
TAst.TernaryExpr(
|
TAst.TernaryExpr(
|
||||||
TAst.BinaryExpr(TAst.Identifier('count'), boLess, TAst.Identifier('len')),
|
TAst.BinaryExpr(TAst.Identifier('count'), TScalar.TBinaryOp.Less, TAst.Identifier('len')),
|
||||||
TAst.Identifier('count'),
|
TAst.Identifier('count'),
|
||||||
TAst.Identifier('len')
|
TAst.Identifier('len')
|
||||||
)
|
)
|
||||||
@@ -280,7 +291,7 @@ var
|
|||||||
result: TDataValue;
|
result: TDataValue;
|
||||||
sw: TStopwatch;
|
sw: TStopwatch;
|
||||||
begin
|
begin
|
||||||
// This script defines a naive, slow recursive 'fib' function.
|
// 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.
|
// The lambda captures its own name ('fib') from the parent scope to perform recursion.
|
||||||
var fibAst :=
|
var fibAst :=
|
||||||
TAst.Block(
|
TAst.Block(
|
||||||
@@ -293,19 +304,19 @@ begin
|
|||||||
TAst.LambdaExpr(
|
TAst.LambdaExpr(
|
||||||
[TAst.Identifier('n')],
|
[TAst.Identifier('n')],
|
||||||
TAst.TernaryExpr(
|
TAst.TernaryExpr(
|
||||||
TAst.BinaryExpr(TAst.Identifier('n'), boLess, TAst.Constant(TScalar.FromInt64(2))),
|
TAst.BinaryExpr(TAst.Identifier('n'), TScalar.TBinaryOp.Less, TAst.Constant(2)),
|
||||||
TAst.Identifier('n'),
|
TAst.Identifier('n'),
|
||||||
TAst.BinaryExpr(
|
TAst.BinaryExpr(
|
||||||
// Naive recursive call using the variable name 'fib'
|
// Naive recursive call using the variable name 'fib'
|
||||||
TAst.FunctionCall(
|
TAst.FunctionCall(
|
||||||
TAst.Identifier('fib'),
|
TAst.Identifier('fib'),
|
||||||
[TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(1)))]
|
[TAst.BinaryExpr(TAst.Identifier('n'), TScalar.TBinaryOp.Subtract, TAst.Constant(1))]
|
||||||
),
|
),
|
||||||
boAdd,
|
TScalar.TBinaryOp.Add,
|
||||||
// Second naive recursive call
|
// Second naive recursive call
|
||||||
TAst.FunctionCall(
|
TAst.FunctionCall(
|
||||||
TAst.Identifier('fib'),
|
TAst.Identifier('fib'),
|
||||||
[TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(2)))]
|
[TAst.BinaryExpr(TAst.Identifier('n'), TScalar.TBinaryOp.Subtract, TAst.Constant(2))]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -324,9 +335,10 @@ begin
|
|||||||
sw := TStopwatch.StartNew;
|
sw := TStopwatch.StartNew;
|
||||||
|
|
||||||
// The script to execute is just the call to the function.
|
// The script to execute is just the call to the function.
|
||||||
root := TAst.FunctionCall(TAst.Identifier('fib'), [TAst.Constant(TScalar.FromInt64(25))]);
|
root := TAst.FunctionCall(TAst.Identifier('fib'), [TAst.Constant(25)]);
|
||||||
|
|
||||||
FLastAst := root;
|
FLastAst := root;
|
||||||
|
|
||||||
// Execute within the scope where 'fib' is defined.
|
// Execute within the scope where 'fib' is defined.
|
||||||
result := ExecuteAst(root, fibScope);
|
result := ExecuteAst(root, fibScope);
|
||||||
|
|
||||||
@@ -343,7 +355,7 @@ begin
|
|||||||
// Create a memoized version by calling the RTL function on 'fib'
|
// Create a memoized version by calling the RTL function on 'fib'
|
||||||
TAst.Assign(TAst.Identifier('fib'), TAst.FunctionCall(TAst.Identifier('Memoize'), [TAst.Identifier('fib')])),
|
TAst.Assign(TAst.Identifier('fib'), TAst.FunctionCall(TAst.Identifier('Memoize'), [TAst.Identifier('fib')])),
|
||||||
// Call the new, memoized function
|
// Call the new, memoized function
|
||||||
TAst.FunctionCall(TAst.Identifier('fib'), [TAst.Constant(TScalar.FromInt64(25))])
|
TAst.FunctionCall(TAst.Identifier('fib'), [TAst.Constant(25)])
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -352,6 +364,7 @@ begin
|
|||||||
|
|
||||||
sw.Stop;
|
sw.Stop;
|
||||||
Memo1.Lines.Add(Format('Result: fib(30) %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
Memo1.Lines.Add(Format('Result: fib(30) %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.PrettyPrintButtonClick(Sender: TObject);
|
procedure TForm1.PrettyPrintButtonClick(Sender: TObject);
|
||||||
@@ -392,12 +405,12 @@ begin
|
|||||||
TAst.LambdaExpr(
|
TAst.LambdaExpr(
|
||||||
[TAst.Identifier('n'), TAst.Identifier('acc')],
|
[TAst.Identifier('n'), TAst.Identifier('acc')],
|
||||||
TAst.TernaryExpr(
|
TAst.TernaryExpr(
|
||||||
TAst.BinaryExpr(TAst.Identifier('n'), boLessOrEqual, TAst.Constant(TScalar.FromInt64(1))),
|
TAst.BinaryExpr(TAst.Identifier('n'), TScalar.TBinaryOp.LessOrEqual, TAst.Constant(1)),
|
||||||
TAst.Identifier('acc'), // Base case: return the accumulator
|
TAst.Identifier('acc'), // Base case: return the accumulator
|
||||||
TAst.Recur( // Tail-recursive step
|
TAst.Recur( // Tail-recursive step
|
||||||
[
|
[
|
||||||
TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(1))),
|
TAst.BinaryExpr(TAst.Identifier('n'), TScalar.TBinaryOp.Subtract, TAst.Constant(1)),
|
||||||
TAst.BinaryExpr(TAst.Identifier('acc'), boMultiply, TAst.Identifier('n'))
|
TAst.BinaryExpr(TAst.Identifier('acc'), TScalar.TBinaryOp.Multiply, TAst.Identifier('n'))
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -408,11 +421,11 @@ begin
|
|||||||
TAst.Identifier('factorial'),
|
TAst.Identifier('factorial'),
|
||||||
TAst.LambdaExpr(
|
TAst.LambdaExpr(
|
||||||
[TAst.Identifier('n')],
|
[TAst.Identifier('n')],
|
||||||
TAst.FunctionCall(TAst.Identifier('fact_iter'), [TAst.Identifier('n'), TAst.Constant(TScalar.FromInt64(1))])
|
TAst.FunctionCall(TAst.Identifier('fact_iter'), [TAst.Identifier('n'), TAst.Constant(1)])
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
// Call the main function
|
// Call the main function
|
||||||
TAst.FunctionCall(TAst.Identifier('factorial'), [TAst.Constant(TScalar.FromInt64(20))])
|
TAst.FunctionCall(TAst.Identifier('factorial'), [TAst.Constant(20)])
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -422,6 +435,7 @@ begin
|
|||||||
|
|
||||||
sw.Stop;
|
sw.Stop;
|
||||||
Memo1.Lines.Add(Format('Result: %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
Memo1.Lines.Add(Format('Result: %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.SeriesTestButtonClick(Sender: TObject);
|
procedure TForm1.SeriesTestButtonClick(Sender: TObject);
|
||||||
@@ -432,6 +446,7 @@ var
|
|||||||
recordDef: TScalarRecordDefinition;
|
recordDef: TScalarRecordDefinition;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
scope: IExecutionScope;
|
scope: IExecutionScope;
|
||||||
|
values: TArray<TScalar.TValue>;
|
||||||
begin
|
begin
|
||||||
Memo1.Lines.Clear;
|
Memo1.Lines.Clear;
|
||||||
Memo1.Lines.Add('--- Series Test ---');
|
Memo1.Lines.Add('--- Series Test ---');
|
||||||
@@ -439,21 +454,19 @@ begin
|
|||||||
scope := TAst.CreateScope(FGScope);
|
scope := TAst.CreateScope(FGScope);
|
||||||
recordDef := TRttiAstHelper.JsonToRecordDefinition(TRttiAstHelper.RecordDefinitionToJson<TOHLCV>);
|
recordDef := TRttiAstHelper.JsonToRecordDefinition(TRttiAstHelper.RecordDefinitionToJson<TOHLCV>);
|
||||||
series := TScalarRecordSeries.Create(recordDef);
|
series := TScalarRecordSeries.Create(recordDef);
|
||||||
|
SetLength(values, 6);
|
||||||
|
|
||||||
for i := 0 to 4 do
|
for i := 0 to 4 do
|
||||||
begin
|
begin
|
||||||
series.Add(
|
// TScalar can no longer hold TDateTime, convert to Int64 for the test.
|
||||||
TScalarRecord.Create(
|
values[0].AsInt64 := Round((Now + i) * 24 * 60 * 60 * 1000);
|
||||||
recordDef,
|
values[1].AsDouble := 100.0 + i;
|
||||||
[
|
values[2].AsDouble := 105.0 + i;
|
||||||
TScalarValue.FromDateTime(Now + i),
|
values[3].AsDouble := 98.0 + i;
|
||||||
TScalarValue.FromDouble(100.0 + i),
|
values[4].AsDouble := 102.0 + i;
|
||||||
TScalarValue.FromDouble(105.0 + i),
|
values[5].AsInt64 := 10000 * (i + 1);
|
||||||
TScalarValue.FromDouble(98.0 + i),
|
|
||||||
TScalarValue.FromDouble(102.0 + i),
|
series.Add(TScalarRecord.Create(recordDef, values));
|
||||||
TScalarValue.FromInt64(10000 * (i + 1))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
end;
|
end;
|
||||||
scope.Define('ohlcvSeries', TDataValue.FromRecordSeries(series));
|
scope.Define('ohlcvSeries', TDataValue.FromRecordSeries(series));
|
||||||
|
|
||||||
@@ -466,7 +479,7 @@ begin
|
|||||||
TAst.Identifier('closeColumn'),
|
TAst.Identifier('closeColumn'),
|
||||||
TAst.MemberAccess(TAst.Identifier('ohlcvSeries'), TAst.Identifier('Close'))
|
TAst.MemberAccess(TAst.Identifier('ohlcvSeries'), TAst.Identifier('Close'))
|
||||||
),
|
),
|
||||||
TAst.Indexer(TAst.Identifier('closeColumn'), TAst.Constant(TScalar.FromInt64(1)))
|
TAst.Indexer(TAst.Identifier('closeColumn'), TAst.Constant(1))
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -476,6 +489,7 @@ begin
|
|||||||
resultValue := ExecuteAst(callAst, scope);
|
resultValue := ExecuteAst(callAst, scope);
|
||||||
|
|
||||||
Memo1.Lines.Add(Format('Result of script: %s', [resultValue.ToString]));
|
Memo1.Lines.Add(Format('Result of script: %s', [resultValue.ToString]));
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.Test1ButtonClick(Sender: TObject);
|
procedure TForm1.Test1ButtonClick(Sender: TObject);
|
||||||
@@ -493,12 +507,9 @@ begin
|
|||||||
[],
|
[],
|
||||||
TAst.Block(
|
TAst.Block(
|
||||||
[
|
[
|
||||||
TAst.VarDecl(TAst.Identifier('a'), TAst.Constant(TScalar.FromInt64(10))),
|
TAst.VarDecl(TAst.Identifier('a'), TAst.Constant(10)),
|
||||||
TAst.VarDecl(
|
TAst.VarDecl(TAst.Identifier('b'), TAst.BinaryExpr(TAst.Identifier('a'), TScalar.TBinaryOp.Multiply, TAst.Constant(2))),
|
||||||
TAst.Identifier('b'),
|
TAst.BinaryExpr(TAst.Identifier('a'), TScalar.TBinaryOp.Add, TAst.Identifier('b'))
|
||||||
TAst.BinaryExpr(TAst.Identifier('a'), boMultiply, TAst.Constant(TScalar.FromInt64(2)))
|
|
||||||
),
|
|
||||||
TAst.BinaryExpr(TAst.Identifier('a'), boAdd, TAst.Identifier('b'))
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -509,6 +520,7 @@ begin
|
|||||||
|
|
||||||
sw.Stop;
|
sw.Stop;
|
||||||
Memo1.Lines.Add(Format('Result: %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
Memo1.Lines.Add(Format('Result: %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.Test2ButtonClick(Sender: TObject);
|
procedure TForm1.Test2ButtonClick(Sender: TObject);
|
||||||
@@ -530,14 +542,14 @@ begin
|
|||||||
[TAst.Identifier('offset')],
|
[TAst.Identifier('offset')],
|
||||||
TAst.Block(
|
TAst.Block(
|
||||||
[
|
[
|
||||||
TAst.VarDecl(TAst.Identifier('baseValue'), TAst.Constant(TScalar.FromInt64(100))),
|
TAst.VarDecl(TAst.Identifier('baseValue'), TAst.Constant(100)),
|
||||||
TAst.BinaryExpr(TAst.Identifier('baseValue'), boAdd, TAst.Identifier('offset'))
|
TAst.BinaryExpr(TAst.Identifier('baseValue'), TScalar.TBinaryOp.Add, TAst.Identifier('offset'))
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
TAst.FunctionCall(TAst.Identifier('createStrategyInstance'), [TAst.Constant(TScalar.FromInt64(20))]),
|
TAst.FunctionCall(TAst.Identifier('createStrategyInstance'), [TAst.Constant(20)]),
|
||||||
TAst.FunctionCall(TAst.Identifier('createStrategyInstance'), [TAst.Constant(TScalar.FromInt64(55))])
|
TAst.FunctionCall(TAst.Identifier('createStrategyInstance'), [TAst.Constant(55)])
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -546,6 +558,7 @@ begin
|
|||||||
|
|
||||||
sw.Stop;
|
sw.Stop;
|
||||||
Memo1.Lines.Add(Format('Result of the final expression: %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
Memo1.Lines.Add(Format('Result of the final expression: %s (calculated in %d ms)', [result.ToString, sw.ElapsedMilliseconds]));
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.WorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
|
procedure TForm1.WorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
|
||||||
@@ -553,15 +566,7 @@ begin
|
|||||||
if Button <> TMouseButton.mbMiddle then
|
if Button <> TMouseButton.mbMiddle then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
if FLastAst <> nil then
|
ShowVizualization(X, Y);
|
||||||
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);
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.OHLCButtonClick(Sender: TObject);
|
procedure TForm1.OHLCButtonClick(Sender: TObject);
|
||||||
@@ -572,6 +577,8 @@ const
|
|||||||
smaFastLength = 10;
|
smaFastLength = 10;
|
||||||
var
|
var
|
||||||
scope: IExecutionScope;
|
scope: IExecutionScope;
|
||||||
|
values: TArray<TScalar.TValue>;
|
||||||
|
recordValue: TScalarRecord;
|
||||||
begin
|
begin
|
||||||
Memo1.Lines.Clear;
|
Memo1.Lines.Clear;
|
||||||
Memo1.Lines.Add(Format('--- Simulating O(1) SMA Crossover Strategy for %d ticks ---', [numRecs]));
|
Memo1.Lines.Add(Format('--- Simulating O(1) SMA Crossover Strategy for %d ticks ---', [numRecs]));
|
||||||
@@ -579,14 +586,8 @@ begin
|
|||||||
var setupAst :=
|
var setupAst :=
|
||||||
TAst.Block(
|
TAst.Block(
|
||||||
[
|
[
|
||||||
TAst.VarDecl(
|
TAst.VarDecl(TAst.Identifier('smaFast'), TAst.FunctionCall(TAst.Identifier('CreateSMA'), [TAst.Constant(smaFastLength)])),
|
||||||
TAst.Identifier('smaFast'),
|
TAst.VarDecl(TAst.Identifier('smaSlow'), TAst.FunctionCall(TAst.Identifier('CreateSMA'), [TAst.Constant(smaSlowLength)])),
|
||||||
TAst.FunctionCall(TAst.Identifier('CreateSMA'), [TAst.Constant(TScalar.FromInt64(smaFastLength))])
|
|
||||||
),
|
|
||||||
TAst.VarDecl(
|
|
||||||
TAst.Identifier('smaSlow'),
|
|
||||||
TAst.FunctionCall(TAst.Identifier('CreateSMA'), [TAst.Constant(TScalar.FromInt64(smaSlowLength))])
|
|
||||||
),
|
|
||||||
TAst.VarDecl(
|
TAst.VarDecl(
|
||||||
TAst.Identifier('maCrossStrategy'),
|
TAst.Identifier('maCrossStrategy'),
|
||||||
TAst.LambdaExpr(
|
TAst.LambdaExpr(
|
||||||
@@ -599,7 +600,7 @@ begin
|
|||||||
),
|
),
|
||||||
TAst.VarDecl(
|
TAst.VarDecl(
|
||||||
TAst.Identifier('currentClose'),
|
TAst.Identifier('currentClose'),
|
||||||
TAst.Indexer(TAst.Identifier('closeSeries'), TAst.Constant(TScalar.FromInt64(0)))
|
TAst.Indexer(TAst.Identifier('closeSeries'), TAst.Constant(0))
|
||||||
),
|
),
|
||||||
TAst.VarDecl(
|
TAst.VarDecl(
|
||||||
TAst.Identifier('valSmaFast'),
|
TAst.Identifier('valSmaFast'),
|
||||||
@@ -616,9 +617,13 @@ begin
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
TAst.TernaryExpr(
|
TAst.TernaryExpr(
|
||||||
TAst.BinaryExpr(TAst.Identifier('valSmaFast'), boGreater, TAst.Identifier('valSmaSlow')),
|
TAst.BinaryExpr(
|
||||||
TAst.Constant(TScalar.FromInt64(1)),
|
TAst.Identifier('valSmaFast'),
|
||||||
TAst.Constant(TScalar.FromInt64(-1))
|
TScalar.TBinaryOp.Greater,
|
||||||
|
TAst.Identifier('valSmaSlow')
|
||||||
|
),
|
||||||
|
TAst.Constant(1),
|
||||||
|
TAst.Constant(-1)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -658,7 +663,6 @@ begin
|
|||||||
var ohlcvRec: TOHLCV;
|
var ohlcvRec: TOHLCV;
|
||||||
for var i := 1 to numRecs do
|
for var i := 1 to numRecs do
|
||||||
begin
|
begin
|
||||||
// ... (simulation logic is unchanged) ...
|
|
||||||
ohlcvRec.Timestamp := nw + (i / (24 * 60));
|
ohlcvRec.Timestamp := nw + (i / (24 * 60));
|
||||||
ohlcvRec.Open := lastClose + (Random * 0.05);
|
ohlcvRec.Open := lastClose + (Random * 0.05);
|
||||||
ohlcvRec.Close := lastClose + (Random - 0.49) * 2;
|
ohlcvRec.Close := lastClose + (Random - 0.49) * 2;
|
||||||
@@ -666,18 +670,16 @@ begin
|
|||||||
ohlcvRec.Low := Min(ohlcvRec.Open, ohlcvRec.Close) - Random;
|
ohlcvRec.Low := Min(ohlcvRec.Open, ohlcvRec.Close) - Random;
|
||||||
ohlcvRec.Volume := RandomRange(1000, 50000);
|
ohlcvRec.Volume := RandomRange(1000, 50000);
|
||||||
lastClose := ohlcvRec.Close;
|
lastClose := ohlcvRec.Close;
|
||||||
var recordValue :=
|
|
||||||
TScalarRecord.Create(
|
SetLength(values, 6);
|
||||||
recDef,
|
values[0].AsDouble := ohlcvRec.Timestamp;
|
||||||
[
|
values[1].AsDouble := ohlcvRec.Open;
|
||||||
TScalarValue.FromDateTime(ohlcvRec.Timestamp),
|
values[2].AsDouble := ohlcvRec.High;
|
||||||
TScalarValue.FromDouble(ohlcvRec.Open),
|
values[3].AsDouble := ohlcvRec.Low;
|
||||||
TScalarValue.FromDouble(ohlcvRec.High),
|
values[4].AsDouble := ohlcvRec.Close;
|
||||||
TScalarValue.FromDouble(ohlcvRec.Low),
|
values[5].AsInt64 := ohlcvRec.Volume;
|
||||||
TScalarValue.FromDouble(ohlcvRec.Close),
|
recordValue := TScalarRecord.Create(recDef, values);
|
||||||
TScalarValue.FromInt64(ohlcvRec.Volume)
|
|
||||||
]
|
|
||||||
);
|
|
||||||
series.AsRecordSeries.Add(recordValue, lookback);
|
series.AsRecordSeries.Add(recordValue, lookback);
|
||||||
|
|
||||||
if series.AsRecordSeries.TotalCount >= smaSlowLength then
|
if series.AsRecordSeries.TotalCount >= smaSlowLength then
|
||||||
@@ -695,10 +697,54 @@ begin
|
|||||||
sw.Stop;
|
sw.Stop;
|
||||||
Memo1.Lines.Add('--- Simulation Finished ---');
|
Memo1.Lines.Add('--- Simulation Finished ---');
|
||||||
Memo1.Lines.Add(Format('Total time: %d ms', [sw.ElapsedMilliseconds]));
|
Memo1.Lines.Add(Format('Total time: %d ms', [sw.ElapsedMilliseconds]));
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TForm1.TailCallButtenClick(Sender: TObject);
|
||||||
|
const
|
||||||
|
// A large number to prove TCO prevents stack overflow
|
||||||
|
RecursionDepth = 1000000;
|
||||||
var
|
var
|
||||||
TriggerScope: IExecutionScope;
|
root: IAstNode;
|
||||||
|
result: TDataValue;
|
||||||
|
sw: TStopwatch;
|
||||||
|
begin
|
||||||
|
Memo1.Lines.Clear;
|
||||||
|
Memo1.Lines.Add(Format('--- Testing TCO with recursion depth of %d ---', [RecursionDepth]));
|
||||||
|
Application.ProcessMessages;
|
||||||
|
sw := TStopwatch.StartNew;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
Memo1.Lines.Add(Format('Result: %s', [result.ToString]));
|
||||||
|
Memo1.Lines.Add(Format('Execution finished in %d ms without stack overflow.', [sw.ElapsedMilliseconds]));
|
||||||
|
UpdateScript;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TForm1.CreateTriggerExampleButtonClick(Sender: TObject);
|
procedure TForm1.CreateTriggerExampleButtonClick(Sender: TObject);
|
||||||
var
|
var
|
||||||
@@ -709,27 +755,31 @@ begin
|
|||||||
blk :=
|
blk :=
|
||||||
TAst.Block(
|
TAst.Block(
|
||||||
[
|
[
|
||||||
TAst.VarDecl(TAst.Identifier('X'), TAst.Constant(TScalar.FromInt64(0))),
|
TAst.VarDecl(TAst.Identifier('X'), TAst.Constant(0)),
|
||||||
TAst.VarDecl(
|
TAst.VarDecl(
|
||||||
TAst.Identifier('tickHandler'),
|
TAst.Identifier('tickHandler'),
|
||||||
TAst.LambdaExpr(
|
TAst.LambdaExpr(
|
||||||
[TAst.Identifier('summand')],
|
[TAst.Identifier('summand')],
|
||||||
TAst.Assign(TAst.Identifier('X'), TAst.BinaryExpr(TAst.Identifier('X'), boAdd, TAst.Identifier('summand')))
|
TAst.Assign(
|
||||||
|
TAst.Identifier('X'),
|
||||||
|
TAst.BinaryExpr(TAst.Identifier('X'), TScalar.TBinaryOp.Add, TAst.Identifier('summand'))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
TriggerScope := TAstBinder.Bind(blk, FGScope).CreateScope(FGScope);
|
FTriggerScope := TAstBinder.Bind(blk, FGScope).CreateScope(FGScope);
|
||||||
|
|
||||||
// This case is simple enough to just inline the logic from ExecuteAst
|
// This case is simple enough to just inline the logic from ExecuteAst
|
||||||
var visitor := CreateVisitor(TriggerScope);
|
var visitor := CreateVisitor(FTriggerScope);
|
||||||
visitor.Execute(blk);
|
visitor.Execute(blk);
|
||||||
|
|
||||||
FLastAst := blk;
|
FLastAst := blk;
|
||||||
|
|
||||||
Memo1.Lines.Add('Variable "X" and function "tickHandler" defined in persistent scope.');
|
Memo1.Lines.Add('Variable "X" and function "tickHandler" defined in persistent scope.');
|
||||||
Memo1.Lines.Add('Click "Do Trigger" to execute.');
|
Memo1.Lines.Add('Click "Do Trigger" to execute.');
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TForm1.CreateVisitor(Scope: IExecutionScope): IAstVisitor;
|
function TForm1.CreateVisitor(Scope: IExecutionScope): IAstVisitor;
|
||||||
@@ -744,24 +794,26 @@ procedure TForm1.DoTriggerButtonClick(Sender: TObject);
|
|||||||
var
|
var
|
||||||
callAst: IFunctionCallNode;
|
callAst: IFunctionCallNode;
|
||||||
begin
|
begin
|
||||||
callAst := TAst.FunctionCall(TAst.Identifier('tickHandler'), [TAst.Constant(TScalar.FromInt64(1))]);
|
callAst := TAst.FunctionCall(TAst.Identifier('tickHandler'), [TAst.Constant(1)]);
|
||||||
FLastAst := callAst;
|
FLastAst := callAst;
|
||||||
|
|
||||||
var X := ExecuteAst(callAst, TriggerScope);
|
var X := ExecuteAst(callAst, FTriggerScope);
|
||||||
|
|
||||||
Memo1.Lines.Add(Format('Tick(1)! New value of X: %s', [X.ToString]));
|
Memo1.Lines.Add(Format('Tick(1)! New value of X: %s', [X.ToString]));
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.DoTrigger2ButtonClick(Sender: TObject);
|
procedure TForm1.DoTrigger2ButtonClick(Sender: TObject);
|
||||||
var
|
var
|
||||||
callAst: IFunctionCallNode;
|
callAst: IFunctionCallNode;
|
||||||
begin
|
begin
|
||||||
callAst := TAst.FunctionCall(TAst.Identifier('tickHandler'), [TAst.Constant(TScalar.FromInt64(2))]);
|
callAst := TAst.FunctionCall(TAst.Identifier('tickHandler'), [TAst.Constant(2)]);
|
||||||
FLastAst := callAst;
|
FLastAst := callAst;
|
||||||
|
|
||||||
var X := ExecuteAst(callAst, TriggerScope);
|
var X := ExecuteAst(callAst, FTriggerScope);
|
||||||
|
|
||||||
Memo1.Lines.Add(Format('Tick(2)! New value of X: %s', [X.ToString]));
|
Memo1.Lines.Add(Format('Tick(2)! New value of X: %s', [X.ToString]));
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.DumpButtonClick(Sender: TObject);
|
procedure TForm1.DumpButtonClick(Sender: TObject);
|
||||||
@@ -804,12 +856,12 @@ begin
|
|||||||
end
|
end
|
||||||
);
|
);
|
||||||
|
|
||||||
callAst :=
|
callAst := TAst.FunctionCall(TAst.Identifier('delphiAdd'), [TAst.Constant(100), TAst.Constant(123)]);
|
||||||
TAst.FunctionCall(TAst.Identifier('delphiAdd'), [TAst.Constant(TScalar.FromInt64(100)), TAst.Constant(TScalar.FromInt64(23))]);
|
|
||||||
|
|
||||||
FLastAst := callAst;
|
FLastAst := callAst;
|
||||||
resultValue := ExecuteAst(callAst, scope);
|
resultValue := ExecuteAst(callAst, scope);
|
||||||
Memo1.Lines.Add(Format('Result from delphiAdd(100, 23): %s', [resultValue.ToString]));
|
Memo1.Lines.Add(Format('Result from delphiAdd(100, 123): %s', [resultValue.ToString]));
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.FailingUpvalueButtonClick(Sender: TObject);
|
procedure TForm1.FailingUpvalueButtonClick(Sender: TObject);
|
||||||
@@ -821,7 +873,7 @@ begin
|
|||||||
TAst.Block(
|
TAst.Block(
|
||||||
[
|
[
|
||||||
// 1. Define the shared variable.
|
// 1. Define the shared variable.
|
||||||
TAst.VarDecl(TAst.Identifier('a'), TAst.Constant(TScalar.FromInt64(10))),
|
TAst.VarDecl(TAst.Identifier('a'), TAst.Constant(10)),
|
||||||
// 2. Define a function that returns a closure that MODIFIES 'a'.
|
// 2. Define a function that returns a closure that MODIFIES 'a'.
|
||||||
// This creates the multi-level capture scenario.
|
// This creates the multi-level capture scenario.
|
||||||
TAst.VarDecl(
|
TAst.VarDecl(
|
||||||
@@ -832,7 +884,7 @@ begin
|
|||||||
[], // Inner closure that is returned
|
[], // Inner closure that is returned
|
||||||
TAst.Assign(
|
TAst.Assign(
|
||||||
TAst.Identifier('a'),
|
TAst.Identifier('a'),
|
||||||
TAst.BinaryExpr(TAst.Identifier('a'), boAdd, TAst.Constant(TScalar.FromInt64(5)))
|
TAst.BinaryExpr(TAst.Identifier('a'), TScalar.TBinaryOp.Add, TAst.Constant(5))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -866,8 +918,14 @@ begin
|
|||||||
Memo1.Lines.Add(Format('FAILURE: Expected 15, but got %s.', [resultValue.ToString]));
|
Memo1.Lines.Add(Format('FAILURE: Expected 15, but got %s.', [resultValue.ToString]));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Assert(TScalar.FromInteger(15) = resultValue.AsScalar, 'The final result should be 15.');
|
Assert(TScalar.FromInt64(15) = resultValue.AsScalar, 'The final result should be 15.');
|
||||||
Memo1.Lines.Add('Please check the new dump.');
|
Memo1.Lines.Add('Please check the new dump.');
|
||||||
|
UpdateScript;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TForm1.FlowOnlyBoxChange(Sender: TObject);
|
||||||
|
begin
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.FromJSONButtonClick(Sender: TObject);
|
procedure TForm1.FromJSONButtonClick(Sender: TObject);
|
||||||
@@ -902,51 +960,20 @@ begin
|
|||||||
finally
|
finally
|
||||||
Memo1.Lines.EndUpdate;
|
Memo1.Lines.EndUpdate;
|
||||||
end;
|
end;
|
||||||
|
UpdateScript;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.TailCallButtenClick(Sender: TObject);
|
procedure TForm1.ScriptMemoChange(Sender: TObject);
|
||||||
const
|
|
||||||
// A large number to prove TCO prevents stack overflow
|
|
||||||
RecursionDepth = 1000000;
|
|
||||||
var
|
|
||||||
root: IAstNode;
|
|
||||||
result: TDataValue;
|
|
||||||
sw: TStopwatch;
|
|
||||||
begin
|
begin
|
||||||
Memo1.Lines.Clear;
|
if FScriptUpdate then
|
||||||
Memo1.Lines.Add(Format('--- Testing TCO with recursion depth of %d ---', [RecursionDepth]));
|
exit;
|
||||||
Application.ProcessMessages;
|
|
||||||
sw := TStopwatch.StartNew;
|
|
||||||
|
|
||||||
root :=
|
try
|
||||||
TAst.Block(
|
FLastAst := TAstScript.Parse(ScriptMemo.Lines.Text)
|
||||||
[
|
except
|
||||||
// var countDown = lambda(n) { ... };
|
on E: Exception do
|
||||||
TAst.VarDecl(
|
Memo1.Lines.Add(E.Message);
|
||||||
TAst.Identifier('countDown'),
|
end;
|
||||||
TAst.LambdaExpr(
|
|
||||||
[TAst.Identifier('n')],
|
|
||||||
// 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, 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'))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
// Initial call to start the recursion
|
|
||||||
TAst.FunctionCall(TAst.Identifier('countDown'), [TAst.Constant(TScalar.FromInt64(RecursionDepth))])
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
FLastAst := root;
|
|
||||||
result := ExecuteAst(root, FGScope);
|
|
||||||
sw.Stop;
|
|
||||||
|
|
||||||
Memo1.Lines.Add(Format('Result: %s', [result.ToString]));
|
|
||||||
Memo1.Lines.Add(Format('Execution finished in %d ms without stack overflow.', [sw.ElapsedMilliseconds]));
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.ToJSONButtonClick(Sender: TObject);
|
procedure TForm1.ToJSONButtonClick(Sender: TObject);
|
||||||
@@ -973,4 +1000,35 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TForm1.UpdateScript;
|
||||||
|
begin
|
||||||
|
try
|
||||||
|
FScriptUpdate := true;
|
||||||
|
try
|
||||||
|
ScriptMemo.Lines.Text := TAstScript.Print(FLastAst);
|
||||||
|
finally
|
||||||
|
FScriptUpdate := false;
|
||||||
|
end;
|
||||||
|
except
|
||||||
|
on E: Exception do
|
||||||
|
ScriptMemo.Lines.Add(E.Message);
|
||||||
|
end;
|
||||||
|
|
||||||
|
FWorkspace.DeleteChildren;
|
||||||
|
ShowVizualization(14, 14);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TForm1.ShowVizualization(X, Y: Single);
|
||||||
|
begin
|
||||||
|
if FLastAst <> 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);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -152,10 +152,8 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
case AValue.AsScalar.Kind of
|
case AValue.AsScalar.Kind of
|
||||||
skInteger: Result := (AValue.AsScalar.Value.AsInteger <> 0);
|
TScalar.TKind.Ordinal: Result := (AValue.AsScalar.Value.AsInt64 <> 0);
|
||||||
skInt64: Result := (AValue.AsScalar.Value.AsInt64 <> 0);
|
TScalar.TKind.Float: Result := (AValue.AsScalar.Value.AsDouble <> 0.0);
|
||||||
skUInt64: Result := (AValue.AsScalar.Value.AsUInt64 <> 0);
|
|
||||||
skBoolean: Result := (AValue.AsScalar.Value.AsBoolean);
|
|
||||||
else
|
else
|
||||||
Result := False;
|
Result := False;
|
||||||
end;
|
end;
|
||||||
@@ -297,13 +295,10 @@ begin
|
|||||||
if Assigned(Node.Lookback) then
|
if Assigned(Node.Lookback) then
|
||||||
begin
|
begin
|
||||||
lookbackValue := Node.Lookback.Accept(Self);
|
lookbackValue := Node.Lookback.Accept(Self);
|
||||||
if (lookbackValue.Kind <> vkScalar) or not (lookbackValue.AsScalar.Kind in [skInteger, skInt64]) then
|
if (lookbackValue.Kind <> vkScalar) or (lookbackValue.AsScalar.Kind <> TScalar.TKind.Ordinal) then
|
||||||
raise EArgumentException.Create('Lookback parameter must be an integer.');
|
raise EArgumentException.Create('Lookback parameter must be an integer.');
|
||||||
|
|
||||||
if lookbackValue.AsScalar.Kind = skInteger then
|
lookback := lookbackValue.AsScalar.Value.AsInt64;
|
||||||
lookback := lookbackValue.AsScalar.Value.AsInteger
|
|
||||||
else
|
|
||||||
lookback := lookbackValue.AsScalar.Value.AsInt64;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
case seriesVar.Kind of
|
case seriesVar.Kind of
|
||||||
@@ -365,21 +360,14 @@ function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
|
|||||||
var
|
var
|
||||||
baseValue, indexValue: TDataValue;
|
baseValue, indexValue: TDataValue;
|
||||||
index: Int64;
|
index: Int64;
|
||||||
indexScalar: TScalar;
|
|
||||||
begin
|
begin
|
||||||
baseValue := Node.Base.Accept(Self);
|
baseValue := Node.Base.Accept(Self);
|
||||||
indexValue := Node.Index.Accept(Self);
|
indexValue := Node.Index.Accept(Self);
|
||||||
|
|
||||||
if (indexValue.Kind <> vkScalar) then
|
if (indexValue.Kind <> vkScalar) or (indexValue.AsScalar.Kind <> TScalar.TKind.Ordinal) then
|
||||||
raise EArgumentException.Create('Indexer `[]` requires a scalar integer argument.');
|
raise EArgumentException.Create('Indexer `[]` requires an integer argument.');
|
||||||
|
|
||||||
indexScalar := indexValue.AsScalar;
|
index := indexValue.AsScalar.Value.AsInt64;
|
||||||
case indexScalar.Kind of
|
|
||||||
skInteger: index := indexScalar.Value.AsInteger;
|
|
||||||
skInt64: index := indexScalar.Value.AsInt64;
|
|
||||||
else
|
|
||||||
raise EArgumentException.Create('Indexer `[]` requires an integer type argument.');
|
|
||||||
end;
|
|
||||||
|
|
||||||
case baseValue.Kind of
|
case baseValue.Kind of
|
||||||
vkSeries:
|
vkSeries:
|
||||||
|
|||||||
+53
-37
@@ -21,7 +21,7 @@ uses
|
|||||||
System.Generics.Collections,
|
System.Generics.Collections,
|
||||||
Myc.Ast,
|
Myc.Ast,
|
||||||
Myc.Data.Scalar,
|
Myc.Data.Scalar,
|
||||||
Myc.Data.Value; // Added for TDataValue
|
Myc.Data.Value;
|
||||||
|
|
||||||
type
|
type
|
||||||
// TJsonAstConverter implements the visitor pattern for serialization
|
// TJsonAstConverter implements the visitor pattern for serialization
|
||||||
@@ -29,10 +29,10 @@ type
|
|||||||
TJsonAstConverter = class(TInterfacedObject, IAstVisitor)
|
TJsonAstConverter = class(TInterfacedObject, IAstVisitor)
|
||||||
private
|
private
|
||||||
FJsonObjectStack: TStack<TJSONObject>;
|
FJsonObjectStack: TStack<TJSONObject>;
|
||||||
procedure ScalarToJson(const AScalar: TScalar; const AParent: TJSONObject; const AName: string);
|
procedure DataValueToJson(const AValue: TDataValue; const AParent: TJSONObject; const AName: string);
|
||||||
function JsonToNode(const AJson: TJSONValue): IAstNode;
|
function JsonToNode(const AJson: TJSONValue): IAstNode;
|
||||||
function JsonToScalar(const AObj: TJSONObject; const AName: string): TScalar;
|
function JsonToDataValue(const AObj: TJSONObject; const AName: string): TDataValue;
|
||||||
// Updated signatures to return specific node interface types
|
|
||||||
function JsonToConstantNode(const AObj: TJSONObject): IConstantNode;
|
function JsonToConstantNode(const AObj: TJSONObject): IConstantNode;
|
||||||
function JsonToIdentifierNode(const AObj: TJSONObject): IIdentifierNode;
|
function JsonToIdentifierNode(const AObj: TJSONObject): IIdentifierNode;
|
||||||
function JsonToBinaryExprNode(const AObj: TJSONObject): IBinaryExpressionNode;
|
function JsonToBinaryExprNode(const AObj: TJSONObject): IBinaryExpressionNode;
|
||||||
@@ -137,23 +137,31 @@ end;
|
|||||||
|
|
||||||
{ TJsonAstConverter - IAstVisitor for Serialization }
|
{ TJsonAstConverter - IAstVisitor for Serialization }
|
||||||
|
|
||||||
procedure TJsonAstConverter.ScalarToJson(const AScalar: TScalar; const AParent: TJSONObject; const AName: string);
|
procedure TJsonAstConverter.DataValueToJson(const AValue: TDataValue; const AParent: TJSONObject; const AName: string);
|
||||||
var
|
var
|
||||||
scalarObj: TJSONObject;
|
valObj, scalarObj: TJSONObject;
|
||||||
begin
|
begin
|
||||||
scalarObj := TJSONObject.Create;
|
valObj := TJSONObject.Create;
|
||||||
scalarObj.AddPair('Kind', TJSONString.Create(AScalar.Kind.ToString));
|
valObj.AddPair('Kind', TJSONString.Create(AValue.Kind.ToString));
|
||||||
|
|
||||||
case AScalar.Kind of
|
case AValue.Kind of
|
||||||
skInt64: scalarObj.AddPair('Value', TJSONNumber.Create(AScalar.Value.AsInt64));
|
vkScalar:
|
||||||
skDouble: scalarObj.AddPair('Value', TJSONNumber.Create(AScalar.Value.AsDouble));
|
begin
|
||||||
skBoolean: scalarObj.AddPair('Value', TJSONBool.Create(AScalar.Value.AsBoolean));
|
scalarObj := TJSONObject.Create;
|
||||||
skString: scalarObj.AddPair('Value', TJSONString.Create(AScalar.Value.AsString));
|
scalarObj.AddPair('Kind', TJSONString.Create(AValue.AsScalar.Kind.ToString));
|
||||||
|
case AValue.AsScalar.Kind of
|
||||||
|
TScalar.TKind.Ordinal: scalarObj.AddPair('Value', TJSONNumber.Create(AValue.AsScalar.Value.AsInt64));
|
||||||
|
TScalar.TKind.Float: scalarObj.AddPair('Value', TJSONNumber.Create(AValue.AsScalar.Value.AsDouble));
|
||||||
|
end;
|
||||||
|
valObj.AddPair('Value', scalarObj);
|
||||||
|
end;
|
||||||
|
vkText: valObj.AddPair('Value', TJSONString.Create(AValue.AsText));
|
||||||
|
vkVoid:; // No value to add
|
||||||
else
|
else
|
||||||
raise ENotSupportedException.CreateFmt('Scalar kind %s not supported for JSON serialization.', [AScalar.Kind.ToString]);
|
raise ENotSupportedException.Create('Unsupported TDataValue kind for constant serialization.');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
AParent.AddPair(AName, scalarObj);
|
AParent.AddPair(AName, valObj);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TJsonAstConverter.VisitConstant(const Node: IConstantNode): TDataValue;
|
function TJsonAstConverter.VisitConstant(const Node: IConstantNode): TDataValue;
|
||||||
@@ -162,7 +170,7 @@ var
|
|||||||
begin
|
begin
|
||||||
obj := TJSONObject.Create;
|
obj := TJSONObject.Create;
|
||||||
obj.AddPair('NodeType', TJSONString.Create('Constant'));
|
obj.AddPair('NodeType', TJSONString.Create('Constant'));
|
||||||
ScalarToJson(Node.Value, obj, 'Value');
|
DataValueToJson(Node.Value, obj, 'Value');
|
||||||
FJsonObjectStack.Push(obj);
|
FJsonObjectStack.Push(obj);
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
@@ -509,32 +517,40 @@ end;
|
|||||||
|
|
||||||
{ TJsonAstConverter - Deserialization }
|
{ TJsonAstConverter - Deserialization }
|
||||||
|
|
||||||
function TJsonAstConverter.JsonToScalar(const AObj: TJSONObject; const AName: string): TScalar;
|
function TJsonAstConverter.JsonToDataValue(const AObj: TJSONObject; const AName: string): TDataValue;
|
||||||
var
|
var
|
||||||
scalarObj: TJSONObject;
|
valObj, scalarObj: TJSONObject;
|
||||||
kindStr: string;
|
kindStr: string;
|
||||||
scalarKind: TScalarKind;
|
scalarKind: TScalar.TKind;
|
||||||
jsonValue: TJSONValue;
|
|
||||||
begin
|
begin
|
||||||
scalarObj := AObj.GetValue<TJSONObject>(AName);
|
valObj := AObj.GetValue<TJSONObject>(AName);
|
||||||
kindStr := scalarObj.GetValue<string>('Kind');
|
kindStr := valObj.GetValue<string>('Kind');
|
||||||
scalarKind := TScalar.StringToKind(kindStr);
|
|
||||||
|
|
||||||
jsonValue := scalarObj.GetValue('Value');
|
if SameText(kindStr, 'Scalar') then
|
||||||
|
begin
|
||||||
case scalarKind of
|
scalarObj := valObj.GetValue<TJSONObject>('Value');
|
||||||
skInt64: Result := TScalar.FromInt64((jsonValue as TJSONNumber).AsInt64);
|
kindStr := scalarObj.GetValue<string>('Kind');
|
||||||
skDouble: Result := TScalar.FromDouble((jsonValue as TJSONNumber).AsDouble);
|
scalarKind := TScalar.StringToKind(kindStr);
|
||||||
skBoolean: Result := TScalar.FromBoolean((jsonValue as TJSONBool).AsBoolean);
|
case scalarKind of
|
||||||
skString: Result := TScalar.FromString((jsonValue as TJSONString).Value);
|
TScalar.TKind.Ordinal: Result := TScalar.FromInt64(scalarObj.GetValue<TJSONNumber>('Value').AsInt64);
|
||||||
|
TScalar.TKind.Float: Result := TScalar.FromDouble(scalarObj.GetValue<TJSONNumber>('Value').AsDouble);
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if SameText(kindStr, 'Text') then
|
||||||
|
begin
|
||||||
|
Result := valObj.GetValue<string>('Value');
|
||||||
|
end
|
||||||
|
else if SameText(kindStr, 'Void') then
|
||||||
|
begin
|
||||||
|
Result := TDataValue.Void;
|
||||||
|
end
|
||||||
else
|
else
|
||||||
raise ENotSupportedException.CreateFmt('Scalar kind %s not supported for JSON deserialization.', [kindStr]);
|
raise ENotSupportedException.Create('Unsupported TDataValue kind for constant deserialization.');
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TJsonAstConverter.JsonToConstantNode(const AObj: TJSONObject): IConstantNode;
|
function TJsonAstConverter.JsonToConstantNode(const AObj: TJSONObject): IConstantNode;
|
||||||
begin
|
begin
|
||||||
Result := TAst.Constant(JsonToScalar(AObj, 'Value'));
|
Result := TAst.Constant(JsonToDataValue(AObj, 'Value'));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TJsonAstConverter.JsonToIdentifierNode(const AObj: TJSONObject): IIdentifierNode;
|
function TJsonAstConverter.JsonToIdentifierNode(const AObj: TJSONObject): IIdentifierNode;
|
||||||
@@ -545,11 +561,11 @@ end;
|
|||||||
function TJsonAstConverter.JsonToBinaryExprNode(const AObj: TJSONObject): IBinaryExpressionNode;
|
function TJsonAstConverter.JsonToBinaryExprNode(const AObj: TJSONObject): IBinaryExpressionNode;
|
||||||
var
|
var
|
||||||
opStr: string;
|
opStr: string;
|
||||||
op: TBinaryOperator;
|
op: TScalar.TBinaryOp;
|
||||||
leftNode, rightNode: IAstNode;
|
leftNode, rightNode: IAstNode;
|
||||||
begin
|
begin
|
||||||
opStr := AObj.GetValue<string>('Operator');
|
opStr := AObj.GetValue<string>('Operator');
|
||||||
for op := Low(TBinaryOperator) to High(TBinaryOperator) do
|
for op := Low(TScalar.TBinaryOp) to High(TScalar.TBinaryOp) do
|
||||||
if SameText(op.ToString, opStr) then
|
if SameText(op.ToString, opStr) then
|
||||||
begin
|
begin
|
||||||
leftNode := JsonToNode(AObj.GetValue('Left'));
|
leftNode := JsonToNode(AObj.GetValue('Left'));
|
||||||
@@ -563,11 +579,11 @@ end;
|
|||||||
function TJsonAstConverter.JsonToUnaryExprNode(const AObj: TJSONObject): IUnaryExpressionNode;
|
function TJsonAstConverter.JsonToUnaryExprNode(const AObj: TJSONObject): IUnaryExpressionNode;
|
||||||
var
|
var
|
||||||
opStr: string;
|
opStr: string;
|
||||||
op: TUnaryOperator;
|
op: TScalar.TUnaryOp;
|
||||||
rightNode: IAstNode;
|
rightNode: IAstNode;
|
||||||
begin
|
begin
|
||||||
opStr := AObj.GetValue<string>('Operator');
|
opStr := AObj.GetValue<string>('Operator');
|
||||||
for op := Low(TUnaryOperator) to High(TUnaryOperator) do
|
for op := Low(TScalar.TUnaryOp) to High(TScalar.TUnaryOp) do
|
||||||
if SameText(op.ToString, opStr) then
|
if SameText(op.ToString, opStr) then
|
||||||
begin
|
begin
|
||||||
rightNode := JsonToNode(AObj.GetValue('Right'));
|
rightNode := JsonToNode(AObj.GetValue('Right'));
|
||||||
|
|||||||
@@ -112,10 +112,11 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
IConstantNode = interface(IAstNode)
|
IConstantNode = interface(IAstNode)
|
||||||
|
// Represents a constant value in the AST (Scalar, Text, or Void).
|
||||||
{$region 'private'}
|
{$region 'private'}
|
||||||
function GetValue: TScalar;
|
function GetValue: TDataValue;
|
||||||
{$endregion}
|
{$endregion}
|
||||||
property Value: TScalar read GetValue;
|
property Value: TDataValue read GetValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
IIdentifierNode = interface(IAstNode)
|
IIdentifierNode = interface(IAstNode)
|
||||||
@@ -130,20 +131,20 @@ type
|
|||||||
IBinaryExpressionNode = interface(IAstNode)
|
IBinaryExpressionNode = interface(IAstNode)
|
||||||
{$region 'private'}
|
{$region 'private'}
|
||||||
function GetLeft: IAstNode;
|
function GetLeft: IAstNode;
|
||||||
function GetOperator: TBinaryOperator;
|
function GetOperator: TScalar.TBinaryOp;
|
||||||
function GetRight: IAstNode;
|
function GetRight: IAstNode;
|
||||||
{$endregion}
|
{$endregion}
|
||||||
property Left: IAstNode read GetLeft;
|
property Left: IAstNode read GetLeft;
|
||||||
property Operator: TBinaryOperator read GetOperator;
|
property Operator: TScalar.TBinaryOp read GetOperator;
|
||||||
property Right: IAstNode read GetRight;
|
property Right: IAstNode read GetRight;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
IUnaryExpressionNode = interface(IAstNode)
|
IUnaryExpressionNode = interface(IAstNode)
|
||||||
{$region 'private'}
|
{$region 'private'}
|
||||||
function GetOperator: TUnaryOperator;
|
function GetOperator: TScalar.TUnaryOp;
|
||||||
function GetRight: IAstNode;
|
function GetRight: IAstNode;
|
||||||
{$endregion}
|
{$endregion}
|
||||||
property Operator: TUnaryOperator read GetOperator;
|
property Operator: TScalar.TUnaryOp read GetOperator;
|
||||||
property Right: IAstNode read GetRight;
|
property Right: IAstNode read GetRight;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+100
-101
@@ -11,15 +11,17 @@ uses
|
|||||||
Myc.Ast;
|
Myc.Ast;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
// A visitor that converts an AST into a LISP-like (Clojure-style) string representation.
|
||||||
TPrettyPrintVisitor = class(TInterfacedObject, IAstVisitor)
|
TPrettyPrintVisitor = class(TInterfacedObject, IAstVisitor)
|
||||||
private
|
private
|
||||||
FBuilder: TStringBuilder;
|
FBuilder: TStringBuilder;
|
||||||
FIndentLevel: Integer;
|
FIndentLevel: Integer;
|
||||||
procedure Indent;
|
procedure Indent;
|
||||||
procedure Unindent;
|
procedure Unindent;
|
||||||
procedure AppendLine(const S: string);
|
procedure Append(const S: string);
|
||||||
|
procedure NewLine;
|
||||||
public
|
public
|
||||||
constructor Create(AIndentLevel: Integer = 0);
|
constructor Create;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
function GetResult: string;
|
function GetResult: string;
|
||||||
// IAstVisitor
|
// IAstVisitor
|
||||||
@@ -46,17 +48,15 @@ type
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Myc.Data.Scalar,
|
Myc.Data.Scalar;
|
||||||
Myc.Data.Decimal;
|
|
||||||
|
|
||||||
{ TPrettyPrintVisitor }
|
{ TPrettyPrintVisitor }
|
||||||
|
|
||||||
constructor TPrettyPrintVisitor.Create(AIndentLevel: Integer);
|
constructor TPrettyPrintVisitor.Create;
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FBuilder := TStringBuilder.Create;
|
FBuilder := TStringBuilder.Create;
|
||||||
FIndentLevel := 0;
|
FIndentLevel := 0;
|
||||||
FIndentLevel := AIndentLevel;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TPrettyPrintVisitor.Destroy;
|
destructor TPrettyPrintVisitor.Destroy;
|
||||||
@@ -72,119 +72,126 @@ end;
|
|||||||
|
|
||||||
procedure TPrettyPrintVisitor.Indent;
|
procedure TPrettyPrintVisitor.Indent;
|
||||||
begin
|
begin
|
||||||
inc(FIndentLevel, 4);
|
inc(FIndentLevel, 2);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TPrettyPrintVisitor.Unindent;
|
procedure TPrettyPrintVisitor.Unindent;
|
||||||
begin
|
begin
|
||||||
dec(FIndentLevel, 4);
|
dec(FIndentLevel, 2);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TPrettyPrintVisitor.AppendLine(const S: string);
|
procedure TPrettyPrintVisitor.Append(const S: string);
|
||||||
begin
|
begin
|
||||||
|
FBuilder.Append(S);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPrettyPrintVisitor.NewLine;
|
||||||
|
begin
|
||||||
|
FBuilder.AppendLine;
|
||||||
FBuilder.Append(''.PadLeft(FIndentLevel));
|
FBuilder.Append(''.PadLeft(FIndentLevel));
|
||||||
FBuilder.AppendLine(S);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.Execute(const RootNode: IAstNode): TDataValue;
|
function TPrettyPrintVisitor.Execute(const RootNode: IAstNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
Result := RootNode.Accept(Self);
|
if Assigned(RootNode) then
|
||||||
|
RootNode.Accept(Self);
|
||||||
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
|
function TPrettyPrintVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
|
||||||
|
var
|
||||||
|
val: TDataValue;
|
||||||
begin
|
begin
|
||||||
AppendLine(Format('Constant (%s)', [Node.Value.ToString]));
|
val := Node.Value;
|
||||||
|
if val.Kind = vkText then
|
||||||
|
Append('"' + val.AsText + '"')
|
||||||
|
else
|
||||||
|
Append(val.ToString);
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
function TPrettyPrintVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
AppendLine(Format('Identifier (%s)', [Node.Name]));
|
Append(Node.Name);
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
function TPrettyPrintVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
AppendLine(Format('BinaryExpr "%s"', [Node.Operator.ToString]));
|
Append('(' + Node.Operator.ToString);
|
||||||
Indent;
|
Append(' ');
|
||||||
Node.Left.Accept(Self);
|
Node.Left.Accept(Self);
|
||||||
|
Append(' ');
|
||||||
Node.Right.Accept(Self);
|
Node.Right.Accept(Self);
|
||||||
Unindent;
|
Append(')');
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
|
function TPrettyPrintVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
AppendLine(Format('UnaryExpr "%s"', [Node.Operator.ToString]));
|
Append('(' + Node.Operator.ToString);
|
||||||
Indent;
|
Append(' ');
|
||||||
Node.Right.Accept(Self);
|
Node.Right.Accept(Self);
|
||||||
Unindent;
|
Append(')');
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
function TPrettyPrintVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
AppendLine('IfExpr');
|
Append('(if ');
|
||||||
Indent;
|
|
||||||
AppendLine('Condition:');
|
|
||||||
Indent;
|
|
||||||
Node.Condition.Accept(Self);
|
Node.Condition.Accept(Self);
|
||||||
Unindent;
|
|
||||||
AppendLine('Then:');
|
|
||||||
Indent;
|
Indent;
|
||||||
|
NewLine;
|
||||||
Node.ThenBranch.Accept(Self);
|
Node.ThenBranch.Accept(Self);
|
||||||
Unindent;
|
if Assigned(Node.ElseBranch) then
|
||||||
if Node.ElseBranch <> nil then
|
|
||||||
begin
|
begin
|
||||||
AppendLine('Else:');
|
NewLine;
|
||||||
Indent;
|
|
||||||
Node.ElseBranch.Accept(Self);
|
Node.ElseBranch.Accept(Self);
|
||||||
Unindent;
|
|
||||||
end;
|
end;
|
||||||
Unindent;
|
Unindent;
|
||||||
|
NewLine;
|
||||||
|
Append(')');
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
function TPrettyPrintVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
AppendLine('TernaryExpr');
|
Append('(if ');
|
||||||
Indent;
|
|
||||||
AppendLine('Condition:');
|
|
||||||
Indent;
|
|
||||||
Node.Condition.Accept(Self);
|
Node.Condition.Accept(Self);
|
||||||
Unindent;
|
|
||||||
AppendLine('Then:');
|
|
||||||
Indent;
|
Indent;
|
||||||
|
NewLine;
|
||||||
Node.ThenBranch.Accept(Self);
|
Node.ThenBranch.Accept(Self);
|
||||||
Unindent;
|
NewLine;
|
||||||
AppendLine('Else:');
|
|
||||||
Indent;
|
|
||||||
Node.ElseBranch.Accept(Self);
|
Node.ElseBranch.Accept(Self);
|
||||||
Unindent;
|
Unindent;
|
||||||
Unindent;
|
NewLine;
|
||||||
|
Append(')');
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
function TPrettyPrintVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||||
var
|
var
|
||||||
param: IIdentifierNode;
|
param: IIdentifierNode;
|
||||||
paramNames: TStringList;
|
sb: TStringBuilder;
|
||||||
begin
|
begin
|
||||||
paramNames := TStringList.Create;
|
sb := TStringBuilder.Create;
|
||||||
try
|
try
|
||||||
for param in Node.Parameters do
|
for param in Node.Parameters do
|
||||||
paramNames.Add(param.Name);
|
sb.Append(param.Name + ' ');
|
||||||
AppendLine(Format('Lambda (params: %s)', [paramNames.CommaText]));
|
if sb.Length > 0 then
|
||||||
|
sb.Remove(sb.Length - 1, 1);
|
||||||
|
|
||||||
|
Append('(fn [' + sb.ToString + ']');
|
||||||
finally
|
finally
|
||||||
paramNames.Free;
|
sb.Free;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Indent;
|
Indent;
|
||||||
AppendLine('Body:');
|
NewLine;
|
||||||
Indent;
|
|
||||||
Node.Body.Accept(Self);
|
Node.Body.Accept(Self);
|
||||||
Unindent;
|
Unindent;
|
||||||
Unindent;
|
NewLine;
|
||||||
|
Append(')');
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -192,18 +199,21 @@ function TPrettyPrintVisitor.VisitFunctionCall(const Node: IFunctionCallNode): T
|
|||||||
var
|
var
|
||||||
arg: IAstNode;
|
arg: IAstNode;
|
||||||
begin
|
begin
|
||||||
AppendLine('FunctionCall');
|
Append('(');
|
||||||
Indent;
|
|
||||||
AppendLine('Callee:');
|
|
||||||
Indent;
|
|
||||||
Node.Callee.Accept(Self);
|
Node.Callee.Accept(Self);
|
||||||
Unindent;
|
|
||||||
AppendLine('Arguments:');
|
|
||||||
Indent;
|
Indent;
|
||||||
for arg in Node.Arguments do
|
for arg in Node.Arguments do
|
||||||
|
begin
|
||||||
|
NewLine;
|
||||||
arg.Accept(Self);
|
arg.Accept(Self);
|
||||||
|
end;
|
||||||
Unindent;
|
Unindent;
|
||||||
Unindent;
|
|
||||||
|
if Length(Node.Arguments) > 0 then
|
||||||
|
NewLine;
|
||||||
|
|
||||||
|
Append(')');
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -211,14 +221,17 @@ function TPrettyPrintVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue;
|
|||||||
var
|
var
|
||||||
arg: IAstNode;
|
arg: IAstNode;
|
||||||
begin
|
begin
|
||||||
AppendLine('Recur');
|
Append('(recur');
|
||||||
Indent;
|
|
||||||
AppendLine('Arguments:');
|
|
||||||
Indent;
|
Indent;
|
||||||
for arg in Node.Arguments do
|
for arg in Node.Arguments do
|
||||||
|
begin
|
||||||
|
NewLine;
|
||||||
arg.Accept(Self);
|
arg.Accept(Self);
|
||||||
|
end;
|
||||||
Unindent;
|
Unindent;
|
||||||
Unindent;
|
if Length(Node.Arguments) > 0 then
|
||||||
|
NewLine;
|
||||||
|
Append(')');
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -226,102 +239,88 @@ function TPrettyPrintVisitor.VisitBlockExpression(const Node: IBlockExpressionNo
|
|||||||
var
|
var
|
||||||
expr: IAstNode;
|
expr: IAstNode;
|
||||||
begin
|
begin
|
||||||
AppendLine('Block');
|
Append('(do');
|
||||||
Indent;
|
Indent;
|
||||||
for expr in Node.Expressions do
|
for expr in Node.Expressions do
|
||||||
|
begin
|
||||||
|
NewLine;
|
||||||
expr.Accept(Self);
|
expr.Accept(Self);
|
||||||
|
end;
|
||||||
Unindent;
|
Unindent;
|
||||||
|
NewLine;
|
||||||
|
Append(')');
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
function TPrettyPrintVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
AppendLine(Format('VarDecl (%s)', [Node.Identifier.Name]));
|
Append('(def ');
|
||||||
|
Node.Identifier.Accept(Self);
|
||||||
if Assigned(Node.Initializer) then
|
if Assigned(Node.Initializer) then
|
||||||
begin
|
begin
|
||||||
Indent;
|
Append(' ');
|
||||||
Node.Initializer.Accept(Self);
|
Node.Initializer.Accept(Self);
|
||||||
Unindent;
|
|
||||||
end;
|
end;
|
||||||
|
Append(')');
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
function TPrettyPrintVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
// Print the assignment node.
|
Append('(assign ');
|
||||||
AppendLine(Format('Assignment (%s)', [Node.Identifier.Name]));
|
Node.Identifier.Accept(Self);
|
||||||
Indent;
|
Append(' ');
|
||||||
Node.Value.Accept(Self);
|
Node.Value.Accept(Self);
|
||||||
Unindent;
|
Append(')');
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
|
function TPrettyPrintVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
AppendLine('Indexer');
|
Append('(get ');
|
||||||
Indent;
|
|
||||||
AppendLine('Base:');
|
|
||||||
Indent;
|
|
||||||
Node.Base.Accept(Self);
|
Node.Base.Accept(Self);
|
||||||
Unindent;
|
Append(' ');
|
||||||
AppendLine('Index:');
|
|
||||||
Indent;
|
|
||||||
Node.Index.Accept(Self);
|
Node.Index.Accept(Self);
|
||||||
Unindent;
|
Append(')');
|
||||||
Unindent;
|
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
|
function TPrettyPrintVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
AppendLine(Format('MemberAccess (Member: %s)', [Node.Member.Name]));
|
Append('(.');
|
||||||
Indent;
|
Node.Member.Accept(Self);
|
||||||
AppendLine('Base:');
|
Append(' ');
|
||||||
Indent;
|
|
||||||
Node.Base.Accept(Self);
|
Node.Base.Accept(Self);
|
||||||
Unindent;
|
Append(')');
|
||||||
Unindent;
|
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
function TPrettyPrintVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
AppendLine('CreateSeries');
|
Append(Format('(new-series "%s")', [Node.Definition]));
|
||||||
Indent;
|
|
||||||
AppendLine('Definition: ' + Node.Definition);
|
|
||||||
Unindent;
|
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
function TPrettyPrintVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
AppendLine('AddSeriesItem');
|
Append('(add-item ');
|
||||||
Indent;
|
|
||||||
AppendLine('Series:');
|
|
||||||
Indent;
|
|
||||||
Node.Series.Accept(Self);
|
Node.Series.Accept(Self);
|
||||||
Unindent;
|
Append(' ');
|
||||||
AppendLine('Value:');
|
|
||||||
Indent;
|
|
||||||
Node.Value.Accept(Self);
|
Node.Value.Accept(Self);
|
||||||
Unindent;
|
|
||||||
if Assigned(Node.Lookback) then
|
if Assigned(Node.Lookback) then
|
||||||
begin
|
begin
|
||||||
AppendLine('Lookback:');
|
Append(' ');
|
||||||
Indent;
|
|
||||||
Node.Lookback.Accept(Self);
|
Node.Lookback.Accept(Self);
|
||||||
Unindent;
|
|
||||||
end;
|
end;
|
||||||
Unindent;
|
Append(')');
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPrettyPrintVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
function TPrettyPrintVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
AppendLine('SeriesLength');
|
Append('(count ');
|
||||||
Indent;
|
|
||||||
Node.Series.Accept(Self);
|
Node.Series.Accept(Self);
|
||||||
Unindent;
|
Append(')');
|
||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
@@ -60,13 +60,9 @@ uses
|
|||||||
class function TRtlFunctions.Abs(const Arg: TScalar): TScalar;
|
class function TRtlFunctions.Abs(const Arg: TScalar): TScalar;
|
||||||
begin
|
begin
|
||||||
case Arg.Kind of
|
case Arg.Kind of
|
||||||
skInteger: Result := TScalar.FromInteger(System.Abs(Arg.Value.AsInteger));
|
TScalar.TKind.Ordinal: Result := TScalar.FromInt64(System.Abs(Arg.Value.AsInt64));
|
||||||
skInt64: Result := TScalar.FromInt64(System.Abs(Arg.Value.AsInt64));
|
TScalar.TKind.Float: Result := TScalar.FromDouble(System.Abs(Arg.Value.AsDouble));
|
||||||
skSingle: Result := TScalar.FromSingle(System.Abs(Arg.Value.AsSingle));
|
|
||||||
skDouble: Result := TScalar.FromDouble(System.Abs(Arg.Value.AsDouble));
|
|
||||||
skDecimal: Result := TScalar.FromDecimal(Arg.Value.AsDecimal.Abs);
|
|
||||||
else
|
else
|
||||||
// This case should not be reached if the wrapper validation is correct.
|
|
||||||
raise EArgumentException.Create('Abs requires a numeric argument.');
|
raise EArgumentException.Create('Abs requires a numeric argument.');
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@@ -74,10 +70,8 @@ end;
|
|||||||
class function TRtlFunctions.Trunc(const Arg: TScalar): TScalar;
|
class function TRtlFunctions.Trunc(const Arg: TScalar): TScalar;
|
||||||
begin
|
begin
|
||||||
case Arg.Kind of
|
case Arg.Kind of
|
||||||
skInteger, skInt64: Result := Arg; // Trunc on an integer is a no-op
|
TScalar.TKind.Ordinal: Result := Arg; // Trunc on an integer is a no-op
|
||||||
skSingle: Result := TScalar.FromInt64(System.Trunc(Arg.Value.AsSingle));
|
TScalar.TKind.Float: Result := TScalar.FromInt64(System.Trunc(Arg.Value.AsDouble));
|
||||||
skDouble: Result := TScalar.FromInt64(System.Trunc(Arg.Value.AsDouble));
|
|
||||||
skDecimal: Result := TScalar.FromInt64(System.Trunc(Arg.Value.AsDecimal));
|
|
||||||
else
|
else
|
||||||
raise EArgumentException.Create('Trunc requires a numeric argument.');
|
raise EArgumentException.Create('Trunc requires a numeric argument.');
|
||||||
end;
|
end;
|
||||||
@@ -86,10 +80,8 @@ end;
|
|||||||
class function TRtlFunctions.Ceil(const Arg: TScalar): TScalar;
|
class function TRtlFunctions.Ceil(const Arg: TScalar): TScalar;
|
||||||
begin
|
begin
|
||||||
case Arg.Kind of
|
case Arg.Kind of
|
||||||
skInteger, skInt64: Result := Arg; // Ceil on an integer is a no-op
|
TScalar.TKind.Ordinal: Result := Arg; // Ceil on an integer is a no-op
|
||||||
skSingle: Result := TScalar.FromInt64(System.Math.Ceil(Arg.Value.AsSingle));
|
TScalar.TKind.Float: Result := TScalar.FromInt64(System.Math.Ceil(Arg.Value.AsDouble));
|
||||||
skDouble: Result := TScalar.FromInt64(System.Math.Ceil(Arg.Value.AsDouble));
|
|
||||||
skDecimal: Result := TScalar.FromInt64(System.Math.Ceil(Double(Arg.Value.AsDecimal)));
|
|
||||||
else
|
else
|
||||||
raise EArgumentException.Create('Ceil requires a numeric argument.');
|
raise EArgumentException.Create('Ceil requires a numeric argument.');
|
||||||
end;
|
end;
|
||||||
@@ -98,10 +90,8 @@ end;
|
|||||||
class function TRtlFunctions.Floor(const Arg: TScalar): TScalar;
|
class function TRtlFunctions.Floor(const Arg: TScalar): TScalar;
|
||||||
begin
|
begin
|
||||||
case Arg.Kind of
|
case Arg.Kind of
|
||||||
skInteger, skInt64: Result := Arg; // Floor on an integer is a no-op
|
TScalar.TKind.Ordinal: Result := Arg; // Floor on an integer is a no-op
|
||||||
skSingle: Result := TScalar.FromInt64(System.Math.Floor(Arg.Value.AsSingle));
|
TScalar.TKind.Float: Result := TScalar.FromInt64(System.Math.Floor(Arg.Value.AsDouble));
|
||||||
skDouble: Result := TScalar.FromInt64(System.Math.Floor(Arg.Value.AsDouble));
|
|
||||||
skDecimal: Result := TScalar.FromInt64(System.Math.Floor(Double(Arg.Value.AsDecimal)));
|
|
||||||
else
|
else
|
||||||
raise EArgumentException.Create('Floor requires a numeric argument.');
|
raise EArgumentException.Create('Floor requires a numeric argument.');
|
||||||
end;
|
end;
|
||||||
@@ -110,11 +100,8 @@ end;
|
|||||||
class function TRtlFunctions.Sign(const Arg: TScalar): TScalar;
|
class function TRtlFunctions.Sign(const Arg: TScalar): TScalar;
|
||||||
begin
|
begin
|
||||||
case Arg.Kind of
|
case Arg.Kind of
|
||||||
skInteger: Result := TScalar.FromInteger(System.Math.Sign(Arg.Value.AsInteger));
|
TScalar.TKind.Ordinal: Result := TScalar.FromInt64(System.Math.Sign(Arg.Value.AsInt64));
|
||||||
skInt64: Result := TScalar.FromInteger(System.Math.Sign(Arg.Value.AsInt64));
|
TScalar.TKind.Float: Result := TScalar.FromInt64(System.Math.Sign(Arg.Value.AsDouble));
|
||||||
skSingle: Result := TScalar.FromInteger(System.Math.Sign(Arg.Value.AsSingle));
|
|
||||||
skDouble: Result := TScalar.FromInteger(System.Math.Sign(Arg.Value.AsDouble));
|
|
||||||
skDecimal: Result := TScalar.FromInteger(Arg.Value.AsDecimal.Sign);
|
|
||||||
else
|
else
|
||||||
raise EArgumentException.Create('Sign requires a numeric argument.');
|
raise EArgumentException.Create('Sign requires a numeric argument.');
|
||||||
end;
|
end;
|
||||||
@@ -145,13 +132,10 @@ begin
|
|||||||
raise EArgumentException.Create('This memoized function can only be called with a single scalar argument.');
|
raise EArgumentException.Create('This memoized function can only be called with a single scalar argument.');
|
||||||
|
|
||||||
argScalar := AArgs[0].AsScalar;
|
argScalar := AArgs[0].AsScalar;
|
||||||
if not (argScalar.Kind in [skInteger, skInt64]) then
|
if argScalar.Kind <> TScalar.TKind.Ordinal then
|
||||||
raise EArgumentException.Create('This memoized function expects an integer argument for caching.');
|
raise EArgumentException.Create('This memoized function expects an ordinal argument for caching.');
|
||||||
|
|
||||||
if argScalar.Kind = skInteger then
|
key := argScalar.Value.AsInt64;
|
||||||
key := argScalar.Value.AsInteger
|
|
||||||
else
|
|
||||||
key := argScalar.Value.AsInt64;
|
|
||||||
|
|
||||||
var cache := cCache.AsObject as TDictionary<Int64, TDataValue>;
|
var cache := cCache.AsObject as TDictionary<Int64, TDataValue>;
|
||||||
|
|
||||||
@@ -248,7 +232,9 @@ begin
|
|||||||
begin
|
begin
|
||||||
item := sourceSeries.Items[i];
|
item := sourceSeries.Items[i];
|
||||||
predicateResult := predicateFunc([TDataValue(item)]);
|
predicateResult := predicateFunc([TDataValue(item)]);
|
||||||
if (predicateResult.Kind = vkScalar) and (predicateResult.AsScalar.Value.AsBoolean) then
|
if (predicateResult.Kind = vkScalar)
|
||||||
|
and (predicateResult.AsScalar.Kind = TScalar.TKind.Ordinal)
|
||||||
|
and (predicateResult.AsScalar.Value.AsInt64 <> 0) then
|
||||||
matchingIndices.Add(i);
|
matchingIndices.Add(i);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -262,7 +248,7 @@ begin
|
|||||||
var
|
var
|
||||||
idx: Integer;
|
idx: Integer;
|
||||||
begin
|
begin
|
||||||
idx := AArgs[0].AsScalar.Value.AsInteger;
|
idx := AArgs[0].AsScalar.Value.AsInt64;
|
||||||
Result := TDataValue(sourceSeries.Items[idx]);
|
Result := TDataValue(sourceSeries.Items[idx]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -297,14 +283,16 @@ begin
|
|||||||
item := sourceSeries.Items[i];
|
item := sourceSeries.Items[i];
|
||||||
predicateResult := predicateFunc([TDataValue(item)]);
|
predicateResult := predicateFunc([TDataValue(item)]);
|
||||||
|
|
||||||
if (predicateResult.Kind = vkScalar) and (predicateResult.AsScalar.Value.AsBoolean) then
|
if (predicateResult.Kind = vkScalar)
|
||||||
|
and (predicateResult.AsScalar.Kind = TScalar.TKind.Ordinal)
|
||||||
|
and (predicateResult.AsScalar.Value.AsInt64 <> 0) then
|
||||||
begin
|
begin
|
||||||
Result := TScalar.FromBoolean(True);
|
Result := TScalar.FromInt64(1);
|
||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Result := TScalar.FromBoolean(False);
|
Result := TScalar.FromInt64(0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -69,9 +69,8 @@ begin
|
|||||||
raise EArgumentException.CreateFmt('%s requires a scalar argument.', [AName]);
|
raise EArgumentException.CreateFmt('%s requires a scalar argument.', [AName]);
|
||||||
|
|
||||||
AArg := AArgs[0].AsScalar;
|
AArg := AArgs[0].AsScalar;
|
||||||
if not (AArg.Kind in [skInteger, skInt64, skSingle, skDouble, skDecimal]) then
|
// The check for specific numeric kinds is no longer necessary,
|
||||||
raise EArgumentException.CreateFmt('%s requires a numeric argument, but got %s.', [AName, AArg.Kind.ToString]);
|
// as TScalar can only be Ordinal or Float.
|
||||||
|
|
||||||
Result := True;
|
Result := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,699 @@
|
|||||||
|
unit Myc.Ast.Script;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
System.SysUtils,
|
||||||
|
Myc.Ast.Nodes;
|
||||||
|
|
||||||
|
type
|
||||||
|
// Provides a high-level facade for parsing and printing the AST.
|
||||||
|
TAstScript = record
|
||||||
|
public
|
||||||
|
class function Parse(const ASource: string): IAstNode; static;
|
||||||
|
class function Print(const ANode: IAstNode): string; static;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses
|
||||||
|
System.Classes,
|
||||||
|
System.Generics.Collections,
|
||||||
|
System.Character,
|
||||||
|
System.Math,
|
||||||
|
Myc.Data.Scalar,
|
||||||
|
Myc.Data.Value,
|
||||||
|
Myc.Ast;
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
// --- Internal Parser Implementation ---
|
||||||
|
|
||||||
|
TTokenKind = (
|
||||||
|
tkLeftParen, // (
|
||||||
|
tkRightParen, // )
|
||||||
|
tkLeftBracket, // [
|
||||||
|
tkRightBracket, // ]
|
||||||
|
tkIdentifier,
|
||||||
|
tkNumber,
|
||||||
|
tkString,
|
||||||
|
tkEOF,
|
||||||
|
tkError
|
||||||
|
);
|
||||||
|
|
||||||
|
TToken = record
|
||||||
|
Kind: TTokenKind;
|
||||||
|
Text: string;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TLexer = class
|
||||||
|
private
|
||||||
|
FSource: string;
|
||||||
|
FCurrentPos: Integer;
|
||||||
|
function Peek: Char;
|
||||||
|
procedure Advance;
|
||||||
|
function ReadNumber: string;
|
||||||
|
function ReadString: string;
|
||||||
|
function ReadIdentifier: string;
|
||||||
|
public
|
||||||
|
constructor Create(const ASource: string);
|
||||||
|
function GetNextToken: TToken;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TExpr = record
|
||||||
|
Token: TToken;
|
||||||
|
Node: IAstNode;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TParser = class
|
||||||
|
private
|
||||||
|
FLexer: TLexer;
|
||||||
|
FCurrentToken: TToken;
|
||||||
|
procedure Consume(AExpectedKind: TTokenKind);
|
||||||
|
procedure NextToken;
|
||||||
|
function ParseList: IAstNode;
|
||||||
|
function ParseParameterList: TArray<IIdentifierNode>;
|
||||||
|
function ParseExpression: TExpr;
|
||||||
|
public
|
||||||
|
constructor Create(const ASource: string);
|
||||||
|
destructor Destroy; override;
|
||||||
|
function Parse: IAstNode;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// --- Internal Printer Implementation ---
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
{ TLexer }
|
||||||
|
|
||||||
|
constructor TLexer.Create(const ASource: string);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FSource := ASource;
|
||||||
|
FCurrentPos := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TLexer.Peek: Char;
|
||||||
|
begin
|
||||||
|
if FCurrentPos > Length(FSource) then
|
||||||
|
Result := #0
|
||||||
|
else
|
||||||
|
Result := FSource[FCurrentPos];
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TLexer.Advance;
|
||||||
|
begin
|
||||||
|
inc(FCurrentPos);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TLexer.ReadIdentifier: string;
|
||||||
|
var
|
||||||
|
startPos: Integer;
|
||||||
|
begin
|
||||||
|
startPos := FCurrentPos;
|
||||||
|
// Corrected W1050: Use CharInSet for robust unicode support.
|
||||||
|
while (Peek <> #0) and (not (Peek.IsWhiteSpace or CharInSet(Peek, ['(', ')', '[', ']']))) do
|
||||||
|
Advance;
|
||||||
|
Result := Copy(FSource, startPos, FCurrentPos - startPos);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TLexer.ReadNumber: string;
|
||||||
|
var
|
||||||
|
startPos: Integer;
|
||||||
|
begin
|
||||||
|
startPos := FCurrentPos;
|
||||||
|
while (Peek <> #0) and (Peek.IsDigit or (Peek = '.')) do
|
||||||
|
Advance;
|
||||||
|
Result := Copy(FSource, startPos, FCurrentPos - startPos);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TLexer.ReadString: string;
|
||||||
|
var
|
||||||
|
startPos: Integer;
|
||||||
|
begin
|
||||||
|
Advance; // Skip opening "
|
||||||
|
startPos := FCurrentPos;
|
||||||
|
while (Peek <> #0) and (Peek <> '"') do
|
||||||
|
Advance;
|
||||||
|
Result := Copy(FSource, startPos, FCurrentPos - startPos);
|
||||||
|
if Peek = '"' then
|
||||||
|
Advance; // Skip closing "
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TLexer.GetNextToken: TToken;
|
||||||
|
begin
|
||||||
|
while (FCurrentPos <= Length(FSource)) and FSource[FCurrentPos].IsWhiteSpace do
|
||||||
|
Advance;
|
||||||
|
|
||||||
|
if FCurrentPos > Length(FSource) then
|
||||||
|
begin
|
||||||
|
Result.Kind := tkEOF;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var c := Peek;
|
||||||
|
case c of
|
||||||
|
'(':
|
||||||
|
begin
|
||||||
|
Result.Kind := tkLeftParen;
|
||||||
|
Advance;
|
||||||
|
end;
|
||||||
|
')':
|
||||||
|
begin
|
||||||
|
Result.Kind := tkRightParen;
|
||||||
|
Advance;
|
||||||
|
end;
|
||||||
|
'[':
|
||||||
|
begin
|
||||||
|
Result.Kind := tkLeftBracket;
|
||||||
|
Advance;
|
||||||
|
end;
|
||||||
|
']':
|
||||||
|
begin
|
||||||
|
Result.Kind := tkRightBracket;
|
||||||
|
Advance;
|
||||||
|
end;
|
||||||
|
'"':
|
||||||
|
begin
|
||||||
|
Result.Kind := tkString;
|
||||||
|
Result.Text := ReadString;
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
if c.IsDigit or ((c = '-') and (FCurrentPos < Length(FSource)) and FSource[FCurrentPos + 1].IsDigit) then
|
||||||
|
begin
|
||||||
|
Result.Kind := tkNumber;
|
||||||
|
Result.Text := ReadNumber;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
Result.Kind := tkIdentifier;
|
||||||
|
Result.Text := ReadIdentifier;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TParser }
|
||||||
|
|
||||||
|
constructor TParser.Create(const ASource: string);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FLexer := TLexer.Create(ASource);
|
||||||
|
NextToken; // Load the first token
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TParser.Destroy;
|
||||||
|
begin
|
||||||
|
FLexer.Free;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TParser.NextToken;
|
||||||
|
begin
|
||||||
|
FCurrentToken := FLexer.GetNextToken;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TParser.Consume(AExpectedKind: TTokenKind);
|
||||||
|
begin
|
||||||
|
if FCurrentToken.Kind <> AExpectedKind then
|
||||||
|
raise Exception.CreateFmt('Syntax Error: Expected token %d, but found %d', [Ord(AExpectedKind), Ord(FCurrentToken.Kind)]);
|
||||||
|
NextToken;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TParser.ParseParameterList: TArray<IIdentifierNode>;
|
||||||
|
var
|
||||||
|
params: TList<IIdentifierNode>;
|
||||||
|
begin
|
||||||
|
Consume(tkLeftBracket);
|
||||||
|
params := TList<IIdentifierNode>.Create;
|
||||||
|
try
|
||||||
|
while FCurrentToken.Kind <> tkRightBracket do
|
||||||
|
begin
|
||||||
|
if FCurrentToken.Kind <> tkIdentifier then
|
||||||
|
raise Exception.Create('Syntax Error: Expected identifier in parameter list.');
|
||||||
|
params.Add(TAst.Identifier(FCurrentToken.Text));
|
||||||
|
NextToken;
|
||||||
|
end;
|
||||||
|
Result := params.ToArray;
|
||||||
|
finally
|
||||||
|
params.Free;
|
||||||
|
end;
|
||||||
|
Consume(tkRightBracket);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TParser.ParseList: IAstNode;
|
||||||
|
|
||||||
|
function IfThen(cond: Boolean; const TrueBranch, FalseBranch: IAstNode): IAstNode;
|
||||||
|
begin
|
||||||
|
if cond then
|
||||||
|
exit(TrueBranch)
|
||||||
|
else
|
||||||
|
exit(FalseBranch);
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
elements: TList<TExpr>;
|
||||||
|
head: TExpr;
|
||||||
|
headIdent: IIdentifierNode;
|
||||||
|
tailTokens: TArray<TToken>;
|
||||||
|
tailNodes: TArray<IAstNode>;
|
||||||
|
begin
|
||||||
|
Consume(tkLeftParen);
|
||||||
|
if FCurrentToken.Kind = tkRightParen then
|
||||||
|
raise Exception.Create('Syntax Error: Empty list () is not a valid expression.');
|
||||||
|
|
||||||
|
elements := TList<TExpr>.Create;
|
||||||
|
try
|
||||||
|
while FCurrentToken.Kind <> tkRightParen do
|
||||||
|
elements.Add(ParseExpression);
|
||||||
|
|
||||||
|
head := elements[0];
|
||||||
|
|
||||||
|
// Convert the rest of the list to an array for the factory functions
|
||||||
|
SetLength(tailTokens, elements.Count - 1);
|
||||||
|
SetLength(tailNodes, elements.Count - 1);
|
||||||
|
for var i := 0 to High(tailNodes) do
|
||||||
|
begin
|
||||||
|
tailTokens[i] := elements[i + 1].Token;
|
||||||
|
tailNodes[i] := elements[i + 1].Node;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if head.Token.Kind = tkIdentifier then
|
||||||
|
begin
|
||||||
|
// Handle special forms
|
||||||
|
if SameText(headIdent.Name, 'if') then
|
||||||
|
Result := TAst.IfExpr(tailNodes[0], tailNodes[1], IfThen(Length(tailNodes) > 2, tailNodes[2], nil))
|
||||||
|
else if SameText(headIdent.Name, 'def') then
|
||||||
|
begin
|
||||||
|
var identNode: IIdentifierNode;
|
||||||
|
if tailTokens[0].Kind <> tkIdentifier then
|
||||||
|
raise Exception.Create('Syntax Error: Expected an identifier for def statement.');
|
||||||
|
Result := TAst.VarDecl(identNode, IfThen(Length(tailNodes) > 1, tailNodes[1], nil));
|
||||||
|
end
|
||||||
|
else if SameText(headIdent.Name, 'assign') then
|
||||||
|
begin
|
||||||
|
var identNode: IIdentifierNode;
|
||||||
|
if tailTokens[0].Kind <> tkIdentifier then
|
||||||
|
raise Exception.Create('Syntax Error: Expected an identifier for assignment.');
|
||||||
|
Result := TAst.Assign(identNode, tailNodes[1]);
|
||||||
|
end
|
||||||
|
else if SameText(headIdent.Name, 'fn') then
|
||||||
|
Result := TAst.LambdaExpr(ParseParameterList, tailNodes[0]) // Special parsing for params
|
||||||
|
else if SameText(headIdent.Name, 'do') then
|
||||||
|
Result := TAst.Block(tailNodes)
|
||||||
|
else if SameText(headIdent.Name, 'recur') then
|
||||||
|
Result := TAst.Recur(tailNodes)
|
||||||
|
else if SameText(headIdent.Name, 'get') then
|
||||||
|
Result := TAst.Indexer(tailNodes[0], tailNodes[1])
|
||||||
|
else if (Length(headIdent.Name) > 1) and (headIdent.Name.StartsWith('.')) then
|
||||||
|
Result := TAst.MemberAccess(tailNodes[0], TAst.Identifier(headIdent.Name.Substring(1)))
|
||||||
|
else
|
||||||
|
Result := TAst.FunctionCall(head.Node, tailNodes); // Default is a function call
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result := TAst.FunctionCall(head.Node, tailNodes); // Callee is a complex expression, e.g. ((fn [x] x) 1)
|
||||||
|
|
||||||
|
finally
|
||||||
|
elements.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Consume(tkRightParen);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TParser.ParseExpression: TExpr;
|
||||||
|
var
|
||||||
|
i64: Int64;
|
||||||
|
dbl: Double;
|
||||||
|
begin
|
||||||
|
Result.Token := FCurrentToken;
|
||||||
|
case FCurrentToken.Kind of
|
||||||
|
tkNumber:
|
||||||
|
begin
|
||||||
|
if TryStrToInt64(FCurrentToken.Text, i64) then
|
||||||
|
Result.Node := TAst.Constant(i64)
|
||||||
|
else if TryStrToFloat(FCurrentToken.Text, dbl) then
|
||||||
|
Result.Node := TAst.Constant(dbl)
|
||||||
|
else
|
||||||
|
raise Exception.CreateFmt('Syntax Error: Invalid number format "%s"', [FCurrentToken.Text]);
|
||||||
|
NextToken;
|
||||||
|
end;
|
||||||
|
tkString:
|
||||||
|
begin
|
||||||
|
Result.Node := TAst.Constant(FCurrentToken.Text);
|
||||||
|
NextToken;
|
||||||
|
end;
|
||||||
|
tkIdentifier:
|
||||||
|
begin
|
||||||
|
Result.Node := TAst.Identifier(FCurrentToken.Text);
|
||||||
|
NextToken;
|
||||||
|
end;
|
||||||
|
tkLeftParen: Result.Node := ParseList;
|
||||||
|
else
|
||||||
|
raise Exception.CreateFmt('Syntax Error: Unexpected token %d', [Ord(FCurrentToken.Kind)]);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TParser.Parse: IAstNode;
|
||||||
|
begin
|
||||||
|
var expr := ParseExpression;
|
||||||
|
if expr.Token.Kind <> tkEOF then
|
||||||
|
raise Exception.Create('Syntax Error: Unexpected characters after end of expression.');
|
||||||
|
Result := expr.Node;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TPrettyPrintVisitor }
|
||||||
|
// ... (Implementation of TPrettyPrintVisitor is unchanged and correct) ...
|
||||||
|
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); // remove trailing space
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
{ TAstScript }
|
||||||
|
|
||||||
|
class function TAstScript.Parse(const ASource: string): IAstNode;
|
||||||
|
var
|
||||||
|
p: TParser;
|
||||||
|
begin
|
||||||
|
if ASource.Trim.IsEmpty then
|
||||||
|
exit(nil);
|
||||||
|
p := TParser.Create(ASource);
|
||||||
|
try
|
||||||
|
Result := p.Parse;
|
||||||
|
finally
|
||||||
|
p.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TAstScript.Print(const ANode: IAstNode): string;
|
||||||
|
var
|
||||||
|
visitor: TPrettyPrintVisitor;
|
||||||
|
begin
|
||||||
|
if not Assigned(ANode) then
|
||||||
|
exit('');
|
||||||
|
visitor := TPrettyPrintVisitor.Create;
|
||||||
|
try
|
||||||
|
visitor.Execute(ANode);
|
||||||
|
Result := visitor.GetResult;
|
||||||
|
finally
|
||||||
|
visitor.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
unit Myc.Ast.ViewModel;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
System.SysUtils,
|
|
||||||
System.Types,
|
|
||||||
System.Generics.Collections,
|
|
||||||
Myc.Ast.Nodes;
|
|
||||||
|
|
||||||
type
|
|
||||||
// An enum to identify the specific type of an AST node without relying on RTTI or GUIDs.
|
|
||||||
// This allows the renderer to depend only on the ViewModel layer.
|
|
||||||
TAstNodeType = (
|
|
||||||
antUndefined,
|
|
||||||
antConstant,
|
|
||||||
antIdentifier,
|
|
||||||
antBinaryExpression,
|
|
||||||
antUnaryExpression,
|
|
||||||
antIfExpression,
|
|
||||||
antTernaryExpression,
|
|
||||||
antLambdaExpression,
|
|
||||||
antFunctionCall,
|
|
||||||
antBlockExpression,
|
|
||||||
antVariableDeclaration,
|
|
||||||
antAssignment,
|
|
||||||
antIndexer,
|
|
||||||
antMemberAccess,
|
|
||||||
antCreateSeries,
|
|
||||||
antAddSeriesItem,
|
|
||||||
antSeriesLength
|
|
||||||
);
|
|
||||||
|
|
||||||
// A unique, stable identifier for a visual node instance.
|
|
||||||
TViewModelID = NativeInt;
|
|
||||||
|
|
||||||
// Defines how a view model displays its children.
|
|
||||||
TVisualizationMode = (vmSyntactic, vmSemantic);
|
|
||||||
|
|
||||||
// Metadata tied to a logical IAstNode. All visual instances of this node will share this data.
|
|
||||||
TLogicalMetadata = record
|
|
||||||
// The concrete type of the IAstNode, used for type-safe operations in the renderer.
|
|
||||||
NodeType: TAstNodeType;
|
|
||||||
Description: String;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Metadata tied to a specific visual instance of a node, identified by its TViewModelID.
|
|
||||||
TVisualInstanceMetadata = record
|
|
||||||
PositionOverride: TPointF;
|
|
||||||
IsCollapsed: Boolean;
|
|
||||||
VisualizationMode: TVisualizationMode;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// The ViewModel for a node in the visual graph.
|
|
||||||
TVisualNodeViewModel = class
|
|
||||||
private
|
|
||||||
FId: TViewModelID;
|
|
||||||
FNode: IAstNode;
|
|
||||||
FLogicalMetadata: TLogicalMetadata;
|
|
||||||
FParents: TList<TVisualNodeViewModel>;
|
|
||||||
FChildren: TList<TVisualNodeViewModel>;
|
|
||||||
public
|
|
||||||
constructor Create(const ANode: IAstNode; const AId: TViewModelID; const ALogicalMetadata: TLogicalMetadata);
|
|
||||||
destructor Destroy; override;
|
|
||||||
|
|
||||||
procedure AddChild(const AChild: TVisualNodeViewModel);
|
|
||||||
|
|
||||||
property Id: TViewModelID read FId;
|
|
||||||
property Node: IAstNode read FNode;
|
|
||||||
property LogicalMetadata: TLogicalMetadata read FLogicalMetadata;
|
|
||||||
property Children: TList<TVisualNodeViewModel> read FChildren;
|
|
||||||
property Parents: TList<TVisualNodeViewModel> read FParents;
|
|
||||||
end;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
{ TVisualNodeViewModel }
|
|
||||||
|
|
||||||
constructor TVisualNodeViewModel.Create(const ANode: IAstNode; const AId: TViewModelID; const ALogicalMetadata: TLogicalMetadata);
|
|
||||||
begin
|
|
||||||
inherited Create;
|
|
||||||
FNode := ANode;
|
|
||||||
FId := AId;
|
|
||||||
FLogicalMetadata := ALogicalMetadata;
|
|
||||||
FParents := TList<TVisualNodeViewModel>.Create;
|
|
||||||
FChildren := TList<TVisualNodeViewModel>.Create;
|
|
||||||
end;
|
|
||||||
|
|
||||||
destructor TVisualNodeViewModel.Destroy;
|
|
||||||
begin
|
|
||||||
FParents.Free;
|
|
||||||
FChildren.Free;
|
|
||||||
inherited;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TVisualNodeViewModel.AddChild(const AChild: TVisualNodeViewModel);
|
|
||||||
begin
|
|
||||||
FChildren.Add(AChild);
|
|
||||||
AChild.FParents.Add(Self);
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
+36
-21
@@ -28,10 +28,12 @@ type
|
|||||||
class function CreateScope(Parent: IExecutionScope; const Descriptor: IScopeDescriptor = nil): IExecutionScope; static;
|
class function CreateScope(Parent: IExecutionScope; const Descriptor: IScopeDescriptor = nil): IExecutionScope; static;
|
||||||
|
|
||||||
// --- Existing factory functions ---
|
// --- Existing factory functions ---
|
||||||
class function Constant(AValue: TScalar): IConstantNode; static;
|
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;
|
||||||
class function Identifier(AName: string): IIdentifierNode; static;
|
class function Identifier(AName: string): IIdentifierNode; static;
|
||||||
class function BinaryExpr(ALeft: IAstNode; AOperator: TBinaryOperator; ARight: IAstNode): IBinaryExpressionNode; static;
|
class function BinaryExpr(ALeft: IAstNode; AOperator: TScalar.TBinaryOp; ARight: IAstNode): IBinaryExpressionNode; static;
|
||||||
class function UnaryExpr(const AOperator: TUnaryOperator; const ARight: IAstNode): IUnaryExpressionNode; static;
|
class function UnaryExpr(const AOperator: TScalar.TUnaryOp; const ARight: IAstNode): IUnaryExpressionNode; static;
|
||||||
class function IfExpr(const ACondition: IAstNode; const AThenBranch, AElseBranch: IAstNode): IIfExpressionNode; static;
|
class function IfExpr(const ACondition: IAstNode; const AThenBranch, AElseBranch: IAstNode): IIfExpressionNode; static;
|
||||||
class function TernaryExpr(const ACondition: IAstNode; const AThenBranch, AElseBranch: IAstNode): ITernaryExpressionNode; static;
|
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 LambdaExpr(const AParameters: TArray<IIdentifierNode>; const ABody: IAstNode): ILambdaExpressionNode; static;
|
||||||
@@ -60,10 +62,10 @@ type
|
|||||||
|
|
||||||
TConstantNode = class(TAstNode, IConstantNode)
|
TConstantNode = class(TAstNode, IConstantNode)
|
||||||
private
|
private
|
||||||
FValue: TScalar;
|
FValue: TDataValue;
|
||||||
function GetValue: TScalar;
|
function GetValue: TDataValue;
|
||||||
public
|
public
|
||||||
constructor Create(AValue: TScalar);
|
constructor Create(const AValue: TDataValue);
|
||||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -84,24 +86,24 @@ type
|
|||||||
TBinaryExpressionNode = class(TAstNode, IBinaryExpressionNode)
|
TBinaryExpressionNode = class(TAstNode, IBinaryExpressionNode)
|
||||||
private
|
private
|
||||||
FLeft: IAstNode;
|
FLeft: IAstNode;
|
||||||
FOperator: TBinaryOperator;
|
FOperator: TScalar.TBinaryOp;
|
||||||
FRight: IAstNode;
|
FRight: IAstNode;
|
||||||
function GetLeft: IAstNode;
|
function GetLeft: IAstNode;
|
||||||
function GetOperator: TBinaryOperator;
|
function GetOperator: TScalar.TBinaryOp;
|
||||||
function GetRight: IAstNode;
|
function GetRight: IAstNode;
|
||||||
public
|
public
|
||||||
constructor Create(ALeft: IAstNode; AOperator: TBinaryOperator; ARight: IAstNode);
|
constructor Create(ALeft: IAstNode; AOperator: TScalar.TBinaryOp; ARight: IAstNode);
|
||||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TUnaryExpressionNode = class(TAstNode, IUnaryExpressionNode)
|
TUnaryExpressionNode = class(TAstNode, IUnaryExpressionNode)
|
||||||
private
|
private
|
||||||
FOperator: TUnaryOperator;
|
FOperator: TScalar.TUnaryOp;
|
||||||
FRight: IAstNode;
|
FRight: IAstNode;
|
||||||
function GetOperator: TUnaryOperator;
|
function GetOperator: TScalar.TUnaryOp;
|
||||||
function GetRight: IAstNode;
|
function GetRight: IAstNode;
|
||||||
public
|
public
|
||||||
constructor Create(const AOperator: TUnaryOperator; const ARight: IAstNode);
|
constructor Create(const AOperator: TScalar.TUnaryOp; const ARight: IAstNode);
|
||||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -269,9 +271,12 @@ uses
|
|||||||
|
|
||||||
{ TConstantNode }
|
{ TConstantNode }
|
||||||
|
|
||||||
constructor TConstantNode.Create(AValue: TScalar);
|
constructor TConstantNode.Create(const AValue: TDataValue);
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
|
// Validate that only allowed constant kinds are used.
|
||||||
|
if not (AValue.Kind in [vkScalar, vkText, vkVoid]) then
|
||||||
|
raise EArgumentException.Create('IConstantNode only supports Scalar, Text, and Void values.');
|
||||||
FValue := AValue;
|
FValue := AValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -280,7 +285,7 @@ begin
|
|||||||
Result := Visitor.VisitConstant(Self);
|
Result := Visitor.VisitConstant(Self);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TConstantNode.GetValue: TScalar;
|
function TConstantNode.GetValue: TDataValue;
|
||||||
begin
|
begin
|
||||||
Result := FValue;
|
Result := FValue;
|
||||||
end;
|
end;
|
||||||
@@ -310,7 +315,7 @@ end;
|
|||||||
|
|
||||||
{ TBinaryExpressionNode }
|
{ TBinaryExpressionNode }
|
||||||
|
|
||||||
constructor TBinaryExpressionNode.Create(ALeft: IAstNode; AOperator: TBinaryOperator; ARight: IAstNode);
|
constructor TBinaryExpressionNode.Create(ALeft: IAstNode; AOperator: TScalar.TBinaryOp; ARight: IAstNode);
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FLeft := ALeft;
|
FLeft := ALeft;
|
||||||
@@ -328,7 +333,7 @@ begin
|
|||||||
Result := FLeft;
|
Result := FLeft;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TBinaryExpressionNode.GetOperator: TBinaryOperator;
|
function TBinaryExpressionNode.GetOperator: TScalar.TBinaryOp;
|
||||||
begin
|
begin
|
||||||
Result := FOperator;
|
Result := FOperator;
|
||||||
end;
|
end;
|
||||||
@@ -340,7 +345,7 @@ end;
|
|||||||
|
|
||||||
{ TUnaryExpressionNode }
|
{ TUnaryExpressionNode }
|
||||||
|
|
||||||
constructor TUnaryExpressionNode.Create(const AOperator: TUnaryOperator; const ARight: IAstNode);
|
constructor TUnaryExpressionNode.Create(const AOperator: TScalar.TUnaryOp; const ARight: IAstNode);
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FOperator := AOperator;
|
FOperator := AOperator;
|
||||||
@@ -352,7 +357,7 @@ begin
|
|||||||
Result := Visitor.VisitUnaryExpression(Self);
|
Result := Visitor.VisitUnaryExpression(Self);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TUnaryExpressionNode.GetOperator: TUnaryOperator;
|
function TUnaryExpressionNode.GetOperator: TScalar.TUnaryOp;
|
||||||
begin
|
begin
|
||||||
Result := FOperator;
|
Result := FOperator;
|
||||||
end;
|
end;
|
||||||
@@ -743,17 +748,27 @@ begin
|
|||||||
Result := TBlockExpressionNode.Create(AExpressions);
|
Result := TBlockExpressionNode.Create(AExpressions);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TAst.Constant(AValue: TScalar): IConstantNode;
|
class function TAst.Constant(const AValue: TDataValue): IConstantNode;
|
||||||
begin
|
begin
|
||||||
Result := TConstantNode.Create(AValue);
|
Result := TConstantNode.Create(AValue);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
class function TAst.Constant(const AValue: TScalar): IConstantNode;
|
||||||
|
begin
|
||||||
|
Result := TConstantNode.Create(TDataValue(AValue));
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TAst.Constant(const AValue: String): IConstantNode;
|
||||||
|
begin
|
||||||
|
Result := TConstantNode.Create(TDataValue(AValue));
|
||||||
|
end;
|
||||||
|
|
||||||
class function TAst.CreateSeries(const ADefinition: String): ICreateSeriesNode;
|
class function TAst.CreateSeries(const ADefinition: String): ICreateSeriesNode;
|
||||||
begin
|
begin
|
||||||
Result := TCreateSeriesNode.Create(ADefinition);
|
Result := TCreateSeriesNode.Create(ADefinition);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TAst.BinaryExpr(ALeft: IAstNode; AOperator: TBinaryOperator; ARight: IAstNode): IBinaryExpressionNode;
|
class function TAst.BinaryExpr(ALeft: IAstNode; AOperator: TScalar.TBinaryOp; ARight: IAstNode): IBinaryExpressionNode;
|
||||||
begin
|
begin
|
||||||
Result := TBinaryExpressionNode.Create(ALeft, AOperator, ARight);
|
Result := TBinaryExpressionNode.Create(ALeft, AOperator, ARight);
|
||||||
end;
|
end;
|
||||||
@@ -823,7 +838,7 @@ begin
|
|||||||
Result := TLambdaExpressionNode.Create(AParameters, ABody);
|
Result := TLambdaExpressionNode.Create(AParameters, ABody);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TAst.UnaryExpr(const AOperator: TUnaryOperator; const ARight: IAstNode): IUnaryExpressionNode;
|
class function TAst.UnaryExpr(const AOperator: TScalar.TUnaryOp; const ARight: IAstNode): IUnaryExpressionNode;
|
||||||
begin
|
begin
|
||||||
Result := TUnaryExpressionNode.Create(AOperator, ARight);
|
Result := TUnaryExpressionNode.Create(AOperator, ARight);
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ uses
|
|||||||
type
|
type
|
||||||
TRttiAstHelper = class
|
TRttiAstHelper = class
|
||||||
private
|
private
|
||||||
// Maps a Delphi RTTI type to its TScalarKind.
|
// Maps a Delphi RTTI type to its TScalar.TKind.
|
||||||
class function TypeToScalarKind(AType: TRttiType): TScalarKind; static;
|
class function TypeToScalarKind(AType: TRttiType): TScalar.TKind; static;
|
||||||
public
|
public
|
||||||
// Creates a JSON definition string from a record type info.
|
// Creates a JSON definition string from a record type info.
|
||||||
class function RecordDefinitionToJson<T: record>: string; static;
|
class function RecordDefinitionToJson<T: record>: string; static;
|
||||||
@@ -43,7 +43,6 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
|
|
||||||
try
|
try
|
||||||
// The root element is expected to be a JSON array directly.
|
|
||||||
if not (jsonValue is TJSONArray) then
|
if not (jsonValue is TJSONArray) then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
@@ -54,12 +53,11 @@ begin
|
|||||||
begin
|
begin
|
||||||
fieldObj := fieldsArray.Items[i] as TJSONObject;
|
fieldObj := fieldsArray.Items[i] as TJSONObject;
|
||||||
if not Assigned(fieldObj) then
|
if not Assigned(fieldObj) then
|
||||||
continue; // or raise error
|
continue;
|
||||||
|
|
||||||
fields[i].Name := fieldObj.GetValue<string>('name');
|
fields[i].Name := fieldObj.GetValue<string>('name');
|
||||||
|
|
||||||
kindStr := fieldObj.GetValue<string>('kind');
|
kindStr := fieldObj.GetValue<string>('kind');
|
||||||
fields[i].Kind := TScalarKind(GetEnumValue(TypeInfo(TScalarKind), kindStr));
|
fields[i].Kind := TScalar.TKind(GetEnumValue(TypeInfo(TScalar.TKind), kindStr));
|
||||||
end;
|
end;
|
||||||
Result := TScalarRecordDefinition.Create(fields);
|
Result := TScalarRecordDefinition.Create(fields);
|
||||||
finally
|
finally
|
||||||
@@ -84,54 +82,43 @@ begin
|
|||||||
|
|
||||||
rttiRecordType := rttiType as TRttiRecordType;
|
rttiRecordType := rttiType as TRttiRecordType;
|
||||||
|
|
||||||
// The root element is now the array itself.
|
|
||||||
fieldsArray := TJSONArray.Create;
|
fieldsArray := TJSONArray.Create;
|
||||||
try
|
try
|
||||||
for field in rttiRecordType.GetFields do
|
for field in rttiRecordType.GetFields do
|
||||||
begin
|
begin
|
||||||
fieldObj := TJSONObject.Create;
|
fieldObj := TJSONObject.Create;
|
||||||
fieldObj.AddPair('name', TJSONString.Create(field.Name));
|
fieldObj.AddPair('name', TJSONString.Create(field.Name));
|
||||||
fieldObj.AddPair('kind', TJSONString.Create(GetEnumName(TypeInfo(TScalarKind), Ord(TypeToScalarKind(field.FieldType)))));
|
fieldObj.AddPair('kind', TJSONString.Create(GetEnumName(TypeInfo(TScalar.TKind), Ord(TypeToScalarKind(field.FieldType)))));
|
||||||
fieldsArray.Add(fieldObj);
|
fieldsArray.Add(fieldObj);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Convert the array directly to a JSON string.
|
|
||||||
Result := fieldsArray.ToJSON;
|
Result := fieldsArray.ToJSON;
|
||||||
finally
|
finally
|
||||||
fieldsArray.Free;
|
fieldsArray.Free;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TRttiAstHelper.TypeToScalarKind(AType: TRttiType): TScalarKind;
|
class function TRttiAstHelper.TypeToScalarKind(AType: TRttiType): TScalar.TKind;
|
||||||
var
|
var
|
||||||
typeHandle: PTypeInfo;
|
typeHandle: PTypeInfo;
|
||||||
begin
|
begin
|
||||||
// 1. Use a case statement or if-checks on AType.Handle.
|
|
||||||
typeHandle := AType.Handle;
|
typeHandle := AType.Handle;
|
||||||
|
|
||||||
// 2. Compare with TypeInfo(Integer), TypeInfo(Double), TypeInfo(TDecimal) etc.
|
if typeHandle = TypeInfo(Int64) then
|
||||||
if typeHandle = TypeInfo(Integer) then
|
Result := TScalar.TKind.Ordinal
|
||||||
Result := skInteger
|
else if typeHandle = TypeInfo(Integer) then
|
||||||
else if typeHandle = TypeInfo(Int64) then
|
Result := TScalar.TKind.Ordinal
|
||||||
Result := skInt64
|
|
||||||
else if typeHandle = TypeInfo(UInt64) then
|
else if typeHandle = TypeInfo(UInt64) then
|
||||||
Result := skUInt64
|
Result := TScalar.TKind.Ordinal
|
||||||
else if typeHandle = TypeInfo(Single) then
|
|
||||||
Result := skSingle
|
|
||||||
else if typeHandle = TypeInfo(Double) then
|
|
||||||
Result := skDouble
|
|
||||||
else if typeHandle = TypeInfo(TDateTime) then
|
|
||||||
Result := skDateTime
|
|
||||||
else if typeHandle = TypeInfo(Boolean) then
|
else if typeHandle = TypeInfo(Boolean) then
|
||||||
Result := skBoolean
|
Result := TScalar.TKind.Ordinal
|
||||||
else if typeHandle = TypeInfo(Char) then
|
else if typeHandle = TypeInfo(Double) then
|
||||||
Result := skChar
|
Result := TScalar.TKind.Float
|
||||||
else if typeHandle = TypeInfo(TDecimal) then
|
else if typeHandle = TypeInfo(Single) then
|
||||||
Result := skDecimal
|
Result := TScalar.TKind.Float
|
||||||
else if typeHandle = TypeInfo(TTimestamp) then
|
else if typeHandle = TypeInfo(TDateTime) then
|
||||||
Result := skTimestamp
|
Result := TScalar.TKind.Float // Correctly handle TDateTime as a Float
|
||||||
else
|
else
|
||||||
// 4. Raise an exception for unsupported types.
|
|
||||||
raise EArgumentException.CreateFmt('Unsupported record field type: %s', [AType.Name]);
|
raise EArgumentException.CreateFmt('Unsupported record field type: %s', [AType.Name]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
+238
-1128
File diff suppressed because it is too large
Load Diff
@@ -53,6 +53,7 @@ type
|
|||||||
class function Void: TDataValue; inline; static;
|
class function Void: TDataValue; inline; static;
|
||||||
|
|
||||||
class operator Implicit(const AValue: TScalar): TDataValue; overload; inline;
|
class operator Implicit(const AValue: TScalar): TDataValue; overload; inline;
|
||||||
|
class operator Implicit(const AValue: TDataValue): TScalar; overload; inline;
|
||||||
class operator Implicit(const AValue: String): TDataValue; overload; inline;
|
class operator Implicit(const AValue: String): TDataValue; overload; inline;
|
||||||
class operator Implicit(const AValue: TDataValue.TFunc): TDataValue; overload; inline;
|
class operator Implicit(const AValue: TDataValue.TFunc): TDataValue; overload; inline;
|
||||||
class operator Implicit(const AValue: TObject): TDataValue; overload; inline;
|
class operator Implicit(const AValue: TObject): TDataValue; overload; inline;
|
||||||
@@ -435,6 +436,11 @@ begin
|
|||||||
Result.FInterface := TObjVal.Create(AValue);
|
Result.FInterface := TObjVal.Create(AValue);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
class operator TDataValue.Implicit(const AValue: TDataValue): TScalar;
|
||||||
|
begin
|
||||||
|
Result := AValue.AsScalar;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TDataValueKindHelper }
|
{ TDataValueKindHelper }
|
||||||
|
|
||||||
function TDataValueKindHelper.ToString: string;
|
function TDataValueKindHelper.ToString: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user