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; 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.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.