Ast editor rondtrip
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -29,7 +29,8 @@ uses
|
||||
Myc.Fmx.AstEditor.Core in '..\Src\AST\Myc.Fmx.AstEditor.Core.pas',
|
||||
Myc.Fmx.AstEditor.Handlers in '..\Src\AST\Myc.Fmx.AstEditor.Handlers.pas',
|
||||
Myc.Fmx.AstEditor.Visualizer in '..\Src\AST\Myc.Fmx.AstEditor.Visualizer.pas',
|
||||
Myc.Fmx.AstEditor.Workspace in '..\Src\AST\Myc.Fmx.AstEditor.Workspace.pas';
|
||||
Myc.Fmx.AstEditor.Workspace in '..\Src\AST\Myc.Fmx.AstEditor.Workspace.pas',
|
||||
Myc.Fmx.AstEditor.Controller in '..\Src\AST\Myc.Fmx.AstEditor.Controller.pas';
|
||||
|
||||
{$R *.res}
|
||||
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
<DCCReference Include="..\Src\AST\Myc.Fmx.AstEditor.Handlers.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Fmx.AstEditor.Visualizer.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Fmx.AstEditor.Workspace.pas"/>
|
||||
<DCCReference Include="..\Src\AST\Myc.Fmx.AstEditor.Controller.pas"/>
|
||||
<BuildConfiguration Include="Base">
|
||||
<Key>Base</Key>
|
||||
</BuildConfiguration>
|
||||
|
||||
+726
-838
File diff suppressed because it is too large
Load Diff
@@ -143,12 +143,8 @@ class function TAstBinder.Bind(
|
||||
const AArgTypes: TArray<IStaticType> = nil
|
||||
): IAstNode;
|
||||
begin
|
||||
var binder := TAstBinder.Create(ParentLayout, AFunctionRegistry, AArgTypes, ALog);
|
||||
try
|
||||
var binder := TAstBinder.Create(ParentLayout, AFunctionRegistry, AArgTypes, ALog) as IAstBinder;
|
||||
Result := binder.Execute(RootNode, Layout);
|
||||
finally
|
||||
binder.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstBinder.Execute(const RootNode: IAstNode; out Layout: IScopeLayout): IAstNode;
|
||||
|
||||
@@ -38,14 +38,6 @@ type
|
||||
procedure Add(const Key: TMonoCacheKey; const Func: TSpecializedMethod);
|
||||
end;
|
||||
|
||||
TCompiledFunction = record
|
||||
public
|
||||
Func: TDataValue.TFunc;
|
||||
StaticType: IStaticType;
|
||||
IsPure: Boolean;
|
||||
constructor Create(const AFunc: TDataValue.TFunc; const AStaticType: IStaticType; AIsPure: Boolean);
|
||||
end;
|
||||
|
||||
// This transformer runs *after* TypeChecker.
|
||||
// It specializes all statically resolvable function calls (RTL and user-defined)
|
||||
// by replacing them with nodes that have a direct StaticTarget.
|
||||
@@ -271,13 +263,4 @@ begin
|
||||
ArgTypes := AArgTypes;
|
||||
end;
|
||||
|
||||
{ TCompiledFunction }
|
||||
|
||||
constructor TCompiledFunction.Create(const AFunc: TDataValue.TFunc; const AStaticType: IStaticType; AIsPure: Boolean);
|
||||
begin
|
||||
Func := AFunc;
|
||||
StaticType := AStaticType;
|
||||
IsPure := AIsPure;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -50,7 +50,12 @@ type
|
||||
|
||||
function Specialize(const Node: IAstNode): IAstNode;
|
||||
|
||||
function Compile(const Node: IFunctionDefinition; const ArgTypes: TArray<IStaticType> = []): TCompiledFunction;
|
||||
function Compile(
|
||||
const Node: IFunctionDefinition;
|
||||
const ArgTypes: TArray<IStaticType> = [];
|
||||
const Log: ICompilerLog = nil
|
||||
): TCompiledFunction;
|
||||
function Link(const Node: ILambdaExpressionNode; const Log: ICompilerLog): TCompiledFunction;
|
||||
|
||||
function CreateEnvironment: IEnvironment;
|
||||
|
||||
@@ -80,15 +85,19 @@ type
|
||||
procedure SetStandardMode;
|
||||
procedure SetDebugMode(ALog: TStrings; AShowScope: Boolean);
|
||||
|
||||
function Run(const ANode: IAstNode; const Params: TArray<IIdentifierNode> = []; const Args: TArray<TDataValue> = []): TDataValue;
|
||||
function ExpandMacros(const Node: IAstNode): IAstNode;
|
||||
function Bind(const Node: IAstNode; const AArgTypes: TArray<IStaticType>; const Log: ICompilerLog): IAstNode;
|
||||
function Specialize(const Node: IAstNode): IAstNode;
|
||||
|
||||
function Compile(
|
||||
const Node: IAstNode;
|
||||
const Params: TArray<IIdentifierNode> = [];
|
||||
const ArgTypes: TArray<IStaticType> = []
|
||||
): TCompiledFunction; overload;
|
||||
|
||||
function Compile(const Node: IFunctionDefinition; const ArgTypes: TArray<IStaticType> = []): TCompiledFunction; overload;
|
||||
function Link(const Node: ILambdaExpressionNode; const Log: ICompilerLog): TCompiledFunction;
|
||||
|
||||
function Run(const ANode: IAstNode; const Params: TArray<IIdentifierNode> = []; const Args: TArray<TDataValue> = []): TDataValue;
|
||||
|
||||
procedure Define(const Name: String; const AScript: IAstNode);
|
||||
|
||||
@@ -180,7 +189,12 @@ type
|
||||
|
||||
function Specialize(const Node: IAstNode): IAstNode;
|
||||
|
||||
function Compile(const Node: IFunctionDefinition; const ArgTypes: TArray<IStaticType>): TCompiledFunction; overload;
|
||||
function Compile(
|
||||
const Node: IFunctionDefinition;
|
||||
const ArgTypes: TArray<IStaticType>;
|
||||
const Log: ICompilerLog = nil
|
||||
): TCompiledFunction;
|
||||
function Link(const Node: ILambdaExpressionNode; const Log: ICompilerLog): TCompiledFunction;
|
||||
end;
|
||||
|
||||
{ TAstEnvironment }
|
||||
@@ -190,6 +204,13 @@ begin
|
||||
FEnvironment := AEnvironment;
|
||||
end;
|
||||
|
||||
function TAstEnvironment.Bind(const Node: IAstNode; const AArgTypes: TArray<IStaticType>; const Log: ICompilerLog): IAstNode;
|
||||
var
|
||||
layout: IScopeLayout;
|
||||
begin
|
||||
Result := FEnvironment.Bind(Node, layout, AArgTypes, Log);
|
||||
end;
|
||||
|
||||
procedure TAstEnvironment.SetStandardMode;
|
||||
begin
|
||||
FEnvironment.SetExecutionStrategy(TStandardExecutionStrategy.Create);
|
||||
@@ -245,6 +266,11 @@ begin
|
||||
RootScope.Define(Name, compiled.Func([]), compiled.StaticType);
|
||||
end;
|
||||
|
||||
function TAstEnvironment.ExpandMacros(const Node: IAstNode): IAstNode;
|
||||
begin
|
||||
Result := FEnvironment.ExpandMacros(Node);
|
||||
end;
|
||||
|
||||
function TAstEnvironment.GetRootScope: IExecutionScope;
|
||||
begin
|
||||
Result := FEnvironment.GetRootScope;
|
||||
@@ -255,14 +281,23 @@ begin
|
||||
Result := FEnvironment.GetMacroRegistry;
|
||||
end;
|
||||
|
||||
function TAstEnvironment.Link(const Node: ILambdaExpressionNode; const Log: ICompilerLog): TCompiledFunction;
|
||||
begin
|
||||
Result := FEnvironment.Link(Node, Log);
|
||||
end;
|
||||
|
||||
function TAstEnvironment.Run(
|
||||
const ANode: IAstNode;
|
||||
const Params: TArray<IIdentifierNode> = [];
|
||||
const Args: TArray<TDataValue> = []
|
||||
): TDataValue;
|
||||
begin
|
||||
var compiled := Compile(ANode, Params);
|
||||
Result := compiled.Func(Args);
|
||||
Result := Compile(ANode, Params).Func(Args);
|
||||
end;
|
||||
|
||||
function TAstEnvironment.Specialize(const Node: IAstNode): IAstNode;
|
||||
begin
|
||||
Result := FEnvironment.Specialize(Node);
|
||||
end;
|
||||
|
||||
{ TStandardExecutionStrategy }
|
||||
@@ -365,45 +400,55 @@ begin
|
||||
Result := TEnvironment.Create(TAst.CreateScope(FRootScope), TMacroRegistry.Create(FMacroRegistry), FExecutionStrategy);
|
||||
end;
|
||||
|
||||
function TEnvironment.Compile(const Node: IFunctionDefinition; const ArgTypes: TArray<IStaticType>): TCompiledFunction;
|
||||
function TEnvironment.Compile(
|
||||
const Node: IFunctionDefinition;
|
||||
const ArgTypes: TArray<IStaticType>;
|
||||
const Log: ICompilerLog = nil
|
||||
): TCompiledFunction;
|
||||
var
|
||||
layout: IScopeLayout;
|
||||
descriptor: IScopeDescriptor;
|
||||
funcType: IStaticType;
|
||||
finalFunc: TDataValue.TFunc;
|
||||
|
||||
typedNode, specialized, tcoOptimized: IAstNode;
|
||||
isPure: Boolean;
|
||||
|
||||
log: ICompilerLog;
|
||||
lg: ICompilerLog;
|
||||
begin
|
||||
log := TCompilerLog.Create;
|
||||
lg :=
|
||||
if Assigned(Log) then Log
|
||||
else TCompilerLog.Create as ICompilerLog;
|
||||
|
||||
// 1. Expand Macros
|
||||
var expanded := ExpandMacros(Node);
|
||||
|
||||
// 2. Bind & TypeCheck (accumulating errors)
|
||||
typedNode := Bind(expanded, layout, ArgTypes, log);
|
||||
var typedNode := Bind(expanded, layout, ArgTypes, lg);
|
||||
|
||||
// 3. Check for compilation errors
|
||||
if log.HasErrors then
|
||||
raise ECompilationFailed.Create(log.GetEntries);
|
||||
if lg.HasErrors then
|
||||
raise ECompilationFailed.Create(lg.GetEntries);
|
||||
|
||||
// Note: If types are Unknown but no errors were logged (edge case), we proceed.
|
||||
// But Binder/TypeChecker logic ensures errors are logged for Unknowns that matter.
|
||||
|
||||
// 4. Specialization & Optimization
|
||||
descriptor := typedNode.AsLambdaExpression.Descriptor;
|
||||
var specialized := Specialize(typedNode);
|
||||
|
||||
Result := Link(specialized.AsLambdaExpression, lg);
|
||||
end;
|
||||
|
||||
function TEnvironment.Link(const Node: ILambdaExpressionNode; const Log: ICompilerLog): TCompiledFunction;
|
||||
var
|
||||
descriptor: IScopeDescriptor;
|
||||
funcType: IStaticType;
|
||||
finalFunc: TDataValue.TFunc;
|
||||
isPure: Boolean;
|
||||
begin
|
||||
descriptor := Node.Descriptor;
|
||||
|
||||
// Descriptor might be nil if Bind failed badly, but HasErrors check above covers that.
|
||||
Assert(Assigned(descriptor));
|
||||
|
||||
funcType := typedNode.AsTypedNode.StaticType;
|
||||
funcType := Node.StaticType;
|
||||
|
||||
specialized := Specialize(typedNode);
|
||||
tcoOptimized := TAstTCO.Optimize(specialized);
|
||||
var tcoOptimized := TAstTCO.Optimize(Node).AsLambdaExpression;
|
||||
|
||||
// 5. Purity Inference
|
||||
isPure := TPurityAnalyzer.IsPure(tcoOptimized.AsLambdaExpression.Body);
|
||||
isPure := TPurityAnalyzer.IsPure(tcoOptimized.Body);
|
||||
|
||||
// 6. Generate Visitor/Closure
|
||||
var visitor := FExecutionStrategy.CreateVisitor(descriptor.CreateScope(FRootScope));
|
||||
|
||||
@@ -64,6 +64,14 @@ type
|
||||
property Errors: TArray<TCompilerError> read FErrors;
|
||||
end;
|
||||
|
||||
TCompiledFunction = record
|
||||
public
|
||||
Func: TDataValue.TFunc;
|
||||
StaticType: IStaticType;
|
||||
IsPure: Boolean;
|
||||
constructor Create(const AFunc: TDataValue.TFunc; const AStaticType: IStaticType; AIsPure: Boolean);
|
||||
end;
|
||||
|
||||
// --- AST Interfaces ---
|
||||
|
||||
IAstVisitor = interface;
|
||||
@@ -839,6 +847,15 @@ uses
|
||||
System.Rtti,
|
||||
System.StrUtils;
|
||||
|
||||
{ TCompiledFunction }
|
||||
|
||||
constructor TCompiledFunction.Create(const AFunc: TDataValue.TFunc; const AStaticType: IStaticType; AIsPure: Boolean);
|
||||
begin
|
||||
Func := AFunc;
|
||||
StaticType := AStaticType;
|
||||
IsPure := AIsPure;
|
||||
end;
|
||||
|
||||
{ TAstNodeKindHelper }
|
||||
|
||||
function TAstNodeKindHelper.ToString: string;
|
||||
|
||||
+175
-26
@@ -123,7 +123,7 @@ type
|
||||
function VisitNop(const Node: INopNode): IAstNode; override;
|
||||
end;
|
||||
|
||||
TAstVisitor = class abstract(TInterfacedObject, IAstVisitor)
|
||||
TAstVisitor = class(TInterfacedObject, IAstVisitor)
|
||||
strict private
|
||||
// IAstVisitor explicit implementation (bridge methods)
|
||||
function IAstVisitor.VisitConstant = DoVisitConstant;
|
||||
@@ -176,30 +176,33 @@ type
|
||||
function DoVisitNop(const Node: INopNode): TDataValue;
|
||||
|
||||
protected
|
||||
// Virtual procedures for descendants (Interpreters/Side-effects) to override
|
||||
procedure VisitConstant(const Node: IConstantNode); virtual; abstract;
|
||||
procedure VisitIdentifier(const Node: IIdentifierNode); virtual; abstract;
|
||||
procedure VisitKeyword(const Node: IKeywordNode); virtual; abstract;
|
||||
procedure VisitIfExpression(const Node: IIfExpressionNode); virtual; abstract;
|
||||
procedure VisitTernaryExpression(const Node: ITernaryExpressionNode); virtual; abstract;
|
||||
procedure VisitLambdaExpression(const Node: ILambdaExpressionNode); virtual; abstract;
|
||||
procedure VisitFunctionCall(const Node: IFunctionCallNode); virtual; abstract;
|
||||
procedure VisitMacroExpansionNode(const Node: IMacroExpansionNode); virtual; abstract;
|
||||
procedure VisitBlockExpression(const Node: IBlockExpressionNode); virtual; abstract;
|
||||
procedure VisitVariableDeclaration(const Node: IVariableDeclarationNode); virtual; abstract;
|
||||
procedure VisitAssignment(const Node: IAssignmentNode); virtual; abstract;
|
||||
procedure VisitMacroDefinition(const Node: IMacroDefinitionNode); virtual; abstract;
|
||||
procedure VisitQuasiquote(const Node: IQuasiquoteNode); virtual; abstract;
|
||||
procedure VisitUnquote(const Node: IUnquoteNode); virtual; abstract;
|
||||
procedure VisitUnquoteSplicing(const Node: IUnquoteSplicingNode); virtual; abstract;
|
||||
procedure VisitIndexer(const Node: IIndexerNode); virtual; abstract;
|
||||
procedure VisitMemberAccess(const Node: IMemberAccessNode); virtual; abstract;
|
||||
procedure VisitRecordLiteral(const Node: IRecordLiteralNode); virtual; abstract;
|
||||
procedure VisitCreateSeries(const Node: ICreateSeriesNode); virtual; abstract;
|
||||
procedure VisitAddSeriesItem(const Node: IAddSeriesItemNode); virtual; abstract;
|
||||
procedure VisitSeriesLength(const Node: ISeriesLengthNode); virtual; abstract;
|
||||
procedure VisitRecurNode(const Node: IRecurNode); virtual; abstract;
|
||||
procedure VisitNop(const Node: INopNode); virtual; abstract;
|
||||
// Entry point for traversal
|
||||
procedure Accept(const Node: IAstNode); virtual;
|
||||
|
||||
// Virtual procedures with default traversal implementation
|
||||
procedure VisitConstant(const Node: IConstantNode); virtual;
|
||||
procedure VisitIdentifier(const Node: IIdentifierNode); virtual;
|
||||
procedure VisitKeyword(const Node: IKeywordNode); virtual;
|
||||
procedure VisitIfExpression(const Node: IIfExpressionNode); virtual;
|
||||
procedure VisitTernaryExpression(const Node: ITernaryExpressionNode); virtual;
|
||||
procedure VisitLambdaExpression(const Node: ILambdaExpressionNode); virtual;
|
||||
procedure VisitFunctionCall(const Node: IFunctionCallNode); virtual;
|
||||
procedure VisitMacroExpansionNode(const Node: IMacroExpansionNode); virtual;
|
||||
procedure VisitBlockExpression(const Node: IBlockExpressionNode); virtual;
|
||||
procedure VisitVariableDeclaration(const Node: IVariableDeclarationNode); virtual;
|
||||
procedure VisitAssignment(const Node: IAssignmentNode); virtual;
|
||||
procedure VisitMacroDefinition(const Node: IMacroDefinitionNode); virtual;
|
||||
procedure VisitQuasiquote(const Node: IQuasiquoteNode); virtual;
|
||||
procedure VisitUnquote(const Node: IUnquoteNode); virtual;
|
||||
procedure VisitUnquoteSplicing(const Node: IUnquoteSplicingNode); virtual;
|
||||
procedure VisitIndexer(const Node: IIndexerNode); virtual;
|
||||
procedure VisitMemberAccess(const Node: IMemberAccessNode); virtual;
|
||||
procedure VisitRecordLiteral(const Node: IRecordLiteralNode); virtual;
|
||||
procedure VisitCreateSeries(const Node: ICreateSeriesNode); virtual;
|
||||
procedure VisitAddSeriesItem(const Node: IAddSeriesItemNode); virtual;
|
||||
procedure VisitSeriesLength(const Node: ISeriesLengthNode); virtual;
|
||||
procedure VisitRecurNode(const Node: IRecurNode); virtual;
|
||||
procedure VisitNop(const Node: INopNode); virtual;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -706,6 +709,153 @@ end;
|
||||
|
||||
{ TAstVisitor }
|
||||
|
||||
procedure TAstVisitor.Accept(const Node: IAstNode);
|
||||
begin
|
||||
if Assigned(Node) then
|
||||
Node.Accept(Self);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitConstant(const Node: IConstantNode);
|
||||
begin
|
||||
// Leaf node
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitIdentifier(const Node: IIdentifierNode);
|
||||
begin
|
||||
// Leaf node
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitKeyword(const Node: IKeywordNode);
|
||||
begin
|
||||
// Leaf node
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitIfExpression(const Node: IIfExpressionNode);
|
||||
begin
|
||||
Accept(Node.Condition);
|
||||
Accept(Node.ThenBranch);
|
||||
Accept(Node.ElseBranch);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode);
|
||||
begin
|
||||
Accept(Node.Condition);
|
||||
Accept(Node.ThenBranch);
|
||||
Accept(Node.ElseBranch);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode);
|
||||
begin
|
||||
for var param in Node.Parameters do
|
||||
Accept(param);
|
||||
Accept(Node.Body);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitFunctionCall(const Node: IFunctionCallNode);
|
||||
begin
|
||||
Accept(Node.Callee);
|
||||
for var arg in Node.Arguments do
|
||||
Accept(arg);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode);
|
||||
begin
|
||||
Accept(Node.CallNode);
|
||||
Accept(Node.ExpandedBody);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitBlockExpression(const Node: IBlockExpressionNode);
|
||||
begin
|
||||
for var expr in Node.Expressions do
|
||||
Accept(expr);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode);
|
||||
begin
|
||||
Accept(Node.Target);
|
||||
Accept(Node.Initializer);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitAssignment(const Node: IAssignmentNode);
|
||||
begin
|
||||
Accept(Node.Target);
|
||||
Accept(Node.Value);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode);
|
||||
begin
|
||||
Accept(Node.Name);
|
||||
for var param in Node.Parameters do
|
||||
Accept(param);
|
||||
Accept(Node.Body);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitQuasiquote(const Node: IQuasiquoteNode);
|
||||
begin
|
||||
Accept(Node.Expression);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitUnquote(const Node: IUnquoteNode);
|
||||
begin
|
||||
Accept(Node.Expression);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode);
|
||||
begin
|
||||
Accept(Node.Expression);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitIndexer(const Node: IIndexerNode);
|
||||
begin
|
||||
Accept(Node.Base);
|
||||
Accept(Node.Index);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitMemberAccess(const Node: IMemberAccessNode);
|
||||
begin
|
||||
Accept(Node.Base);
|
||||
Accept(Node.Member);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode);
|
||||
begin
|
||||
for var field in Node.Fields do
|
||||
begin
|
||||
Accept(field.Key);
|
||||
Accept(field.Value);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitCreateSeries(const Node: ICreateSeriesNode);
|
||||
begin
|
||||
// Leaf node
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode);
|
||||
begin
|
||||
Accept(Node.Series);
|
||||
Accept(Node.Value);
|
||||
Accept(Node.Lookback);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitSeriesLength(const Node: ISeriesLengthNode);
|
||||
begin
|
||||
Accept(Node.Series);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitRecurNode(const Node: IRecurNode);
|
||||
begin
|
||||
for var arg in Node.Arguments do
|
||||
Accept(arg);
|
||||
end;
|
||||
|
||||
procedure TAstVisitor.VisitNop(const Node: INopNode);
|
||||
begin
|
||||
// Leaf node
|
||||
end;
|
||||
|
||||
// ... [Private Bridge Methods implementation remains unchanged] ...
|
||||
|
||||
function TAstVisitor.DoVisitConstant(const Node: IConstantNode): TDataValue;
|
||||
begin
|
||||
VisitConstant(Node);
|
||||
@@ -818,7 +968,6 @@ end;
|
||||
|
||||
function TAstVisitor.DoVisitNop(const Node: INopNode): TDataValue;
|
||||
begin
|
||||
// Added Nop implementation
|
||||
VisitNop(Node);
|
||||
end;
|
||||
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
unit Myc.Fmx.AstEditor.Controller;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
System.Classes,
|
||||
System.Types,
|
||||
System.Generics.Collections,
|
||||
FMX.Controls,
|
||||
Myc.Ast,
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Identities,
|
||||
Myc.Ast.Types,
|
||||
Myc.Ast.Visitor,
|
||||
Myc.Ast.Scope,
|
||||
Myc.Ast.Environment,
|
||||
Myc.Fmx.AstEditor.Core,
|
||||
Myc.Fmx.AstEditor.Workspace;
|
||||
|
||||
type
|
||||
TAstEditorController = class
|
||||
private
|
||||
type
|
||||
TNodeMap = TDictionary<IAstIdentity, TAstViewNode>;
|
||||
|
||||
// Visitor to propagate Type Information from TypedAST to Visual Nodes
|
||||
TTypePropagator = class(TAstVisitor)
|
||||
private
|
||||
FMap: TNodeMap;
|
||||
procedure ApplyType(const Node: IAstNode);
|
||||
protected
|
||||
// Central hook: Called for every node during traversal
|
||||
procedure Accept(const Node: IAstNode); override;
|
||||
public
|
||||
constructor Create(AMap: TNodeMap);
|
||||
end;
|
||||
|
||||
private
|
||||
FEnv: TAstEnvironment;
|
||||
FWorkspace: TAstWorkspace;
|
||||
FNodeMap: TNodeMap;
|
||||
FRootViewNode: TAstViewNode;
|
||||
|
||||
procedure CollectNodes(Parent: TControl);
|
||||
procedure ClearVisuals;
|
||||
procedure ApplyErrors(const Log: ICompilerLog);
|
||||
procedure ApplyTypes(const TypedAst: IAstNode);
|
||||
|
||||
public
|
||||
constructor Create(const AEnv: TAstEnvironment; AWorkspace: TAstWorkspace);
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure ValidateAndShow;
|
||||
procedure SetRoot(const AstNode: IAstNode);
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Myc.Data.Value;
|
||||
|
||||
{ TAstEditorController }
|
||||
|
||||
constructor TAstEditorController.Create(const AEnv: TAstEnvironment; AWorkspace: TAstWorkspace);
|
||||
begin
|
||||
inherited Create;
|
||||
FEnv := AEnv;
|
||||
FWorkspace := AWorkspace;
|
||||
FNodeMap := TNodeMap.Create;
|
||||
end;
|
||||
|
||||
destructor TAstEditorController.Destroy;
|
||||
begin
|
||||
FNodeMap.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TAstEditorController.SetRoot(const AstNode: IAstNode);
|
||||
begin
|
||||
FWorkspace.DeleteChildren;
|
||||
FWorkspace.Build(AstNode, TPointF.Create(50, 50));
|
||||
|
||||
if FWorkspace.ChildrenCount > 0 then
|
||||
begin
|
||||
for var i := 0 to FWorkspace.ChildrenCount - 1 do
|
||||
if FWorkspace.Children[i] is TAstViewNode then
|
||||
begin
|
||||
FRootViewNode := TAstViewNode(FWorkspace.Children[i]);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
|
||||
ValidateAndShow;
|
||||
end;
|
||||
|
||||
procedure TAstEditorController.CollectNodes(Parent: TControl);
|
||||
var
|
||||
i: Integer;
|
||||
child: TControl;
|
||||
viewNode: TAstViewNode;
|
||||
begin
|
||||
for i := 0 to Parent.ChildrenCount - 1 do
|
||||
begin
|
||||
if Parent.Children[i] is TControl then
|
||||
begin
|
||||
child := TControl(Parent.Children[i]);
|
||||
|
||||
if child is TAstViewNode then
|
||||
begin
|
||||
viewNode := TAstViewNode(child);
|
||||
// Register Identity -> ViewNode
|
||||
// Note: ReconstructAst updates the Node property of the ViewNode,
|
||||
// so we must ensure we use the identity from the *logical* node held by the view.
|
||||
if Assigned(viewNode.Node) and Assigned(viewNode.Node.Identity) then
|
||||
begin
|
||||
FNodeMap.AddOrSetValue(viewNode.Node.Identity, viewNode);
|
||||
end;
|
||||
end;
|
||||
|
||||
if child.ChildrenCount > 0 then
|
||||
CollectNodes(child);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstEditorController.ClearVisuals;
|
||||
begin
|
||||
for var viewNode in FNodeMap.Values do
|
||||
begin
|
||||
viewNode.ErrorMessage := '';
|
||||
viewNode.TypeInfoText := '';
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstEditorController.ApplyErrors(const Log: ICompilerLog);
|
||||
var
|
||||
entry: TCompilerError;
|
||||
viewNode: TAstViewNode;
|
||||
begin
|
||||
for entry in Log.GetEntries do
|
||||
begin
|
||||
if (entry.Level = elError) and Assigned(entry.Node) and Assigned(entry.Node.Identity) then
|
||||
begin
|
||||
if FNodeMap.TryGetValue(entry.Node.Identity, viewNode) then
|
||||
begin
|
||||
if viewNode.ErrorMessage = '' then
|
||||
viewNode.ErrorMessage := entry.Message
|
||||
else
|
||||
viewNode.ErrorMessage := viewNode.ErrorMessage + sLineBreak + entry.Message;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstEditorController.ApplyTypes(const TypedAst: IAstNode);
|
||||
var
|
||||
propagator: TTypePropagator;
|
||||
begin
|
||||
if not Assigned(TypedAst) then
|
||||
Exit;
|
||||
|
||||
propagator := TTypePropagator.Create(FNodeMap);
|
||||
try
|
||||
// Start traversal. The overload Accept(Node) will be called.
|
||||
propagator.Accept(TypedAst);
|
||||
finally
|
||||
propagator.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstEditorController.ValidateAndShow;
|
||||
var
|
||||
logicalAst: IAstNode;
|
||||
begin
|
||||
if not Assigned(FRootViewNode) then
|
||||
Exit;
|
||||
|
||||
// 1. Reconstruct Logical AST from UI
|
||||
// This updates the .Node property on ViewNodes with fresh Identities (if configured) or reuses them.
|
||||
logicalAst := FRootViewNode.CreateAst;
|
||||
|
||||
// 2. Rebuild Mapping based on the new logical nodes
|
||||
FNodeMap.Clear;
|
||||
CollectNodes(FWorkspace);
|
||||
|
||||
// 3. Clear visuals
|
||||
ClearVisuals;
|
||||
|
||||
// 4. Compile
|
||||
try
|
||||
// To get type info, we re-bind/check manually as Compile doesn't return the TypedAST.
|
||||
var log := TCompilerLog.Create as ICompilerLog;
|
||||
|
||||
// We need to expand macros first to match the structure Compile used
|
||||
var expanded := FEnv.ExpandMacros(logicalAst);
|
||||
var typedAst := FEnv.Bind(expanded, [], log);
|
||||
|
||||
var specAst := FEnv.Specialize(typedAst);
|
||||
|
||||
ApplyTypes(specAst);
|
||||
|
||||
if specAst.Kind = akLambdaExpression then
|
||||
var compiled := FEnv.Link(specAst.AsLambdaExpression, log);
|
||||
|
||||
ApplyErrors(log);
|
||||
except
|
||||
on E: ECompilationFailed do
|
||||
begin
|
||||
var tempLog := TCompilerLog.Create as ICompilerLog;
|
||||
for var err in E.Errors do
|
||||
tempLog.Add(err.Level, err.Message, err.Node);
|
||||
ApplyErrors(tempLog);
|
||||
end;
|
||||
end;
|
||||
|
||||
FWorkspace.Repaint;
|
||||
end;
|
||||
|
||||
{ TAstEditorController.TTypePropagator }
|
||||
|
||||
constructor TAstEditorController.TTypePropagator.Create(AMap: TNodeMap);
|
||||
begin
|
||||
inherited Create;
|
||||
FMap := AMap;
|
||||
end;
|
||||
|
||||
procedure TAstEditorController.TTypePropagator.ApplyType(const Node: IAstNode);
|
||||
var
|
||||
viewNode: TAstViewNode;
|
||||
begin
|
||||
if Assigned(Node) and Assigned(Node.Identity) then
|
||||
begin
|
||||
if FMap.TryGetValue(Node.Identity, viewNode) then
|
||||
begin
|
||||
if Node.IsTyped then
|
||||
begin
|
||||
var st := Node.AsTypedNode.StaticType;
|
||||
if Assigned(st) then
|
||||
viewNode.TypeInfoText := st.ToString;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TAstEditorController.TTypePropagator.Accept(const Node: IAstNode);
|
||||
begin
|
||||
if Node = nil then
|
||||
Exit;
|
||||
|
||||
// 1. Apply side effect
|
||||
ApplyType(Node);
|
||||
|
||||
// 2. Continue traversal (Standard TAstVisitor logic calls VisitXY which calls Accept for children)
|
||||
Node.Accept(Self);
|
||||
end;
|
||||
|
||||
end.
|
||||
Reference in New Issue
Block a user