Binder refactoring - extracted type checking
This commit is contained in:
+62
-71
@@ -38,12 +38,13 @@ uses
|
||||
Myc.Ast.Debugger,
|
||||
Myc.Fmx.AstEditor.Node,
|
||||
Myc.Fmx.AstEditor.Workspace,
|
||||
Myc.Ast.MacroExpander,
|
||||
FMX.DialogService,
|
||||
FMX.ListView.Types,
|
||||
FMX.ListView.Appearances,
|
||||
FMX.ListView.Adapters.Base,
|
||||
FMX.ListView;
|
||||
FMX.ListView, // Added for platform-independent dialogs
|
||||
Myc.Ast.MacroExpander,
|
||||
Myc.Ast.TypeChecker;
|
||||
|
||||
type
|
||||
// A test record
|
||||
@@ -120,7 +121,9 @@ type
|
||||
FScriptUpdate: Boolean;
|
||||
procedure WorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
|
||||
function CreateEvaluator(const Scope: IExecutionScope): IEvaluatorVisitor;
|
||||
// Helper function to encapsulate the Bind -> Evaluate pattern
|
||||
// Helper function to encapsulate the 3-stage compilation pipeline
|
||||
function CompileAst(const ANode: IAstNode; const AParentScope: IExecutionScope; out ADescriptor: IScopeDescriptor): IAstNode;
|
||||
// Helper function to encapsulate the Compile -> Evaluate pattern
|
||||
function ExecuteAst(const ANode: IAstNode; const AParentScope: IExecutionScope): TDataValue;
|
||||
procedure UpdateScript;
|
||||
procedure ShowVizualization(X, Y: Single);
|
||||
@@ -207,36 +210,43 @@ begin
|
||||
RegisterNativeFunctions(FGScope);
|
||||
end;
|
||||
|
||||
function TForm1.ExecuteAst(const ANode: IAstNode; const AParentScope: IExecutionScope): TDataValue;
|
||||
function TForm1.CompileAst(const ANode: IAstNode; const AParentScope: IExecutionScope; out ADescriptor: IScopeDescriptor): IAstNode;
|
||||
var
|
||||
boundAst: IAstNode;
|
||||
descriptor: IScopeDescriptor; // This is the final (binder) descriptor
|
||||
evalScope: IExecutionScope;
|
||||
visitor: IEvaluatorVisitor;
|
||||
macroDescriptor: IScopeDescriptor; // This is the macro-pass descriptor
|
||||
expandedAst: IAstNode;
|
||||
macroDescriptor: IScopeDescriptor;
|
||||
expandedAst, boundAst: IAstNode;
|
||||
begin
|
||||
// The new, streamlined pipeline: The binder handles macro expansion internally.
|
||||
FCurrUnboundAst := ANode;
|
||||
|
||||
// Step 1: Expand macros (Phase 1)
|
||||
// The expander uses the parent scope to find macros and the factory to eval unquotes.
|
||||
expandedAst := TMacroExpander.ExpandMacros(AParentScope, ANode, macroDescriptor, CreateEvaluator);
|
||||
|
||||
// Step 2: Bind names and types (Phase 2)
|
||||
// The binder runs on the *expanded* AST. It uses the same parent scope.
|
||||
// It no longer needs the factory.
|
||||
boundAst := TAstBinder.Bind(AParentScope, expandedAst, descriptor);
|
||||
// Step 2: Bind names and addresses (Phase 2)
|
||||
// The binder uses the parent scope, but produces the final descriptor
|
||||
boundAst := TAstBinder.Bind(AParentScope, expandedAst, ADescriptor);
|
||||
|
||||
// Step 3: Check and infer types (Phase 3)
|
||||
// The type checker runs on the bound AST and the final descriptor
|
||||
Result := TTypeChecker.CheckTypes(boundAst, ADescriptor);
|
||||
end;
|
||||
|
||||
function TForm1.ExecuteAst(const ANode: IAstNode; const AParentScope: IExecutionScope): TDataValue;
|
||||
var
|
||||
descriptor: IScopeDescriptor;
|
||||
evalScope: IExecutionScope;
|
||||
visitor: IEvaluatorVisitor;
|
||||
typedAst: IAstNode;
|
||||
begin
|
||||
FCurrUnboundAst := ANode;
|
||||
|
||||
// Call the new helper function for the 3-stage pipeline
|
||||
typedAst := CompileAst(ANode, AParentScope, descriptor);
|
||||
|
||||
// Store the final bound AST for visualization and debugging.
|
||||
FCurrAst := boundAst;
|
||||
FCurrAst := typedAst;
|
||||
FCurrDesc := descriptor;
|
||||
|
||||
// Step 3: Create the final scope and evaluator for runtime execution.
|
||||
// The runtime scope is created from the *binder's* descriptor, using the parent scope.
|
||||
// Create the final scope and evaluator for runtime execution.
|
||||
evalScope := descriptor.CreateScope(AParentScope);
|
||||
visitor := CreateEvaluator(evalScope);
|
||||
Result := visitor.Execute(boundAst);
|
||||
Result := visitor.Execute(typedAst);
|
||||
end;
|
||||
|
||||
procedure TForm1.FormCreate(Sender: TObject);
|
||||
@@ -250,10 +260,8 @@ begin
|
||||
TAst.RegisterLibrary(
|
||||
procedure(const Scope: IExecutionScope)
|
||||
var
|
||||
smaAst, boundSmaAst, expandedAst: IAstNode;
|
||||
// binder: IAstBinder; // <-- REMOVED
|
||||
smaAst, typedAst: IAstNode;
|
||||
smaDescriptor: IScopeDescriptor;
|
||||
macroDescriptor: IScopeDescriptor;
|
||||
smaScope: IExecutionScope;
|
||||
smaVisitor: IEvaluatorVisitor;
|
||||
begin
|
||||
@@ -308,14 +316,15 @@ begin
|
||||
)
|
||||
);
|
||||
|
||||
// Phase 1: Expand
|
||||
expandedAst := TMacroExpander.ExpandMacros(Scope, smaAst, macroDescriptor, CreateEvaluator);
|
||||
// Phase 2: Bind
|
||||
boundSmaAst := TAstBinder.Bind(Scope, expandedAst, smaDescriptor);
|
||||
// Run the full pipeline
|
||||
typedAst := CompileAst(smaAst, Scope, smaDescriptor);
|
||||
|
||||
smaScope := smaDescriptor.CreateScope(Scope);
|
||||
smaVisitor := CreateEvaluator(smaScope);
|
||||
|
||||
// Execute the typed AST to define the function
|
||||
smaVisitor.Execute(typedAst);
|
||||
|
||||
Scope.Define(
|
||||
'print',
|
||||
TDataValue(
|
||||
@@ -543,8 +552,6 @@ var
|
||||
jsonString: string;
|
||||
jsonObj: TJSONObject;
|
||||
converter: IJsonAstConverter;
|
||||
macroDescriptor: IScopeDescriptor;
|
||||
expandedAst: IAstNode;
|
||||
begin
|
||||
Memo1.Lines.BeginUpdate;
|
||||
try
|
||||
@@ -566,12 +573,10 @@ begin
|
||||
try
|
||||
var unboundAst := converter.Deserialize(jsonObj);
|
||||
|
||||
// Phase 1: Expand
|
||||
expandedAst := TMacroExpander.ExpandMacros(FGScope, unboundAst, macroDescriptor, CreateEvaluator);
|
||||
// Phase 2: Bind
|
||||
FCurrAst := TAstBinder.Bind(FGScope, expandedAst, FCurrDesc);
|
||||
// Run the full pipeline
|
||||
FCurrAst := CompileAst(unboundAst, FGScope, FCurrDesc);
|
||||
|
||||
Memo1.Lines.Add('AST deserialized successfully from JSON.');
|
||||
Memo1.Lines.Add('AST deserialized and bound successfully from JSON.');
|
||||
Memo1.Lines.Add('You can now visualize it (Middle Mouse Click) or pretty-print it.');
|
||||
finally
|
||||
jsonObj.Free;
|
||||
@@ -625,32 +630,31 @@ end;
|
||||
|
||||
procedure TForm1.DumpButtonClick(Sender: TObject);
|
||||
var
|
||||
boundNode: IAstNode;
|
||||
macroDescriptor: IScopeDescriptor;
|
||||
typedNode: IAstNode;
|
||||
begin
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add('--- AST Dump ---');
|
||||
|
||||
if not Assigned(FCurrAst) then
|
||||
if not Assigned(FCurrUnboundAst) then
|
||||
begin
|
||||
Memo1.Lines.Add('No AST has been generated yet. Click a test button first.');
|
||||
exit;
|
||||
end;
|
||||
|
||||
// Re-expand and re-bind the *unbound* AST (FCurrUnboundAst)
|
||||
var expandedAst := TMacroExpander.ExpandMacros(FGScope, FCurrUnboundAst, macroDescriptor, CreateEvaluator);
|
||||
boundNode := TAstBinder.Bind(FGScope, expandedAst, FCurrDesc);
|
||||
TAstDumper.Dump(boundNode, Memo1.Lines);
|
||||
// Re-run the full pipeline for the dump
|
||||
typedNode := CompileAst(FCurrUnboundAst, FGScope, FCurrDesc);
|
||||
|
||||
TAstDumper.Dump(typedNode, Memo1.Lines);
|
||||
end;
|
||||
|
||||
procedure TForm1.FibonacciButtonClick(Sender: TObject);
|
||||
var
|
||||
root, fibAst, boundFibAst: IAstNode;
|
||||
root, fibAst, typedAst: IAstNode;
|
||||
result: TDataValue;
|
||||
sw: TStopwatch;
|
||||
fibScope: IExecutionScope;
|
||||
visitor: IEvaluatorVisitor;
|
||||
macroDescriptor: IScopeDescriptor;
|
||||
desc: IScopeDescriptor;
|
||||
begin
|
||||
fibAst :=
|
||||
TAst.Block(
|
||||
@@ -680,15 +684,12 @@ begin
|
||||
]
|
||||
);
|
||||
|
||||
// Phase 1: Expand
|
||||
var expandedAst := TMacroExpander.ExpandMacros(FGScope, fibAst, macroDescriptor, CreateEvaluator);
|
||||
// Phase 2: Bind
|
||||
var desc: IScopeDescriptor;
|
||||
boundFibAst := TAstBinder.Bind(FGScope, expandedAst, desc);
|
||||
// Run the full pipeline
|
||||
typedAst := CompileAst(fibAst, FGScope, desc);
|
||||
fibScope := desc.CreateScope(FGScope);
|
||||
|
||||
visitor := CreateEvaluator(fibScope);
|
||||
visitor.Execute(boundFibAst);
|
||||
visitor.Execute(typedAst);
|
||||
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add('--- Naive recursive fib with AST---');
|
||||
@@ -920,8 +921,8 @@ var
|
||||
scope: IExecutionScope;
|
||||
values: TArray<TScalar.TValue>;
|
||||
recordValue: TScalarRecord;
|
||||
setupAst, boundSetupAst, callAst, boundCallAst: IAstNode;
|
||||
setupDescriptor, callDescriptor, macroDescriptor: IScopeDescriptor;
|
||||
setupAst, boundCallAst, callAst, typedAst: IAstNode;
|
||||
setupDescriptor, callDescriptor: IScopeDescriptor;
|
||||
seriesAddress: TResolvedSymbol; // Use TResolvedSymbol
|
||||
begin
|
||||
Memo1.Lines.Clear;
|
||||
@@ -976,26 +977,19 @@ begin
|
||||
]
|
||||
);
|
||||
|
||||
// 1. Bind and execute the setup script.
|
||||
// Phase 1
|
||||
var expandedAst := TMacroExpander.ExpandMacros(FGScope, setupAst, macroDescriptor, CreateEvaluator);
|
||||
// Phase 2
|
||||
boundSetupAst := TAstBinder.Bind(FGScope, expandedAst, setupDescriptor);
|
||||
|
||||
// 1. Compile and execute the setup script.
|
||||
typedAst := CompileAst(setupAst, FGScope, setupDescriptor);
|
||||
scope := setupDescriptor.CreateScope(FGScope);
|
||||
var setupVisitor := CreateEvaluator(scope);
|
||||
setupVisitor.Execute(boundSetupAst);
|
||||
setupVisitor.Execute(typedAst);
|
||||
|
||||
// 2. Prepare for the simulation loop
|
||||
scope.Define('current_series', TDataValue.Void);
|
||||
var currentSeriesIdent := TAst.Identifier('current_series');
|
||||
callAst := TAst.FunctionCall(TAst.Identifier('maCrossStrategy'), [currentSeriesIdent]);
|
||||
|
||||
// 3. Re-bind the call AST within the now-populated scope to resolve the new variable.
|
||||
// Phase 1
|
||||
var expandedAst2 := TMacroExpander.ExpandMacros(scope, callAst, macroDescriptor, CreateEvaluator);
|
||||
// Phase 2
|
||||
boundCallAst := TAstBinder.Bind(scope, expandedAst2, callDescriptor);
|
||||
// 3. Re-compile the call AST within the now-populated scope to resolve the new variable.
|
||||
boundCallAst := CompileAst(callAst, scope, callDescriptor);
|
||||
|
||||
// 4. Get the address of 'current_series' from the new descriptor.
|
||||
seriesAddress := callDescriptor.FindSymbol('current_series');
|
||||
@@ -1101,7 +1095,6 @@ procedure TForm1.CreateTriggerExampleButtonClick(Sender: TObject);
|
||||
var
|
||||
blk: IAstNode;
|
||||
visitor: IEvaluatorVisitor;
|
||||
macroDescriptor: IScopeDescriptor;
|
||||
begin
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add('--- Creating Trigger Blueprint ---');
|
||||
@@ -1122,10 +1115,8 @@ begin
|
||||
]
|
||||
);
|
||||
|
||||
// Phase 1
|
||||
var expandedAst := TMacroExpander.ExpandMacros(FGScope, blk, macroDescriptor, CreateEvaluator);
|
||||
// Phase 2
|
||||
FCurrAst := TAstBinder.Bind(FGScope, expandedAst, FCurrDesc);
|
||||
// Run the pipeline
|
||||
FCurrAst := CompileAst(blk, FGScope, FCurrDesc);
|
||||
FTriggerScope := FCurrDesc.CreateScope(FGScope);
|
||||
|
||||
visitor := CreateEvaluator(FTriggerScope);
|
||||
@@ -1305,7 +1296,7 @@ begin
|
||||
jsonObj := jsonValue as TJSONObject;
|
||||
|
||||
converter := TJsonAstConverter.Create;
|
||||
FCurrAst := converter.Deserialize(jsonObj);
|
||||
FCurrUnboundAst := converter.Deserialize(jsonObj); // <-- Store unbound AST
|
||||
|
||||
// Update the UI
|
||||
UpdateScript; // This will print to ScriptMemo and show visualization
|
||||
|
||||
@@ -25,12 +25,7 @@ type
|
||||
TAstBinder = class(TAstTransformer, IAstBinder)
|
||||
private
|
||||
type
|
||||
TUpvalueMapping = class
|
||||
public
|
||||
Map: TDictionary<TResolvedAddress, Integer>;
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
TUpvalueMapping = TDictionary<TResolvedAddress, Integer>;
|
||||
private
|
||||
FInitialScope: IExecutionScope;
|
||||
FCurrentDescriptor: IScopeDescriptor;
|
||||
@@ -110,19 +105,6 @@ begin
|
||||
Result := Result * 23 + Value.SlotIndex;
|
||||
end;
|
||||
|
||||
{ TAstBinder.TUpvalueMapping }
|
||||
constructor TAstBinder.TUpvalueMapping.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
Map := TDictionary<TResolvedAddress, Integer>.Create(TResolvedAddressComparer.Create);
|
||||
end;
|
||||
|
||||
destructor TAstBinder.TUpvalueMapping.Destroy;
|
||||
begin
|
||||
Map.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
{ TAstBinder }
|
||||
|
||||
constructor TAstBinder.Create(const AInitialScope: IExecutionScope);
|
||||
@@ -501,7 +483,7 @@ var
|
||||
lastNestedLambdaCount: Integer;
|
||||
boundLambda: ILambdaExpressionNode;
|
||||
begin
|
||||
FUpvalueStack.Push(TUpvalueMapping.Create);
|
||||
FUpvalueStack.Push(TUpvalueMapping.Create(TResolvedAddressComparer.Create));
|
||||
try
|
||||
EnterScope;
|
||||
try
|
||||
@@ -529,7 +511,7 @@ begin
|
||||
|
||||
// Upvalue mapping extraction remains the same
|
||||
var upvalueMapping := FUpvalueStack.Peek;
|
||||
var sortedPairs := upvalueMapping.Map.ToArray;
|
||||
var sortedPairs := upvalueMapping.ToArray;
|
||||
TArray.Sort<TPair<TResolvedAddress, Integer>>(
|
||||
sortedPairs,
|
||||
TComparer<TPair<TResolvedAddress, Integer>>.Construct(
|
||||
@@ -610,10 +592,10 @@ begin
|
||||
var upvalue := FUpvalueStack.Peek;
|
||||
dec(adr.ScopeDepth); // Adjust address to be relative to the lambda's parent
|
||||
var upvalueIndex: Integer;
|
||||
if not upvalue.Map.TryGetValue(adr, upvalueIndex) then
|
||||
if not upvalue.TryGetValue(adr, upvalueIndex) then
|
||||
begin
|
||||
upvalueIndex := upvalue.Map.Count;
|
||||
upvalue.Map.Add(adr, upvalueIndex);
|
||||
upvalueIndex := upvalue.Count;
|
||||
upvalue.Add(adr, upvalueIndex);
|
||||
end;
|
||||
boundNode := TBoundIdentifierNode.Create(Node, TResolvedAddress.Create(akUpvalue, 0, upvalueIndex));
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user