From 0915d6d90dbfb9e082892e775c775d60d0bee593 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Wed, 5 Nov 2025 16:52:40 +0100 Subject: [PATCH] Resolved dependency between AST nodes and scope --- ASTPlayground/MainForm.pas | 3 +- Src/AST/Myc.Ast.Dumper.pas | 3 +- Src/AST/Myc.Ast.MacroExpander.pas | 138 ++++++++++++++++++++++++++---- Src/AST/Myc.Ast.Nodes.pas | 38 +------- Src/AST/Myc.Ast.Scope.pas | 88 ++++++++++--------- 5 files changed, 170 insertions(+), 100 deletions(-) diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas index da98df4..04a122f 100644 --- a/ASTPlayground/MainForm.pas +++ b/ASTPlayground/MainForm.pas @@ -214,11 +214,10 @@ end; function TForm1.CompileAst(const ANode: IAstNode; const AParentScope: IExecutionScope; out ADescriptor: IScopeDescriptor): IAstNode; var - macroDescriptor: IScopeDescriptor; expandedAst, boundAst, typedAst, loweredAst: IAstNode; begin // Step 1: Expand macros (Phase 1) - expandedAst := TMacroExpander.ExpandMacros(AParentScope, ANode, macroDescriptor, CreateEvaluator); + expandedAst := TMacroExpander.ExpandMacros(AParentScope, ANode, CreateEvaluator); // Step 2: Bind names and addresses (Phase 2) boundAst := TAstBinder.Bind(AParentScope, expandedAst, ADescriptor); diff --git a/Src/AST/Myc.Ast.Dumper.pas b/Src/AST/Myc.Ast.Dumper.pas index 6bebd60..d844398 100644 --- a/Src/AST/Myc.Ast.Dumper.pas +++ b/Src/AST/Myc.Ast.Dumper.pas @@ -6,10 +6,11 @@ uses System.SysUtils, System.Classes, System.Generics.Collections, - Myc.Ast.Nodes, Myc.Ast.Visitor, Myc.Data.Value, Myc.Data.Scalar, + Myc.Ast.Nodes, + Myc.Ast.Scope, Myc.Ast; type diff --git a/Src/AST/Myc.Ast.MacroExpander.pas b/Src/AST/Myc.Ast.MacroExpander.pas index 0dfbc9d..078c8ce 100644 --- a/Src/AST/Myc.Ast.MacroExpander.pas +++ b/Src/AST/Myc.Ast.MacroExpander.pas @@ -1,6 +1,3 @@ -//================================================================================================== -//== FULL UNIT START: Myc.Ast.MacroExpander (from Myc.Ast.MacroExpander.pas) -//================================================================================================== unit Myc.Ast.MacroExpander; interface @@ -19,7 +16,8 @@ uses type IAstMacroExpander = interface(IAstVisitor) - function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode; + // Signatur geändert: Descriptor wird nicht mehr zurückgegeben + function Execute(const RootNode: IAstNode): IAstNode; end; // Callback type for compile-time evaluation of Unquote expressions @@ -48,10 +46,29 @@ type // This transformer traverses the entire AST *before* the binder, // to find and expand macros. TMacroExpander = class(TAstTransformer, IAstMacroExpander) + type + TMacroRegistry = class + private + FParent: TMacroRegistry; + FMacros: TDictionary; + public + constructor Create(AParent: TMacroRegistry); + destructor Destroy; override; + procedure Define(const Name: string; const Node: IMacroDefinitionNode); + function Find(const Name: string): IMacroDefinitionNode; + end; + private FInitialScope: IExecutionScope; - FCurrentDescriptor: IScopeDescriptor; + + // Ersetzt FCurrentDescriptor + FCurrentMacroRegistry: TMacroRegistry; + FEvaluatorFactory: TEvaluatorFactory; + + procedure EnterMacroScope; + procedure ExitMacroScope; + protected // Finds macro definitions function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override; @@ -60,14 +77,21 @@ type function VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode; override; function VisitUnquote(const Node: IUnquoteNode): IAstNode; override; function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode; override; + + // NEU: Überschrieben für lexikalisches Makro-Scoping + function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override; + function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override; + public constructor Create(const AInitialScope: IExecutionScope; const AEvaluatorFactory: TEvaluatorFactory); - function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode; + destructor Destroy; override; + + // Signatur geändert + function Execute(const RootNode: IAstNode): IAstNode; class function ExpandMacros( const InitialScope: IExecutionScope; const RootNode: IAstNode; - out Descriptor: IScopeDescriptor; const EvaluatorFactory: TEvaluatorFactory ): IAstNode; static; end; @@ -80,6 +104,40 @@ uses Myc.Ast.Evaluator, // Required for TEvaluatorVisitor.Execute Myc.Data.Keyword; +{ TMacroRegistry } + +constructor TMacroExpander.TMacroRegistry.Create(AParent: TMacroRegistry); +begin + inherited Create; + FParent := AParent; + FMacros := TDictionary.Create; +end; + +destructor TMacroExpander.TMacroRegistry.Destroy; +begin + FMacros.Free; + inherited Destroy; +end; + +procedure TMacroExpander.TMacroRegistry.Define(const Name: string; const Node: IMacroDefinitionNode); +begin + FMacros.AddOrSetValue(Name, Node); +end; + +function TMacroExpander.TMacroRegistry.Find(const Name: string): IMacroDefinitionNode; +var + current: TMacroRegistry; +begin + current := Self; + while Assigned(current) do + begin + if current.FMacros.TryGetValue(Name, Result) then + exit; + current := current.FParent; + end; + Result := nil; +end; + { TExpansionVisitor } constructor TExpansionVisitor.Create(const AMacroScope: IExecutionScope; const AEvaluate: TEvaluateProc); @@ -232,22 +290,44 @@ begin Assert(Assigned(AEvaluatorFactory)); FInitialScope := AInitialScope; FEvaluatorFactory := AEvaluatorFactory; - // Create the top-level descriptor for macros - FCurrentDescriptor := AInitialScope.CreateDescriptor; + + // Erzeugt die Root-Registry für Makros + FCurrentMacroRegistry := TMacroRegistry.Create(nil); +end; + +destructor TMacroExpander.Destroy; +begin + FCurrentMacroRegistry.Free; + inherited Destroy; +end; + +procedure TMacroExpander.EnterMacroScope; +begin + // Erstellt eine neue Registry mit der aktuellen als Parent + FCurrentMacroRegistry := TMacroRegistry.Create(FCurrentMacroRegistry); +end; + +procedure TMacroExpander.ExitMacroScope; +var + oldRegistry: TMacroRegistry; +begin + oldRegistry := FCurrentMacroRegistry; + FCurrentMacroRegistry := oldRegistry.FParent; + oldRegistry.Free; end; class function TMacroExpander.ExpandMacros( const InitialScope: IExecutionScope; const RootNode: IAstNode; - out Descriptor: IScopeDescriptor; const EvaluatorFactory: TEvaluatorFactory ): IAstNode; begin + // IAstMacroExpander.Execute gibt keinen Descriptor mehr zurück var expander := TMacroExpander.Create(InitialScope, EvaluatorFactory) as IAstMacroExpander; - Result := expander.Execute(RootNode, Descriptor); + Result := expander.Execute(RootNode); end; -function TMacroExpander.Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode; +function TMacroExpander.Execute(const RootNode: IAstNode): IAstNode; begin // This executes the transformation (Accept) Result := Accept(RootNode); // Calls the IAstNode-returning helper @@ -255,13 +335,35 @@ begin if not Assigned(Result) then Result := TAst.Block([]); // e.g. if root was (defmacro ...) - Descriptor := FCurrentDescriptor; + // Descriptor := FCurrentDescriptor; // Entfernt +end; + +function TMacroExpander.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; +begin + // Ein 'do'-Block eröffnet einen neuen Scope für Makros + EnterMacroScope; + try + Result := inherited VisitBlockExpression(Node); + finally + ExitMacroScope; + end; +end; + +function TMacroExpander.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; +begin + // Eine 'fn' eröffnet einen neuen Scope für Makros + EnterMacroScope; + try + Result := inherited VisitLambdaExpression(Node); + finally + ExitMacroScope; + end; end; function TMacroExpander.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; begin - // Register the macro in the current descriptor - FCurrentDescriptor.DefineMacro(Node.Name.Name, Node); + // Register the macro in the *current* macro registry + FCurrentMacroRegistry.Define(Node.Name.Name, Node); // Keep the node in the AST (for the visualizer). Result := Node; @@ -279,8 +381,8 @@ begin calleeIdentifier := Node.Callee.AsIdentifier; - // Check if this identifier is a macro - macroDef := FCurrentDescriptor.FindMacro(calleeIdentifier.Name); + // Check if this identifier is a macro in the current registry + macroDef := FCurrentMacroRegistry.Find(calleeIdentifier.Name); if macroDef = nil then exit(inherited VisitFunctionCall(Node)); // Not a macro, let transformer continue @@ -309,7 +411,7 @@ begin boundSubAst: IAstNode; begin // This is the compile-time evaluation (Binder + Evaluator) - // [FIX] It must run in the 'expansionScope' which contains the parameters. + // Es wird der 'expansionScope' verwendet, der die AST-Argumente enthält boundSubAst := TAstBinder.Bind(expansionScope, ANodeToEvaluate, subDescriptor); // Create eval scope from the new descriptor, parented to the expansion scope diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index c9fdd49..da826df 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -8,6 +8,7 @@ uses Myc.Data.Scalar, Myc.Data.Value, Myc.Data.Keyword, + Myc.Ast.Scope, Myc.Ast.Types; type @@ -78,19 +79,6 @@ type // --- Concrete Type Definitions --- - // Defines how an identifier's address was resolved. - TAddressKind = (akUnresolved, akLocalOrParent, akUpvalue); - - TResolvedAddress = record - Kind: TAddressKind; - ScopeDepth: Integer; - SlotIndex: Integer; - constructor Create(AKind: TAddressKind; AScopeDepth, ASlotIndex: Integer); - class operator Initialize(out Dest: TResolvedAddress); - public - class operator Equal(const A: TResolvedAddress; B: TResolvedAddress): Boolean; - end; - // A single field in a record literal TRecordFieldLiteral = record Key: IKeywordNode; @@ -397,6 +385,9 @@ type function Execute(const RootNode: IAstNode): TDataValue; end; + // A factory for creating visitors, primarily used by the debugger subsystem. + TEvaluatorFactory = reference to function(const Scope: IExecutionScope): IEvaluatorVisitor; + implementation uses @@ -413,27 +404,6 @@ begin Result := Result.Substring(2); end; -{ TResolvedAddress } - -constructor TResolvedAddress.Create(AKind: TAddressKind; AScopeDepth, ASlotIndex: Integer); -begin - Kind := AKind; - ScopeDepth := AScopeDepth; - SlotIndex := ASlotIndex; -end; - -class operator TResolvedAddress.Equal(const A: TResolvedAddress; B: TResolvedAddress): Boolean; -begin - Result := (A.Kind = B.Kind) and (A.ScopeDepth = B.ScopeDepth) and (A.SlotIndex = B.SlotIndex); -end; - -class operator TResolvedAddress.Initialize(out Dest: TResolvedAddress); -begin - Dest.Kind := akUnresolved; - Dest.ScopeDepth := -1; - Dest.SlotIndex := -1; -end; - { TRecordFieldLiteral } constructor TRecordFieldLiteral.Create(const AKey: IKeywordNode; const AValue: IAstNode); diff --git a/Src/AST/Myc.Ast.Scope.pas b/Src/AST/Myc.Ast.Scope.pas index b28fe60..039b2ae 100644 --- a/Src/AST/Myc.Ast.Scope.pas +++ b/Src/AST/Myc.Ast.Scope.pas @@ -7,13 +7,25 @@ uses System.Generics.Collections, System.Classes, Myc.Data.Value, - Myc.Ast.Nodes, Myc.Ast.Types; type IExecutionScope = interface; IScopeDescriptor = interface; + // Defines how an identifier's address was resolved. + TAddressKind = (akUnresolved, akLocalOrParent, akUpvalue); + + TResolvedAddress = record + Kind: TAddressKind; + ScopeDepth: Integer; + SlotIndex: Integer; + constructor Create(AKind: TAddressKind; AScopeDepth, ASlotIndex: Integer); + class operator Initialize(out Dest: TResolvedAddress); + public + class operator Equal(const A: TResolvedAddress; B: TResolvedAddress): Boolean; + end; + // A resolved symbol, containing its runtime address and static type. TResolvedSymbol = record Address: TResolvedAddress; @@ -68,18 +80,15 @@ type function Define(const Name: string; const AType: IStaticType = nil): Integer; // Updates the type of an already defined symbol (e.g., for lambda self-reference). procedure UpdateType(SlotIndex: Integer; const AType: IStaticType); - procedure DefineMacro(const Name: string; const Node: IMacroDefinitionNode); + // Finds a symbol, returns its address and type. function FindSymbol(const Name: string): TResolvedSymbol; - function FindMacro(const Name: string): IMacroDefinitionNode; + property Parent: IScopeDescriptor read GetParent; property SlotCount: Integer read GetSlotCount; property Symbols: TDictionary read GetSymbols; end; - // A factory for creating visitors, primarily used by the debugger subsystem. - TEvaluatorFactory = reference to function(const Scope: IExecutionScope): IEvaluatorVisitor; - TScope = record class function CreateScope( const Parent: IExecutionScope; @@ -93,8 +102,7 @@ implementation uses System.Generics.Defaults, - System.SyncObjs, - Myc.Ast; + System.SyncObjs; type TExecutionScope = class(TInterfacedObject, IExecutionScope) @@ -151,7 +159,7 @@ type private FParent: IScopeDescriptor; FSymbols: TDictionary; - FMacros: TDictionary; + // FMacros: TDictionary; // Entfernt FSlotTypes: TArray; // Stores types by SlotIndex function GetParent: IScopeDescriptor; function GetSlotCount: Integer; @@ -163,14 +171,35 @@ type class function CreateDescriptor(const Scope: IExecutionScope): IScopeDescriptor; static; function Define(const Name: string; const AType: IStaticType): Integer; procedure UpdateType(SlotIndex: Integer; const AType: IStaticType = nil); - procedure DefineMacro(const Name: string; const Node: IMacroDefinitionNode); + // procedure DefineMacro(const Name: string; const Node: IMacroDefinitionNode); // Entfernt function FindSymbol(const Name: string): TResolvedSymbol; - function FindMacro(const Name: string): IMacroDefinitionNode; + // function FindMacro(const Name: string): IMacroDefinitionNode; // Entfernt function CreateScope(const Parent: IExecutionScope): IExecutionScope; procedure PopulateFromScope(Scope: TExecutionScope); property Symbols: TDictionary read FSymbols; end; +{ TResolvedAddress } + +constructor TResolvedAddress.Create(AKind: TAddressKind; AScopeDepth, ASlotIndex: Integer); +begin + Kind := AKind; + ScopeDepth := AScopeDepth; + SlotIndex := ASlotIndex; +end; + +class operator TResolvedAddress.Equal(const A: TResolvedAddress; B: TResolvedAddress): Boolean; +begin + Result := (A.Kind = B.Kind) and (A.ScopeDepth = B.ScopeDepth) and (A.SlotIndex = B.SlotIndex); +end; + +class operator TResolvedAddress.Initialize(out Dest: TResolvedAddress); +begin + Dest.Kind := akUnresolved; + Dest.ScopeDepth := -1; + Dest.SlotIndex := -1; +end; + { TResolvedSymbol } constructor TResolvedSymbol.Create(const AAddress: TResolvedAddress; const AStaticType: IStaticType); @@ -499,14 +528,14 @@ begin inherited Create; FParent := AParent; FSymbols := TDictionary.Create; - FMacros := TDictionary.Create; + // FMacros := TDictionary.Create; // Entfernt FSlotTypes := []; end; destructor TScopeDescriptor.Destroy; begin FSlotTypes := nil; - FMacros.Free; + // FMacros.Free; // Entfernt FSymbols.Free; inherited; end; @@ -547,26 +576,6 @@ begin else TTypes.Unknown; end; -procedure TScopeDescriptor.DefineMacro(const Name: string; const Node: IMacroDefinitionNode); -begin - FMacros.AddOrSetValue(Name, Node); -end; - -function TScopeDescriptor.FindMacro(const Name: string): IMacroDefinitionNode; -var - currentDescriptor: TScopeDescriptor; -begin - // Find a macro by searching up the parent scope chain. - Result := nil; - currentDescriptor := Self; - while Assigned(currentDescriptor) do - begin - if currentDescriptor.FMacros.TryGetValue(Name, Result) then - exit; - currentDescriptor := currentDescriptor.FParent as TScopeDescriptor; - end; -end; - function TScopeDescriptor.FindSymbol(const Name: string): TResolvedSymbol; var currentDescriptor: IScopeDescriptor; @@ -633,18 +642,7 @@ begin // We cannot recover the static type from the runtime scope. // FSlotTypes[slotIndex] remains TTypes.Unknown. - // Check if the symbol is also a macro and add it to the macro table. - - var val := Scope.ValuesArray[slotIndex].GetContent; - if val.Kind = vkInterface then - begin - var node := val.AsIntf; - if (node <> nil) and (node.Kind = akMacroDefinition) then - begin - if not FMacros.ContainsKey(name) then - FMacros.Add(name, node.AsMacroDefinition); - end; - end; + // Makro-Logik hier entfernt end; end;