AST development

This commit is contained in:
Michael Schimmel
2025-09-01 19:24:22 +02:00
parent 3b3d94291d
commit 375e64411b
8 changed files with 864 additions and 297 deletions
+7
View File
@@ -106,6 +106,13 @@ object Form1: TForm1
TextSettings.Trimming = None
OnClick = ClearButtonClick
end
object FlowOnlyBox: TCheckBox
Position.X = 24.000000000000000000
Position.Y = 8.000000000000000000
TabOrder = 12
Text = 'Flow only'
OnChange = ClearButtonClick
end
end
object Panel2: TPanel
Align = Client
+94 -20
View File
@@ -45,6 +45,7 @@ type
DoTrigger2Button: TButton;
Panel2: TPanel;
ClearButton: TButton;
FlowOnlyBox: TCheckBox;
procedure ClearButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure CrerateTriggerExampleButtonClick(Sender: TObject);
@@ -92,7 +93,7 @@ begin
FWorkspace.OnMouseDown := WorkspaceMouseDown;
FGScope := T_ExecutionScope.Create(nil);
FGScope := TExecutionScope.Create(nil);
end;
procedure TForm1.DebugButtonClick(Sender: TObject);
@@ -109,7 +110,7 @@ begin
exit;
end;
scope := T_ExecutionScope.Create(FGScope);
scope := TExecutionScope.Create(FGScope);
Memo1.Lines.Clear;
Memo1.Lines.Add('--- Debug Evaluator Trace ---');
@@ -193,8 +194,8 @@ begin
[TAst.Identifier('n')],
TAst.IfExpr(
TAst.BinaryExpr(TAst.Identifier('n'), boLess, TAst.Constant(TScalar.FromInt64(2))),
TAst.VarDecl(TAst.Identifier('Result'), TAst.Identifier('n')),
TAst.VarDecl(
TAst.Assign(TAst.Identifier('Result'), TAst.Identifier('n')),
TAst.Assign(
TAst.Identifier('Result'),
TAst.BinaryExpr(
TAst.FunctionCall(
@@ -215,8 +216,39 @@ begin
]
);
root :=
TAst.Block(
[
TAst.VarDecl(
TAst.Identifier('fib'),
TAst.LambdaExpr(
[TAst.Identifier('n')],
// The body is now a single ternary expression that returns a value.
TAst.TernaryExpr(
TAst.BinaryExpr(TAst.Identifier('n'), boLess, TAst.Constant(TScalar.FromInt64(2))),
// The value if true.
TAst.Identifier('n'),
// The value if false.
TAst.BinaryExpr(
TAst.FunctionCall(
TAst.Identifier('fib'),
[TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(1)))]
),
boAdd,
TAst.FunctionCall(
TAst.Identifier('fib'),
[TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(2)))]
)
)
)
)
),
TAst.FunctionCall(TAst.Identifier('fib'), [TAst.Constant(TScalar.FromInt64(30))])
]
);
FLastAst := root;
scope := T_ExecutionScope.Create(nil);
scope := TExecutionScope.Create(nil);
visitor := TEvaluatorVisitor.Create(scope);
result := root.Accept(visitor);
@@ -270,6 +302,7 @@ begin
Memo1.Lines.Add('--- Recursive factorial(20) ---');
sw.Start;
{
root :=
TAst.Block(
[
@@ -279,7 +312,6 @@ begin
[TAst.Identifier('n')],
TAst.Block(
[
TAst.VarDecl(TAst.Identifier('Result'), TAst.Constant(TScalar.FromDouble(0))),
TAst.IfExpr(
TAst.BinaryExpr(TAst.Identifier('n'), boLess, TAst.Constant(TScalar.FromInt64(2))),
TAst.Assign(TAst.Identifier('Result'), TAst.Constant(TScalar.FromInt64(1))),
@@ -302,9 +334,41 @@ begin
TAst.FunctionCall(TAst.Identifier('factorial'), [TAst.Constant(TScalar.FromInt64(20))])
]
);
}
root :=
TAst.Block(
[
TAst.VarDecl(
TAst.Identifier('factorial'),
TAst.LambdaExpr(
[TAst.Identifier('n')],
TAst.Block(
[
TAst.Assign(
TAst.Identifier('Result'),
TAst.TernaryExpr(
TAst.BinaryExpr(TAst.Identifier('n'), boLess, TAst.Constant(TScalar.FromInt64(2))),
TAst.Constant(TScalar.FromInt64(1)),
TAst.BinaryExpr(
TAst.Identifier('n'),
boMultiply,
TAst.FunctionCall(
TAst.Identifier('factorial'),
[TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(1)))]
)
)
)
)
]
)
)
),
TAst.FunctionCall(TAst.Identifier('factorial'), [TAst.Constant(TScalar.FromInt64(20))])
]
);
FLastAst := root;
scope := T_ExecutionScope.Create(nil);
scope := TExecutionScope.Create(nil);
visitor := TEvaluatorVisitor.Create(scope);
result := root.Accept(visitor);
@@ -317,7 +381,6 @@ end;
procedure TForm1.Test1ButtonClick(Sender: TObject);
var
root: IAstNode;
scope: IExecutionScope;
visitor: IAstVisitor;
result: TAstValue;
@@ -330,20 +393,26 @@ begin
Memo1.Lines.Add('--- Simple AST Execution ---');
sw.Start;
root :=
TAst.Block(
[
TAst.VarDecl(TAst.Identifier('a'), TAst.Constant(TScalar.FromInt64(10))),
TAst.VarDecl(TAst.Identifier('b'), TAst.BinaryExpr(TAst.Identifier('a'), boMultiply, TAst.Constant(TScalar.FromInt64(2)))),
TAst.BinaryExpr(TAst.Identifier('a'), boAdd, TAst.Identifier('b'))
]
var main :=
TAst.LambdaExpr(
[],
TAst.Block(
[
TAst.VarDecl(TAst.Identifier('a'), TAst.Constant(TScalar.FromInt64(10))),
TAst.VarDecl(
TAst.Identifier('b'),
TAst.BinaryExpr(TAst.Identifier('a'), boMultiply, TAst.Constant(TScalar.FromInt64(2)))
),
TAst.AssignResult(TAst.BinaryExpr(TAst.Identifier('a'), boAdd, TAst.Identifier('b')))
]
)
);
FLastAst := root;
scope := T_ExecutionScope.Create(FGScope);
FLastAst := main;
scope := TExecutionScope.Create(FGScope);
visitor := TEvaluatorVisitor.Create(scope);
result := root.Accept(visitor);
result := TAst.FunctionCall(main, []).Accept(visitor);
sw.Stop;
if not result.IsVoid then
@@ -395,7 +464,7 @@ begin
FLastAst := root;
scope := T_ExecutionScope.Create(FGScope);
scope := TExecutionScope.Create(FGScope);
visitor := TEvaluatorVisitor.Create(scope);
result := root.Accept(visitor);
@@ -520,7 +589,12 @@ begin
exit;
if FLastAst <> nil then
FWorkspace.BuildTree(FLastAst, TPointF.Create(X, Y));
begin
var visu := TVisualizationMode.vmDetailed;
if FlowOnlyBox.IsChecked then
visu := TVisualizationMode.vmControlFlow;
FWorkspace.BuildTree(FLastAst, TPointF.Create(X, Y), visu);
end;
end;
end.
File diff suppressed because it is too large Load Diff
+36 -6
View File
@@ -25,6 +25,7 @@ type
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TAstValue; virtual;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TAstValue; virtual;
function VisitIfExpression(const Node: IIfExpressionNode): TAstValue; virtual;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue; virtual;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue; virtual;
function VisitFunctionCall(const Node: IFunctionCallNode): TAstValue; virtual;
function VisitBlockExpression(const Node: IBlockExpressionNode): TAstValue; virtual;
@@ -52,6 +53,7 @@ type
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TAstValue; override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TAstValue; override;
function VisitIfExpression(const Node: IIfExpressionNode): TAstValue; override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue; override;
function VisitFunctionCall(const Node: IFunctionCallNode): TAstValue; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): TAstValue; override;
@@ -170,15 +172,13 @@ end;
function TEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue;
var
varName: string;
initValue: TAstValue;
begin
varName := Node.Identifier.Name;
if Assigned(Node.Initializer) then
initValue := Node.Initializer.Accept(Self)
Result := Node.Initializer.Accept(Self)
else
initValue := TAstValue.Void;
FScope.SetValue(varName, initValue);
Result := TAstValue.Void;
Result := TAstValue.Void;
FScope.SetValue(varName, Result);
end;
function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue;
@@ -210,7 +210,7 @@ begin
if (arguments.Count <> Length(closure.Parameters)) then
raise EArgumentException.CreateFmt('Argument count mismatch: expected %d, got %d', [Length(closure.Parameters), arguments.Count]);
callScope := T_ExecutionScope.Create(closure.ClosureScope);
callScope := TExecutionScope.Create(closure.ClosureScope);
for i := 0 to arguments.Count - 1 do
begin
@@ -219,6 +219,13 @@ begin
end;
innerVisitor := Self.CreateVisitorForScope(callScope);
// Introduce 'Result' variable for imperative-style assignments within the closure.
callScope.SetValue('Result', TAstValue.Void);
// The return value of the function is the value of its body expression.
// This supports both functional-style returns (direct value) and
// imperative-style returns (via an assignment, which also returns the value).
Result := closure.Body.Accept(innerVisitor);
end;
@@ -314,6 +321,17 @@ begin
Result := Node.ElseBranch.Accept(Self);
end;
function TEvaluatorVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue;
var
conditionValue: TAstValue;
begin
conditionValue := Node.Condition.Accept(Self);
if IsTruthy(conditionValue) then
Result := Node.ThenBranch.Accept(Self)
else
Result := Node.ElseBranch.Accept(Self);
end;
function TEvaluatorVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TAstValue;
var
expression: IExpressionNode;
@@ -457,6 +475,18 @@ begin
AppendLine(Format('} -> %s', [Result.ToString]));
end;
function TDebugEvaluatorVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue;
begin
AppendLine('TernaryExpr{');
Indent;
try
Result := inherited VisitTernaryExpression(Node);
finally
Unindent;
end;
AppendLine(Format('} -> %s', [Result.ToString]));
end;
function TDebugEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue;
begin
AppendLine('LambdaExpr{');
+42 -24
View File
@@ -33,6 +33,8 @@ type
IBinaryExpressionNode = interface;
IUnaryExpressionNode = interface;
IIfExpressionNode = interface;
// Added forward declaration for the new ternary expression node.
ITernaryExpressionNode = interface;
ILambdaExpressionNode = interface;
IFunctionCallNode = interface;
IBlockExpressionNode = interface;
@@ -55,9 +57,25 @@ type
TAstValue = record
private
FKind: TAstValueKind;
FScalar: TScalar;
FInterface: IInterface;
type
IVal<T> = interface
['{70210CAF-0857-4BF1-8254-B8239A155A02}']
function GetValue: T;
property Value: T read GetValue;
end;
TVal<T> = class(TInterfacedObject, IVal<T>)
private
FValue: T;
function GetValue: T;
public
constructor Create(const AValue: T);
end;
var
FKind: TAstValueKind;
FScalar: TScalar;
FInterface: IInterface;
function GetKind: TAstValueKind; inline;
function GetIsVoid: Boolean; inline;
public
@@ -93,6 +111,8 @@ type
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TAstValue;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TAstValue;
function VisitIfExpression(const Node: IIfExpressionNode): TAstValue;
// Added visitor method for the new ternary expression node.
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue;
function VisitFunctionCall(const Node: IFunctionCallNode): TAstValue;
function VisitBlockExpression(const Node: IBlockExpressionNode): TAstValue;
@@ -141,6 +161,7 @@ type
property Right: IExpressionNode read GetRight;
end;
// Represents an if-statement for control flow.
IIfExpressionNode = interface(IExpressionNode)
{$region 'private'}
function GetCondition: IExpressionNode;
@@ -152,6 +173,18 @@ type
property ElseBranch: IExpressionNode read GetElseBranch;
end;
// Represents a ternary expression (e.g., condition ? true_expr : false_expr) for value selection.
ITernaryExpressionNode = interface(IExpressionNode)
{$region 'private'}
function GetCondition: IExpressionNode;
function GetThenBranch: IExpressionNode;
function GetElseBranch: IExpressionNode;
{$endregion}
property Condition: IExpressionNode read GetCondition;
property ThenBranch: IExpressionNode read GetThenBranch;
property ElseBranch: IExpressionNode read GetElseBranch;
end;
ILambdaExpressionNode = interface(IExpressionNode)
{$region 'private'}
function GetParameters: TArray<IIdentifierNode>;
@@ -200,31 +233,15 @@ implementation
uses
System.Classes;
type
// Private interface to encapsulate a non scalar value for reference counting.
IAstValue<T> = interface
function GetValue: string;
property Value: string read GetValue;
end;
// Implementation of the text value interface.
TAstValue<T> = class(TInterfacedObject, IAstValue<T>)
private
FValue: string;
function GetValue: string;
public
constructor Create(const AValue: string);
end;
{ TAstValue }
constructor TAstValue<T>.Create(const AValue: string);
constructor TAstValue.TVal<T>.Create(const AValue: T);
begin
inherited Create;
FValue := AValue;
end;
function TAstValue<T>.GetValue: string;
function TAstValue.TVal<T>.GetValue: T;
begin
Result := FValue;
end;
@@ -255,7 +272,8 @@ function TAstValue.AsText: String;
begin
if (FKind <> avkText) then
raise EInvalidCast.Create('Cannot read value as Text.');
Result := IAstValue<String>(FInterface).Value;
Result := (FInterface as IVal<String>).Value;
end;
class function TAstValue.FromClosure(const AValue: IEvaluatorClosure): TAstValue;
@@ -275,7 +293,7 @@ end;
class function TAstValue.FromText(const AValue: String): TAstValue;
begin
Result.FKind := avkText;
Result.FInterface := TAstValue<String>.Create(AValue);
Result.FInterface := TVal<String>.Create(AValue);
Result.FScalar := Default(TScalar);
end;
@@ -313,7 +331,7 @@ begin
case Self of
boAdd: Result := '+';
boSubtract: Result := '-';
boMultiply: Result := #$2A2F;
boMultiply: Result := '*';
boDivide: Result := '/';
boEqual: Result := '==';
boNotEqual: Result := '!=';
+21
View File
@@ -27,6 +27,7 @@ type
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TAstValue;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TAstValue;
function VisitIfExpression(const Node: IIfExpressionNode): TAstValue;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue;
function VisitFunctionCall(const Node: IFunctionCallNode): TAstValue;
function VisitBlockExpression(const Node: IBlockExpressionNode): TAstValue;
@@ -128,6 +129,26 @@ begin
Result := TAstValue.Void;
end;
function TPrettyPrintVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue;
begin
AppendLine('TernaryExpr');
Indent;
AppendLine('Condition:');
Indent;
Node.Condition.Accept(Self);
Unindent;
AppendLine('Then:');
Indent;
Node.ThenBranch.Accept(Self);
Unindent;
AppendLine('Else:');
Indent;
Node.ElseBranch.Accept(Self);
Unindent;
Unindent;
Result := TAstValue.Void;
end;
function TPrettyPrintVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue;
var
param: IIdentifierNode;
+12 -12
View File
@@ -9,7 +9,7 @@ uses
Myc.Ast.Nodes;
type
T_ExecutionScope = class(TInterfacedObject, IExecutionScope)
TExecutionScope = class(TInterfacedObject, IExecutionScope)
private
FParent: IExecutionScope;
FVariables: TDictionary<string, TAstValue>;
@@ -29,27 +29,27 @@ type
implementation
{ T_ExecutionScope }
{ TExecutionScope }
constructor T_ExecutionScope.Create(AParent: IExecutionScope);
constructor TExecutionScope.Create(AParent: IExecutionScope);
begin
inherited Create;
FParent := AParent;
FVariables := TDictionary<string, TAstValue>.Create;
end;
destructor T_ExecutionScope.Destroy;
destructor TExecutionScope.Destroy;
begin
FVariables.Free;
inherited Destroy;
end;
function T_ExecutionScope.GetParent: IExecutionScope;
function TExecutionScope.GetParent: IExecutionScope;
begin
Result := FParent;
end;
procedure T_ExecutionScope.AssignValue(const Name: string; const Value: TAstValue);
procedure TExecutionScope.AssignValue(const Name: string; const Value: TAstValue);
begin
if FVariables.ContainsKey(Name) then
FVariables.AddOrSetValue(Name, Value)
@@ -59,14 +59,14 @@ begin
raise Exception.CreateFmt('Cannot assign to undeclared variable "%s".', [Name]);
end;
procedure T_ExecutionScope.Clear;
procedure TExecutionScope.Clear;
begin
FVariables.Clear;
if Assigned(FParent) then
FParent.Clear;
end;
procedure T_ExecutionScope.DumpScope(const ABuilder: TStringBuilder; AIndent: Integer);
procedure TExecutionScope.DumpScope(const ABuilder: TStringBuilder; AIndent: Integer);
var
pair: TPair<string, TAstValue>;
indentStr: string;
@@ -88,11 +88,11 @@ begin
// As FParent is now an interface, we must cast it back to the class to call the private DumpScope.
// This indicates that DumpScope should perhaps be part of the interface or handled differently.
// For now, we use a cast to keep the functionality.
(FParent as T_ExecutionScope).DumpScope(ABuilder, AIndent + 2);
(FParent as TExecutionScope).DumpScope(ABuilder, AIndent + 2);
end;
end;
function T_ExecutionScope.Dump: string;
function TExecutionScope.Dump: string;
var
builder: TStringBuilder;
begin
@@ -106,7 +106,7 @@ begin
end;
end;
function T_ExecutionScope.FindValue(const Name: string; out Value: TAstValue): Boolean;
function TExecutionScope.FindValue(const Name: string; out Value: TAstValue): Boolean;
begin
Result := FVariables.TryGetValue(Name, Value);
if not Result and Assigned(FParent) then
@@ -115,7 +115,7 @@ begin
end;
end;
procedure T_ExecutionScope.SetValue(const Name: string; const Value: TAstValue);
procedure TExecutionScope.SetValue(const Name: string; const Value: TAstValue);
begin
FVariables.AddOrSetValue(Name, Value);
end;
+60
View File
@@ -22,11 +22,17 @@ type
const ACondition: IExpressionNode;
const AThenBranch, AElseBranch: IExpressionNode
): IIfExpressionNode; static;
// Added factory function for the new ternary expression node.
class function TernaryExpr(
const ACondition: IExpressionNode;
const AThenBranch, AElseBranch: IExpressionNode
): ITernaryExpressionNode; static;
class function LambdaExpr(const AParameters: TArray<IIdentifierNode>; const ABody: IExpressionNode): ILambdaExpressionNode; static;
class function FunctionCall(const ACallee: IExpressionNode; const AArguments: array of IExpressionNode): IFunctionCallNode; static;
class function Block(const AExpressions: array of IExpressionNode): IBlockExpressionNode; static;
class function VarDecl(const AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationNode; static;
class function Assign(const AIdentifier: IIdentifierNode; const AValue: IExpressionNode): IAssignmentNode; static;
class function AssignResult(const AValue: IExpressionNode): IAssignmentNode; static;
end;
implementation
@@ -103,6 +109,20 @@ type
function Accept(const Visitor: IAstVisitor): TAstValue; override;
end;
{ TTernaryExpressionNode }
TTernaryExpressionNode = class(TAstNode, ITernaryExpressionNode)
private
FCondition: IExpressionNode;
FThenBranch: IExpressionNode;
FElseBranch: IExpressionNode;
function GetCondition: IExpressionNode;
function GetThenBranch: IExpressionNode;
function GetElseBranch: IExpressionNode;
public
constructor Create(const ACondition, AThenBranch, AElseBranch: IExpressionNode);
function Accept(const Visitor: IAstVisitor): TAstValue; override;
end;
{ TLambdaExpressionNode }
TLambdaExpressionNode = class(TAstNode, ILambdaExpressionNode)
private
@@ -283,6 +303,36 @@ begin
Result := FThenBranch;
end;
{ TTernaryExpressionNode }
constructor TTernaryExpressionNode.Create(const ACondition, AThenBranch, AElseBranch: IExpressionNode);
begin
inherited Create;
FCondition := ACondition;
FThenBranch := AThenBranch;
FElseBranch := AElseBranch;
end;
function TTernaryExpressionNode.Accept(const Visitor: IAstVisitor): TAstValue;
begin
Result := Visitor.VisitTernaryExpression(Self);
end;
function TTernaryExpressionNode.GetCondition: IExpressionNode;
begin
Result := FCondition;
end;
function TTernaryExpressionNode.GetElseBranch: IExpressionNode;
begin
Result := FElseBranch;
end;
function TTernaryExpressionNode.GetThenBranch: IExpressionNode;
begin
Result := FThenBranch;
end;
{ TLambdaExpressionNode }
constructor TLambdaExpressionNode.Create(const AParameters: TArray<IIdentifierNode>; const ABody: IExpressionNode);
@@ -424,6 +474,11 @@ begin
Result := TAssignmentNode.Create(AIdentifier, AValue);
end;
class function TAst.AssignResult(const AValue: IExpressionNode): IAssignmentNode;
begin
Result := Assign(TAst.Identifier('Result'), AValue);
end;
class function TAst.Block(const AExpressions: array of IExpressionNode): IBlockExpressionNode;
begin
Result := TBlockExpressionNode.Create(AExpressions);
@@ -454,6 +509,11 @@ begin
Result := TIfExpressionNode.Create(ACondition, AThenBranch, AElseBranch);
end;
class function TAst.TernaryExpr(const ACondition, AThenBranch, AElseBranch: IExpressionNode): ITernaryExpressionNode;
begin
Result := TTernaryExpressionNode.Create(ACondition, AThenBranch, AElseBranch);
end;
class function TAst.LambdaExpr(const AParameters: TArray<IIdentifierNode>; const ABody: IExpressionNode): ILambdaExpressionNode;
begin
Result := TLambdaExpressionNode.Create(AParameters, ABody);