Resolved dependency between AST nodes and scope

This commit is contained in:
Michael Schimmel
2025-11-05 16:52:40 +01:00
parent b98e7d98e6
commit 0915d6d90d
5 changed files with 170 additions and 100 deletions
+1 -2
View File
@@ -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);
+2 -1
View File
@@ -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
+120 -18
View File
@@ -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<string, IMacroDefinitionNode>;
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<string, IMacroDefinitionNode>.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
+4 -34
View File
@@ -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);
+43 -45
View File
@@ -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<string, Integer> 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<string, Integer>;
FMacros: TDictionary<string, IMacroDefinitionNode>;
// FMacros: TDictionary<string, IMacroDefinitionNode>; // Entfernt
FSlotTypes: TArray<IStaticType>; // 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<string, Integer> 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<string, Integer>.Create;
FMacros := TDictionary<string, IMacroDefinitionNode>.Create;
// FMacros := TDictionary<string, IMacroDefinitionNode>.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<IAstNode>;
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;