Ast Binding
This commit is contained in:
+327
-4
@@ -10,6 +10,7 @@ uses
|
||||
type
|
||||
// Record acting as a namespace for the factory functions.
|
||||
TAst = record
|
||||
// --- Existing factory functions ---
|
||||
class function Constant(AValue: TScalar): IConstantNode; static;
|
||||
class function Identifier(AName: string): IIdentifierNode; static;
|
||||
class function BinaryExpr(
|
||||
@@ -31,7 +32,7 @@ type
|
||||
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;
|
||||
class function AssignResult(const AValue: IExpressionNode): IAssignmentNode; static; deprecated;
|
||||
class function Indexer(const ABase: IExpressionNode; const AIndex: IExpressionNode): IIndexerNode; static;
|
||||
class function MemberAccess(const ABase: IExpressionNode; const AMember: IIdentifierNode): IMemberAccessNode; static;
|
||||
class function CreateSeries(const ADefinition: String): ICreateSeriesNode; static;
|
||||
@@ -40,8 +41,32 @@ type
|
||||
const AValue: IExpressionNode;
|
||||
const ALookback: IExpressionNode = nil
|
||||
): IAddSeriesItemNode; static;
|
||||
// Added the new factory function for SeriesLength
|
||||
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;
|
||||
|
||||
implementation
|
||||
@@ -69,13 +94,22 @@ type
|
||||
end;
|
||||
|
||||
{ TIdentifierNode }
|
||||
// TIdentifierNode now includes fields for binder annotations.
|
||||
TIdentifierNode = class(TAstNode, IIdentifierNode)
|
||||
private
|
||||
FName: string;
|
||||
// --- Annotation fields added for the binder ---
|
||||
FIsResolved: Boolean;
|
||||
FScopeDepth: Integer;
|
||||
FSlotIndex: Integer;
|
||||
function GetName: string;
|
||||
public
|
||||
constructor Create(AName: string);
|
||||
function Accept(const Visitor: IAstVisitor): TAstValue; override;
|
||||
|
||||
function Resolve(out ScopeDepth: Integer; out SlotIndex: Integer): Boolean;
|
||||
|
||||
property Name: string read FName;
|
||||
end;
|
||||
|
||||
{ TBinaryExpressionNode }
|
||||
@@ -205,7 +239,6 @@ type
|
||||
end;
|
||||
|
||||
{ TMemberAccessNode }
|
||||
// Concrete implementation for the member access node.
|
||||
TMemberAccessNode = class(TAstNode, IMemberAccessNode)
|
||||
private
|
||||
FBase: IExpressionNode;
|
||||
@@ -241,7 +274,6 @@ type
|
||||
function Accept(const Visitor: IAstVisitor): TAstValue; override;
|
||||
end;
|
||||
|
||||
// Added concrete class for the series length node.
|
||||
{ TSeriesLengthNode }
|
||||
TSeriesLengthNode = class(TAstNode, ISeriesLengthNode)
|
||||
private
|
||||
@@ -252,6 +284,40 @@ type
|
||||
function Accept(const Visitor: IAstVisitor): TAstValue; override;
|
||||
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 }
|
||||
|
||||
constructor TConstantNode.Create(AValue: TScalar);
|
||||
@@ -276,6 +342,9 @@ constructor TIdentifierNode.Create(AName: string);
|
||||
begin
|
||||
inherited Create;
|
||||
FName := AName;
|
||||
FIsResolved := False;
|
||||
FScopeDepth := -1;
|
||||
FSlotIndex := -1;
|
||||
end;
|
||||
|
||||
function TIdentifierNode.Accept(const Visitor: IAstVisitor): TAstValue;
|
||||
@@ -288,6 +357,16 @@ begin
|
||||
Result := FName;
|
||||
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 }
|
||||
|
||||
constructor TBinaryExpressionNode.Create(ALeft: IExpressionNode; AOperator: TBinaryOperator; ARight: IExpressionNode);
|
||||
@@ -650,8 +729,148 @@ begin
|
||||
Result := FSeries;
|
||||
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 }
|
||||
|
||||
class procedure TAst.Bind(const ARootNode: IExpressionNode);
|
||||
var
|
||||
binder: IAstVisitor;
|
||||
begin
|
||||
binder := TBinder.Create;
|
||||
ARootNode.Accept(binder);
|
||||
end;
|
||||
|
||||
class function TAst.AddSeriesItem(
|
||||
const ASeries: IIdentifierNode;
|
||||
const AValue: IExpressionNode;
|
||||
@@ -741,4 +960,108 @@ begin
|
||||
Result := TVariableDeclarationNode.Create(AIdentifier, AInitializer);
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user