Ast environments
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user