AST
This commit is contained in:
@@ -0,0 +1,882 @@
|
||||
unit Myc.Ast;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
System.Generics.Collections,
|
||||
Myc.Data.Types;
|
||||
|
||||
type
|
||||
// Operators are now type-safe enums
|
||||
TBinaryOperator = (boAdd, boSubtract, boMultiply, boDivide);
|
||||
TUnaryOperator = (uoNegate, uoNot);
|
||||
|
||||
// Forward declarations for interfaces
|
||||
IAstVisitor = interface;
|
||||
IAstNode = interface;
|
||||
IExpressionNode = interface;
|
||||
IStatementNode = interface;
|
||||
IConstantNode = interface;
|
||||
IIdentifierNode = interface;
|
||||
IBinaryExpressionNode = interface;
|
||||
IUnaryExpressionNode = interface;
|
||||
IIfExpressionNode = interface;
|
||||
ILambdaExpressionNode = interface;
|
||||
IFunctionCallNode = interface;
|
||||
IBlockStatementNode = interface;
|
||||
IVariableDeclarationStatementNode = interface;
|
||||
IExpressionStatementNode = interface;
|
||||
|
||||
// --- Abstract Node Interfaces ---
|
||||
|
||||
// Base interface for all AST nodes
|
||||
IAstNode = interface(IInterface)
|
||||
// Accept is a function that returns the result of the visit.
|
||||
function Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
end;
|
||||
|
||||
// Abstract interface for all nodes that evaluate to a value.
|
||||
IExpressionNode = interface(IAstNode)
|
||||
end;
|
||||
|
||||
// Abstract interface for all nodes that perform an action.
|
||||
IStatementNode = interface(IAstNode)
|
||||
end;
|
||||
|
||||
// --- Concrete Expression Node Interfaces ---
|
||||
|
||||
IConstantNode = interface(IExpressionNode)
|
||||
{$region 'private'}
|
||||
function GetValue: IDataValue;
|
||||
{$endregion}
|
||||
property Value: IDataValue read GetValue;
|
||||
end;
|
||||
|
||||
IIdentifierNode = interface(IExpressionNode)
|
||||
{$region 'private'}
|
||||
function GetName: string;
|
||||
{$endregion}
|
||||
property Name: string read GetName;
|
||||
end;
|
||||
|
||||
IBinaryExpressionNode = interface(IExpressionNode)
|
||||
{$region 'private'}
|
||||
function GetLeft: IExpressionNode;
|
||||
function GetOperator: TBinaryOperator;
|
||||
function GetRight: IExpressionNode;
|
||||
{$endregion}
|
||||
property Left: IExpressionNode read GetLeft;
|
||||
property Operator: TBinaryOperator read GetOperator;
|
||||
property Right: IExpressionNode read GetRight;
|
||||
end;
|
||||
|
||||
IUnaryExpressionNode = interface(IExpressionNode)
|
||||
{$region 'private'}
|
||||
function GetOperator: TUnaryOperator;
|
||||
function GetRight: IExpressionNode;
|
||||
{$endregion}
|
||||
property Operator: TUnaryOperator read GetOperator;
|
||||
property Right: IExpressionNode read GetRight;
|
||||
end;
|
||||
|
||||
IIfExpressionNode = 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: TList<IIdentifierNode>;
|
||||
function GetBody: IAstNode; // Can be an expression or a block statement
|
||||
{$endregion}
|
||||
property Parameters: TList<IIdentifierNode> read GetParameters;
|
||||
property Body: IAstNode read GetBody;
|
||||
end;
|
||||
|
||||
IFunctionCallNode = interface(IExpressionNode)
|
||||
{$region 'private'}
|
||||
function GetCallee: IExpressionNode;
|
||||
function GetArguments: TList<IExpressionNode>;
|
||||
{$endregion}
|
||||
property Callee: IExpressionNode read GetCallee;
|
||||
property Arguments: TList<IExpressionNode> read GetArguments;
|
||||
end;
|
||||
|
||||
// --- Concrete Statement Node Interfaces ---
|
||||
|
||||
IBlockStatementNode = interface(IStatementNode)
|
||||
{$region 'private'}
|
||||
function GetStatements: TList<IStatementNode>;
|
||||
{$endregion}
|
||||
property Statements: TList<IStatementNode> read GetStatements;
|
||||
end;
|
||||
|
||||
IVariableDeclarationStatementNode = interface(IStatementNode)
|
||||
{$region 'private'}
|
||||
function GetIdentifier: IIdentifierNode;
|
||||
function GetInitializer: IExpressionNode; // Can be nil
|
||||
{$endregion}
|
||||
property Identifier: IIdentifierNode read GetIdentifier;
|
||||
property Initializer: IExpressionNode read GetInitializer;
|
||||
end;
|
||||
|
||||
IExpressionStatementNode = interface(IStatementNode)
|
||||
{$region 'private'}
|
||||
function GetExpression: IExpressionNode;
|
||||
{$endregion}
|
||||
property Expression: IExpressionNode read GetExpression;
|
||||
end;
|
||||
|
||||
// All visitor methods are functions returning a value.
|
||||
IAstVisitor = interface
|
||||
['{5F4110E9-0158-41E9-A512-E57A843E8A5A}']
|
||||
// Expression visitors
|
||||
function VisitConstant(const Node: IConstantNode): IDataValue;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): IDataValue;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): IDataValue;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): IDataValue;
|
||||
|
||||
// Statement visitors
|
||||
function VisitBlockStatement(const Node: IBlockStatementNode): IDataValue;
|
||||
function VisitVariableDeclarationStatement(const Node: IVariableDeclarationStatementNode): IDataValue;
|
||||
function VisitExpressionStatement(const Node: IExpressionStatementNode): IDataValue;
|
||||
end;
|
||||
|
||||
// Record acting as a namespace for the factory functions.
|
||||
TAst = record
|
||||
// Expressions
|
||||
class function Constant(AValue: IDataValue): IConstantNode; static;
|
||||
class function Identifier(AName: string): IIdentifierNode; static;
|
||||
class function BinaryExpr(
|
||||
ALeft: IExpressionNode;
|
||||
AOperator: TBinaryOperator;
|
||||
ARight: IExpressionNode
|
||||
): IBinaryExpressionNode; static;
|
||||
class function UnaryExpr(AOperator: TUnaryOperator; ARight: IExpressionNode): IUnaryExpressionNode; static;
|
||||
class function IfExpr(ACondition: IExpressionNode; AThenBranch, AElseBranch: IExpressionNode): IIfExpressionNode; static;
|
||||
class function LambdaExpr(const AParameters: array of IIdentifierNode; ABody: IAstNode): ILambdaExpressionNode; static;
|
||||
class function FunctionCall(ACallee: IExpressionNode; const AArguments: array of IExpressionNode): IFunctionCallNode; static;
|
||||
// Statements
|
||||
class function Block(const AStatements: array of IStatementNode): IBlockStatementNode; static;
|
||||
class function VarDecl(AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationStatementNode; static;
|
||||
class function ExprStmt(AExpression: IExpressionNode): IExpressionStatementNode; static;
|
||||
end;
|
||||
|
||||
// Manages the scope of execution, holding variables and their values.
|
||||
TExecutionScope = class
|
||||
private
|
||||
FParent: TExecutionScope;
|
||||
FVariables: TDictionary<string, IDataValue>;
|
||||
public
|
||||
constructor Create(AParent: TExecutionScope = nil);
|
||||
destructor Destroy; override;
|
||||
function FindValue(const Name: string; out Value: IDataValue): Boolean;
|
||||
procedure SetValue(const Name: string; const Value: IDataValue);
|
||||
end;
|
||||
|
||||
// TEvaluatorVisitor is stateless and thread-safe.
|
||||
TEvaluatorVisitor = class(TInterfacedObject, IAstVisitor)
|
||||
private
|
||||
FScope: TExecutionScope;
|
||||
function IsTruthy(AValue: IDataValue): Boolean;
|
||||
public
|
||||
constructor Create(AScope: TExecutionScope);
|
||||
// Expression visitors
|
||||
function VisitConstant(const Node: IConstantNode): IDataValue;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): IDataValue;
|
||||
function VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue;
|
||||
function VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue;
|
||||
function VisitIfExpression(const Node: IIfExpressionNode): IDataValue;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue;
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): IDataValue;
|
||||
|
||||
// Statement visitors
|
||||
function VisitBlockStatement(const Node: IBlockStatementNode): IDataValue;
|
||||
function VisitVariableDeclarationStatement(const Node: IVariableDeclarationStatementNode): IDataValue;
|
||||
function VisitExpressionStatement(const Node: IExpressionStatementNode): IDataValue;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
type
|
||||
TConstantNodeImpl = class(TInterfacedObject, IConstantNode)
|
||||
private
|
||||
FValue: IDataValue;
|
||||
function GetValue: IDataValue;
|
||||
public
|
||||
constructor Create(AValue: IDataValue);
|
||||
function Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
end;
|
||||
|
||||
TIdentifierNodeImpl = class(TInterfacedObject, IIdentifierNode)
|
||||
private
|
||||
FName: string;
|
||||
function GetName: string;
|
||||
public
|
||||
constructor Create(AName: string);
|
||||
function Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
end;
|
||||
|
||||
TBinaryExpressionNodeImpl = class(TInterfacedObject, IBinaryExpressionNode)
|
||||
private
|
||||
FLeft: IExpressionNode;
|
||||
FOperator: TBinaryOperator;
|
||||
FRight: IExpressionNode;
|
||||
function GetLeft: IExpressionNode;
|
||||
function GetOperator: TBinaryOperator;
|
||||
function GetRight: IExpressionNode;
|
||||
public
|
||||
constructor Create(ALeft: IExpressionNode; AOperator: TBinaryOperator; ARight: IExpressionNode);
|
||||
function Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
end;
|
||||
|
||||
TUnaryExpressionNodeImpl = class(TInterfacedObject, IUnaryExpressionNode)
|
||||
private
|
||||
FOperator: TUnaryOperator;
|
||||
FRight: IExpressionNode;
|
||||
function GetOperator: TUnaryOperator;
|
||||
function GetRight: IExpressionNode;
|
||||
public
|
||||
constructor Create(AOperator: TUnaryOperator; ARight: IExpressionNode);
|
||||
function Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
end;
|
||||
|
||||
TIfExpressionNodeImpl = class(TInterfacedObject, IIfExpressionNode)
|
||||
private
|
||||
FCondition: IExpressionNode;
|
||||
FThenBranch: IExpressionNode;
|
||||
FElseBranch: IExpressionNode;
|
||||
function GetCondition: IExpressionNode;
|
||||
function GetThenBranch: IExpressionNode;
|
||||
function GetElseBranch: IExpressionNode;
|
||||
public
|
||||
constructor Create(ACondition, AThenBranch, AElseBranch: IExpressionNode);
|
||||
function Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
end;
|
||||
|
||||
TLambdaExpressionNodeImpl = class(TInterfacedObject, ILambdaExpressionNode)
|
||||
private
|
||||
FParameters: TList<IIdentifierNode>;
|
||||
FBody: IAstNode;
|
||||
function GetParameters: TList<IIdentifierNode>;
|
||||
function GetBody: IAstNode;
|
||||
public
|
||||
constructor Create(AParameters: TList<IIdentifierNode>; ABody: IAstNode);
|
||||
destructor Destroy; override;
|
||||
function Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
end;
|
||||
|
||||
TFunctionCallNodeImpl = class(TInterfacedObject, IFunctionCallNode)
|
||||
private
|
||||
FCallee: IExpressionNode;
|
||||
FArguments: TList<IExpressionNode>;
|
||||
function GetCallee: IExpressionNode;
|
||||
function GetArguments: TList<IExpressionNode>;
|
||||
public
|
||||
constructor Create(ACallee: IExpressionNode; AArguments: TList<IExpressionNode>);
|
||||
destructor Destroy; override;
|
||||
function Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
end;
|
||||
|
||||
TBlockStatementNodeImpl = class(TInterfacedObject, IBlockStatementNode)
|
||||
private
|
||||
FStatements: TList<IStatementNode>;
|
||||
function GetStatements: TList<IStatementNode>;
|
||||
public
|
||||
constructor Create(AStatements: TList<IStatementNode>);
|
||||
destructor Destroy; override;
|
||||
function Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
end;
|
||||
|
||||
TVariableDeclarationStatementNodeImpl = class(TInterfacedObject, IVariableDeclarationStatementNode)
|
||||
private
|
||||
FIdentifier: IIdentifierNode;
|
||||
FInitializer: IExpressionNode;
|
||||
function GetIdentifier: IIdentifierNode;
|
||||
function GetInitializer: IExpressionNode;
|
||||
public
|
||||
constructor Create(AIdentifier: IIdentifierNode; AInitializer: IExpressionNode);
|
||||
function Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
end;
|
||||
|
||||
TExpressionStatementNodeImpl = class(TInterfacedObject, IExpressionStatementNode)
|
||||
private
|
||||
FExpression: IExpressionNode;
|
||||
function GetExpression: IExpressionNode;
|
||||
public
|
||||
constructor Create(AExpression: IExpressionNode);
|
||||
function Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
end;
|
||||
|
||||
{ TAst - Factory Function Implementations }
|
||||
|
||||
class function TAst.Constant(AValue: IDataValue): IConstantNode;
|
||||
begin
|
||||
Result := TConstantNodeImpl.Create(AValue);
|
||||
end;
|
||||
|
||||
class function TAst.Identifier(AName: string): IIdentifierNode;
|
||||
begin
|
||||
Result := TIdentifierNodeImpl.Create(AName);
|
||||
end;
|
||||
|
||||
class function TAst.BinaryExpr(ALeft: IExpressionNode; AOperator: TBinaryOperator; ARight: IExpressionNode): IBinaryExpressionNode;
|
||||
begin
|
||||
Result := TBinaryExpressionNodeImpl.Create(ALeft, AOperator, ARight);
|
||||
end;
|
||||
|
||||
class function TAst.UnaryExpr(AOperator: TUnaryOperator; ARight: IExpressionNode): IUnaryExpressionNode;
|
||||
begin
|
||||
Result := TUnaryExpressionNodeImpl.Create(AOperator, ARight);
|
||||
end;
|
||||
|
||||
class function TAst.IfExpr(ACondition: IExpressionNode; AThenBranch, AElseBranch: IExpressionNode): IIfExpressionNode;
|
||||
begin
|
||||
Result := TIfExpressionNodeImpl.Create(ACondition, AThenBranch, AElseBranch);
|
||||
end;
|
||||
|
||||
class function TAst.LambdaExpr(const AParameters: array of IIdentifierNode; ABody: IAstNode): ILambdaExpressionNode;
|
||||
var
|
||||
paramList: TList<IIdentifierNode>;
|
||||
param: IIdentifierNode;
|
||||
begin
|
||||
paramList := TList<IIdentifierNode>.Create;
|
||||
for param in AParameters do
|
||||
paramList.Add(param);
|
||||
Result := TLambdaExpressionNodeImpl.Create(paramList, ABody);
|
||||
end;
|
||||
|
||||
class function TAst.FunctionCall(ACallee: IExpressionNode; const AArguments: array of IExpressionNode): IFunctionCallNode;
|
||||
var
|
||||
argList: TList<IExpressionNode>;
|
||||
arg: IExpressionNode;
|
||||
begin
|
||||
argList := TList<IExpressionNode>.Create;
|
||||
for arg in AArguments do
|
||||
argList.Add(arg);
|
||||
Result := TFunctionCallNodeImpl.Create(ACallee, argList);
|
||||
end;
|
||||
|
||||
class function TAst.Block(const AStatements: array of IStatementNode): IBlockStatementNode;
|
||||
var
|
||||
stmtList: TList<IStatementNode>;
|
||||
stmt: IStatementNode;
|
||||
begin
|
||||
stmtList := TList<IStatementNode>.Create;
|
||||
for stmt in AStatements do
|
||||
stmtList.Add(stmt);
|
||||
Result := TBlockStatementNodeImpl.Create(stmtList);
|
||||
end;
|
||||
|
||||
class function TAst.VarDecl(AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationStatementNode;
|
||||
begin
|
||||
Result := TVariableDeclarationStatementNodeImpl.Create(AIdentifier, AInitializer);
|
||||
end;
|
||||
|
||||
class function TAst.ExprStmt(AExpression: IExpressionNode): IExpressionStatementNode;
|
||||
begin
|
||||
Result := TExpressionStatementNodeImpl.Create(AExpression);
|
||||
end;
|
||||
|
||||
{ TConstantNodeImpl }
|
||||
|
||||
constructor TConstantNodeImpl.Create(AValue: IDataValue);
|
||||
begin
|
||||
inherited Create;
|
||||
FValue := AValue;
|
||||
end;
|
||||
|
||||
function TConstantNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
begin
|
||||
Result := Visitor.VisitConstant(Self);
|
||||
end;
|
||||
|
||||
function TConstantNodeImpl.GetValue: IDataValue;
|
||||
begin
|
||||
Result := FValue;
|
||||
end;
|
||||
|
||||
{ TIdentifierNodeImpl }
|
||||
|
||||
constructor TIdentifierNodeImpl.Create(AName: string);
|
||||
begin
|
||||
inherited Create;
|
||||
FName := AName;
|
||||
end;
|
||||
|
||||
function TIdentifierNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
begin
|
||||
Result := Visitor.VisitIdentifier(Self);
|
||||
end;
|
||||
|
||||
function TIdentifierNodeImpl.GetName: string;
|
||||
begin
|
||||
Result := FName;
|
||||
end;
|
||||
|
||||
{ TBinaryExpressionNodeImpl }
|
||||
|
||||
constructor TBinaryExpressionNodeImpl.Create(ALeft: IExpressionNode; AOperator: TBinaryOperator; ARight: IExpressionNode);
|
||||
begin
|
||||
inherited Create;
|
||||
FLeft := ALeft;
|
||||
FOperator := AOperator;
|
||||
FRight := ARight;
|
||||
end;
|
||||
|
||||
function TBinaryExpressionNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
begin
|
||||
Result := Visitor.VisitBinaryExpression(Self);
|
||||
end;
|
||||
|
||||
function TBinaryExpressionNodeImpl.GetLeft: IExpressionNode;
|
||||
begin
|
||||
Result := FLeft;
|
||||
end;
|
||||
|
||||
function TBinaryExpressionNodeImpl.GetOperator: TBinaryOperator;
|
||||
begin
|
||||
Result := FOperator;
|
||||
end;
|
||||
|
||||
function TBinaryExpressionNodeImpl.GetRight: IExpressionNode;
|
||||
begin
|
||||
Result := FRight;
|
||||
end;
|
||||
|
||||
{ TUnaryExpressionNodeImpl }
|
||||
|
||||
constructor TUnaryExpressionNodeImpl.Create(AOperator: TUnaryOperator; ARight: IExpressionNode);
|
||||
begin
|
||||
inherited Create;
|
||||
FOperator := AOperator;
|
||||
FRight := ARight;
|
||||
end;
|
||||
|
||||
function TUnaryExpressionNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
begin
|
||||
Result := Visitor.VisitUnaryExpression(Self);
|
||||
end;
|
||||
|
||||
function TUnaryExpressionNodeImpl.GetOperator: TUnaryOperator;
|
||||
begin
|
||||
Result := FOperator;
|
||||
end;
|
||||
|
||||
function TUnaryExpressionNodeImpl.GetRight: IExpressionNode;
|
||||
begin
|
||||
Result := FRight;
|
||||
end;
|
||||
|
||||
{ TIfExpressionNodeImpl }
|
||||
|
||||
constructor TIfExpressionNodeImpl.Create(ACondition, AThenBranch, AElseBranch: IExpressionNode);
|
||||
begin
|
||||
inherited Create;
|
||||
FCondition := ACondition;
|
||||
FThenBranch := AThenBranch;
|
||||
FElseBranch := AElseBranch;
|
||||
end;
|
||||
|
||||
function TIfExpressionNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
begin
|
||||
Result := Visitor.VisitIfExpression(Self);
|
||||
end;
|
||||
|
||||
function TIfExpressionNodeImpl.GetCondition: IExpressionNode;
|
||||
begin
|
||||
Result := FCondition;
|
||||
end;
|
||||
|
||||
function TIfExpressionNodeImpl.GetElseBranch: IExpressionNode;
|
||||
begin
|
||||
Result := FElseBranch;
|
||||
end;
|
||||
|
||||
function TIfExpressionNodeImpl.GetThenBranch: IExpressionNode;
|
||||
begin
|
||||
Result := FThenBranch;
|
||||
end;
|
||||
|
||||
{ TLambdaExpressionNodeImpl }
|
||||
|
||||
constructor TLambdaExpressionNodeImpl.Create(AParameters: TList<IIdentifierNode>; ABody: IAstNode);
|
||||
begin
|
||||
inherited Create;
|
||||
FParameters := AParameters;
|
||||
FBody := ABody;
|
||||
end;
|
||||
|
||||
destructor TLambdaExpressionNodeImpl.Destroy;
|
||||
begin
|
||||
FParameters.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TLambdaExpressionNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
begin
|
||||
Result := Visitor.VisitLambdaExpression(Self);
|
||||
end;
|
||||
|
||||
function TLambdaExpressionNodeImpl.GetBody: IAstNode;
|
||||
begin
|
||||
Result := FBody;
|
||||
end;
|
||||
|
||||
function TLambdaExpressionNodeImpl.GetParameters: TList<IIdentifierNode>;
|
||||
begin
|
||||
Result := FParameters;
|
||||
end;
|
||||
|
||||
{ TFunctionCallNodeImpl }
|
||||
|
||||
constructor TFunctionCallNodeImpl.Create(ACallee: IExpressionNode; AArguments: TList<IExpressionNode>);
|
||||
begin
|
||||
inherited Create;
|
||||
FCallee := ACallee;
|
||||
FArguments := AArguments;
|
||||
end;
|
||||
|
||||
destructor TFunctionCallNodeImpl.Destroy;
|
||||
begin
|
||||
FArguments.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TFunctionCallNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
begin
|
||||
Result := Visitor.VisitFunctionCall(Self);
|
||||
end;
|
||||
|
||||
function TFunctionCallNodeImpl.GetArguments: TList<IExpressionNode>;
|
||||
begin
|
||||
Result := FArguments;
|
||||
end;
|
||||
|
||||
function TFunctionCallNodeImpl.GetCallee: IExpressionNode;
|
||||
begin
|
||||
Result := FCallee;
|
||||
end;
|
||||
|
||||
{ TBlockStatementNodeImpl }
|
||||
|
||||
constructor TBlockStatementNodeImpl.Create(AStatements: TList<IStatementNode>);
|
||||
begin
|
||||
inherited Create;
|
||||
FStatements := AStatements;
|
||||
end;
|
||||
|
||||
destructor TBlockStatementNodeImpl.Destroy;
|
||||
begin
|
||||
FStatements.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TBlockStatementNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
begin
|
||||
Result := Visitor.VisitBlockStatement(Self);
|
||||
end;
|
||||
|
||||
function TBlockStatementNodeImpl.GetStatements: TList<IStatementNode>;
|
||||
begin
|
||||
Result := FStatements;
|
||||
end;
|
||||
|
||||
{ TVariableDeclarationStatementNodeImpl }
|
||||
|
||||
constructor TVariableDeclarationStatementNodeImpl.Create(AIdentifier: IIdentifierNode; AInitializer: IExpressionNode);
|
||||
begin
|
||||
inherited Create;
|
||||
FIdentifier := AIdentifier;
|
||||
FInitializer := AInitializer;
|
||||
end;
|
||||
|
||||
function TVariableDeclarationStatementNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
begin
|
||||
Result := Visitor.VisitVariableDeclarationStatement(Self);
|
||||
end;
|
||||
|
||||
function TVariableDeclarationStatementNodeImpl.GetIdentifier: IIdentifierNode;
|
||||
begin
|
||||
Result := FIdentifier;
|
||||
end;
|
||||
|
||||
function TVariableDeclarationStatementNodeImpl.GetInitializer: IExpressionNode;
|
||||
begin
|
||||
Result := FInitializer;
|
||||
end;
|
||||
|
||||
{ TExpressionStatementNodeImpl }
|
||||
|
||||
constructor TExpressionStatementNodeImpl.Create(AExpression: IExpressionNode);
|
||||
begin
|
||||
inherited Create;
|
||||
FExpression := AExpression;
|
||||
end;
|
||||
|
||||
function TExpressionStatementNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue;
|
||||
begin
|
||||
Result := Visitor.VisitExpressionStatement(Self);
|
||||
end;
|
||||
|
||||
function TExpressionStatementNodeImpl.GetExpression: IExpressionNode;
|
||||
begin
|
||||
Result := FExpression;
|
||||
end;
|
||||
|
||||
{ TExecutionScope }
|
||||
|
||||
constructor TExecutionScope.Create(AParent: TExecutionScope = nil);
|
||||
begin
|
||||
inherited Create;
|
||||
FParent := AParent;
|
||||
FVariables := TDictionary<string, IDataValue>.Create;
|
||||
end;
|
||||
|
||||
destructor TExecutionScope.Destroy;
|
||||
begin
|
||||
FVariables.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TExecutionScope.FindValue(const Name: string; out Value: IDataValue): Boolean;
|
||||
begin
|
||||
Result := FVariables.TryGetValue(Name, Value);
|
||||
if not Result and Assigned(FParent) then
|
||||
begin
|
||||
Result := FParent.FindValue(Name, Value);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TExecutionScope.SetValue(const Name: string; const Value: IDataValue);
|
||||
begin
|
||||
// This defines a variable in the current scope. It can shadow a parent variable.
|
||||
FVariables.AddOrSetValue(Name, Value);
|
||||
end;
|
||||
|
||||
{ TEvaluatorVisitor }
|
||||
|
||||
constructor TEvaluatorVisitor.Create(AScope: TExecutionScope);
|
||||
begin
|
||||
inherited Create;
|
||||
Assert(Assigned(AScope));
|
||||
FScope := AScope;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.IsTruthy(AValue: IDataValue): Boolean;
|
||||
begin
|
||||
// Defines the language's concept of "truthiness".
|
||||
// For now, only ordinals can be conditions. 0 is false, everything else is true.
|
||||
if not Assigned(AValue) then
|
||||
Exit(False);
|
||||
|
||||
case AValue.DataType.Kind of
|
||||
dkOrdinal: Result := (TDataType.TValue(AValue).AsOrdinal.Value <> 0);
|
||||
else
|
||||
Result := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): IDataValue;
|
||||
begin
|
||||
Result := Node.Value;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): IDataValue;
|
||||
var
|
||||
val: IDataValue;
|
||||
begin
|
||||
if FScope.FindValue(Node.Name, val) then
|
||||
Result := val
|
||||
else
|
||||
raise EArgumentException.CreateFmt('Identifier not found: "%s"', [Node.Name]);
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue;
|
||||
var
|
||||
leftValue, rightValue: IDataValue;
|
||||
begin
|
||||
leftValue := Node.Left.Accept(Self);
|
||||
rightValue := Node.Right.Accept(Self);
|
||||
|
||||
if (leftValue.DataType.Kind <> rightValue.DataType.Kind) then
|
||||
raise ENotSupportedException.CreateFmt(
|
||||
'Binary operations on different types (%s and %s) are not supported',
|
||||
[leftValue.DataType.Name, rightValue.DataType.Name]);
|
||||
|
||||
case leftValue.DataType.Kind of
|
||||
dkOrdinal:
|
||||
begin
|
||||
var leftOrdinal := TDataType.TValue(leftValue).AsOrdinal;
|
||||
var rightOrdinal := TDataType.TValue(rightValue).AsOrdinal;
|
||||
var resultVal: Int64;
|
||||
|
||||
case Node.Operator of
|
||||
boAdd: resultVal := leftOrdinal.Value + rightOrdinal.Value;
|
||||
boSubtract: resultVal := leftOrdinal.Value - rightOrdinal.Value;
|
||||
boMultiply: resultVal := leftOrdinal.Value * rightOrdinal.Value;
|
||||
boDivide: resultVal := leftOrdinal.Value div rightOrdinal.Value;
|
||||
else
|
||||
raise ENotSupportedException.Create('Operator not supported for Ordinal type');
|
||||
end;
|
||||
Result := TDataType.Ordinal.CreateValue(resultVal);
|
||||
end;
|
||||
dkText:
|
||||
begin
|
||||
if (Node.Operator = boAdd) then
|
||||
begin
|
||||
var leftText := TDataType.TValue(leftValue).AsText;
|
||||
var rightText := TDataType.TValue(rightValue).AsText;
|
||||
Result := TDataType.Text.CreateValue(leftText.Value + rightText.Value);
|
||||
end
|
||||
else
|
||||
raise ENotSupportedException.Create('Operator not supported for Text type');
|
||||
end;
|
||||
else
|
||||
raise ENotSupportedException.CreateFmt('Binary operation not supported for type %s', [leftValue.DataType.Name]);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue;
|
||||
var
|
||||
rightValue: IDataValue;
|
||||
ordinalVal: IDataOrdinalValue;
|
||||
begin
|
||||
rightValue := Node.Right.Accept(Self);
|
||||
|
||||
case Node.Operator of
|
||||
uoNegate:
|
||||
begin
|
||||
if (rightValue.DataType.Kind = dkOrdinal) then
|
||||
begin
|
||||
ordinalVal := TDataType.TValue(rightValue).AsOrdinal;
|
||||
Result := TDataType.Ordinal.CreateValue(-ordinalVal.Value);
|
||||
end
|
||||
else
|
||||
raise ENotSupportedException.CreateFmt('Unary "-" not supported for type %s', [rightValue.DataType.Name]);
|
||||
end;
|
||||
uoNot: raise ENotImplemented.Create('Unary "not" operator is not yet implemented');
|
||||
else
|
||||
raise ENotSupportedException.Create('Unary operator not supported');
|
||||
end;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): IDataValue;
|
||||
var
|
||||
conditionValue: IDataValue;
|
||||
begin
|
||||
conditionValue := Node.Condition.Accept(Self);
|
||||
|
||||
if IsTruthy(conditionValue) then
|
||||
Result := Node.ThenBranch.Accept(Self)
|
||||
else
|
||||
Result := Node.ElseBranch.Accept(Self);
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue;
|
||||
var
|
||||
lambdaBody: IAstNode;
|
||||
lambdaParams: TList<IIdentifierNode>;
|
||||
closureScope: TExecutionScope;
|
||||
methodType: IDataMethodType;
|
||||
begin
|
||||
closureScope := FScope;
|
||||
lambdaBody := Node.Body;
|
||||
lambdaParams := Node.Parameters;
|
||||
methodType := TDataType.MethodOf(TDataType.Ordinal, TDataType.Ordinal);
|
||||
|
||||
Result :=
|
||||
methodType.CreateValue(
|
||||
function(const AValue: IDataValue): IDataValue
|
||||
var
|
||||
callScope: TExecutionScope;
|
||||
innerVisitor: IAstVisitor;
|
||||
begin
|
||||
callScope := TExecutionScope.Create(closureScope);
|
||||
try
|
||||
if (lambdaParams.Count <> 1) then
|
||||
raise EArgumentException.Create('This simple implementation only supports single-parameter lambdas.');
|
||||
|
||||
callScope.SetValue(lambdaParams[0].Name, AValue);
|
||||
|
||||
innerVisitor := TEvaluatorVisitor.Create(callScope);
|
||||
Result := lambdaBody.Accept(innerVisitor);
|
||||
finally
|
||||
callScope.Free;
|
||||
end;
|
||||
end
|
||||
);
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): IDataValue;
|
||||
var
|
||||
calleeValue, argValue: IDataValue;
|
||||
methodProc: TDataMethodProc;
|
||||
arguments: TList<IExpressionNode>;
|
||||
begin
|
||||
calleeValue := Node.Callee.Accept(Self);
|
||||
|
||||
if (calleeValue.DataType.Kind <> dkMethod) then
|
||||
raise EArgumentException.Create('Expression is not callable.');
|
||||
|
||||
arguments := Node.Arguments;
|
||||
if (arguments.Count <> 1) then
|
||||
raise EArgumentException.Create('This simple implementation only supports single-argument calls.');
|
||||
|
||||
argValue := arguments[0].Accept(Self);
|
||||
|
||||
methodProc := TDataType.TValue(calleeValue).AsMethod.Value;
|
||||
Result := methodProc(argValue);
|
||||
end;
|
||||
|
||||
// --- Statement Visitor Implementations ---
|
||||
|
||||
function TEvaluatorVisitor.VisitBlockStatement(const Node: IBlockStatementNode): IDataValue;
|
||||
var
|
||||
statement: IStatementNode;
|
||||
begin
|
||||
// Execute all statements in the block sequentially.
|
||||
for statement in Node.Statements do
|
||||
begin
|
||||
statement.Accept(Self); // The result is ignored.
|
||||
end;
|
||||
Result := TDataType.Void.Value;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitVariableDeclarationStatement(const Node: IVariableDeclarationStatementNode): IDataValue;
|
||||
var
|
||||
varName: string;
|
||||
initValue: IDataValue;
|
||||
begin
|
||||
varName := Node.Identifier.Name;
|
||||
|
||||
// Evaluate the initializer expression, if it exists.
|
||||
if Assigned(Node.Initializer) then
|
||||
initValue := Node.Initializer.Accept(Self)
|
||||
else
|
||||
initValue := TDataType.Void.Value; // Default value if no initializer is provided.
|
||||
|
||||
// Define the variable in the current scope.
|
||||
FScope.SetValue(varName, initValue);
|
||||
|
||||
Result := TDataType.Void.Value;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitExpressionStatement(const Node: IExpressionStatementNode): IDataValue;
|
||||
begin
|
||||
// Evaluate the expression for its side-effects and discard the result.
|
||||
Node.Expression.Accept(Self);
|
||||
Result := TDataType.Void.Value;
|
||||
end;
|
||||
|
||||
end.
|
||||
@@ -0,0 +1,102 @@
|
||||
unit TestAst;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Myc.Ast,
|
||||
DUnitX.TestFramework;
|
||||
|
||||
type
|
||||
[TestFixture]
|
||||
TTestAst = class
|
||||
private
|
||||
FScope: TExecutionScope;
|
||||
FVisitor: IAstVisitor;
|
||||
public
|
||||
[Setup]
|
||||
procedure Setup;
|
||||
[TearDown]
|
||||
procedure TearDown;
|
||||
|
||||
[Test]
|
||||
procedure TestHelloWorld;
|
||||
[Test]
|
||||
procedure TestVariableDeclarationAndUsage;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.Generics.Collections,
|
||||
Myc.Data.Types;
|
||||
|
||||
{ TTestAst }
|
||||
|
||||
procedure TTestAst.Setup;
|
||||
begin
|
||||
// Create scope and visitor once for each test
|
||||
FScope := TExecutionScope.Create;
|
||||
FVisitor := TEvaluatorVisitor.Create(FScope);
|
||||
end;
|
||||
|
||||
procedure TTestAst.TearDown;
|
||||
begin
|
||||
FVisitor := nil;
|
||||
FScope.Free;
|
||||
end;
|
||||
|
||||
procedure TTestAst.TestHelloWorld;
|
||||
var
|
||||
ast: IExpressionNode;
|
||||
resultValue: IDataValue;
|
||||
textValue: IDataTextValue;
|
||||
begin
|
||||
// 1. Build the AST for the expression: "hello " + "world"
|
||||
ast := TAst.BinaryExpr(TAst.Constant(TDataType.Text.CreateValue('hello ')), boAdd, TAst.Constant(TDataType.Text.CreateValue('world')));
|
||||
|
||||
// 2. Execute (evaluate) the AST
|
||||
resultValue := ast.Accept(FVisitor);
|
||||
|
||||
// 3. Assert the result
|
||||
Assert.IsNotNull(resultValue, 'The evaluation must return a value.');
|
||||
Assert.AreEqual(dkText, resultValue.DataType.Kind, 'The result must be of text kind.');
|
||||
|
||||
textValue := TDataType.TValue(resultValue).AsText;
|
||||
Assert.AreEqual('hello world', textValue.Value, 'The string concatenation result is wrong.');
|
||||
end;
|
||||
|
||||
procedure TTestAst.TestVariableDeclarationAndUsage;
|
||||
var
|
||||
varDeclStmt: IStatementNode;
|
||||
blockStmt: IStatementNode;
|
||||
identifierExpr: IExpressionNode;
|
||||
resultValue: IDataValue;
|
||||
ordinalValue: IDataOrdinalValue;
|
||||
begin
|
||||
// 1. Build the AST for the statement: let myVar = 42;
|
||||
varDeclStmt := TAst.VarDecl(TAst.Identifier('myVar'), TAst.Constant(TDataType.Ordinal.CreateValue(42)));
|
||||
|
||||
// It's good practice to wrap statements in a block
|
||||
blockStmt := TAst.Block([varDeclStmt]);
|
||||
|
||||
// 2. Build the AST for the expression that uses the variable: myVar
|
||||
identifierExpr := TAst.Identifier('myVar');
|
||||
|
||||
// 3. Execute the statement to declare and assign the variable
|
||||
blockStmt.Accept(FVisitor);
|
||||
|
||||
// 4. Execute the expression to retrieve the variable's value
|
||||
resultValue := identifierExpr.Accept(FVisitor);
|
||||
|
||||
// 5. Assert the result
|
||||
Assert.IsNotNull(resultValue, 'The evaluation must return a value.');
|
||||
Assert.AreEqual(dkOrdinal, resultValue.DataType.Kind, 'The result must be of ordinal kind.');
|
||||
|
||||
ordinalValue := TDataType.TValue(resultValue).AsOrdinal;
|
||||
Assert.AreEqual(Int64(42), ordinalValue.Value, 'The variable value is wrong.');
|
||||
end;
|
||||
|
||||
initialization
|
||||
TDUnitX.RegisterTestFixture(TTestAst);
|
||||
|
||||
end.
|
||||
Reference in New Issue
Block a user