Json Schema for LLMs

This commit is contained in:
Michael Schimmel
2026-01-06 19:56:27 +01:00
parent 60952494c1
commit 4618573d6c
5 changed files with 730 additions and 475 deletions
+18 -11
View File
@@ -288,7 +288,7 @@ begin
CompilerStageBox.BringToFront; CompilerStageBox.BringToFront;
ClearButtonClick(Self); ClearButtonClick(Self);
Memo1.Lines.Add(TAstSchema.GenerateFullSystemPrompt); Memo1.Lines.Add(TAstSchema.GenerateFullSystemPrompt(FEnvironment.RootScope));
end; end;
procedure TForm1.ShowVizualization; procedure TForm1.ShowVizualization;
@@ -743,45 +743,52 @@ end;
procedure TForm1.FromJSONButtonClick(Sender: TObject); procedure TForm1.FromJSONButtonClick(Sender: TObject);
var var
jsonString: string; jsonString: string;
jsonObj: TJSONObject; jsonValue: TJSONValue;
converter: IJsonAstConverter; converter: IJsonAstConverter;
begin begin
Memo1.Lines.BeginUpdate; Memo1.Lines.BeginUpdate;
try try
jsonString := Memo1.Lines.Text; jsonString := Memo1.Lines.Text;
Memo1.Lines.Clear;
if jsonString.IsEmpty then if jsonString.IsEmpty then
begin begin
Memo1.Lines.Clear;
Memo1.Lines.Add('Memo is empty. Please paste an AST JSON string.'); Memo1.Lines.Add('Memo is empty. Please paste an AST JSON string.');
exit; exit;
end; end;
try try
converter := TJsonAstConverter.Create; converter := TJsonAstConverter.Create;
jsonObj := TJSONObject.ParseJSONValue(jsonString) as TJSONObject; // TJSONValue.ParseJSONValue ist der korrekte Einstiegspunkt
if not Assigned(jsonObj) then jsonValue := TJSONValue.ParseJSONValue(jsonString);
raise Exception.Create('Invalid JSON format.');
if not Assigned(jsonValue) then
raise Exception.Create('Invalid JSON syntax.');
try try
FCurrUnboundAst := converter.Deserialize(jsonObj); if not (jsonValue is TJSONArray) then
raise Exception.Create('AST Root must be a JSON Array.');
// Run the full pipeline via environment FCurrUnboundAst := converter.Deserialize(jsonValue);
// Die Pipeline nutzt den Environment-Helper (Managed Record)
// Compile akzeptiert IAstNode und erzeugt intern ein Lambda
FCurrCompiled := FEnvironment.Compile(FCurrUnboundAst); FCurrCompiled := FEnvironment.Compile(FCurrUnboundAst);
FCurrExec := FEnvironment.Link(FCurrCompiled); FCurrExec := FEnvironment.Link(FCurrCompiled);
Memo1.Lines.Clear;
Memo1.Lines.Add('AST deserialized and bound 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.'); Memo1.Lines.Add('You can now visualize it (Middle Mouse Click) or pretty-print it.');
finally finally
jsonObj.Free; jsonValue.Free;
end; end;
except except
on E: Exception do on E: Exception do
begin begin
Memo1.Lines.Clear;
Memo1.Lines.Add('Error deserializing AST from JSON:'); Memo1.Lines.Add('Error deserializing AST from JSON:');
Memo1.Lines.Add(E.Message); Memo1.Lines.Add(E.Message);
Memo1.Lines.Add('--- Original JSON ---'); Memo1.Lines.Add('--- Original JSON ---');
Memo1.Lines.Text := Memo1.Lines.Text + sLineBreak + jsonString; Memo1.Lines.Add(jsonString);
end; end;
end; end;
finally finally
+113
View File
@@ -0,0 +1,113 @@
unit Myc.Ast.Attributes;
interface
uses
system.sysutils;
type
{ Defines the content type for the LLM schema generation. }
TFieldKind = (
fkNode, // IAstNode (recursive structure)
fkTuple, // ITupleNode (explicit ["Tuple", [...]] structure)
fkString, // Raw String (for identifiers/names)
fkValue, // TDataValue (primitive constants like number, string, boolean)
fkNullableNode, // Optional Node (emits 'AstNode | null' in TypeScript)
fkArrayOfPairs // Special structure for CondExpr: [[Cond, Branch], ...]
);
{ AstTagAttribute: Defines the JSON Tag for an AST Node. }
AstTagAttribute = class(TCustomAttribute)
private
FTag: string;
public
constructor Create(const ATag: string);
property Tag: string read FTag;
end;
{ AstFieldAttribute: Defines a field within the JSON Array. }
AstFieldAttribute = class(TCustomAttribute)
private
FName: string;
FKind: TFieldKind;
FIndex: Integer;
public
constructor Create(AIndex: Integer; const AName: string; AKind: TFieldKind);
property Index: Integer read FIndex;
property Name: string read FName;
property Kind: TFieldKind read FKind;
end;
{ AstDocAttribute: Provides semantic documentation for the LLM. }
AstDocAttribute = class(TCustomAttribute)
private
FDescription: string;
public
constructor Create(const ADescription: string);
property Description: string read FDescription;
end;
{ AstScriptExampleAttribute: Provides an example in script syntax.
The generator converts this to JSON AST for the LLM prompt. }
AstScriptExampleAttribute = class(TCustomAttribute)
private
FScript: string;
public
constructor Create(const AScript: string);
property Script: string read FScript;
end;
{ AstSignatureAttribute: Defines a specific overload signature for an RTL function. }
AstSignatureAttribute = class(TCustomAttribute)
private
FSignature: string;
public
constructor Create(const ASignature: string);
property Signature: string read FSignature;
end;
implementation
{ AstTagAttribute }
constructor AstTagAttribute.Create(const ATag: string);
begin
inherited Create;
FTag := ATag;
end;
{ AstFieldAttribute }
constructor AstFieldAttribute.Create(AIndex: Integer; const AName: string; AKind: TFieldKind);
begin
inherited Create;
FIndex := AIndex;
FName := AName;
FKind := AKind;
end;
{ AstDocAttribute }
constructor AstDocAttribute.Create(const ADescription: string);
begin
inherited Create;
FDescription := ADescription;
end;
{ AstScriptExampleAttribute }
constructor AstScriptExampleAttribute.Create(const AScript: string);
begin
inherited Create;
FScript := AScript;
end;
{ AstSignatureAttribute }
constructor AstSignatureAttribute.Create(const ASignature: string);
begin
inherited Create;
FSignature := ASignature;
end;
end.
+292 -249
View File
@@ -1,14 +1,11 @@
//==================================================================================================
//== FULL UNIT START: Myc.Ast.Json (from Myc.Ast.Json.pas)
//==================================================================================================
unit Myc.Ast.Json; unit Myc.Ast.Json;
interface interface
uses uses
System.SysUtils, system.sysutils,
System.Generics.Collections, system.generics.collections,
System.JSON, system.json,
Myc.Data.Scalar, Myc.Data.Scalar,
Myc.Data.Value, Myc.Data.Value,
Myc.Ast, Myc.Ast,
@@ -21,19 +18,16 @@ type
function Deserialize(const AJson: TJSONValue): IAstNode; function Deserialize(const AJson: TJSONValue): IAstNode;
end; end;
// Compact JSON Converter: [Kind, Arg1, Arg2, ...] { Compact JSON Converter: [Tag, Arg1, Arg2, ...] }
TJsonAstConverter = class(TAstVisitor<TJSONValue>, IJsonAstConverter) TJsonAstConverter = class(TAstVisitor<TJSONValue>, IJsonAstConverter)
private private
// Helpers for compact data values
function DataValueToJson(const AValue: TDataValue): TJSONValue; function DataValueToJson(const AValue: TDataValue): TJSONValue;
function JsonToDataValue(const AJson: TJSONValue): TDataValue; function JsonToDataValue(const AJson: TJSONValue): TDataValue;
// Deserialization Dispatcher
function JsonToNode(const AJson: TJSONValue): IAstNode; function JsonToNode(const AJson: TJSONValue): IAstNode;
function JsonToTuple(const AArray: TJSONArray): ITupleNode; function JsonToTuple(const AArray: TJSONArray): ITupleNode;
strict private strict private
// Serialization Visitors (Array Builders)
function VisitConstant(const Node: IAstNode): TJSONValue; function VisitConstant(const Node: IAstNode): TJSONValue;
function VisitIdentifier(const Node: IAstNode): TJSONValue; function VisitIdentifier(const Node: IAstNode): TJSONValue;
function VisitKeyword(const Node: IAstNode): TJSONValue; function VisitKeyword(const Node: IAstNode): TJSONValue;
@@ -42,16 +36,16 @@ type
function VisitIfExpression(const Node: IAstNode): TJSONValue; function VisitIfExpression(const Node: IAstNode): TJSONValue;
function VisitCondExpression(const Node: IAstNode): TJSONValue; function VisitCondExpression(const Node: IAstNode): TJSONValue;
function VisitLambdaExpression(const Node: IAstNode): TJSONValue; function VisitLambdaExpression(const Node: IAstNode): TJSONValue;
function VisitMacroDefinition(const Node: IAstNode): TJSONValue;
function VisitQuasiquote(const Node: IAstNode): TJSONValue;
function VisitUnquote(const Node: IAstNode): TJSONValue;
function VisitUnquoteSplicing(const Node: IAstNode): TJSONValue;
function VisitFunctionCall(const Node: IAstNode): TJSONValue; function VisitFunctionCall(const Node: IAstNode): TJSONValue;
function VisitMacroExpansionNode(const Node: IAstNode): TJSONValue; function VisitMacroExpansionNode(const Node: IAstNode): TJSONValue;
function VisitRecurNode(const Node: IAstNode): TJSONValue; function VisitRecurNode(const Node: IAstNode): TJSONValue;
function VisitBlockExpression(const Node: IAstNode): TJSONValue; function VisitBlockExpression(const Node: IAstNode): TJSONValue;
function VisitVariableDeclaration(const Node: IAstNode): TJSONValue; function VisitVariableDeclaration(const Node: IAstNode): TJSONValue;
function VisitAssignment(const Node: IAstNode): TJSONValue; function VisitAssignment(const Node: IAstNode): TJSONValue;
function VisitMacroDefinition(const Node: IAstNode): TJSONValue;
function VisitQuasiquote(const Node: IAstNode): TJSONValue;
function VisitUnquote(const Node: IAstNode): TJSONValue;
function VisitUnquoteSplicing(const Node: IAstNode): TJSONValue;
function VisitIndexer(const Node: IAstNode): TJSONValue; function VisitIndexer(const Node: IAstNode): TJSONValue;
function VisitMemberAccess(const Node: IAstNode): TJSONValue; function VisitMemberAccess(const Node: IAstNode): TJSONValue;
function VisitRecordLiteral(const Node: IAstNode): TJSONValue; function VisitRecordLiteral(const Node: IAstNode): TJSONValue;
@@ -125,31 +119,7 @@ begin
Result := JsonToNode(AJson); Result := JsonToNode(AJson);
end; end;
// --- Serialization (AST -> JSON Array) --- // --- Serialization Helpers ---
function TJsonAstConverter.VisitIdentifier(const Node: IAstNode): TJSONValue;
begin
var arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Id'));
arr.AddElement(TJSONString.Create(Node.AsIdentifier.Name));
Result := arr;
end;
function TJsonAstConverter.VisitKeyword(const Node: IAstNode): TJSONValue;
begin
var arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Key'));
arr.AddElement(TJSONString.Create(Node.AsKeyword.Value.Name));
Result := arr;
end;
function TJsonAstConverter.VisitConstant(const Node: IAstNode): TJSONValue;
begin
var arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Const'));
arr.AddElement(DataValueToJson(Node.AsConstant.Value));
Result := arr;
end;
function TJsonAstConverter.DataValueToJson(const AValue: TDataValue): TJSONValue; function TJsonAstConverter.DataValueToJson(const AValue: TDataValue): TJSONValue;
begin begin
@@ -177,97 +147,74 @@ begin
end; end;
end; end;
function TJsonAstConverter.VisitTuple(const Node: IAstNode): TJSONValue; // --- Visitor Implementations ---
function TJsonAstConverter.VisitConstant(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
begin begin
var arr := TJSONArray.Create; arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Tuple')); arr.Add('Const');
var elems := TJSONArray.Create; arr.AddElement(DataValueToJson(Node.AsConstant.Value));
Result := arr;
end;
function TJsonAstConverter.VisitIdentifier(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
begin
arr := TJSONArray.Create;
arr.Add('Id');
arr.Add(Node.AsIdentifier.Name);
Result := arr;
end;
function TJsonAstConverter.VisitKeyword(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
begin
arr := TJSONArray.Create;
arr.Add('Key');
arr.Add(Node.AsKeyword.Value.Name);
Result := arr;
end;
function TJsonAstConverter.VisitTuple(const Node: IAstNode): TJSONValue;
var
arr, elems: TJSONArray;
begin
arr := TJSONArray.Create;
arr.Add('Tuple');
elems := TJSONArray.Create;
for var item in Node.AsTuple.Elements do for var item in Node.AsTuple.Elements do
elems.AddElement(Visit(item)); elems.AddElement(Visit(item));
arr.AddElement(elems); arr.AddElement(elems);
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitFunctionCall(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitRecordField(const Node: IAstNode): TJSONValue;
var var
C: IFunctionCallNode; arr: TJSONArray;
begin begin
C := Node.AsFunctionCall; arr := TJSONArray.Create;
var arr := TJSONArray.Create; arr.Add('Field');
arr.AddElement(TJSONString.Create('Call')); arr.AddElement(Visit(Node.AsRecordField.Key));
arr.AddElement(Visit(C.Callee)); arr.AddElement(Visit(Node.AsRecordField.Value));
arr.AddElement(Visit(C.Arguments)); // Arguments is a Tuple
Result := arr;
end;
function TJsonAstConverter.VisitBlockExpression(const Node: IAstNode): TJSONValue;
begin
var arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Block'));
arr.AddElement(Visit(Node.AsBlockExpression.Expressions)); // Tuple
Result := arr;
end;
function TJsonAstConverter.VisitLambdaExpression(const Node: IAstNode): TJSONValue;
var
L: ILambdaExpressionNode;
begin
L := Node.AsLambdaExpression;
var arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Fn'));
arr.AddElement(Visit(L.Parameters)); // Tuple
arr.AddElement(Visit(L.Body));
Result := arr;
end;
function TJsonAstConverter.VisitMacroDefinition(const Node: IAstNode): TJSONValue;
var
M: IMacroDefinitionNode;
begin
M := Node.AsMacroDefinition;
var arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Macro'));
arr.AddElement(Visit(M.Name));
arr.AddElement(Visit(M.Parameters));
arr.AddElement(Visit(M.Body));
Result := arr;
end;
function TJsonAstConverter.VisitVariableDeclaration(const Node: IAstNode): TJSONValue;
var
V: IVariableDeclarationNode;
begin
V := Node.AsVariableDeclaration;
var arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Var'));
arr.AddElement(Visit(V.Target));
if Assigned(V.Initializer) then
arr.AddElement(Visit(V.Initializer))
else
arr.AddElement(TJSONNull.Create);
Result := arr;
end;
function TJsonAstConverter.VisitAssignment(const Node: IAstNode): TJSONValue;
begin
var arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Assign'));
arr.AddElement(Visit(Node.AsAssignment.Target));
arr.AddElement(Visit(Node.AsAssignment.Value));
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitIfExpression(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitIfExpression(const Node: IAstNode): TJSONValue;
var var
E: IIfExpressionNode; arr: TJSONArray;
n: IIfExpressionNode;
begin begin
E := Node.AsIfExpression; n := Node.AsIfExpression;
var arr := TJSONArray.Create; arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('If')); arr.Add('If');
arr.AddElement(Visit(E.Condition)); arr.AddElement(Visit(n.Condition));
arr.AddElement(Visit(E.ThenBranch)); arr.AddElement(Visit(n.ThenBranch));
if Assigned(E.ElseBranch) then if Assigned(n.ElseBranch) then
arr.AddElement(Visit(E.ElseBranch)) arr.AddElement(Visit(n.ElseBranch))
else else
arr.AddElement(TJSONNull.Create); arr.AddElement(TJSONNull.Create);
Result := arr; Result := arr;
@@ -275,147 +222,248 @@ end;
function TJsonAstConverter.VisitCondExpression(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitCondExpression(const Node: IAstNode): TJSONValue;
var var
E: ICondExpressionNode; arr, pairs: TJSONArray;
pairs: TJSONArray; n: ICondExpressionNode;
begin begin
E := Node.AsCondExpression; n := Node.AsCondExpression;
var arr := TJSONArray.Create; arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Cond')); arr.Add('Cond');
pairs := TJSONArray.Create; pairs := TJSONArray.Create;
for var p in E.Pairs do for var p in n.Pairs do
begin begin
var pArr := TJSONArray.Create; var pairArr := TJSONArray.Create;
pArr.AddElement(Visit(p.Condition)); pairArr.AddElement(Visit(p.Condition));
pArr.AddElement(Visit(p.Branch)); pairArr.AddElement(Visit(p.Branch));
pairs.AddElement(pArr); pairs.AddElement(pairArr);
end; end;
arr.AddElement(pairs); arr.AddElement(pairs);
if Assigned(E.ElseBranch) then if Assigned(n.ElseBranch) then
arr.AddElement(Visit(E.ElseBranch)) arr.AddElement(Visit(n.ElseBranch))
else else
arr.AddElement(TJSONNull.Create); arr.AddElement(TJSONNull.Create);
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitQuasiquote(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitLambdaExpression(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
n: ILambdaExpressionNode;
begin begin
var arr := TJSONArray.Create; n := Node.AsLambdaExpression;
arr.AddElement(TJSONString.Create('Quote')); arr := TJSONArray.Create;
arr.Add('Fn');
arr.AddElement(Visit(n.Parameters));
arr.AddElement(Visit(n.Body));
Result := arr;
end;
function TJsonAstConverter.VisitFunctionCall(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
n: IFunctionCallNode;
begin
n := Node.AsFunctionCall;
arr := TJSONArray.Create;
arr.Add('Call');
arr.AddElement(Visit(n.Callee));
arr.AddElement(Visit(n.Arguments));
Result := arr;
end;
function TJsonAstConverter.VisitMacroExpansionNode(const Node: IAstNode): TJSONValue;
begin
Result := Visit(Node.AsMacroExpansion.CallNode);
end;
function TJsonAstConverter.VisitBlockExpression(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
begin
arr := TJSONArray.Create;
arr.Add('Block');
arr.AddElement(Visit(Node.AsBlockExpression.Expressions));
Result := arr;
end;
function TJsonAstConverter.VisitVariableDeclaration(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
n: IVariableDeclarationNode;
begin
n := Node.AsVariableDeclaration;
arr := TJSONArray.Create;
arr.Add('Var');
arr.AddElement(Visit(n.Target));
if Assigned(n.Initializer) then
arr.AddElement(Visit(n.Initializer))
else
arr.AddElement(TJSONNull.Create);
Result := arr;
end;
function TJsonAstConverter.VisitAssignment(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
n: IAssignmentNode;
begin
n := Node.AsAssignment;
arr := TJSONArray.Create;
arr.Add('Assign');
arr.AddElement(Visit(n.Target));
arr.AddElement(Visit(n.Value));
Result := arr;
end;
function TJsonAstConverter.VisitMacroDefinition(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
n: IMacroDefinitionNode;
begin
n := Node.AsMacroDefinition;
arr := TJSONArray.Create;
arr.Add('Macro');
arr.AddElement(Visit(n.Name));
arr.AddElement(Visit(n.Parameters));
arr.AddElement(Visit(n.Body));
Result := arr;
end;
function TJsonAstConverter.VisitQuasiquote(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
begin
arr := TJSONArray.Create;
arr.Add('Quote');
arr.AddElement(Visit(Node.AsQuasiquote.Expression)); arr.AddElement(Visit(Node.AsQuasiquote.Expression));
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitUnquote(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitUnquote(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
begin begin
var arr := TJSONArray.Create; arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Unquote')); arr.Add('Unquote');
arr.AddElement(Visit(Node.AsUnquote.Expression)); arr.AddElement(Visit(Node.AsUnquote.Expression));
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitUnquoteSplicing(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitUnquoteSplicing(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
begin begin
var arr := TJSONArray.Create; arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Splice')); arr.Add('Splice');
arr.AddElement(Visit(Node.AsUnquoteSplicing.Expression)); arr.AddElement(Visit(Node.AsUnquoteSplicing.Expression));
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitRecurNode(const Node: IAstNode): TJSONValue;
begin
var arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Recur'));
arr.AddElement(Visit(Node.AsRecur.Arguments)); // Tuple
Result := arr;
end;
function TJsonAstConverter.VisitIndexer(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitIndexer(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
n: IIndexerNode;
begin begin
var arr := TJSONArray.Create; n := Node.AsIndexer;
arr.AddElement(TJSONString.Create('Index')); arr := TJSONArray.Create;
arr.AddElement(Visit(Node.AsIndexer.Base)); arr.Add('Index');
arr.AddElement(Visit(Node.AsIndexer.Index)); arr.AddElement(Visit(n.Base));
arr.AddElement(Visit(n.Index));
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitMemberAccess(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitMemberAccess(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
n: IMemberAccessNode;
begin begin
var arr := TJSONArray.Create; n := Node.AsMemberAccess;
arr.AddElement(TJSONString.Create('Member')); arr := TJSONArray.Create;
arr.AddElement(Visit(Node.AsMemberAccess.Base)); arr.Add('Member');
arr.AddElement(Visit(Node.AsMemberAccess.Member)); arr.AddElement(Visit(n.Base));
Result := arr; arr.AddElement(Visit(n.Member));
end;
function TJsonAstConverter.VisitRecordField(const Node: IAstNode): TJSONValue;
begin
// Helper for RecordLiteral
var arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Field'));
arr.AddElement(Visit(Node.AsRecordField.Key));
arr.AddElement(Visit(Node.AsRecordField.Value));
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitRecordLiteral(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitRecordLiteral(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
begin begin
var arr := TJSONArray.Create; arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Record')); arr.Add('Record');
arr.AddElement(Visit(Node.AsRecordLiteral.Fields)); // Tuple arr.AddElement(Visit(Node.AsRecordLiteral.Fields));
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitCreateSeries(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitCreateSeries(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
begin begin
var arr := TJSONArray.Create; arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Series')); arr.Add('Series');
arr.AddElement(Visit(Node.AsCreateSeries.DefinitionNode)); arr.AddElement(Visit(Node.AsCreateSeries.DefinitionNode));
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitAddSeriesItem(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitAddSeriesItem(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
n: IAddSeriesItemNode;
begin begin
var arr := TJSONArray.Create; n := Node.AsAddSeriesItem;
arr.AddElement(TJSONString.Create('Add')); arr := TJSONArray.Create;
arr.AddElement(Visit(Node.AsAddSeriesItem.Series)); arr.Add('Add');
arr.AddElement(Visit(Node.AsAddSeriesItem.Value)); arr.AddElement(Visit(n.Series));
if Assigned(Node.AsAddSeriesItem.Lookback) then arr.AddElement(Visit(n.Value));
arr.AddElement(Visit(Node.AsAddSeriesItem.Lookback)) if Assigned(n.Lookback) then
arr.AddElement(Visit(n.Lookback))
else else
arr.AddElement(TJSONNull.Create); arr.AddElement(TJSONNull.Create);
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitSeriesLength(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitSeriesLength(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
begin begin
var arr := TJSONArray.Create; arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Count')); arr.Add('Count');
arr.AddElement(Visit(Node.AsSeriesLength.Series)); arr.AddElement(Visit(Node.AsSeriesLength.Series));
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitPipe(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitRecurNode(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
begin begin
var arr := TJSONArray.Create; arr := TJSONArray.Create;
arr.AddElement(TJSONString.Create('Pipe')); arr.Add('Recur');
arr.AddElement(Visit(Node.AsPipe.Inputs)); arr.AddElement(Visit(Node.AsRecur.Arguments));
arr.AddElement(Visit(Node.AsPipe.Transformation));
Result := arr; Result := arr;
end; end;
function TJsonAstConverter.VisitNop(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitNop(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
begin begin
Result := TJSONArray.Create; arr := TJSONArray.Create;
TJSONArray(Result).AddElement(TJSONString.Create('Nop')); arr.Add('Nop');
Result := arr;
end; end;
function TJsonAstConverter.VisitMacroExpansionNode(const Node: IAstNode): TJSONValue; function TJsonAstConverter.VisitPipe(const Node: IAstNode): TJSONValue;
var
arr: TJSONArray;
n: IPipeNode;
begin begin
// Serialize original call n := Node.AsPipe;
Result := Visit(Node.AsMacroExpansion.CallNode); arr := TJSONArray.Create;
arr.Add('Pipe');
arr.AddElement(Visit(n.Inputs));
arr.AddElement(Visit(n.Transformation));
Result := arr;
end; end;
// --- Deserialization (JSON Array -> AST) --- // --- Deserialization ---
function TJsonAstConverter.JsonToDataValue(const AJson: TJSONValue): TDataValue; function TJsonAstConverter.JsonToDataValue(const AJson: TJSONValue): TDataValue;
var var
@@ -458,8 +506,18 @@ function TJsonAstConverter.JsonToNode(const AJson: TJSONValue): IAstNode;
var var
arr: TJSONArray; arr: TJSONArray;
kind: string; kind: string;
function UnpackTuple(Val: TJSONValue): TArray<IAstNode>;
begin begin
if AJson is TJSONNull then var n := JsonToNode(Val);
if Assigned(n) then
Result := n.AsTuple.Elements
else
Result := [];
end;
begin
if (not Assigned(AJson)) or (AJson is TJSONNull) then
exit(nil); exit(nil);
if not (AJson is TJSONArray) then if not (AJson is TJSONArray) then
@@ -472,32 +530,45 @@ begin
kind := arr.Items[0].Value; kind := arr.Items[0].Value;
if kind = 'Id' then if kind = 'Id' then
Exit(TAst.Identifier(arr.Items[1].Value)); exit(TAst.Identifier(arr.Items[1].Value));
if kind = 'Key' then if kind = 'Key' then
Exit(TAst.Keyword(arr.Items[1].Value)); exit(TAst.Keyword(arr.Items[1].Value));
if kind = 'Const' then if kind = 'Const' then
Exit(TAst.Constant(JsonToDataValue(arr.Items[1]))); exit(TAst.Constant(JsonToDataValue(arr.Items[1])));
if kind = 'Tuple' then
Exit(JsonToTuple(arr.Items[1] as TJSONArray));
if kind = 'Block' then
Exit(TAst.Block(JsonToTuple(arr.Items[1] as TJSONArray).Elements));
if kind = 'Nop' then if kind = 'Nop' then
Exit(TAst.Nop); exit(TAst.Nop);
if kind = 'Tuple' then
exit(JsonToTuple(arr.Items[1] as TJSONArray));
if kind = 'Block' then
exit(TAst.Block(UnpackTuple(arr.Items[1])));
if kind = 'Record' then
begin
var fields := UnpackTuple(arr.Items[1]);
var recFields := TList<IRecordFieldNode>.Create;
try
for var f in fields do
recFields.Add(f.AsRecordField);
exit(TAst.RecordLiteral(recFields.ToArray));
finally
recFields.Free;
end;
end;
if kind = 'Field' then
exit(TAst.RecordField(JsonToNode(arr.Items[1]).AsKeyword, JsonToNode(arr.Items[2])));
if kind = 'Call' then if kind = 'Call' then
Exit(TAst.FunctionCall(JsonToNode(arr.Items[1]), JsonToTuple(arr.Items[2] as TJSONArray).Elements)); exit(TAst.FunctionCall(JsonToNode(arr.Items[1]), UnpackTuple(arr.Items[2])));
if kind = 'Fn' then if kind = 'Fn' then
// Pass TIdentities.Structural as Identity to satisfy the overload requiring (Identity, Tuple, Body, ...) exit(TAst.LambdaExpr(TIdentities.Structural, JsonToNode(arr.Items[1]).AsTuple, JsonToNode(arr.Items[2])));
Exit(TAst.LambdaExpr(TIdentities.Structural, JsonToTuple(arr.Items[1] as TJSONArray), JsonToNode(arr.Items[2]))); if kind = 'Recur' then
exit(TAst.Recur(UnpackTuple(arr.Items[1])));
if kind = 'Macro' then if kind = 'Macro' then
// Pass TIdentities.Structural as Identity exit(
Exit(
TAst.MacroDef( TAst.MacroDef(
TIdentities.Structural, TIdentities.Structural,
JsonToNode(arr.Items[1]).AsIdentifier, JsonToNode(arr.Items[1]).AsIdentifier,
JsonToTuple(arr.Items[2] as TJSONArray), JsonToNode(arr.Items[2]).AsTuple,
JsonToNode(arr.Items[3]) JsonToNode(arr.Items[3])
) )
); );
@@ -505,20 +576,20 @@ begin
if kind = 'Var' then if kind = 'Var' then
begin begin
var init: IAstNode := nil; var init: IAstNode := nil;
if arr.Count > 2 then if (arr.Count > 2) and (not (arr.Items[2] is TJSONNull)) then
init := JsonToNode(arr.Items[2]); init := JsonToNode(arr.Items[2]);
Exit(TAst.VarDecl(JsonToNode(arr.Items[1]), init)); exit(TAst.VarDecl(JsonToNode(arr.Items[1]), init));
end; end;
if kind = 'Assign' then if kind = 'Assign' then
Exit(TAst.Assign(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2]))); exit(TAst.Assign(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2])));
if kind = 'If' then if kind = 'If' then
begin begin
var el: IAstNode := nil; var el: IAstNode := nil;
if arr.Count > 3 then if (arr.Count > 3) and (not (arr.Items[3] is TJSONNull)) then
el := JsonToNode(arr.Items[3]); el := JsonToNode(arr.Items[3]);
Exit(TAst.IfExpr(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2]), el)); exit(TAst.IfExpr(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2]), el));
end; end;
if kind = 'Cond' then if kind = 'Cond' then
@@ -532,68 +603,40 @@ begin
pairs[i] := TCondPair.Create(JsonToNode(p.Items[0]), JsonToNode(p.Items[1])); pairs[i] := TCondPair.Create(JsonToNode(p.Items[0]), JsonToNode(p.Items[1]));
end; end;
var el: IAstNode := nil; var el: IAstNode := nil;
if arr.Count > 2 then if (arr.Count > 2) and (not (arr.Items[2] is TJSONNull)) then
el := JsonToNode(arr.Items[2]); el := JsonToNode(arr.Items[2]);
Exit(TAst.CondExpr(pairs, el)); exit(TAst.CondExpr(pairs, el));
end; end;
if kind = 'Quote' then if kind = 'Quote' then
Exit(TAst.Quasiquote(JsonToNode(arr.Items[1]))); exit(TAst.Quasiquote(JsonToNode(arr.Items[1])));
if kind = 'Unquote' then if kind = 'Unquote' then
Exit(TAst.Unquote(JsonToNode(arr.Items[1]))); exit(TAst.Unquote(JsonToNode(arr.Items[1])));
// Splice Fix: Direkte Instanziierung um Factory-Overload E2250 zu umgehen
if kind = 'Splice' then if kind = 'Splice' then
Exit(TAst.UnquoteSplicing(JsonToNode(arr.Items[1]).AsQuasiquote)); exit(TUnquoteSplicingNode.Create(JsonToNode(arr.Items[1]), TIdentities.Structural(nil)));
if kind = 'Recur' then
Exit(TAst.Recur(JsonToTuple(arr.Items[1] as TJSONArray).Elements));
if kind = 'Index' then if kind = 'Index' then
Exit(TAst.Indexer(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2]))); exit(TAst.Indexer(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2])));
if kind = 'Member' then if kind = 'Member' then
Exit(TAst.MemberAccess(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2]).AsKeyword)); exit(TAst.MemberAccess(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2]).AsKeyword));
if kind = 'Record' then
begin
var fieldsTuple := JsonToTuple(arr.Items[1] as TJSONArray);
var recFields: TList<IRecordFieldNode> := TList<IRecordFieldNode>.Create;
try
for var elem in fieldsTuple.Elements do
begin
if elem.Kind = akRecordField then
recFields.Add(elem.AsRecordField)
else
raise EInvalidCast.Create('Expected RecordField');
end;
Exit(TAst.RecordLiteral(recFields.ToArray));
finally
recFields.Free;
end;
end;
// Explicit RecordField deserializer support
if kind = 'Field' then
Exit(TAst.RecordField(JsonToNode(arr.Items[1]).AsKeyword, JsonToNode(arr.Items[2])));
if kind = 'Series' then if kind = 'Series' then
Exit(TAst.CreateSeries(JsonToNode(arr.Items[1]))); exit(TAst.CreateSeries(JsonToNode(arr.Items[1])));
if kind = 'Count' then
exit(TAst.SeriesLength(JsonToNode(arr.Items[1]).AsIdentifier));
if kind = 'Pipe' then
exit(TAst.Pipe(JsonToNode(arr.Items[1]).AsTuple, JsonToNode(arr.Items[2]).AsLambdaExpression));
if kind = 'Add' then if kind = 'Add' then
begin begin
var lb: IAstNode := nil; var lb: IAstNode := nil;
if arr.Count > 3 then if (arr.Count > 3) and (not (arr.Items[3] is TJSONNull)) then
lb := JsonToNode(arr.Items[3]); lb := JsonToNode(arr.Items[3]);
Exit(TAst.AddSeriesItem(JsonToNode(arr.Items[1]).AsIdentifier, JsonToNode(arr.Items[2]), lb)); exit(TAst.AddSeriesItem(JsonToNode(arr.Items[1]).AsIdentifier, JsonToNode(arr.Items[2]), lb));
end; end;
if kind = 'Count' then
Exit(TAst.SeriesLength(JsonToNode(arr.Items[1]).AsIdentifier));
if kind = 'Pipe' then
Exit(TAst.Pipe(JsonToTuple(arr.Items[1] as TJSONArray), JsonToNode(arr.Items[2]).AsLambdaExpression));
raise ENotSupportedException.Create('Unknown JSON AST Kind: ' + kind); raise ENotSupportedException.Create('Unknown JSON AST Kind: ' + kind);
end; end;
end. end.
//==================================================================================================
//== FULL UNIT END: Myc.Ast.Json
//==================================================================================================
+239 -113
View File
@@ -9,16 +9,28 @@ uses
system.generics.defaults, system.generics.defaults,
system.rtti, system.rtti,
system.typinfo, system.typinfo,
system.json,
Myc.Data.Scalar,
Myc.Data.Value,
Myc.Ast.Nodes, Myc.Ast.Nodes,
Myc.Ast.Attributes, Myc.Ast.Attributes,
Myc.Ast.RTL, Myc.Ast.RTL,
Myc.Ast.RTL.Core; Myc.Ast.RTL.Core,
Myc.Ast.Scope,
Myc.Ast.Types,
Myc.Ast.Script,
Myc.Ast.Json;
type type
{ Scans AST and RTL metadata to generate a high-precision LLM system prompt. } { Scans AST, RTL and Scope metadata to generate a context-aware LLM system prompt. }
TAstSchema = class TAstSchema = class
strict private strict private
type type
TRtlMeta = record
Doc: string;
Signatures: TArray<string>;
end;
TFieldInfo = record TFieldInfo = record
Idx: Integer; Idx: Integer;
Name: string; Name: string;
@@ -26,18 +38,23 @@ type
end; end;
class function GetTsType(Kind: TFieldKind): string; static; class function GetTsType(Kind: TFieldKind): string; static;
class function TypeToTs(const AType: IStaticType): string; static;
class function GetRtlMetadata: TDictionary<string, TRtlMeta>; static;
public public
// Generates the TypeScript definition including JSDoc and multiple @example entries // Generates the TypeScript definition for AST nodes
class function GenerateTypeScriptDefinition: string; static; class function GenerateTypeScriptDefinition: string; static;
// Generates documented RTL function signatures with all overloads // Unified section: Lists only symbols available in the current scope with their docs/types
class function GenerateRtlDocumentation: string; static; class function GenerateAvailableSymbols(const AScope: IExecutionScope): string; static;
// Detailed generation rules to eliminate ambiguity // Documentation for all RTL functions (used as fallback or for general prompts)
class function GenerateAllRtlDocumentation: string; static;
// Detailed generation rules
class function GenerateGenerationRules: string; static; class function GenerateGenerationRules: string; static;
// The final consolidated system prompt // The final consolidated system prompt for the AI
class function GenerateFullSystemPrompt: string; static; class function GenerateFullSystemPrompt(const AScope: IExecutionScope = nil): string; static;
end; end;
implementation implementation
@@ -58,6 +75,103 @@ begin
end; end;
end; end;
class function TAstSchema.TypeToTs(const AType: IStaticType): string;
begin
if not Assigned(AType) then
exit('any');
case AType.Kind of
stOrdinal, stFloat: Result := 'number';
stBoolean: Result := 'boolean';
stDateTime: Result := 'datetime';
stText: Result := 'string';
stKeyword: Result := 'keyword';
stSeries: Result := Format('Series<%s>', [TypeToTs(AType.AsSeries.ElementType)]);
stRecord:
begin
var def := AType.AsRecord.Definition;
var fields := TList<string>.Create;
try
for var i := 0 to def.Count - 1 do
fields.Add(Format('%s: %s', [def.Keywords[i].Name, def[i].ToString.ToLower]));
Result := '{ ' + string.Join(', ', fields.ToArray) + ' }';
finally
fields.Free;
end;
end;
stRecordSeries: Result := 'Stream<' + TypeToTs(TTypes.CreateRecord(AType.AsRecord.Definition)) + '>';
stMethod:
begin
if Length(AType.AsMethod.Signatures) = 0 then
exit('function');
var sig := AType.AsMethod.Signatures[0];
var args := TList<string>.Create;
try
for var p in sig.ParamTypes do
args.Add(TypeToTs(p));
Result := Format('(%s) => %s', [string.Join(', ', args.ToArray), TypeToTs(sig.ReturnType)]);
finally
args.Free;
end;
end;
else
Result := 'any';
end;
if AType.IsOptional then
Result := Result + ' | null';
end;
class function TAstSchema.GetRtlMetadata: TDictionary<string, TRtlMeta>;
var
ctx: TRttiContext;
typ: TRttiType;
meth: TRttiMethod;
attr: TCustomAttribute;
meta: TRtlMeta;
rtlName: string;
sigs: TList<string>;
begin
Result := TDictionary<string, TRtlMeta>.Create;
sigs := TList<string>.Create;
try
typ := ctx.GetType(TypeInfo(TRtlFunctions));
for meth in typ.GetMethods do
begin
rtlName := '';
meta.Doc := '';
sigs.Clear;
for attr in meth.GetAttributes do
begin
if attr is AstDocAttribute then
meta.Doc := AstDocAttribute(attr).Description;
if attr is TRtlExportAttribute then
rtlName := TRtlExportAttribute(attr).Name;
if attr is AstSignatureAttribute then
sigs.Add(AstSignatureAttribute(attr).Signature);
end;
if rtlName <> '' then
begin
meta.Signatures := sigs.ToArray;
if Result.ContainsKey(rtlName) then
begin
var existing := Result[rtlName];
var combinedSigs := TList<string>.Create;
combinedSigs.AddRange(existing.Signatures);
combinedSigs.AddRange(meta.Signatures);
existing.Signatures := combinedSigs.ToArray;
combinedSigs.Free;
Result[rtlName] := existing;
end
else
Result.Add(rtlName, meta);
end;
end;
finally
sigs.Free;
end;
end;
class function TAstSchema.GenerateTypeScriptDefinition: string; class function TAstSchema.GenerateTypeScriptDefinition: string;
var var
ctx: TRttiContext; ctx: TRttiContext;
@@ -71,9 +185,11 @@ var
tag, doc: string; tag, doc: string;
fieldList: TList<TFieldInfo>; fieldList: TList<TFieldInfo>;
exampleList: TList<string>; exampleList: TList<string>;
jsonConv: IJsonAstConverter;
i: Integer; i: Integer;
begin begin
ctx := TRttiContext.Create; ctx := TRttiContext.Create;
jsonConv := TJsonAstConverter.Create;
nodeDefs := TDictionary<string, TList<TFieldInfo>>.Create; nodeDefs := TDictionary<string, TList<TFieldInfo>>.Create;
nodeDocs := TDictionary<string, string>.Create; nodeDocs := TDictionary<string, string>.Create;
nodeExamples := TDictionary<string, TList<string>>.Create; nodeExamples := TDictionary<string, TList<string>>.Create;
@@ -84,27 +200,33 @@ begin
begin begin
if typ.TypeKind <> tkInterface then if typ.TypeKind <> tkInterface then
continue; continue;
tag := ''; tag := '';
doc := ''; doc := '';
fieldList := nil; fieldList := nil;
exampleList := nil; exampleList := nil;
for attr in typ.GetAttributes do for attr in typ.GetAttributes do
begin begin
if attr is AstTagAttribute then if attr is AstTagAttribute then
tag := AstTagAttribute(attr).Tag; tag := AstTagAttribute(attr).Tag;
if attr is AstDocAttribute then if attr is AstDocAttribute then
doc := AstDocAttribute(attr).Description; doc := AstDocAttribute(attr).Description;
if attr is AstScriptExampleAttribute then
if attr is AstExampleAttribute then
begin begin
if exampleList = nil then if exampleList = nil then
exampleList := TList<string>.Create; exampleList := TList<string>.Create;
exampleList.Add(AstExampleAttribute(attr).Example); try
var node := TAstScript.Parse(AstScriptExampleAttribute(attr).Script);
var jsonVal := jsonConv.Serialize(node);
try
exampleList.Add(jsonVal.ToJSON);
finally
jsonVal.Free;
end;
except
on E: Exception do
exampleList.Add('/* Error in example */');
end;
end; end;
if attr is AstFieldAttribute then if attr is AstFieldAttribute then
begin begin
if fieldList = nil then if fieldList = nil then
@@ -116,7 +238,6 @@ begin
fieldList.Add(f); fieldList.Add(f);
end; end;
end; end;
if tag <> '' then if tag <> '' then
begin begin
unionTypes.Add(tag); unionTypes.Add(tag);
@@ -134,13 +255,8 @@ begin
exampleList.Free; exampleList.Free;
end; end;
end; end;
sb.AppendLine('// AST Schema Definition' + sLineBreak + '// Format: Compact JSON Arrays [Tag, Arg1, Arg2, ...]' + sLineBreak);
sb.AppendLine('// AST Schema Definition (Auto-Generated)');
sb.AppendLine('// Format: Compact JSON Arrays [Tag, Arg1, Arg2, ...]');
sb.AppendLine;
unionTypes.Sort; unionTypes.Sort;
sb.AppendLine('type AstNode = '); sb.AppendLine('type AstNode = ');
for i := 0 to unionTypes.Count - 1 do for i := 0 to unionTypes.Count - 1 do
begin begin
@@ -148,38 +264,33 @@ begin
if i < unionTypes.Count - 1 then if i < unionTypes.Count - 1 then
sb.AppendLine; sb.AppendLine;
end; end;
sb.AppendLine(';'); sb.AppendLine(
sb.AppendLine; ';'
+ sLineBreak
sb.AppendLine('/** Explicit list structure */'); + sLineBreak
sb.AppendLine('type Tuple = ["Tuple", AstNode[]];'); + '/** Explicit list structure */'
sb.AppendLine; + sLineBreak
+ 'type Tuple = ["Tuple", AstNode[]];'
+ sLineBreak
);
for tag in unionTypes do for tag in unionTypes do
begin begin
if tag = 'Tuple' then if tag = 'Tuple' then
continue; continue;
sb.Append('/** '); sb.Append('/** ');
if nodeDocs.TryGetValue(tag, doc) then if nodeDocs.TryGetValue(tag, doc) then
sb.Append(doc); sb.Append(doc);
if nodeExamples.TryGetValue(tag, exampleList) then if nodeExamples.TryGetValue(tag, exampleList) then
begin
for var ex in exampleList do for var ex in exampleList do
sb.Append(sLineBreak + ' @example ' + ex); sb.Append(sLineBreak + ' @example ' + ex);
end;
sb.AppendLine(' */'); sb.AppendLine(' */');
fieldList := nodeDefs[tag]; fieldList := nodeDefs[tag];
fieldList.Sort(TComparer<TFieldInfo>.Construct(function(const L, R: TFieldInfo): Integer begin Result := L.Idx - R.Idx; end)); fieldList.Sort(TComparer<TFieldInfo>.Construct(function(const L, R: TFieldInfo): Integer begin Result := L.Idx - R.Idx; end));
sb.Append('type ' + tag + ' = ["' + tag + '"'); sb.Append('type ' + tag + ' = ["' + tag + '"');
for var f in fieldList do for var f in fieldList do
sb.Append(Format(', %s /* %s */', [GetTsType(f.Kind), f.Name])); sb.Append(Format(', %s /* %s */', [GetTsType(f.Kind), f.Name]));
sb.AppendLine('];'); sb.AppendLine('];');
end; end;
Result := sb.ToString; Result := sb.ToString;
finally finally
for fieldList in nodeDefs.Values do for fieldList in nodeDefs.Values do
@@ -195,111 +306,126 @@ begin
end; end;
end; end;
class function TAstSchema.GenerateRtlDocumentation: string; class function TAstSchema.GenerateAvailableSymbols(const AScope: IExecutionScope): string;
var var
ctx: TRttiContext;
typ: TRttiType;
meth: TRttiMethod;
attr: TCustomAttribute;
doc, rtlName: string;
sigs: TList<string>;
sb: TStringBuilder; sb: TStringBuilder;
processedNames: THashSet<string>; rtlMeta: TDictionary<string, TRtlMeta>;
layout: IScopeLayout;
symbols: TArray<string>;
name: string;
meta: TRtlMeta;
typ: IStaticType;
begin begin
ctx := TRttiContext.Create; if not Assigned(AScope) then
exit('');
rtlMeta := GetRtlMetadata;
sb := TStringBuilder.Create; sb := TStringBuilder.Create;
sigs := TList<string>.Create;
processedNames := THashSet<string>.Create;
try try
typ := ctx.GetType(TypeInfo(TRtlFunctions)); layout := AScope.Descriptor.Layout;
sb.AppendLine('### 3. Runtime Library (Available Functions)'); symbols := layout.GetSymbols;
TArray.Sort<string>(symbols);
sb.AppendLine('### 3. Available Symbols (Environment & Library)');
sb.AppendLine('The following symbols are currently defined and can be used in your script:');
sb.AppendLine; sb.AppendLine;
for meth in typ.GetMethods do for name in symbols do
begin begin
doc := ''; if name.StartsWith('<') or name.EndsWith('>') then
rtlName := ''; continue;
sigs.Clear;
for attr in meth.GetAttributes do sb.Append(Format('- `%s`', [name]));
if rtlMeta.TryGetValue(name, meta) then
begin begin
if attr is AstDocAttribute then if Length(meta.Signatures) > 0 then
doc := AstDocAttribute(attr).Description; sb.Append(': ' + string.Join(' | ', meta.Signatures));
if attr is TRtlExportAttribute then if meta.Doc <> '' then
rtlName := TRtlExportAttribute(attr).Name; sb.Append(' — ' + meta.Doc);
if attr is AstSignatureAttribute then end
sigs.Add(AstSignatureAttribute(attr).Signature); else
begin
typ := AScope.Descriptor.GetSymbolType(layout.FindSlot(name));
sb.Append(': ' + TypeToTs(typ));
end; end;
if (rtlName <> '') and (not processedNames.Contains(rtlName)) then
begin
sb.Append(Format('- `%s`', [rtlName]));
if sigs.Count > 0 then
sb.Append(': ' + string.Join(' | ', sigs.ToArray));
if doc <> '' then
sb.Append(' — ' + doc);
sb.AppendLine; sb.AppendLine;
processedNames.Add(rtlName);
end;
end; end;
Result := sb.ToString; Result := sb.ToString;
finally finally
processedNames.Free;
sigs.Free;
sb.Free; sb.Free;
ctx.Free; rtlMeta.Free;
end;
end;
class function TAstSchema.GenerateAllRtlDocumentation: string;
var
rtlMeta: TDictionary<string, TRtlMeta>;
sb: TStringBuilder;
pair: TPair<string, TRtlMeta>;
begin
rtlMeta := GetRtlMetadata;
sb := TStringBuilder.Create;
try
sb.AppendLine('### 3. Runtime Library (Global Functions)');
sb.AppendLine;
for pair in rtlMeta do
begin
sb.Append(Format('- `%s`', [pair.Key]));
if Length(pair.Value.Signatures) > 0 then
sb.Append(': ' + string.Join(' | ', pair.Value.Signatures));
if pair.Value.Doc <> '' then
sb.Append(' — ' + pair.Value.Doc);
sb.AppendLine;
end;
Result := sb.ToString;
finally
sb.Free;
rtlMeta.Free;
end; end;
end; end;
class function TAstSchema.GenerateGenerationRules: string; class function TAstSchema.GenerateGenerationRules: string;
var
sb: TStringBuilder;
begin
sb := TStringBuilder.Create;
try
sb.AppendLine('**⚠️ CRITICAL GENERATION RULES:**');
sb.AppendLine;
sb.AppendLine('1. **NO JSON OBJECTS:** Do not use `{}`. Every node MUST be a JSON Array `["Tag", Arg1, Arg2]`.');
sb.AppendLine('2. **EXPLICIT TUPLES:** Fields defined as `Tuple` MUST be wrapped in `["Tuple", [...]]`.');
sb.AppendLine('3. **EXPLICIT NULLS:** Optional fields (`| null`) MUST be explicit `null` if not present.');
sb.AppendLine('4. **STRICT ORDER:** Array elements must follow the schema index exactly. Never swap arguments.');
sb.AppendLine(
'5. **OPERATORS:** Use `Call` with an `Id` for operators: `["Call", ["Id", "+"], ["Tuple", [["Const", 1], ["Const", 2]]]]`.'
);
sb.AppendLine(
'6. **ID VS KEY:** Use `Key` for record keys (e.g. `["Field", ["Key", "p"], ...]`). Use `Id` for variables, functions, and operators.'
);
sb.AppendLine('7. **BOOLEANS:** The literals `true` and `false` are identifiers: `["Id", "true"]` or `["Id", "false"]`.');
sb.AppendLine(
'8. **SCALAR TYPES:** Valid types for series definitions are: `:Ordinal`, `:Float`, `:Boolean`, `:Text`, `:DateTime`.'
);
sb.AppendLine('9. **NESTED ARRAYS:** In "Cond", the pairs are an array of arrays: `[ [cond1, branch1], [cond2, branch2] ]`.');
sb.AppendLine('10. **RECURSION:** Use `Recur` for tail-recursive calls to enable optimization.');
sb.AppendLine('11. **NO COMMENTS:** Output raw JSON only. No explanations.');
Result := sb.ToString;
finally
sb.Free;
end;
end;
class function TAstSchema.GenerateFullSystemPrompt: string;
begin begin
Result := Result :=
'You are a compiler frontend for a domain-specific scripting language.' '''
+ sLineBreak **⚠️ CRITICAL GENERATION RULES:**
+ 'Your task is to generate a valid Abstract Syntax Tree (AST) in a specific Compact JSON format.'
+ sLineBreak 1. **NO JSON OBJECTS:** Do not use `{}`. Every node MUST be a JSON Array `["Tag", Arg1, Arg2]`.
+ sLineBreak 2. **EXPLICIT TUPLES:** Fields defined as `Tuple` MUST be wrapped in `["Tuple", [...]]`.
+ '### 1. AST Schema (TypeScript Definition)' 3. **EXPLICIT NULLS:** Optional fields (`| null`) MUST be explicit `null` if not present.
4. **STRICT ORDER:** Array elements must follow the schema index exactly.
5. **OPERATORS:** Use `Call` with an `Id` for operators: `["Call", ["Id", "+"], ["Tuple", [["Const", 1], ["Const", 2]]]]`.
6. **ID VS KEY:** Use `Key` for record keys. Use `Id` for variables, functions, and operators.
7. **BOOLEANS:** The literals `true` and `false` are identifiers: `["Id", "true"]` or `["Id", "false"]`.
8. **SCALAR TYPES:** Valid types for series definitions are: `:Ordinal`, `:Float`, `:Boolean`, `:Text`, `:DateTime`.
9. **NESTED ARRAYS:** In "Cond", the pairs are an array of arrays: `[ [cond1, branch1], [cond2, branch2] ]`.
10. **RECURSION:** Use `Recur` for tail-recursive calls to enable optimization.
11. **NO COMMENTS:** Output raw JSON only.
''';
end;
class function TAstSchema.GenerateFullSystemPrompt(const AScope: IExecutionScope): string;
begin
Result :=
'''
You are a compiler frontend for a domain-specific scripting language.
Your task is to generate a valid Abstract Syntax Tree (AST) in a specific Compact JSON format.
### 1. AST Schema (TypeScript Definition)
'''
+ sLineBreak + sLineBreak
+ GenerateTypeScriptDefinition + GenerateTypeScriptDefinition
+ sLineBreak + sLineBreak
+ '### 2. Constraints & Rules' + '### 2. Constraints & Rules'
+ sLineBreak + sLineBreak
+ GenerateGenerationRules + GenerateGenerationRules
+ sLineBreak + sLineBreak;
+ GenerateRtlDocumentation;
if Assigned(AScope) then
Result := Result + GenerateAvailableSymbols(AScope)
else
Result := Result + GenerateAllRtlDocumentation;
end; end;
end. end.
+68 -102
View File
@@ -206,18 +206,18 @@ type
property IsPure: Boolean read GetIsPure; property IsPure: Boolean read GetIsPure;
end; end;
// --- Specialized Node Interfaces with Examples and Docs --- // --- Specialized Node Interfaces with Metadata ---
[AstTag('Nop')] [AstTag('Nop')]
[AstDoc('A placeholder that performs no action.')] [AstDoc('A placeholder that performs no action.')]
[AstExample('["Nop"]')] [AstScriptExample('...')]
INopNode = interface(IAstTypedNode) INopNode = interface(IAstTypedNode)
end; end;
[AstTag('Const')] [AstTag('Const')]
[AstDoc('A literal value (number, string, or boolean).')] [AstDoc('A literal value (number, string, or boolean).')]
[AstExample('["Const", 42]')] [AstScriptExample('42')]
[AstExample('["Const", "Myc"]')] [AstScriptExample('"Myc Script"')]
[AstField(0, 'value', fkValue)] [AstField(0, 'value', fkValue)]
IConstantNode = interface(IAstTypedNode) IConstantNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
@@ -228,7 +228,8 @@ type
[AstTag('Id')] [AstTag('Id')]
[AstDoc('A name referring to a variable, parameter or function.')] [AstDoc('A name referring to a variable, parameter or function.')]
[AstExample('["Id", "x"]')] [AstScriptExample('price')]
[AstScriptExample('+')]
[AstField(0, 'name', fkString)] [AstField(0, 'name', fkString)]
IIdentifierNode = interface(IAstTypedNode) IIdentifierNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
@@ -240,8 +241,8 @@ type
end; end;
[AstTag('Key')] [AstTag('Key')]
[AstDoc('An interned keyword literal.')] [AstDoc('A keyword literal, typically used as record keys.')]
[AstExample('["Key", "volume"]')] [AstScriptExample(':volume')]
[AstField(0, 'name', fkString)] [AstField(0, 'name', fkString)]
IKeywordNode = interface(IAstTypedNode) IKeywordNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
@@ -252,7 +253,8 @@ type
[AstTag('Tuple')] [AstTag('Tuple')]
[AstDoc('A positional collection of elements (Vector).')] [AstDoc('A positional collection of elements (Vector).')]
[AstExample('["Tuple", [["Const", 1], ["Id", "y"]]]')] [AstScriptExample('[1 2 3]')]
[AstScriptExample('["a" :b c]')]
[AstField(0, 'elements', fkNode)] [AstField(0, 'elements', fkNode)]
ITupleNode = interface(IAstTypedNode) ITupleNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
@@ -276,17 +278,8 @@ type
[AstTag('If')] [AstTag('If')]
[AstDoc('Conditional ternary expression.')] [AstDoc('Conditional ternary expression.')]
[ [AstScriptExample('(if (> x 0) 1 -1)')]
AstExample( [AstScriptExample('(if true "yes")')]
'''
["If",
["Call", ["Id", ">"], ["Tuple", [["Id", "x"], ["Const", 0]]]],
["Const", "ok"],
["Const", "fail"]
]
'''
)
]
[AstField(0, 'cond', fkNode)] [AstField(0, 'cond', fkNode)]
[AstField(1, 'then', fkNode)] [AstField(1, 'then', fkNode)]
[AstField(2, 'else', fkNullableNode)] [AstField(2, 'else', fkNullableNode)]
@@ -304,15 +297,11 @@ type
[AstTag('Cond')] [AstTag('Cond')]
[AstDoc('Multi-branch condition. Pairs are [ [cond, branch], ... ].')] [AstDoc('Multi-branch condition. Pairs are [ [cond, branch], ... ].')]
[ [
AstExample( AstScriptExample(
''' '''
["Cond", (? (> x 0) :positive
[ (< x 0) :negative
[["Call", ["Id", "="], ["Tuple", [["Id", "s"], ["Const", 1]]]], ["Key", "up"]], :zero)
[["Call", ["Id", "="], ["Tuple", [["Id", "s"], ["Const", 2]]]], ["Key", "down"]]
],
["Key", "flat"]
]
''' '''
) )
] ]
@@ -328,17 +317,8 @@ type
end; end;
[AstTag('Fn')] [AstTag('Fn')]
[AstDoc('Anonymous function definition (lambda).')] [AstDoc('An anonymous function definition (lambda).')]
[ [AstScriptExample('(fn [a b] (+ a b))')]
AstExample(
'''
["Fn",
["Tuple", [["Id", "a"], ["Id", "b"]]],
["Call", ["Id", "+"], ["Tuple", [["Id", "a"], ["Id", "b"]]]]
]
'''
)
]
[AstField(0, 'params', fkTuple)] [AstField(0, 'params', fkTuple)]
[AstField(1, 'body', fkNode)] [AstField(1, 'body', fkNode)]
ILambdaExpressionNode = interface(IFunctionDefinition) ILambdaExpressionNode = interface(IFunctionDefinition)
@@ -355,8 +335,9 @@ type
end; end;
[AstTag('Call')] [AstTag('Call')]
[AstDoc('Function or operator invocation.')] [AstDoc('A function or operator invocation.')]
[AstExample('["Call", ["Id", "abs"], ["Tuple", [["Const", -5]]]]')] [AstScriptExample('(abs -5)')]
[AstScriptExample('(SMA src 14)')]
[AstField(0, 'callee', fkNode)] [AstField(0, 'callee', fkNode)]
[AstField(1, 'args', fkTuple)] [AstField(1, 'args', fkTuple)]
IFunctionCallNode = interface(IAstTypedNode) IFunctionCallNode = interface(IAstTypedNode)
@@ -375,15 +356,14 @@ type
end; end;
[AstTag('Block')] [AstTag('Block')]
[AstDoc('Sequential execution of nodes.')] [AstDoc('Sequential execution of nodes, returning the last result.')]
[ [
AstExample( AstScriptExample(
''' '''
["Block", ["Tuple", [ (do
["Var", ["Id", "a"], ["Const", 1]], (def x 10)
["Assign", ["Id", "a"], ["Call", ["Id", "+"], ["Tuple", [["Id", "a"], ["Const", 1]]]]], (assign x (* x 2))
["Id", "a"] x)
]]]
''' '''
) )
] ]
@@ -396,8 +376,9 @@ type
end; end;
[AstTag('Var')] [AstTag('Var')]
[AstDoc('Variable declaration.')] [AstDoc('Declares a new variable in the current scope.')]
[AstExample('["Var", ["Id", "val"], ["Const", 0]]')] [AstScriptExample('(def x 10)')]
[AstScriptExample('(def y)')]
[AstField(0, 'target', fkNode)] [AstField(0, 'target', fkNode)]
[AstField(1, 'init', fkNullableNode)] [AstField(1, 'init', fkNullableNode)]
IVariableDeclarationNode = interface(IAstTypedNode) IVariableDeclarationNode = interface(IAstTypedNode)
@@ -412,8 +393,8 @@ type
end; end;
[AstTag('Assign')] [AstTag('Assign')]
[AstDoc('Variable assignment.')] [AstDoc('Updates the value of an existing variable.')]
[AstExample('["Assign", ["Id", "x"], ["Const", 1]]')] [AstScriptExample('(assign x 20)')]
[AstField(0, 'target', fkNode)] [AstField(0, 'target', fkNode)]
[AstField(1, 'value', fkNode)] [AstField(1, 'value', fkNode)]
IAssignmentNode = interface(IAstTypedNode) IAssignmentNode = interface(IAstTypedNode)
@@ -426,15 +407,12 @@ type
end; end;
[AstTag('Macro')] [AstTag('Macro')]
[AstDoc('Syntactic macro definition.')] [AstDoc('Defines a compile-time syntactic macro.')]
[ [
AstExample( AstScriptExample(
''' '''
["Macro", (defmacro unless [c b]
["Id", "unless"], `(if ~c ... ~b))
["Tuple", [["Id", "condition"], ["Id", "body"]]],
["Quote", ["If", ["Id", "condition"], ["Nop"], ["Unquote", ["Id", "body"]]]]
]
''' '''
) )
] ]
@@ -453,7 +431,8 @@ type
end; end;
[AstTag('Quote')] [AstTag('Quote')]
[AstDoc('Suppresses evaluation of an expression.')] [AstDoc('Quotes an expression to prevent immediate evaluation.')]
[AstScriptExample('`(+ 1 2)')]
[AstField(0, 'expr', fkNode)] [AstField(0, 'expr', fkNode)]
IQuasiquoteNode = interface(IAstNode) IQuasiquoteNode = interface(IAstNode)
{$region 'private'} {$region 'private'}
@@ -463,7 +442,8 @@ type
end; end;
[AstTag('Unquote')] [AstTag('Unquote')]
[AstDoc('Enables evaluation inside a quoted context.')] [AstDoc('Evaluates an expression inside a quoted context.')]
[AstScriptExample('~x')]
[AstField(0, 'expr', fkNode)] [AstField(0, 'expr', fkNode)]
IUnquoteNode = interface(IAstNode) IUnquoteNode = interface(IAstNode)
{$region 'private'} {$region 'private'}
@@ -473,18 +453,19 @@ type
end; end;
[AstTag('Splice')] [AstTag('Splice')]
[AstDoc('Splicing evaluation into a list.')] [AstDoc('Unrolls a list into the surrounding quoted list.')]
[AstField(0, 'expr', fkNode)] [AstField(0, 'expr', fkNode)]
IUnquoteSplicingNode = interface(IAstNode) IUnquoteSplicingNode = interface(IAstNode)
{$region 'private'} {$region 'private'}
function GetExpression: IQuasiquoteNode; // BUGFIX: Should return IAstNode, not IQuasiquoteNode
function GetExpression: IAstNode;
{$endregion} {$endregion}
property Expression: IQuasiquoteNode read GetExpression; property Expression: IAstNode read GetExpression;
end; end;
[AstTag('Index')] [AstTag('Index')]
[AstDoc('Element access by position.')] [AstDoc('Positional access to a vector or series element.')]
[AstExample('["Index", ["Id", "v"], ["Const", 0]]')] [AstScriptExample('(get prices 0)')]
[AstField(0, 'base', fkNode)] [AstField(0, 'base', fkNode)]
[AstField(1, 'index', fkNode)] [AstField(1, 'index', fkNode)]
IIndexerNode = interface(IAstTypedNode) IIndexerNode = interface(IAstTypedNode)
@@ -497,8 +478,8 @@ type
end; end;
[AstTag('Member')] [AstTag('Member')]
[AstDoc('Field access by keyword.')] [AstDoc('Accesses a record field or stream member by key.')]
[AstExample('["Member", ["Id", "rec"], ["Key", "price"]]')] [AstScriptExample('(.price rec)')]
[AstField(0, 'base', fkNode)] [AstField(0, 'base', fkNode)]
[AstField(1, 'member', fkNode)] [AstField(1, 'member', fkNode)]
IMemberAccessNode = interface(IAstTypedNode) IMemberAccessNode = interface(IAstTypedNode)
@@ -511,17 +492,8 @@ type
end; end;
[AstTag('Record')] [AstTag('Record')]
[AstDoc('Defines a record literal.')] [AstDoc('A literal key-value mapping.')]
[ [AstScriptExample('{:x 1 :y 2}')]
AstExample(
'''
["Record", ["Tuple", [
["Field", ["Key", "p"], ["Id", "p_val"]],
["Field", ["Key", "q"], ["Const", 100]]
]]]
'''
)
]
[AstField(0, 'fields', fkTuple)] [AstField(0, 'fields', fkTuple)]
IRecordLiteralNode = interface(IAstTypedNode) IRecordLiteralNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
@@ -535,8 +507,9 @@ type
end; end;
[AstTag('Series')] [AstTag('Series')]
[AstDoc('Stream schema definition.')] [AstDoc('Defines a new data series schema.')]
[AstExample('["Series", ["Key", "Float"]]')] [AstScriptExample('(new-series :Float)')]
[AstScriptExample('(new-series [[:p :Float] [:v :Ordinal]])')]
[AstField(0, 'definition', fkNode)] [AstField(0, 'definition', fkNode)]
ICreateSeriesNode = interface(IAstTypedNode) ICreateSeriesNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
@@ -548,8 +521,9 @@ type
end; end;
[AstTag('Add')] [AstTag('Add')]
[AstDoc('Pushes data to a series.')] [AstDoc('Appends a value to a data series.')]
[AstExample('["Add", ["Id", "s"], ["Const", 10]]')] [AstScriptExample('(add-item s 10.5)')]
[AstScriptExample('(add-item s 10.5 100)')]
[AstField(0, 'series', fkNode)] [AstField(0, 'series', fkNode)]
[AstField(1, 'value', fkNode)] [AstField(1, 'value', fkNode)]
[AstField(2, 'lookback', fkNullableNode)] [AstField(2, 'lookback', fkNullableNode)]
@@ -565,8 +539,8 @@ type
end; end;
[AstTag('Count')] [AstTag('Count')]
[AstDoc('Series length.')] [AstDoc('Returns the current number of elements in a series.')]
[AstExample('["Count", ["Id", "src"]]')] [AstScriptExample('(count prices)')]
[AstField(0, 'series', fkNode)] [AstField(0, 'series', fkNode)]
ISeriesLengthNode = interface(IAstTypedNode) ISeriesLengthNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
@@ -576,8 +550,8 @@ type
end; end;
[AstTag('Recur')] [AstTag('Recur')]
[AstDoc('Tail-recursive jump.')] [AstDoc('Tail-recursive call to the current function.')]
[AstExample('["Recur", ["Tuple", [["Call", ["Id", "-"], ["Tuple", [["Id", "i"], ["Const", 1]]]]]]]')] [AstScriptExample('(recur (- n 1))')]
[AstField(0, 'args', fkTuple)] [AstField(0, 'args', fkTuple)]
IRecurNode = interface(IAstTypedNode) IRecurNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
@@ -589,18 +563,10 @@ type
[AstTag('Pipe')] [AstTag('Pipe')]
[AstDoc('Reactive pipeline from source streams to a transformation lambda.')] [AstDoc('Reactive pipeline from source streams to a transformation lambda.')]
[ [
AstExample( AstScriptExample(
''' '''
["Pipe", (pipe [[src [:val]]]
["Tuple", [ (fn [v] {:res (* v 2)}))
["Tuple", [["Id", "src"], ["Tuple", [["Key", "price"]]]]]
]],
["Fn", ["Tuple", [["Id", "p"]]],
["Record", ["Tuple", [
["Field", ["Key", "res"], ["Call", ["Id", "*"], ["Tuple", [["Id", "p"], ["Const", 2]]]]]
]]]
]
]
''' '''
) )
] ]
@@ -946,12 +912,12 @@ type
TUnquoteSplicingNode = class(TAstNode, IUnquoteSplicingNode) TUnquoteSplicingNode = class(TAstNode, IUnquoteSplicingNode)
private private
FExpression: IQuasiquoteNode; FExpression: IAstNode; // Changed to IAstNode
function GetExpression: IQuasiquoteNode; function GetExpression: IAstNode;
protected protected
function GetKind: TAstNodeKind; override; function GetKind: TAstNodeKind; override;
public public
constructor Create(const AExpression: IQuasiquoteNode; const AIdentity: IAstIdentity); constructor Create(const AExpression: IAstNode; const AIdentity: IAstIdentity);
function AsUnquoteSplicing: IUnquoteSplicingNode; override; function AsUnquoteSplicing: IUnquoteSplicingNode; override;
end; end;
@@ -1859,7 +1825,7 @@ end;
{ TUnquoteSplicingNode } { TUnquoteSplicingNode }
constructor TUnquoteSplicingNode.Create(const AExpression: IQuasiquoteNode; const AIdentity: IAstIdentity); constructor TUnquoteSplicingNode.Create(const AExpression: IAstNode; const AIdentity: IAstIdentity);
begin begin
inherited Create(AIdentity); inherited Create(AIdentity);
FExpression := AExpression; FExpression := AExpression;
@@ -1869,7 +1835,7 @@ function TUnquoteSplicingNode.GetKind: TAstNodeKind;
begin begin
Result := akUnquoteSplicing; Result := akUnquoteSplicing;
end; end;
function TUnquoteSplicingNode.GetExpression: IQuasiquoteNode; function TUnquoteSplicingNode.GetExpression: IAstNode;
begin begin
Result := FExpression; Result := FExpression;
end; end;