Ast environments

This commit is contained in:
Michael Schimmel
2025-11-07 19:40:35 +01:00
parent 4ccf3bb5fd
commit c0f871ce02
12 changed files with 722 additions and 364 deletions
+11 -10
View File
@@ -26,7 +26,6 @@ type
type
TUpvalueMapping = TDictionary<TResolvedAddress, Integer>;
private
FInitialScope: IExecutionScope;
FCurrentDescriptor: IScopeDescriptor;
FUpvalueStack: TStack<TUpvalueMapping>;
FNestedLambdaCount: Integer;
@@ -54,12 +53,12 @@ type
function VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode; override;
public
constructor Create(const AInitialScope: IExecutionScope);
constructor Create(const AInitialDescriptor: IScopeDescriptor);
destructor Destroy; override;
function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
class function Bind(
const InitialScope: IExecutionScope;
const InitialDescriptor: IScopeDescriptor;
const RootNode: IAstNode;
out Descriptor: IScopeDescriptor
): IAstNode; static;
@@ -96,13 +95,12 @@ end;
{ TAstBinder }
constructor TAstBinder.Create(const AInitialScope: IExecutionScope);
constructor TAstBinder.Create(const AInitialDescriptor: IScopeDescriptor);
begin
inherited Create;
Assert(Assigned(AInitialScope));
Assert(Assigned(AInitialDescriptor));
FInitialScope := AInitialScope;
FCurrentDescriptor := AInitialScope.CreateDescriptor;
FCurrentDescriptor := AInitialDescriptor;
FUpvalueStack := TObjectStack<TUpvalueMapping>.Create(True); // Use Comparer
FNestedLambdaCount := 0;
FBoxedDeclarations := nil;
@@ -115,9 +113,13 @@ begin
inherited;
end;
class function TAstBinder.Bind(const InitialScope: IExecutionScope; const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
class function TAstBinder.Bind(
const InitialDescriptor: IScopeDescriptor;
const RootNode: IAstNode;
out Descriptor: IScopeDescriptor
): IAstNode;
begin
var binder := TAstBinder.Create(InitialScope) as IAstBinder;
var binder := TAstBinder.Create(InitialDescriptor) as IAstBinder;
Result := binder.Execute(RootNode, Descriptor);
end;
@@ -155,7 +157,6 @@ begin
try
EnterScope;
try
// Main pass: Run the mutator
Result := Accept(RootNode); // Accept returns IAstNode
if not Assigned(Result) then
Result := TAst.Block([]);
+36 -87
View File
@@ -12,11 +12,12 @@ uses
Myc.Ast.Visitor,
Myc.Ast.Scope,
Myc.Ast.Types,
Myc.Ast;
Myc.Ast,
Myc.Ast.Environment; // Added for IMacroRegistry
type
IAstMacroExpander = interface(IAstVisitor)
// Signatur geändert: Descriptor wird nicht mehr zurückgegeben
// Signatur geaendert: Descriptor wird nicht mehr zurueckgegeben
function Execute(const RootNode: IAstNode): IAstNode;
end;
@@ -46,28 +47,10 @@ 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 Node: IMacroDefinitionNode);
function Find(const Name: string): IMacroDefinitionNode;
end;
strict private
class var
FGlobalMacroRegistry: TMacroRegistry;
class constructor CreateClass;
class destructor DestroyClass;
private
FInitialScope: IExecutionScope;
FCurrentMacroRegistry: TMacroRegistry;
FCurrentMacroRegistry: IMacroRegistry; // Changed from TMacroRegistry
FEvaluatorFactory: TEvaluatorFactory;
@@ -87,18 +70,23 @@ type
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
public
constructor Create(const AInitialScope: IExecutionScope; const AEvaluatorFactory: TEvaluatorFactory);
destructor Destroy; override;
// Updated Constructor
constructor Create(
const ARootRegistry: IMacroRegistry;
const AInitialScope: IExecutionScope;
const AEvaluatorFactory: TEvaluatorFactory
);
destructor Destroy; override; // Modified
function Execute(const RootNode: IAstNode): IAstNode;
// Updated static helper
class function ExpandMacros(
const InitialScope: IExecutionScope;
const ARootRegistry: IMacroRegistry; // Added
const AInitialScope: IExecutionScope;
const RootNode: IAstNode;
const EvaluatorFactory: TEvaluatorFactory
): IAstNode; static;
class property GlobalMacroRegistry: TMacroRegistry read FGlobalMacroRegistry;
end;
implementation
@@ -109,40 +97,6 @@ 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 Node: IMacroDefinitionNode);
begin
FMacros.AddOrSetValue(Node.Name.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);
@@ -288,57 +242,52 @@ end;
{ TMacroExpander }
constructor TMacroExpander.Create(const AInitialScope: IExecutionScope; const AEvaluatorFactory: TEvaluatorFactory);
constructor TMacroExpander.Create(
const ARootRegistry: IMacroRegistry;
const AInitialScope: IExecutionScope;
const AEvaluatorFactory: TEvaluatorFactory
);
begin
inherited Create;
Assert(Assigned(ARootRegistry));
Assert(Assigned(AInitialScope));
Assert(Assigned(AEvaluatorFactory));
FInitialScope := AInitialScope;
FEvaluatorFactory := AEvaluatorFactory;
// Erzeugt die Root-Registry für Makros
FCurrentMacroRegistry := TMacroRegistry.Create(FGlobalMacroRegistry);
end;
class constructor TMacroExpander.CreateClass;
begin
FGlobalMacroRegistry := TMacroRegistry.Create(nil);
// Creates the root registry for this specific compilation run,
// parented to the global registry from the environment.
FCurrentMacroRegistry := ARootRegistry.CreateChildRegistry;
end;
destructor TMacroExpander.Destroy;
begin
FCurrentMacroRegistry.Free;
// FCurrentMacroRegistry is an interface, managed by ARC
inherited Destroy;
end;
class destructor TMacroExpander.DestroyClass;
begin
FGlobalMacroRegistry.Free;
end;
procedure TMacroExpander.EnterMacroScope;
begin
// Erstellt eine neue Registry mit der aktuellen als Parent
FCurrentMacroRegistry := TMacroRegistry.Create(FCurrentMacroRegistry);
// Creates a new registry with the current as Parent
FCurrentMacroRegistry := FCurrentMacroRegistry.CreateChildRegistry;
end;
procedure TMacroExpander.ExitMacroScope;
var
oldRegistry: TMacroRegistry;
begin
oldRegistry := FCurrentMacroRegistry;
FCurrentMacroRegistry := oldRegistry.FParent;
oldRegistry.Free;
// Revert to parent registry. ARC will free the old child.
FCurrentMacroRegistry := FCurrentMacroRegistry.Parent;
end;
class function TMacroExpander.ExpandMacros(
const InitialScope: IExecutionScope;
const ARootRegistry: IMacroRegistry;
const AInitialScope: IExecutionScope;
const RootNode: IAstNode;
const EvaluatorFactory: TEvaluatorFactory
): IAstNode;
begin
// IAstMacroExpander.Execute gibt keinen Descriptor mehr zurück
var expander := TMacroExpander.Create(InitialScope, EvaluatorFactory) as IAstMacroExpander;
// IAstMacroExpander.Execute gibt keinen Descriptor mehr zurueck
var expander := TMacroExpander.Create(ARootRegistry, AInitialScope, EvaluatorFactory) as IAstMacroExpander;
Result := expander.Execute(RootNode);
end;
@@ -400,7 +349,7 @@ begin
// --- It is a macro, expand it ---
// 1. Create the temporary scope for the macro parameters
// It must be parented to the *initial* scope to find other macros/globals.
// It must be parented to the *initial* scope to find other globals.
var expansionScope := TAst.CreateScope(FInitialScope);
var params := macroDef.Parameters;
if Length(Node.Arguments) <> Length(params) then
@@ -422,8 +371,8 @@ begin
boundSubAst: IAstNode;
begin
// This is the compile-time evaluation (Binder + Evaluator)
// Es wird der 'expansionScope' verwendet, der die AST-Argumente enthält
boundSubAst := TAstBinder.Bind(expansionScope, ANodeToEvaluate, subDescriptor);
// Es wird der 'expansionScope' verwendet, der die AST-Argumente enthaelt
boundSubAst := TAstBinder.Bind(expansionScope.CreateDescriptor, ANodeToEvaluate, subDescriptor);
// Create eval scope from the new descriptor, parented to the expansion scope
evalScope := subDescriptor.CreateScope(expansionScope);
+419
View File
@@ -0,0 +1,419 @@
unit Myc.Ast.Environment;
interface
uses
System.SysUtils,
System.Classes,
System.Generics.Collections,
Myc.Data.Value,
Myc.Ast,
Myc.Ast.Nodes,
Myc.Ast.Scope;
type
IMacroRegistry = interface; // Forward
IEnvironment = interface; // Forward
IExecutionStrategy = interface; // Forward
IExecutable = interface; // Forward
// Defines the Strategy for creating an Evaluator.
IExecutionStrategy = interface
function CreateVisitor(const AScope: IExecutionScope): IEvaluatorVisitor;
end;
// Defines the compile-time macro storage.
IMacroRegistry = interface
{$region 'private'}
function GetParent: IMacroRegistry;
{$endregion}
procedure Define(const Node: IMacroDefinitionNode);
function Find(const Name: string): IMacroDefinitionNode;
function CreateChildRegistry: IMacroRegistry;
property Parent: IMacroRegistry read GetParent;
end;
// Represents a compiled, ready-to-run script.
// This encapsulates the compiled AST and its internal layout (Descriptor).
IExecutable = interface
{$region 'private'}
function GetCompiledAst: IAstNode;
function GetDescriptor: IScopeDescriptor;
{$endregion}
// The final, compiled AST (for debugging, serialization, etc.)
property CompiledAst: IAstNode read GetCompiledAst;
// The internal layout (hidden from TForm1, used by IEnvironment.Execute)
property Descriptor: IScopeDescriptor read GetDescriptor;
end;
// Defines the central environment for compilation and execution.
IEnvironment = interface
{$region 'private'}
function GetRootScope: IExecutionScope;
function GetMacroRegistry: IMacroRegistry;
{$endregion}
procedure SetExecutionStrategy(const AStrategy: IExecutionStrategy);
// Compiles source AST into a runnable script.
function Compile(const ANode: IAstNode): IExecutable; // Changed return type
// Executes a previously compiled script.
function Execute(const AScript: IExecutable): TDataValue;
function CreateEnvironment: IEnvironment;
property RootScope: IExecutionScope read GetRootScope;
property MacroRegistry: IMacroRegistry read GetMacroRegistry;
end;
// Interface Helper for IEnvironment
TAstEnvironment = record
private
FEnvironment: IEnvironment;
function GetRootScope: IExecutionScope; inline;
function GetMacroRegistry: IMacroRegistry; inline;
public
constructor Create(const AEnvironment: IEnvironment);
class operator Initialize(out Dest: TAstEnvironment);
class operator Implicit(const A: IEnvironment): TAstEnvironment;
class operator Implicit(const A: TAstEnvironment): IEnvironment;
function CreateEnvironment: TAstEnvironment;
procedure SetStandardMode;
procedure SetDebugMode(ALog: TStrings; AShowScope: Boolean);
function Compile(const ANode: IAstNode): IExecutable;
function Execute(const AScript: IExecutable): TDataValue;
function CompileAndExecute(const AScript: IAstNode): TDataValue;
procedure Define(const Name: String; const AScript: IAstNode);
property RootScope: IExecutionScope read GetRootScope;
property MacroRegistry: IMacroRegistry read GetMacroRegistry;
end;
implementation
uses
Myc.Ast.RTL,
Myc.Ast.Evaluator,
Myc.Ast.Debugger,
Myc.Ast.Compiler.Macros,
Myc.Ast.Compiler.Binder,
Myc.Ast.Compiler.TypeChecker,
Myc.Ast.Compiler.Lowering,
Myc.Ast.Compiler.TCO;
type
{ TStandardExecutionStrategy }
TStandardExecutionStrategy = class(TInterfacedObject, IExecutionStrategy)
public
function CreateVisitor(const AScope: IExecutionScope): IEvaluatorVisitor;
end;
{ TDebugExecutionStrategy }
TDebugExecutionStrategy = class(TInterfacedObject, IExecutionStrategy)
private
FLog: TStrings;
FShowScope: Boolean;
public
constructor Create(ALog: TStrings; AShowScope: Boolean);
function CreateVisitor(const AScope: IExecutionScope): IEvaluatorVisitor;
end;
{ TMacroRegistryImpl }
TMacroRegistryImpl = class(TInterfacedObject, IMacroRegistry)
private
FParent: IMacroRegistry;
FMacros: TDictionary<string, IMacroDefinitionNode>;
function GetParent: IMacroRegistry;
public
constructor Create(AParent: IMacroRegistry);
destructor Destroy; override;
procedure Define(const Node: IMacroDefinitionNode);
function Find(const Name: string): IMacroDefinitionNode;
function CreateChildRegistry: IMacroRegistry;
end;
{ TExecutable }
TExecutable = class(TInterfacedObject, IExecutable)
private
FCompiledAst: IAstNode;
FDescriptor: IScopeDescriptor;
function GetCompiledAst: IAstNode;
function GetDescriptor: IScopeDescriptor;
public
constructor Create(ACompiledAst: IAstNode; ADescriptor: IScopeDescriptor);
end;
{ TEnvironment }
TEnvironment = class(TInterfacedObject, IEnvironment)
private
FRootScope: IExecutionScope;
FMacroRegistry: IMacroRegistry;
FExecutionStrategy: IExecutionStrategy;
function GetRootScope: IExecutionScope;
function GetMacroRegistry: IMacroRegistry;
public
constructor Create(
const ARootScope: IExecutionScope;
const AMacroRegistry: IMacroRegistry;
const AExecutionStrategy: IExecutionStrategy
);
destructor Destroy; override;
function CreateEnvironment: IEnvironment;
procedure SetExecutionStrategy(const AStrategy: IExecutionStrategy);
function Compile(const ANode: IAstNode): IExecutable;
function Execute(const AScript: IExecutable): TDataValue;
end;
// --- Factory Implementations ---
{ TAstEnvironment }
class operator TAstEnvironment.Initialize(out Dest: TAstEnvironment);
begin
Dest.FEnvironment :=
TEnvironment.Create(TScope.CreateScope(nil, nil, nil), TMacroRegistryImpl.Create(nil), TStandardExecutionStrategy.Create);
end;
constructor TAstEnvironment.Create(const AEnvironment: IEnvironment);
begin
FEnvironment := AEnvironment;
end;
procedure TAstEnvironment.SetStandardMode;
begin
FEnvironment.SetExecutionStrategy(TStandardExecutionStrategy.Create);
end;
procedure TAstEnvironment.SetDebugMode(ALog: TStrings; AShowScope: Boolean);
begin
FEnvironment.SetExecutionStrategy(TDebugExecutionStrategy.Create(ALog, AShowScope));
end;
class operator TAstEnvironment.Implicit(const A: IEnvironment): TAstEnvironment;
begin
Result.FEnvironment := A;
end;
class operator TAstEnvironment.Implicit(const A: TAstEnvironment): IEnvironment;
begin
Result := A.FEnvironment;
end;
function TAstEnvironment.Compile(const ANode: IAstNode): IExecutable;
begin
Result := FEnvironment.Compile(ANode);
end;
function TAstEnvironment.CompileAndExecute(const AScript: IAstNode): TDataValue;
begin
Result := Execute(Compile(AScript));
end;
function TAstEnvironment.CreateEnvironment: TAstEnvironment;
begin
Result := FEnvironment.CreateEnvironment;
end;
procedure TAstEnvironment.Define(const Name: String; const AScript: IAstNode);
begin
RootScope.Define(Name, CompileAndExecute(AScript));
end;
function TAstEnvironment.Execute(const AScript: IExecutable): TDataValue;
begin
Result := FEnvironment.Execute(AScript);
end;
function TAstEnvironment.GetRootScope: IExecutionScope;
begin
Result := FEnvironment.GetRootScope;
end;
function TAstEnvironment.GetMacroRegistry: IMacroRegistry;
begin
Result := FEnvironment.GetMacroRegistry;
end;
{ TStandardExecutionStrategy }
function TStandardExecutionStrategy.CreateVisitor(const AScope: IExecutionScope): IEvaluatorVisitor;
begin
Result := TEvaluatorVisitor.Create(AScope);
end;
{ TDebugExecutionStrategy }
constructor TDebugExecutionStrategy.Create(ALog: TStrings; AShowScope: Boolean);
begin
inherited Create;
FLog := ALog;
FShowScope := AShowScope;
end;
function TDebugExecutionStrategy.CreateVisitor(const AScope: IExecutionScope): IEvaluatorVisitor;
begin
Result := TDebugEvaluatorVisitor.Create(AScope, FLog, FShowScope, 0);
end;
{ TMacroRegistryImpl }
constructor TMacroRegistryImpl.Create(AParent: IMacroRegistry);
begin
inherited Create;
FParent := AParent;
FMacros := TDictionary<string, IMacroDefinitionNode>.Create;
end;
destructor TMacroRegistryImpl.Destroy;
begin
FMacros.Free;
inherited Destroy;
end;
function TMacroRegistryImpl.GetParent: IMacroRegistry;
begin
Result := FParent;
end;
procedure TMacroRegistryImpl.Define(const Node: IMacroDefinitionNode);
begin
FMacros.AddOrSetValue(Node.Name.Name, Node);
end;
function TMacroRegistryImpl.Find(const Name: string): IMacroDefinitionNode;
var
current: IMacroRegistry;
begin
current := Self;
while Assigned(current) do
begin
if (current as TMacroRegistryImpl).FMacros.TryGetValue(Name, Result) then
exit;
current := current.Parent;
end;
Result := nil;
end;
function TMacroRegistryImpl.CreateChildRegistry: IMacroRegistry;
begin
Result := TMacroRegistryImpl.Create(Self);
end;
{ TExecutable }
constructor TExecutable.Create(ACompiledAst: IAstNode; ADescriptor: IScopeDescriptor);
begin
inherited Create;
FCompiledAst := ACompiledAst;
FDescriptor := ADescriptor;
end;
function TExecutable.GetCompiledAst: IAstNode;
begin
Result := FCompiledAst;
end;
function TExecutable.GetDescriptor: IScopeDescriptor;
begin
Result := FDescriptor;
end;
{ TEnvironment }
constructor TEnvironment.Create(
const ARootScope: IExecutionScope;
const AMacroRegistry: IMacroRegistry;
const AExecutionStrategy: IExecutionStrategy
);
begin
inherited Create;
FRootScope := ARootScope;
FMacroRegistry := AMacroRegistry;
FExecutionStrategy := AExecutionStrategy;
end;
destructor TEnvironment.Destroy;
begin
inherited Destroy;
end;
function TEnvironment.GetMacroRegistry: IMacroRegistry;
begin
Result := FMacroRegistry;
end;
function TEnvironment.GetRootScope: IExecutionScope;
begin
Result := FRootScope;
end;
procedure TEnvironment.SetExecutionStrategy(const AStrategy: IExecutionStrategy);
begin
FExecutionStrategy := AStrategy;
end;
function TEnvironment.Compile(const ANode: IAstNode): IExecutable;
var
expandedAst, boundAst, typedAst, loweredAst: IAstNode;
descriptor: IScopeDescriptor;
begin
// Step 1: Expand macros
expandedAst :=
TMacroExpander.ExpandMacros(
FMacroRegistry,
FRootScope,
ANode,
function(const Scope: IExecutionScope): IEvaluatorVisitor begin Result := FExecutionStrategy.CreateVisitor(Scope); end
);
// Step 2: Bind names
boundAst := TAstBinder.Bind(FRootScope.CreateDescriptor, expandedAst, descriptor);
// Step 3: Check types
typedAst := TTypeChecker.CheckTypes(boundAst, descriptor);
// Step 4: Lowering
loweredAst := TAstLowerer.Lower(typedAst);
// Step 5: TCO
var compiledAst := TAstTCO.Optimize(loweredAst);
// Step 6: Create the executable package
Result := TExecutable.Create(compiledAst, descriptor);
end;
function TEnvironment.CreateEnvironment: IEnvironment;
begin
Result := TEnvironment.Create(TScope.CreateScope(FRootScope, nil, nil), TMacroRegistryImpl.Create(FMacroRegistry), FExecutionStrategy);
end;
function TEnvironment.Execute(const AScript: IExecutable): TDataValue;
var
evalScope: IExecutionScope;
visitor: IEvaluatorVisitor;
begin
// 1. Create the runtime scope *directly from the descriptor*.
// This creates the raw scope with the correct slot count for local variables,
// parented to FRootScope.
evalScope := AScript.Descriptor.CreateScope(FRootScope);
// 2. Use the strategy to create the final evaluator
visitor := FExecutionStrategy.CreateVisitor(evalScope);
// 3. Execute the compiled AST
Result := visitor.Execute(AScript.CompiledAst);
end;
end.
+1 -1
View File
@@ -399,7 +399,7 @@ type
function Execute(const RootNode: IAstNode): TDataValue;
end;
// A factory for creating visitors, primarily used by the debugger subsystem.
// A factory for creating visitors
TEvaluatorFactory = reference to function(const Scope: IExecutionScope): IEvaluatorVisitor;
implementation
+3 -1
View File
@@ -10,6 +10,7 @@ uses
Myc.Data.Scalar,
Myc.Data.Value,
Myc.Ast,
Myc.Ast.Scope,
Myc.Ast.Nodes;
type
@@ -21,12 +22,13 @@ type
constructor Create(const AName: string);
end;
procedure RegisterRtlFunctions(const AScope: IExecutionScope);
implementation
uses
System.Rtti,
System.TypInfo,
Myc.Ast.Scope,
Myc.Ast.RTL.Core;
//--------------------------------------------------------------------------------------------------
+48 -3
View File
@@ -49,7 +49,8 @@ type
procedure SetValues(const Address: TResolvedAddress; const Value: TDataValue);
{$endregion}
procedure Define(const Name: string; const Value: TDataValue);
// Defines a symbol and returns its runtime address
function Define(const Name: string; const Value: TDataValue): TResolvedAddress;
// Defines a variable at a given address and stores it directly in a shared cell (boxing).
procedure DefineBoxed(SlotIndex: Integer; const Value: TDataValue);
@@ -59,6 +60,8 @@ type
function Capture(const Address: TResolvedAddress): IValueCell;
function GetNameID(const Name: String): Integer;
// Resolves a name to an address by searching the runtime scope chain.
function Resolve(const Name: string): TResolvedAddress;
function CreateDescriptor: IScopeDescriptor;
@@ -143,8 +146,9 @@ type
destructor Destroy; override;
procedure Clear;
function GetNameID(const Name: String): Integer;
function Resolve(const Name: string): TResolvedAddress;
function Dump: string;
procedure Define(const Name: string; const Value: TDataValue);
function Define(const Name: string; const Value: TDataValue): TResolvedAddress;
procedure DefineBoxed(SlotIndex: Integer; const Value: TDataValue);
function Capture(const Address: TResolvedAddress): IValueCell;
function CreateDescriptor: IScopeDescriptor;
@@ -336,7 +340,7 @@ begin
Result := TScopeDescriptor.CreateDescriptor(Self);
end;
procedure TExecutionScope.Define(const Name: string; const Value: TDataValue);
function TExecutionScope.Define(const Name: string; const Value: TDataValue): TResolvedAddress;
var
id: Integer;
index: Integer;
@@ -351,6 +355,11 @@ begin
FValues[index].Value := Value;
FValues[index].IsBoxed := False;
FNameToIndex.Add(id, index);
// Return the address
Result.Kind := akLocalOrParent;
Result.ScopeDepth := 0;
Result.SlotIndex := index;
end;
procedure TExecutionScope.DefineBoxed(SlotIndex: Integer; const Value: TDataValue);
@@ -480,6 +489,42 @@ begin
end;
end;
function TExecutionScope.Resolve(const Name: string): TResolvedAddress;
var
nameID: Integer;
slotIndex: Integer;
currentScope: IExecutionScope;
depth: Integer;
begin
currentScope := Self;
depth := 0;
// GetNameID is shared (or recreated) across the scope chain
nameID := GetNameID(Name);
while Assigned(currentScope) do
begin
// We must cast to the implementation class to access 'NameToIndex'
var execScopeImpl := (currentScope as TExecutionScope);
// Accessing .NameToIndex property ensures NeedNameToIndex is called
if execScopeImpl.NameToIndex.TryGetValue(nameID, slotIndex) then
begin
// Found in this scope
Result.Kind := akLocalOrParent;
Result.ScopeDepth := depth;
Result.SlotIndex := slotIndex;
exit;
end;
// Not found, go to parent
inc(depth);
currentScope := currentScope.Parent;
end;
// Not found in the entire chain
Result := Default(TResolvedAddress); // Kind = akUnresolved
end;
procedure TExecutionScope.SetValues(const Address: TResolvedAddress; const Value: TDataValue);
var
targetScope: TExecutionScope;