diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index 2f6795b..6948633 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -145,6 +145,7 @@ type {$region 'private'} function GetName: string; {$endregion} + function Resolve(out ScopeDepth: Integer; out SlotIndex: Integer): Boolean; property Name: string read GetName; end; diff --git a/Src/AST/Myc.Ast.pas b/Src/AST/Myc.Ast.pas index 1e042ad..9bdedac 100644 --- a/Src/AST/Myc.Ast.pas +++ b/Src/AST/Myc.Ast.pas @@ -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; + 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.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.