Ast editor rondtrip

This commit is contained in:
Michael Schimmel
2025-11-28 16:07:47 +01:00
parent 0b4201fe9b
commit 521d0ac28f
10 changed files with 1247 additions and 909 deletions
File diff suppressed because one or more lines are too long
+2 -1
View File
@@ -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}
+1
View File
@@ -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>
+721 -833
View File
File diff suppressed because it is too large Load Diff
+2 -6
View File
@@ -143,12 +143,8 @@ class function TAstBinder.Bind(
const AArgTypes: TArray<IStaticType> = nil
): IAstNode;
begin
var binder := TAstBinder.Create(ParentLayout, AFunctionRegistry, AArgTypes, ALog);
try
Result := binder.Execute(RootNode, Layout);
finally
binder.Free;
end;
var binder := TAstBinder.Create(ParentLayout, AFunctionRegistry, AArgTypes, ALog) as IAstBinder;
Result := binder.Execute(RootNode, Layout);
end;
function TAstBinder.Execute(const RootNode: IAstNode; out Layout: IScopeLayout): IAstNode;
-17
View File
@@ -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.
+70 -25
View File
@@ -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));
+17
View File
@@ -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
View File
@@ -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;
+258
View File
@@ -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.