From 4d380c8f98ea56940917d8e41cbd3f7fa8e10084 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Wed, 17 Sep 2025 15:20:14 +0200 Subject: [PATCH] Refactoring Binder --- ASTPlayground/ASTPlayground.dproj | 2 +- Src/AST/Myc.Ast.Binding.pas | 115 +++++++++++++----------------- 2 files changed, 51 insertions(+), 66 deletions(-) diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj index b620b06..91a3108 100644 --- a/ASTPlayground/ASTPlayground.dproj +++ b/ASTPlayground/ASTPlayground.dproj @@ -4,7 +4,7 @@ 20.3 FMX True - Release + Debug Win64 ASTPlayground 2 diff --git a/Src/AST/Myc.Ast.Binding.pas b/Src/AST/Myc.Ast.Binding.pas index a26ebcd..12da01a 100644 --- a/Src/AST/Myc.Ast.Binding.pas +++ b/Src/AST/Myc.Ast.Binding.pas @@ -12,7 +12,7 @@ uses Myc.Ast.Scope; type - TAstBinder = class(TAstTraverser) + TAstBinder = class(TAstTraverser) type TUpvalueMapping = class Map: TDictionary; @@ -25,12 +25,13 @@ type FCurrentDescriptor: IScopeDescriptor; FUpvalueStack: TStack; FNestedLambdaCount: Integer; + FIsTailStack: TStack; FNextIsTail: Boolean; procedure EnterScope; procedure ExitScope; protected - function EnterNode(const Node: IAstNode): Boolean; override; + function Accept(const Node: IAstNode): TDataValue; override; public constructor Create(const AInitialScope: IExecutionScope); @@ -51,7 +52,6 @@ type function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; property CurrentDescriptor: IScopeDescriptor read FCurrentDescriptor; - property NextIsTail: Boolean write FNextIsTail; end; implementation @@ -61,12 +61,6 @@ uses Myc.Ast; type - TResolvedAddressComparer = class(TEqualityComparer) - public - function Equals(const Left, Right: TResolvedAddress): Boolean; override; - function GetHashCode(const Value: TResolvedAddress): Integer; override; - end; - TScopeDescriptor = class(TInterfacedObject, IScopeDescriptor) private FParent: IScopeDescriptor; @@ -84,22 +78,6 @@ type property Symbols: TDictionary read FSymbols; end; -{ TResolvedAddressComparer } - -function TResolvedAddressComparer.Equals(const Left, Right: TResolvedAddress): Boolean; -begin - Result := (Left.Kind = Right.Kind) and (Left.ScopeDepth = Right.ScopeDepth) and (Left.SlotIndex = Right.SlotIndex); -end; - -function TResolvedAddressComparer.GetHashCode(const Value: TResolvedAddress): Integer; -begin - // Simple combining hash function - Result := 17; - Result := Result * 23 + Ord(Value.Kind); - Result := Result * 23 + Value.ScopeDepth; - Result := Result * 23 + Value.SlotIndex; -end; - { TAstBinder } constructor TAstBinder.Create(const AInitialScope: IExecutionScope); @@ -108,15 +86,31 @@ begin FCurrentDescriptor := CreateDescriptor(AInitialScope); FUpvalueStack := TObjectStack.Create(true); FNestedLambdaCount := 0; - FNextIsTail := False; + FIsTailStack := TStack.Create; + // The content of the root node is in a tail position. + FNextIsTail := true; end; destructor TAstBinder.Destroy; begin + FIsTailStack.Free; FUpvalueStack.Free; inherited; end; +function TAstBinder.Accept(const Node: IAstNode): TDataValue; +begin + if not Assigned(Node) or Done then + exit; + + FIsTailStack.Push(FNextIsTail); + try + Result := inherited Accept(Node); + finally + FNextIsTail := FIsTailStack.Pop; + end; +end; + class function TAstBinder.Bind(const RootNode: IAstNode; const ParentScope: IExecutionScope): IScopeDescriptor; var binder: TAstBinder; @@ -125,7 +119,7 @@ begin try binder.EnterScope; try - // Start the traversal. The content of the root node is in a tail position. + // Start the traversal binder.Accept(RootNode); Result := binder.CurrentDescriptor; finally @@ -158,12 +152,6 @@ begin FCurrentDescriptor := FCurrentDescriptor.Parent; end; -function TAstBinder.EnterNode(const Node: IAstNode): Boolean; -begin - // The context for the current node is whatever the parent Visit... method prepared. - Result := FNextIsTail; -end; - function TAstBinder.VisitAssignment(const Node: IAssignmentNode): TDataValue; begin FNextIsTail := False; @@ -177,27 +165,24 @@ begin end; function TAstBinder.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; -var - i: Integer; begin - for i := 0 to Node.Expressions.Count - 2 do - begin - FNextIsTail := False; - Accept(Node.Expressions[i]); - end; + FNextIsTail := False; - if Node.Expressions.Count > 0 then + var n := Node.Expressions.Count - 1; + for var i := 0 to n do begin // The last expression is in a tail position IF the block itself is. - FNextIsTail := Data.Peek; - Accept(Node.Expressions.Last); + if i = n then + FNextIsTail := FIsTailStack.Peek; + + Accept(Node.Expressions[i]); end; end; function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; begin // Annotate this node based on its context, which is on top of the stack. - (Node as TFunctionCallNode).IsTailCall := Data.Peek; + (Node as TFunctionCallNode).IsTailCall := FIsTailStack.Peek; // Let the default traverser visit children (callee, args), but ensure // their context is non-tail. @@ -250,7 +235,7 @@ begin Accept(Node.Condition); // The branches are in a tail position if the if-expression itself is. - FNextIsTail := Data.Peek; + FNextIsTail := FIsTailStack.Peek; Accept(Node.ThenBranch); if Assigned(Node.ElseBranch) then Accept(Node.ElseBranch); @@ -266,14 +251,13 @@ begin try EnterScope; try - (FCurrentDescriptor as TScopeDescriptor).Define('Self'); + FCurrentDescriptor.Define('Self'); for param in Node.Parameters do - (FCurrentDescriptor as TScopeDescriptor).Define(param.Name); + FCurrentDescriptor.Define(param.Name); var lastNestedLambdaCount := FNestedLambdaCount; - // Manually traverse children since we can't call 'inherited' from the simple traverser. // Parameters are never in a tail position. FNextIsTail := False; for param in Node.Parameters do @@ -290,22 +274,23 @@ begin end; finally var upvalue := FUpvalueStack.Peek; + try + sortedPairs := upvalue.Map.ToArray; + TArray.Sort>( + sortedPairs, + TComparer>.Construct( + function(const Left, Right: TPair): Integer begin Result := Left.Value - Right.Value; end + ) + ); - sortedPairs := upvalue.Map.ToArray; - TArray.Sort>( - sortedPairs, - TComparer>.Construct( - function(const Left, Right: TPair): Integer begin Result := Left.Value - Right.Value; end - ) - ); + SetLength(sourceAddresses, Length(sortedPairs)); + for var i := 0 to High(sortedPairs) do + sourceAddresses[i] := sortedPairs[i].Key; - SetLength(sourceAddresses, Length(sortedPairs)); - for var i := 0 to High(sortedPairs) do - sourceAddresses[i] := sortedPairs[i].Key; - - (Node as TLambdaExpressionNode).Upvalues := sourceAddresses; - - FUpvalueStack.Pop; + (Node as TLambdaExpressionNode).Upvalues := sourceAddresses; + finally + FUpvalueStack.Pop; + end; inc(FNestedLambdaCount); end; @@ -318,7 +303,7 @@ begin Accept(Node.Condition); // The branches are in a tail position if the ternary expression itself is. - FNextIsTail := Data.Peek; + FNextIsTail := FIsTailStack.Peek; Accept(Node.ThenBranch); Accept(Node.ElseBranch); end; @@ -338,7 +323,7 @@ begin if Assigned(Node.Initializer) then Accept(Node.Initializer); - slotIndex := (FCurrentDescriptor as TScopeDescriptor).Define(Node.Identifier.Name); + slotIndex := FCurrentDescriptor.Define(Node.Identifier.Name); (Node.Identifier as TIdentifierNode).Address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex); FNextIsTail := False; @@ -412,7 +397,7 @@ end; constructor TAstBinder.TUpvalueMapping.Create; begin inherited Create; - Map := TDictionary.Create(TResolvedAddressComparer.Default); + Map := TDictionary.Create; Nodes := TList.Create(); end;