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; function TForm1.CompileAst(const ANode: IAstNode; const AParentScope: IExecutionScope; out ADescriptor: IScopeDescriptor): IAstNode;
var var
macroDescriptor: IScopeDescriptor;
expandedAst, boundAst, typedAst, loweredAst: IAstNode; expandedAst, boundAst, typedAst, loweredAst: IAstNode;
begin begin
// Step 1: Expand macros (Phase 1) // 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) // Step 2: Bind names and addresses (Phase 2)
boundAst := TAstBinder.Bind(AParentScope, expandedAst, ADescriptor); boundAst := TAstBinder.Bind(AParentScope, expandedAst, ADescriptor);
+2 -1
View File
@@ -6,10 +6,11 @@ uses
System.SysUtils, System.SysUtils,
System.Classes, System.Classes,
System.Generics.Collections, System.Generics.Collections,
Myc.Ast.Nodes,
Myc.Ast.Visitor, Myc.Ast.Visitor,
Myc.Data.Value, Myc.Data.Value,
Myc.Data.Scalar, Myc.Data.Scalar,
Myc.Ast.Nodes,
Myc.Ast.Scope,
Myc.Ast; Myc.Ast;
type type
+120 -18
View File
@@ -1,6 +1,3 @@
//==================================================================================================
//== FULL UNIT START: Myc.Ast.MacroExpander (from Myc.Ast.MacroExpander.pas)
//==================================================================================================
unit Myc.Ast.MacroExpander; unit Myc.Ast.MacroExpander;
interface interface
@@ -19,7 +16,8 @@ uses
type type
IAstMacroExpander = interface(IAstVisitor) 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; end;
// Callback type for compile-time evaluation of Unquote expressions // Callback type for compile-time evaluation of Unquote expressions
@@ -48,10 +46,29 @@ type
// This transformer traverses the entire AST *before* the binder, // This transformer traverses the entire AST *before* the binder,
// to find and expand macros. // to find and expand macros.
TMacroExpander = class(TAstTransformer, IAstMacroExpander) 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 private
FInitialScope: IExecutionScope; FInitialScope: IExecutionScope;
FCurrentDescriptor: IScopeDescriptor;
// Ersetzt FCurrentDescriptor
FCurrentMacroRegistry: TMacroRegistry;
FEvaluatorFactory: TEvaluatorFactory; FEvaluatorFactory: TEvaluatorFactory;
procedure EnterMacroScope;
procedure ExitMacroScope;
protected protected
// Finds macro definitions // Finds macro definitions
function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override; function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override;
@@ -60,14 +77,21 @@ type
function VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode; override; function VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode; override;
function VisitUnquote(const Node: IUnquoteNode): IAstNode; override; function VisitUnquote(const Node: IUnquoteNode): IAstNode; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): 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 public
constructor Create(const AInitialScope: IExecutionScope; const AEvaluatorFactory: TEvaluatorFactory); 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( class function ExpandMacros(
const InitialScope: IExecutionScope; const InitialScope: IExecutionScope;
const RootNode: IAstNode; const RootNode: IAstNode;
out Descriptor: IScopeDescriptor;
const EvaluatorFactory: TEvaluatorFactory const EvaluatorFactory: TEvaluatorFactory
): IAstNode; static; ): IAstNode; static;
end; end;
@@ -80,6 +104,40 @@ uses
Myc.Ast.Evaluator, // Required for TEvaluatorVisitor.Execute Myc.Ast.Evaluator, // Required for TEvaluatorVisitor.Execute
Myc.Data.Keyword; 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 } { TExpansionVisitor }
constructor TExpansionVisitor.Create(const AMacroScope: IExecutionScope; const AEvaluate: TEvaluateProc); constructor TExpansionVisitor.Create(const AMacroScope: IExecutionScope; const AEvaluate: TEvaluateProc);
@@ -232,22 +290,44 @@ begin
Assert(Assigned(AEvaluatorFactory)); Assert(Assigned(AEvaluatorFactory));
FInitialScope := AInitialScope; FInitialScope := AInitialScope;
FEvaluatorFactory := AEvaluatorFactory; 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; end;
class function TMacroExpander.ExpandMacros( class function TMacroExpander.ExpandMacros(
const InitialScope: IExecutionScope; const InitialScope: IExecutionScope;
const RootNode: IAstNode; const RootNode: IAstNode;
out Descriptor: IScopeDescriptor;
const EvaluatorFactory: TEvaluatorFactory const EvaluatorFactory: TEvaluatorFactory
): IAstNode; ): IAstNode;
begin begin
// IAstMacroExpander.Execute gibt keinen Descriptor mehr zurück
var expander := TMacroExpander.Create(InitialScope, EvaluatorFactory) as IAstMacroExpander; var expander := TMacroExpander.Create(InitialScope, EvaluatorFactory) as IAstMacroExpander;
Result := expander.Execute(RootNode, Descriptor); Result := expander.Execute(RootNode);
end; end;
function TMacroExpander.Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode; function TMacroExpander.Execute(const RootNode: IAstNode): IAstNode;
begin begin
// This executes the transformation (Accept) // This executes the transformation (Accept)
Result := Accept(RootNode); // Calls the IAstNode-returning helper Result := Accept(RootNode); // Calls the IAstNode-returning helper
@@ -255,13 +335,35 @@ begin
if not Assigned(Result) then if not Assigned(Result) then
Result := TAst.Block([]); // e.g. if root was (defmacro ...) 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; end;
function TMacroExpander.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; function TMacroExpander.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode;
begin begin
// Register the macro in the current descriptor // Register the macro in the *current* macro registry
FCurrentDescriptor.DefineMacro(Node.Name.Name, Node); FCurrentMacroRegistry.Define(Node.Name.Name, Node);
// Keep the node in the AST (for the visualizer). // Keep the node in the AST (for the visualizer).
Result := Node; Result := Node;
@@ -279,8 +381,8 @@ begin
calleeIdentifier := Node.Callee.AsIdentifier; calleeIdentifier := Node.Callee.AsIdentifier;
// Check if this identifier is a macro // Check if this identifier is a macro in the current registry
macroDef := FCurrentDescriptor.FindMacro(calleeIdentifier.Name); macroDef := FCurrentMacroRegistry.Find(calleeIdentifier.Name);
if macroDef = nil then if macroDef = nil then
exit(inherited VisitFunctionCall(Node)); // Not a macro, let transformer continue exit(inherited VisitFunctionCall(Node)); // Not a macro, let transformer continue
@@ -309,7 +411,7 @@ begin
boundSubAst: IAstNode; boundSubAst: IAstNode;
begin begin
// This is the compile-time evaluation (Binder + Evaluator) // 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); boundSubAst := TAstBinder.Bind(expansionScope, ANodeToEvaluate, subDescriptor);
// Create eval scope from the new descriptor, parented to the expansion scope // 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.Scalar,
Myc.Data.Value, Myc.Data.Value,
Myc.Data.Keyword, Myc.Data.Keyword,
Myc.Ast.Scope,
Myc.Ast.Types; Myc.Ast.Types;
type type
@@ -78,19 +79,6 @@ type
// --- Concrete Type Definitions --- // --- 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 // A single field in a record literal
TRecordFieldLiteral = record TRecordFieldLiteral = record
Key: IKeywordNode; Key: IKeywordNode;
@@ -397,6 +385,9 @@ type
function Execute(const RootNode: IAstNode): TDataValue; function Execute(const RootNode: IAstNode): TDataValue;
end; end;
// A factory for creating visitors, primarily used by the debugger subsystem.
TEvaluatorFactory = reference to function(const Scope: IExecutionScope): IEvaluatorVisitor;
implementation implementation
uses uses
@@ -413,27 +404,6 @@ begin
Result := Result.Substring(2); Result := Result.Substring(2);
end; 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 } { TRecordFieldLiteral }
constructor TRecordFieldLiteral.Create(const AKey: IKeywordNode; const AValue: IAstNode); constructor TRecordFieldLiteral.Create(const AKey: IKeywordNode; const AValue: IAstNode);
+43 -45
View File
@@ -7,13 +7,25 @@ uses
System.Generics.Collections, System.Generics.Collections,
System.Classes, System.Classes,
Myc.Data.Value, Myc.Data.Value,
Myc.Ast.Nodes,
Myc.Ast.Types; Myc.Ast.Types;
type type
IExecutionScope = interface; IExecutionScope = interface;
IScopeDescriptor = 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. // A resolved symbol, containing its runtime address and static type.
TResolvedSymbol = record TResolvedSymbol = record
Address: TResolvedAddress; Address: TResolvedAddress;
@@ -68,18 +80,15 @@ type
function Define(const Name: string; const AType: IStaticType = nil): Integer; function Define(const Name: string; const AType: IStaticType = nil): Integer;
// Updates the type of an already defined symbol (e.g., for lambda self-reference). // Updates the type of an already defined symbol (e.g., for lambda self-reference).
procedure UpdateType(SlotIndex: Integer; const AType: IStaticType); procedure UpdateType(SlotIndex: Integer; const AType: IStaticType);
procedure DefineMacro(const Name: string; const Node: IMacroDefinitionNode);
// Finds a symbol, returns its address and type. // Finds a symbol, returns its address and type.
function FindSymbol(const Name: string): TResolvedSymbol; function FindSymbol(const Name: string): TResolvedSymbol;
function FindMacro(const Name: string): IMacroDefinitionNode;
property Parent: IScopeDescriptor read GetParent; property Parent: IScopeDescriptor read GetParent;
property SlotCount: Integer read GetSlotCount; property SlotCount: Integer read GetSlotCount;
property Symbols: TDictionary<string, Integer> read GetSymbols; property Symbols: TDictionary<string, Integer> read GetSymbols;
end; end;
// A factory for creating visitors, primarily used by the debugger subsystem.
TEvaluatorFactory = reference to function(const Scope: IExecutionScope): IEvaluatorVisitor;
TScope = record TScope = record
class function CreateScope( class function CreateScope(
const Parent: IExecutionScope; const Parent: IExecutionScope;
@@ -93,8 +102,7 @@ implementation
uses uses
System.Generics.Defaults, System.Generics.Defaults,
System.SyncObjs, System.SyncObjs;
Myc.Ast;
type type
TExecutionScope = class(TInterfacedObject, IExecutionScope) TExecutionScope = class(TInterfacedObject, IExecutionScope)
@@ -151,7 +159,7 @@ type
private private
FParent: IScopeDescriptor; FParent: IScopeDescriptor;
FSymbols: TDictionary<string, Integer>; FSymbols: TDictionary<string, Integer>;
FMacros: TDictionary<string, IMacroDefinitionNode>; // FMacros: TDictionary<string, IMacroDefinitionNode>; // Entfernt
FSlotTypes: TArray<IStaticType>; // Stores types by SlotIndex FSlotTypes: TArray<IStaticType>; // Stores types by SlotIndex
function GetParent: IScopeDescriptor; function GetParent: IScopeDescriptor;
function GetSlotCount: Integer; function GetSlotCount: Integer;
@@ -163,14 +171,35 @@ type
class function CreateDescriptor(const Scope: IExecutionScope): IScopeDescriptor; static; class function CreateDescriptor(const Scope: IExecutionScope): IScopeDescriptor; static;
function Define(const Name: string; const AType: IStaticType): Integer; function Define(const Name: string; const AType: IStaticType): Integer;
procedure UpdateType(SlotIndex: Integer; const AType: IStaticType = nil); 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 FindSymbol(const Name: string): TResolvedSymbol;
function FindMacro(const Name: string): IMacroDefinitionNode; // function FindMacro(const Name: string): IMacroDefinitionNode; // Entfernt
function CreateScope(const Parent: IExecutionScope): IExecutionScope; function CreateScope(const Parent: IExecutionScope): IExecutionScope;
procedure PopulateFromScope(Scope: TExecutionScope); procedure PopulateFromScope(Scope: TExecutionScope);
property Symbols: TDictionary<string, Integer> read FSymbols; property Symbols: TDictionary<string, Integer> read FSymbols;
end; 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 } { TResolvedSymbol }
constructor TResolvedSymbol.Create(const AAddress: TResolvedAddress; const AStaticType: IStaticType); constructor TResolvedSymbol.Create(const AAddress: TResolvedAddress; const AStaticType: IStaticType);
@@ -499,14 +528,14 @@ begin
inherited Create; inherited Create;
FParent := AParent; FParent := AParent;
FSymbols := TDictionary<string, Integer>.Create; FSymbols := TDictionary<string, Integer>.Create;
FMacros := TDictionary<string, IMacroDefinitionNode>.Create; // FMacros := TDictionary<string, IMacroDefinitionNode>.Create; // Entfernt
FSlotTypes := []; FSlotTypes := [];
end; end;
destructor TScopeDescriptor.Destroy; destructor TScopeDescriptor.Destroy;
begin begin
FSlotTypes := nil; FSlotTypes := nil;
FMacros.Free; // FMacros.Free; // Entfernt
FSymbols.Free; FSymbols.Free;
inherited; inherited;
end; end;
@@ -547,26 +576,6 @@ begin
else TTypes.Unknown; else TTypes.Unknown;
end; 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; function TScopeDescriptor.FindSymbol(const Name: string): TResolvedSymbol;
var var
currentDescriptor: IScopeDescriptor; currentDescriptor: IScopeDescriptor;
@@ -633,18 +642,7 @@ begin
// We cannot recover the static type from the runtime scope. // We cannot recover the static type from the runtime scope.
// FSlotTypes[slotIndex] remains TTypes.Unknown. // FSlotTypes[slotIndex] remains TTypes.Unknown.
// Check if the symbol is also a macro and add it to the macro table. // Makro-Logik hier entfernt
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;
end; end;
end; end;