Ast Binding
This commit is contained in:
@@ -145,6 +145,7 @@ type
|
|||||||
{$region 'private'}
|
{$region 'private'}
|
||||||
function GetName: string;
|
function GetName: string;
|
||||||
{$endregion}
|
{$endregion}
|
||||||
|
function Resolve(out ScopeDepth: Integer; out SlotIndex: Integer): Boolean;
|
||||||
property Name: string read GetName;
|
property Name: string read GetName;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
+327
-4
@@ -10,6 +10,7 @@ uses
|
|||||||
type
|
type
|
||||||
// Record acting as a namespace for the factory functions.
|
// Record acting as a namespace for the factory functions.
|
||||||
TAst = record
|
TAst = record
|
||||||
|
// --- Existing factory functions ---
|
||||||
class function Constant(AValue: TScalar): IConstantNode; static;
|
class function Constant(AValue: TScalar): IConstantNode; static;
|
||||||
class function Identifier(AName: string): IIdentifierNode; static;
|
class function Identifier(AName: string): IIdentifierNode; static;
|
||||||
class function BinaryExpr(
|
class function BinaryExpr(
|
||||||
@@ -31,7 +32,7 @@ type
|
|||||||
class function Block(const AExpressions: array of IExpressionNode): IBlockExpressionNode; static;
|
class function Block(const AExpressions: array of IExpressionNode): IBlockExpressionNode; static;
|
||||||
class function VarDecl(const AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationNode; static;
|
class function VarDecl(const AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationNode; static;
|
||||||
class function Assign(const AIdentifier: IIdentifierNode; const AValue: IExpressionNode): IAssignmentNode; static;
|
class function Assign(const AIdentifier: IIdentifierNode; const AValue: IExpressionNode): IAssignmentNode; static;
|
||||||
class function AssignResult(const AValue: IExpressionNode): IAssignmentNode; static;
|
class function AssignResult(const AValue: IExpressionNode): IAssignmentNode; static; deprecated;
|
||||||
class function Indexer(const ABase: IExpressionNode; const AIndex: IExpressionNode): IIndexerNode; static;
|
class function Indexer(const ABase: IExpressionNode; const AIndex: IExpressionNode): IIndexerNode; static;
|
||||||
class function MemberAccess(const ABase: IExpressionNode; const AMember: IIdentifierNode): IMemberAccessNode; static;
|
class function MemberAccess(const ABase: IExpressionNode; const AMember: IIdentifierNode): IMemberAccessNode; static;
|
||||||
class function CreateSeries(const ADefinition: String): ICreateSeriesNode; static;
|
class function CreateSeries(const ADefinition: String): ICreateSeriesNode; static;
|
||||||
@@ -40,8 +41,32 @@ type
|
|||||||
const AValue: IExpressionNode;
|
const AValue: IExpressionNode;
|
||||||
const ALookback: IExpressionNode = nil
|
const ALookback: IExpressionNode = nil
|
||||||
): IAddSeriesItemNode; static;
|
): IAddSeriesItemNode; static;
|
||||||
// Added the new factory function for SeriesLength
|
|
||||||
class function SeriesLength(const ASeries: IIdentifierNode): ISeriesLengthNode; static;
|
class function SeriesLength(const ASeries: IIdentifierNode): ISeriesLengthNode; static;
|
||||||
|
|
||||||
|
// --- Static method to run the binder ---
|
||||||
|
// Traverses the AST and resolves all identifiers. This must be called before evaluating the AST.
|
||||||
|
class procedure Bind(const ARootNode: IExpressionNode); static;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// TAstTraverser provides a default AST traversal implementation.
|
||||||
|
TAstTraverser = class abstract(TInterfacedObject, IAstVisitor)
|
||||||
|
public
|
||||||
|
function VisitConstant(const Node: IConstantNode): TAstValue; virtual;
|
||||||
|
function VisitIdentifier(const Node: IIdentifierNode): TAstValue; virtual;
|
||||||
|
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;
|
||||||
|
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; virtual;
|
||||||
|
function VisitAssignment(const Node: IAssignmentNode): TAstValue; virtual;
|
||||||
|
function VisitIndexer(const Node: IIndexerNode): TAstValue; virtual;
|
||||||
|
function VisitMemberAccess(const Node: IMemberAccessNode): TAstValue; virtual;
|
||||||
|
function VisitCreateSeries(const Node: ICreateSeriesNode): TAstValue; virtual;
|
||||||
|
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TAstValue; virtual;
|
||||||
|
function VisitSeriesLength(const Node: ISeriesLengthNode): TAstValue; virtual;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@@ -69,13 +94,22 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
{ TIdentifierNode }
|
{ TIdentifierNode }
|
||||||
|
// TIdentifierNode now includes fields for binder annotations.
|
||||||
TIdentifierNode = class(TAstNode, IIdentifierNode)
|
TIdentifierNode = class(TAstNode, IIdentifierNode)
|
||||||
private
|
private
|
||||||
FName: string;
|
FName: string;
|
||||||
|
// --- Annotation fields added for the binder ---
|
||||||
|
FIsResolved: Boolean;
|
||||||
|
FScopeDepth: Integer;
|
||||||
|
FSlotIndex: Integer;
|
||||||
function GetName: string;
|
function GetName: string;
|
||||||
public
|
public
|
||||||
constructor Create(AName: string);
|
constructor Create(AName: string);
|
||||||
function Accept(const Visitor: IAstVisitor): TAstValue; override;
|
function Accept(const Visitor: IAstVisitor): TAstValue; override;
|
||||||
|
|
||||||
|
function Resolve(out ScopeDepth: Integer; out SlotIndex: Integer): Boolean;
|
||||||
|
|
||||||
|
property Name: string read FName;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TBinaryExpressionNode }
|
{ TBinaryExpressionNode }
|
||||||
@@ -205,7 +239,6 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
{ TMemberAccessNode }
|
{ TMemberAccessNode }
|
||||||
// Concrete implementation for the member access node.
|
|
||||||
TMemberAccessNode = class(TAstNode, IMemberAccessNode)
|
TMemberAccessNode = class(TAstNode, IMemberAccessNode)
|
||||||
private
|
private
|
||||||
FBase: IExpressionNode;
|
FBase: IExpressionNode;
|
||||||
@@ -241,7 +274,6 @@ type
|
|||||||
function Accept(const Visitor: IAstVisitor): TAstValue; override;
|
function Accept(const Visitor: IAstVisitor): TAstValue; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Added concrete class for the series length node.
|
|
||||||
{ TSeriesLengthNode }
|
{ TSeriesLengthNode }
|
||||||
TSeriesLengthNode = class(TAstNode, ISeriesLengthNode)
|
TSeriesLengthNode = class(TAstNode, ISeriesLengthNode)
|
||||||
private
|
private
|
||||||
@@ -252,6 +284,40 @@ type
|
|||||||
function Accept(const Visitor: IAstVisitor): TAstValue; override;
|
function Accept(const Visitor: IAstVisitor): TAstValue; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// --- Binder Implementation ---
|
||||||
|
|
||||||
|
TSymbol = record
|
||||||
|
Name: string;
|
||||||
|
SlotIndex: Integer;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TSymbolTable = class
|
||||||
|
private
|
||||||
|
FParent: TSymbolTable;
|
||||||
|
FSymbols: TDictionary<string, TSymbol>;
|
||||||
|
FNextSlotIndex: Integer;
|
||||||
|
public
|
||||||
|
constructor Create(AParent: TSymbolTable);
|
||||||
|
destructor Destroy; override;
|
||||||
|
function Define(const Name: string): TSymbol;
|
||||||
|
function Resolve(const Name: string; out Symbol: TSymbol; out Depth: Integer): Boolean;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// TBinder inherits from the base traverser and overrides methods with specific binding logic.
|
||||||
|
TBinder = class(TAstTraverser)
|
||||||
|
private
|
||||||
|
FCurrentScope: TSymbolTable;
|
||||||
|
procedure EnterScope;
|
||||||
|
procedure ExitScope;
|
||||||
|
public
|
||||||
|
constructor Create;
|
||||||
|
destructor Destroy; override;
|
||||||
|
function VisitIdentifier(const Node: IIdentifierNode): TAstValue; override;
|
||||||
|
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue; override;
|
||||||
|
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; override;
|
||||||
|
function VisitBlockExpression(const Node: IBlockExpressionNode): TAstValue; override;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TConstantNode }
|
{ TConstantNode }
|
||||||
|
|
||||||
constructor TConstantNode.Create(AValue: TScalar);
|
constructor TConstantNode.Create(AValue: TScalar);
|
||||||
@@ -276,6 +342,9 @@ constructor TIdentifierNode.Create(AName: string);
|
|||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FName := AName;
|
FName := AName;
|
||||||
|
FIsResolved := False;
|
||||||
|
FScopeDepth := -1;
|
||||||
|
FSlotIndex := -1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TIdentifierNode.Accept(const Visitor: IAstVisitor): TAstValue;
|
function TIdentifierNode.Accept(const Visitor: IAstVisitor): TAstValue;
|
||||||
@@ -288,6 +357,16 @@ begin
|
|||||||
Result := FName;
|
Result := FName;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TIdentifierNode.Resolve(out ScopeDepth: Integer; out SlotIndex: Integer): Boolean;
|
||||||
|
begin
|
||||||
|
Result := FIsResolved;
|
||||||
|
if Result then
|
||||||
|
begin
|
||||||
|
ScopeDepth := FScopeDepth;
|
||||||
|
SlotIndex := FSlotIndex;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TBinaryExpressionNode }
|
{ TBinaryExpressionNode }
|
||||||
|
|
||||||
constructor TBinaryExpressionNode.Create(ALeft: IExpressionNode; AOperator: TBinaryOperator; ARight: IExpressionNode);
|
constructor TBinaryExpressionNode.Create(ALeft: IExpressionNode; AOperator: TBinaryOperator; ARight: IExpressionNode);
|
||||||
@@ -650,8 +729,148 @@ begin
|
|||||||
Result := FSeries;
|
Result := FSeries;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TSymbolTable }
|
||||||
|
|
||||||
|
constructor TSymbolTable.Create(AParent: TSymbolTable);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FParent := AParent;
|
||||||
|
FSymbols := TDictionary<string, TSymbol>.Create;
|
||||||
|
FNextSlotIndex := 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TSymbolTable.Destroy;
|
||||||
|
begin
|
||||||
|
FSymbols.Free;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSymbolTable.Define(const Name: string): TSymbol;
|
||||||
|
begin
|
||||||
|
Result.Name := Name;
|
||||||
|
Result.SlotIndex := FNextSlotIndex;
|
||||||
|
inc(FNextSlotIndex);
|
||||||
|
FSymbols.Add(Name, Result);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSymbolTable.Resolve(const Name: string; out Symbol: TSymbol; out Depth: Integer): Boolean;
|
||||||
|
var
|
||||||
|
currentScope: TSymbolTable;
|
||||||
|
begin
|
||||||
|
Depth := 0;
|
||||||
|
currentScope := Self;
|
||||||
|
while Assigned(currentScope) do
|
||||||
|
begin
|
||||||
|
if currentScope.FSymbols.TryGetValue(Name, Symbol) then
|
||||||
|
Exit(True);
|
||||||
|
|
||||||
|
inc(Depth);
|
||||||
|
currentScope := currentScope.FParent;
|
||||||
|
end;
|
||||||
|
Result := False;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TBinder }
|
||||||
|
|
||||||
|
constructor TBinder.Create;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FCurrentScope := TSymbolTable.Create(nil); // Start with a global scope
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TBinder.Destroy;
|
||||||
|
begin
|
||||||
|
Assert(not Assigned(FCurrentScope.FParent), 'Scope leak in binder.');
|
||||||
|
FCurrentScope.Free;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinder.EnterScope;
|
||||||
|
begin
|
||||||
|
FCurrentScope := TSymbolTable.Create(FCurrentScope);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinder.ExitScope;
|
||||||
|
var
|
||||||
|
oldScope: TSymbolTable;
|
||||||
|
begin
|
||||||
|
oldScope := FCurrentScope;
|
||||||
|
FCurrentScope := FCurrentScope.FParent;
|
||||||
|
oldScope.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TBinder.VisitBlockExpression(const Node: IBlockExpressionNode): TAstValue;
|
||||||
|
begin
|
||||||
|
EnterScope;
|
||||||
|
try
|
||||||
|
// Default traversal of all expressions in the block
|
||||||
|
inherited VisitBlockExpression(Node);
|
||||||
|
finally
|
||||||
|
ExitScope;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TBinder.VisitIdentifier(const Node: IIdentifierNode): TAstValue;
|
||||||
|
var
|
||||||
|
symbol: TSymbol;
|
||||||
|
depth: Integer;
|
||||||
|
identNode: TIdentifierNode;
|
||||||
|
begin
|
||||||
|
identNode := Node as TIdentifierNode;
|
||||||
|
if identNode.FIsResolved then
|
||||||
|
Exit;
|
||||||
|
|
||||||
|
if FCurrentScope.Resolve(identNode.Name, symbol, depth) then
|
||||||
|
begin
|
||||||
|
identNode.FIsResolved := True;
|
||||||
|
identNode.FScopeDepth := depth;
|
||||||
|
identNode.FSlotIndex := symbol.SlotIndex;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise Exception.CreateFmt('Undefined identifier: "%s"', [identNode.Name]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue;
|
||||||
|
var
|
||||||
|
param: IIdentifierNode;
|
||||||
|
begin
|
||||||
|
EnterScope;
|
||||||
|
try
|
||||||
|
// 1. Define all parameters in the new scope.
|
||||||
|
for param in Node.Parameters do
|
||||||
|
FCurrentScope.Define(param.Name);
|
||||||
|
|
||||||
|
// 2. Now visit the parameter identifiers to resolve them to their new definitions,
|
||||||
|
// and visit the body to resolve its identifiers.
|
||||||
|
inherited VisitLambdaExpression(Node);
|
||||||
|
finally
|
||||||
|
ExitScope;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue;
|
||||||
|
begin
|
||||||
|
// 1. First, visit the initializer expression to resolve its identifiers.
|
||||||
|
if Assigned(Node.Initializer) then
|
||||||
|
Node.Initializer.Accept(Self);
|
||||||
|
|
||||||
|
// 2. Then, define the new variable in the current scope.
|
||||||
|
FCurrentScope.Define(Node.Identifier.Name);
|
||||||
|
|
||||||
|
// 3. Finally, visit the declaration's own identifier to resolve it.
|
||||||
|
Node.Identifier.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
{ TAst }
|
{ TAst }
|
||||||
|
|
||||||
|
class procedure TAst.Bind(const ARootNode: IExpressionNode);
|
||||||
|
var
|
||||||
|
binder: IAstVisitor;
|
||||||
|
begin
|
||||||
|
binder := TBinder.Create;
|
||||||
|
ARootNode.Accept(binder);
|
||||||
|
end;
|
||||||
|
|
||||||
class function TAst.AddSeriesItem(
|
class function TAst.AddSeriesItem(
|
||||||
const ASeries: IIdentifierNode;
|
const ASeries: IIdentifierNode;
|
||||||
const AValue: IExpressionNode;
|
const AValue: IExpressionNode;
|
||||||
@@ -741,4 +960,108 @@ begin
|
|||||||
Result := TVariableDeclarationNode.Create(AIdentifier, AInitializer);
|
Result := TVariableDeclarationNode.Create(AIdentifier, AInitializer);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TAstTraverser }
|
||||||
|
|
||||||
|
function TAstTraverser.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TAstValue;
|
||||||
|
begin
|
||||||
|
Node.Series.Accept(Self);
|
||||||
|
Node.Value.Accept(Self);
|
||||||
|
if Assigned(Node.Lookback) then
|
||||||
|
Node.Lookback.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitAssignment(const Node: IAssignmentNode): TAstValue;
|
||||||
|
begin
|
||||||
|
Node.Value.Accept(Self);
|
||||||
|
Node.Identifier.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitBinaryExpression(const Node: IBinaryExpressionNode): TAstValue;
|
||||||
|
begin
|
||||||
|
Node.Left.Accept(Self);
|
||||||
|
Node.Right.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitBlockExpression(const Node: IBlockExpressionNode): TAstValue;
|
||||||
|
var
|
||||||
|
expr: IExpressionNode;
|
||||||
|
begin
|
||||||
|
for expr in Node.Expressions do
|
||||||
|
expr.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitConstant(const Node: IConstantNode): TAstValue;
|
||||||
|
begin
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitCreateSeries(const Node: ICreateSeriesNode): TAstValue;
|
||||||
|
begin
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitFunctionCall(const Node: IFunctionCallNode): TAstValue;
|
||||||
|
var
|
||||||
|
arg: IExpressionNode;
|
||||||
|
begin
|
||||||
|
Node.Callee.Accept(Self);
|
||||||
|
for arg in Node.Arguments do
|
||||||
|
arg.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitIdentifier(const Node: IIdentifierNode): TAstValue;
|
||||||
|
begin
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitIfExpression(const Node: IIfExpressionNode): TAstValue;
|
||||||
|
begin
|
||||||
|
Node.Condition.Accept(Self);
|
||||||
|
Node.ThenBranch.Accept(Self);
|
||||||
|
if Assigned(Node.ElseBranch) then
|
||||||
|
Node.ElseBranch.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitIndexer(const Node: IIndexerNode): TAstValue;
|
||||||
|
begin
|
||||||
|
Node.Base.Accept(Self);
|
||||||
|
Node.Index.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue;
|
||||||
|
var
|
||||||
|
param: IIdentifierNode;
|
||||||
|
begin
|
||||||
|
for param in Node.Parameters do
|
||||||
|
param.Accept(Self);
|
||||||
|
Node.Body.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitMemberAccess(const Node: IMemberAccessNode): TAstValue;
|
||||||
|
begin
|
||||||
|
// Do not visit the member identifier, as it's not a variable in the current scope.
|
||||||
|
Node.Base.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitSeriesLength(const Node: ISeriesLengthNode): TAstValue;
|
||||||
|
begin
|
||||||
|
Node.Series.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue;
|
||||||
|
begin
|
||||||
|
Node.Condition.Accept(Self);
|
||||||
|
Node.ThenBranch.Accept(Self);
|
||||||
|
Node.ElseBranch.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitUnaryExpression(const Node: IUnaryExpressionNode): TAstValue;
|
||||||
|
begin
|
||||||
|
Node.Right.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAstTraverser.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue;
|
||||||
|
begin
|
||||||
|
if Assigned(Node.Initializer) then
|
||||||
|
Node.Initializer.Accept(Self);
|
||||||
|
Node.Identifier.Accept(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
Reference in New Issue
Block a user