diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas index 3ece1cc..7b81972 100644 --- a/ASTPlayground/MainForm.pas +++ b/ASTPlayground/MainForm.pas @@ -288,7 +288,7 @@ begin CompilerStageBox.BringToFront; ClearButtonClick(Self); - Memo1.Lines.Add(TAstSchema.GenerateFullSystemPrompt); + Memo1.Lines.Add(TAstSchema.GenerateFullSystemPrompt(FEnvironment.RootScope)); end; procedure TForm1.ShowVizualization; @@ -743,45 +743,52 @@ end; procedure TForm1.FromJSONButtonClick(Sender: TObject); var jsonString: string; - jsonObj: TJSONObject; + jsonValue: TJSONValue; converter: IJsonAstConverter; begin Memo1.Lines.BeginUpdate; try jsonString := Memo1.Lines.Text; - Memo1.Lines.Clear; - if jsonString.IsEmpty then begin + Memo1.Lines.Clear; Memo1.Lines.Add('Memo is empty. Please paste an AST JSON string.'); exit; end; try converter := TJsonAstConverter.Create; - jsonObj := TJSONObject.ParseJSONValue(jsonString) as TJSONObject; - if not Assigned(jsonObj) then - raise Exception.Create('Invalid JSON format.'); + // TJSONValue.ParseJSONValue ist der korrekte Einstiegspunkt + jsonValue := TJSONValue.ParseJSONValue(jsonString); + + if not Assigned(jsonValue) then + raise Exception.Create('Invalid JSON syntax.'); 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); FCurrExec := FEnvironment.Link(FCurrCompiled); + Memo1.Lines.Clear; Memo1.Lines.Add('AST deserialized and bound successfully from JSON.'); Memo1.Lines.Add('You can now visualize it (Middle Mouse Click) or pretty-print it.'); finally - jsonObj.Free; + jsonValue.Free; end; except on E: Exception do begin + Memo1.Lines.Clear; Memo1.Lines.Add('Error deserializing AST from JSON:'); Memo1.Lines.Add(E.Message); Memo1.Lines.Add('--- Original JSON ---'); - Memo1.Lines.Text := Memo1.Lines.Text + sLineBreak + jsonString; + Memo1.Lines.Add(jsonString); end; end; finally diff --git a/Src/AST/Myc.Ast.Attributes.pas b/Src/AST/Myc.Ast.Attributes.pas new file mode 100644 index 0000000..cf3a30a --- /dev/null +++ b/Src/AST/Myc.Ast.Attributes.pas @@ -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. diff --git a/Src/AST/Myc.Ast.JSON.pas b/Src/AST/Myc.Ast.JSON.pas index 0a090fc..3b469c2 100644 --- a/Src/AST/Myc.Ast.JSON.pas +++ b/Src/AST/Myc.Ast.JSON.pas @@ -1,14 +1,11 @@ -//================================================================================================== -//== FULL UNIT START: Myc.Ast.Json (from Myc.Ast.Json.pas) -//================================================================================================== unit Myc.Ast.Json; interface uses - System.SysUtils, - System.Generics.Collections, - System.JSON, + system.sysutils, + system.generics.collections, + system.json, Myc.Data.Scalar, Myc.Data.Value, Myc.Ast, @@ -21,19 +18,16 @@ type function Deserialize(const AJson: TJSONValue): IAstNode; end; - // Compact JSON Converter: [Kind, Arg1, Arg2, ...] + { Compact JSON Converter: [Tag, Arg1, Arg2, ...] } TJsonAstConverter = class(TAstVisitor, IJsonAstConverter) private - // Helpers for compact data values function DataValueToJson(const AValue: TDataValue): TJSONValue; function JsonToDataValue(const AJson: TJSONValue): TDataValue; - // Deserialization Dispatcher function JsonToNode(const AJson: TJSONValue): IAstNode; function JsonToTuple(const AArray: TJSONArray): ITupleNode; strict private - // Serialization Visitors (Array Builders) function VisitConstant(const Node: IAstNode): TJSONValue; function VisitIdentifier(const Node: IAstNode): TJSONValue; function VisitKeyword(const Node: IAstNode): TJSONValue; @@ -42,16 +36,16 @@ type function VisitIfExpression(const Node: IAstNode): TJSONValue; function VisitCondExpression(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 VisitMacroExpansionNode(const Node: IAstNode): TJSONValue; function VisitRecurNode(const Node: IAstNode): TJSONValue; function VisitBlockExpression(const Node: IAstNode): TJSONValue; function VisitVariableDeclaration(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 VisitMemberAccess(const Node: IAstNode): TJSONValue; function VisitRecordLiteral(const Node: IAstNode): TJSONValue; @@ -125,31 +119,7 @@ begin Result := JsonToNode(AJson); end; -// --- Serialization (AST -> JSON Array) --- - -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; +// --- Serialization Helpers --- function TJsonAstConverter.DataValueToJson(const AValue: TDataValue): TJSONValue; begin @@ -177,97 +147,74 @@ begin end; end; -function TJsonAstConverter.VisitTuple(const Node: IAstNode): TJSONValue; +// --- Visitor Implementations --- + +function TJsonAstConverter.VisitConstant(const Node: IAstNode): TJSONValue; +var + arr: TJSONArray; begin - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Tuple')); - var elems := TJSONArray.Create; + arr := TJSONArray.Create; + arr.Add('Const'); + 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 elems.AddElement(Visit(item)); arr.AddElement(elems); Result := arr; end; -function TJsonAstConverter.VisitFunctionCall(const Node: IAstNode): TJSONValue; +function TJsonAstConverter.VisitRecordField(const Node: IAstNode): TJSONValue; var - C: IFunctionCallNode; + arr: TJSONArray; begin - C := Node.AsFunctionCall; - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Call')); - arr.AddElement(Visit(C.Callee)); - 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)); + arr := TJSONArray.Create; + arr.Add('Field'); + arr.AddElement(Visit(Node.AsRecordField.Key)); + arr.AddElement(Visit(Node.AsRecordField.Value)); Result := arr; end; function TJsonAstConverter.VisitIfExpression(const Node: IAstNode): TJSONValue; var - E: IIfExpressionNode; + arr: TJSONArray; + n: IIfExpressionNode; begin - E := Node.AsIfExpression; - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('If')); - arr.AddElement(Visit(E.Condition)); - arr.AddElement(Visit(E.ThenBranch)); - if Assigned(E.ElseBranch) then - arr.AddElement(Visit(E.ElseBranch)) + n := Node.AsIfExpression; + arr := TJSONArray.Create; + arr.Add('If'); + arr.AddElement(Visit(n.Condition)); + arr.AddElement(Visit(n.ThenBranch)); + if Assigned(n.ElseBranch) then + arr.AddElement(Visit(n.ElseBranch)) else arr.AddElement(TJSONNull.Create); Result := arr; @@ -275,147 +222,248 @@ end; function TJsonAstConverter.VisitCondExpression(const Node: IAstNode): TJSONValue; var - E: ICondExpressionNode; - pairs: TJSONArray; + arr, pairs: TJSONArray; + n: ICondExpressionNode; begin - E := Node.AsCondExpression; - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Cond')); + n := Node.AsCondExpression; + arr := TJSONArray.Create; + arr.Add('Cond'); pairs := TJSONArray.Create; - for var p in E.Pairs do + for var p in n.Pairs do begin - var pArr := TJSONArray.Create; - pArr.AddElement(Visit(p.Condition)); - pArr.AddElement(Visit(p.Branch)); - pairs.AddElement(pArr); + var pairArr := TJSONArray.Create; + pairArr.AddElement(Visit(p.Condition)); + pairArr.AddElement(Visit(p.Branch)); + pairs.AddElement(pairArr); end; arr.AddElement(pairs); - if Assigned(E.ElseBranch) then - arr.AddElement(Visit(E.ElseBranch)) + if Assigned(n.ElseBranch) then + arr.AddElement(Visit(n.ElseBranch)) else arr.AddElement(TJSONNull.Create); Result := arr; end; -function TJsonAstConverter.VisitQuasiquote(const Node: IAstNode): TJSONValue; +function TJsonAstConverter.VisitLambdaExpression(const Node: IAstNode): TJSONValue; +var + arr: TJSONArray; + n: ILambdaExpressionNode; begin - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Quote')); + n := Node.AsLambdaExpression; + 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)); Result := arr; end; function TJsonAstConverter.VisitUnquote(const Node: IAstNode): TJSONValue; +var + arr: TJSONArray; begin - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Unquote')); + arr := TJSONArray.Create; + arr.Add('Unquote'); arr.AddElement(Visit(Node.AsUnquote.Expression)); Result := arr; end; function TJsonAstConverter.VisitUnquoteSplicing(const Node: IAstNode): TJSONValue; +var + arr: TJSONArray; begin - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Splice')); + arr := TJSONArray.Create; + arr.Add('Splice'); arr.AddElement(Visit(Node.AsUnquoteSplicing.Expression)); Result := arr; 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; +var + arr: TJSONArray; + n: IIndexerNode; begin - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Index')); - arr.AddElement(Visit(Node.AsIndexer.Base)); - arr.AddElement(Visit(Node.AsIndexer.Index)); + n := Node.AsIndexer; + arr := TJSONArray.Create; + arr.Add('Index'); + arr.AddElement(Visit(n.Base)); + arr.AddElement(Visit(n.Index)); Result := arr; end; function TJsonAstConverter.VisitMemberAccess(const Node: IAstNode): TJSONValue; +var + arr: TJSONArray; + n: IMemberAccessNode; begin - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Member')); - arr.AddElement(Visit(Node.AsMemberAccess.Base)); - arr.AddElement(Visit(Node.AsMemberAccess.Member)); - Result := arr; -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)); + n := Node.AsMemberAccess; + arr := TJSONArray.Create; + arr.Add('Member'); + arr.AddElement(Visit(n.Base)); + arr.AddElement(Visit(n.Member)); Result := arr; end; function TJsonAstConverter.VisitRecordLiteral(const Node: IAstNode): TJSONValue; +var + arr: TJSONArray; begin - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Record')); - arr.AddElement(Visit(Node.AsRecordLiteral.Fields)); // Tuple + arr := TJSONArray.Create; + arr.Add('Record'); + arr.AddElement(Visit(Node.AsRecordLiteral.Fields)); Result := arr; end; function TJsonAstConverter.VisitCreateSeries(const Node: IAstNode): TJSONValue; +var + arr: TJSONArray; begin - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Series')); + arr := TJSONArray.Create; + arr.Add('Series'); arr.AddElement(Visit(Node.AsCreateSeries.DefinitionNode)); Result := arr; end; function TJsonAstConverter.VisitAddSeriesItem(const Node: IAstNode): TJSONValue; +var + arr: TJSONArray; + n: IAddSeriesItemNode; begin - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Add')); - arr.AddElement(Visit(Node.AsAddSeriesItem.Series)); - arr.AddElement(Visit(Node.AsAddSeriesItem.Value)); - if Assigned(Node.AsAddSeriesItem.Lookback) then - arr.AddElement(Visit(Node.AsAddSeriesItem.Lookback)) + n := Node.AsAddSeriesItem; + arr := TJSONArray.Create; + arr.Add('Add'); + arr.AddElement(Visit(n.Series)); + arr.AddElement(Visit(n.Value)); + if Assigned(n.Lookback) then + arr.AddElement(Visit(n.Lookback)) else arr.AddElement(TJSONNull.Create); Result := arr; end; function TJsonAstConverter.VisitSeriesLength(const Node: IAstNode): TJSONValue; +var + arr: TJSONArray; begin - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Count')); + arr := TJSONArray.Create; + arr.Add('Count'); arr.AddElement(Visit(Node.AsSeriesLength.Series)); Result := arr; end; -function TJsonAstConverter.VisitPipe(const Node: IAstNode): TJSONValue; +function TJsonAstConverter.VisitRecurNode(const Node: IAstNode): TJSONValue; +var + arr: TJSONArray; begin - var arr := TJSONArray.Create; - arr.AddElement(TJSONString.Create('Pipe')); - arr.AddElement(Visit(Node.AsPipe.Inputs)); - arr.AddElement(Visit(Node.AsPipe.Transformation)); + arr := TJSONArray.Create; + arr.Add('Recur'); + arr.AddElement(Visit(Node.AsRecur.Arguments)); Result := arr; end; function TJsonAstConverter.VisitNop(const Node: IAstNode): TJSONValue; +var + arr: TJSONArray; begin - Result := TJSONArray.Create; - TJSONArray(Result).AddElement(TJSONString.Create('Nop')); + arr := TJSONArray.Create; + arr.Add('Nop'); + Result := arr; end; -function TJsonAstConverter.VisitMacroExpansionNode(const Node: IAstNode): TJSONValue; +function TJsonAstConverter.VisitPipe(const Node: IAstNode): TJSONValue; +var + arr: TJSONArray; + n: IPipeNode; begin - // Serialize original call - Result := Visit(Node.AsMacroExpansion.CallNode); + n := Node.AsPipe; + arr := TJSONArray.Create; + arr.Add('Pipe'); + arr.AddElement(Visit(n.Inputs)); + arr.AddElement(Visit(n.Transformation)); + Result := arr; end; -// --- Deserialization (JSON Array -> AST) --- +// --- Deserialization --- function TJsonAstConverter.JsonToDataValue(const AJson: TJSONValue): TDataValue; var @@ -458,8 +506,18 @@ function TJsonAstConverter.JsonToNode(const AJson: TJSONValue): IAstNode; var arr: TJSONArray; kind: string; + + function UnpackTuple(Val: TJSONValue): TArray; + begin + var n := JsonToNode(Val); + if Assigned(n) then + Result := n.AsTuple.Elements + else + Result := []; + end; + begin - if AJson is TJSONNull then + if (not Assigned(AJson)) or (AJson is TJSONNull) then exit(nil); if not (AJson is TJSONArray) then @@ -472,32 +530,45 @@ begin kind := arr.Items[0].Value; if kind = 'Id' then - Exit(TAst.Identifier(arr.Items[1].Value)); + exit(TAst.Identifier(arr.Items[1].Value)); if kind = 'Key' then - Exit(TAst.Keyword(arr.Items[1].Value)); + exit(TAst.Keyword(arr.Items[1].Value)); if kind = 'Const' then - 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)); + exit(TAst.Constant(JsonToDataValue(arr.Items[1]))); 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.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 - 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 - // Pass TIdentities.Structural as Identity to satisfy the overload requiring (Identity, Tuple, Body, ...) - Exit(TAst.LambdaExpr(TIdentities.Structural, JsonToTuple(arr.Items[1] as TJSONArray), JsonToNode(arr.Items[2]))); + exit(TAst.LambdaExpr(TIdentities.Structural, JsonToNode(arr.Items[1]).AsTuple, JsonToNode(arr.Items[2]))); + if kind = 'Recur' then + exit(TAst.Recur(UnpackTuple(arr.Items[1]))); if kind = 'Macro' then - // Pass TIdentities.Structural as Identity - Exit( + exit( TAst.MacroDef( TIdentities.Structural, JsonToNode(arr.Items[1]).AsIdentifier, - JsonToTuple(arr.Items[2] as TJSONArray), + JsonToNode(arr.Items[2]).AsTuple, JsonToNode(arr.Items[3]) ) ); @@ -505,20 +576,20 @@ begin if kind = 'Var' then begin 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]); - Exit(TAst.VarDecl(JsonToNode(arr.Items[1]), init)); + exit(TAst.VarDecl(JsonToNode(arr.Items[1]), init)); end; 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 begin 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]); - 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; if kind = 'Cond' then @@ -532,68 +603,40 @@ begin pairs[i] := TCondPair.Create(JsonToNode(p.Items[0]), JsonToNode(p.Items[1])); end; 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]); - Exit(TAst.CondExpr(pairs, el)); + exit(TAst.CondExpr(pairs, el)); end; if kind = 'Quote' then - Exit(TAst.Quasiquote(JsonToNode(arr.Items[1]))); + exit(TAst.Quasiquote(JsonToNode(arr.Items[1]))); 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 - Exit(TAst.UnquoteSplicing(JsonToNode(arr.Items[1]).AsQuasiquote)); - if kind = 'Recur' then - Exit(TAst.Recur(JsonToTuple(arr.Items[1] as TJSONArray).Elements)); + exit(TUnquoteSplicingNode.Create(JsonToNode(arr.Items[1]), TIdentities.Structural(nil))); 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 - 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 := TList.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]))); - + exit(TAst.MemberAccess(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2]).AsKeyword)); 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 begin 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]); - 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; - 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); end; end. - //================================================================================================== -//== FULL UNIT END: Myc.Ast.Json -//================================================================================================== diff --git a/Src/AST/Myc.Ast.Json.Schema.pas b/Src/AST/Myc.Ast.Json.Schema.pas index 05c05af..40c3536 100644 --- a/Src/AST/Myc.Ast.Json.Schema.pas +++ b/Src/AST/Myc.Ast.Json.Schema.pas @@ -9,16 +9,28 @@ uses system.generics.defaults, system.rtti, system.typinfo, + system.json, + Myc.Data.Scalar, + Myc.Data.Value, Myc.Ast.Nodes, Myc.Ast.Attributes, Myc.Ast.RTL, - Myc.Ast.RTL.Core; + Myc.Ast.RTL.Core, + Myc.Ast.Scope, + Myc.Ast.Types, + Myc.Ast.Script, + Myc.Ast.Json; 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 strict private type + TRtlMeta = record + Doc: string; + Signatures: TArray; + end; + TFieldInfo = record Idx: Integer; Name: string; @@ -26,18 +38,23 @@ type end; class function GetTsType(Kind: TFieldKind): string; static; + class function TypeToTs(const AType: IStaticType): string; static; + class function GetRtlMetadata: TDictionary; static; public - // Generates the TypeScript definition including JSDoc and multiple @example entries + // Generates the TypeScript definition for AST nodes class function GenerateTypeScriptDefinition: string; static; - // Generates documented RTL function signatures with all overloads - class function GenerateRtlDocumentation: string; static; + // Unified section: Lists only symbols available in the current scope with their docs/types + 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; - // The final consolidated system prompt - class function GenerateFullSystemPrompt: string; static; + // The final consolidated system prompt for the AI + class function GenerateFullSystemPrompt(const AScope: IExecutionScope = nil): string; static; end; implementation @@ -58,6 +75,103 @@ begin 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.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.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; +var + ctx: TRttiContext; + typ: TRttiType; + meth: TRttiMethod; + attr: TCustomAttribute; + meta: TRtlMeta; + rtlName: string; + sigs: TList; +begin + Result := TDictionary.Create; + sigs := TList.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.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; var ctx: TRttiContext; @@ -71,9 +185,11 @@ var tag, doc: string; fieldList: TList; exampleList: TList; + jsonConv: IJsonAstConverter; i: Integer; begin ctx := TRttiContext.Create; + jsonConv := TJsonAstConverter.Create; nodeDefs := TDictionary>.Create; nodeDocs := TDictionary.Create; nodeExamples := TDictionary>.Create; @@ -84,27 +200,33 @@ begin begin if typ.TypeKind <> tkInterface then continue; - tag := ''; doc := ''; fieldList := nil; exampleList := nil; - for attr in typ.GetAttributes do begin if attr is AstTagAttribute then tag := AstTagAttribute(attr).Tag; - if attr is AstDocAttribute then doc := AstDocAttribute(attr).Description; - - if attr is AstExampleAttribute then + if attr is AstScriptExampleAttribute then begin if exampleList = nil then exampleList := TList.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; - if attr is AstFieldAttribute then begin if fieldList = nil then @@ -116,7 +238,6 @@ begin fieldList.Add(f); end; end; - if tag <> '' then begin unionTypes.Add(tag); @@ -134,13 +255,8 @@ begin exampleList.Free; end; end; - - sb.AppendLine('// AST Schema Definition (Auto-Generated)'); - sb.AppendLine('// Format: Compact JSON Arrays [Tag, Arg1, Arg2, ...]'); - sb.AppendLine; - + sb.AppendLine('// AST Schema Definition' + sLineBreak + '// Format: Compact JSON Arrays [Tag, Arg1, Arg2, ...]' + sLineBreak); unionTypes.Sort; - sb.AppendLine('type AstNode = '); for i := 0 to unionTypes.Count - 1 do begin @@ -148,38 +264,33 @@ begin if i < unionTypes.Count - 1 then sb.AppendLine; end; - sb.AppendLine(';'); - sb.AppendLine; - - sb.AppendLine('/** Explicit list structure */'); - sb.AppendLine('type Tuple = ["Tuple", AstNode[]];'); - sb.AppendLine; - + sb.AppendLine( + ';' + + sLineBreak + + sLineBreak + + '/** Explicit list structure */' + + sLineBreak + + 'type Tuple = ["Tuple", AstNode[]];' + + sLineBreak + ); for tag in unionTypes do begin if tag = 'Tuple' then continue; - sb.Append('/** '); if nodeDocs.TryGetValue(tag, doc) then sb.Append(doc); - if nodeExamples.TryGetValue(tag, exampleList) then - begin for var ex in exampleList do sb.Append(sLineBreak + ' @example ' + ex); - end; sb.AppendLine(' */'); - fieldList := nodeDefs[tag]; fieldList.Sort(TComparer.Construct(function(const L, R: TFieldInfo): Integer begin Result := L.Idx - R.Idx; end)); - sb.Append('type ' + tag + ' = ["' + tag + '"'); for var f in fieldList do sb.Append(Format(', %s /* %s */', [GetTsType(f.Kind), f.Name])); sb.AppendLine('];'); end; - Result := sb.ToString; finally for fieldList in nodeDefs.Values do @@ -195,111 +306,126 @@ begin end; end; -class function TAstSchema.GenerateRtlDocumentation: string; +class function TAstSchema.GenerateAvailableSymbols(const AScope: IExecutionScope): string; var - ctx: TRttiContext; - typ: TRttiType; - meth: TRttiMethod; - attr: TCustomAttribute; - doc, rtlName: string; - sigs: TList; sb: TStringBuilder; - processedNames: THashSet; + rtlMeta: TDictionary; + layout: IScopeLayout; + symbols: TArray; + name: string; + meta: TRtlMeta; + typ: IStaticType; begin - ctx := TRttiContext.Create; + if not Assigned(AScope) then + exit(''); + + rtlMeta := GetRtlMetadata; sb := TStringBuilder.Create; - sigs := TList.Create; - processedNames := THashSet.Create; try - typ := ctx.GetType(TypeInfo(TRtlFunctions)); - sb.AppendLine('### 3. Runtime Library (Available Functions)'); + layout := AScope.Descriptor.Layout; + symbols := layout.GetSymbols; + TArray.Sort(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; - for meth in typ.GetMethods do + for name in symbols do begin - doc := ''; - rtlName := ''; - sigs.Clear; + if name.StartsWith('<') or name.EndsWith('>') then + continue; - for attr in meth.GetAttributes do - begin - if attr is AstDocAttribute then - doc := AstDocAttribute(attr).Description; - if attr is TRtlExportAttribute then - rtlName := TRtlExportAttribute(attr).Name; - if attr is AstSignatureAttribute then - sigs.Add(AstSignatureAttribute(attr).Signature); - end; + sb.Append(Format('- `%s`', [name])); - if (rtlName <> '') and (not processedNames.Contains(rtlName)) then + if rtlMeta.TryGetValue(name, meta) 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; - processedNames.Add(rtlName); + if Length(meta.Signatures) > 0 then + sb.Append(': ' + string.Join(' | ', meta.Signatures)); + if meta.Doc <> '' then + sb.Append(' — ' + meta.Doc); + end + else + begin + typ := AScope.Descriptor.GetSymbolType(layout.FindSlot(name)); + sb.Append(': ' + TypeToTs(typ)); end; + sb.AppendLine; end; Result := sb.ToString; finally - processedNames.Free; - sigs.Free; sb.Free; - ctx.Free; + rtlMeta.Free; + end; +end; + +class function TAstSchema.GenerateAllRtlDocumentation: string; +var + rtlMeta: TDictionary; + sb: TStringBuilder; + pair: TPair; +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; 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 Result := - 'You are a compiler frontend for a domain-specific scripting language.' - + sLineBreak - + 'Your task is to generate a valid Abstract Syntax Tree (AST) in a specific Compact JSON format.' - + sLineBreak - + sLineBreak - + '### 1. AST Schema (TypeScript Definition)' + ''' + **⚠️ CRITICAL GENERATION RULES:** + + 1. **NO JSON OBJECTS:** Do not use `{}`. Every node MUST be a JSON Array `["Tag", Arg1, Arg2]`. + 2. **EXPLICIT TUPLES:** Fields defined as `Tuple` MUST be wrapped in `["Tuple", [...]]`. + 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 + GenerateTypeScriptDefinition + sLineBreak + '### 2. Constraints & Rules' + sLineBreak + GenerateGenerationRules - + sLineBreak - + GenerateRtlDocumentation; + + sLineBreak; + + if Assigned(AScope) then + Result := Result + GenerateAvailableSymbols(AScope) + else + Result := Result + GenerateAllRtlDocumentation; end; end. diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index c3a1483..3c6be90 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -206,18 +206,18 @@ type property IsPure: Boolean read GetIsPure; end; - // --- Specialized Node Interfaces with Examples and Docs --- + // --- Specialized Node Interfaces with Metadata --- [AstTag('Nop')] [AstDoc('A placeholder that performs no action.')] - [AstExample('["Nop"]')] + [AstScriptExample('...')] INopNode = interface(IAstTypedNode) end; [AstTag('Const')] [AstDoc('A literal value (number, string, or boolean).')] - [AstExample('["Const", 42]')] - [AstExample('["Const", "Myc"]')] + [AstScriptExample('42')] + [AstScriptExample('"Myc Script"')] [AstField(0, 'value', fkValue)] IConstantNode = interface(IAstTypedNode) {$region 'private'} @@ -228,7 +228,8 @@ type [AstTag('Id')] [AstDoc('A name referring to a variable, parameter or function.')] - [AstExample('["Id", "x"]')] + [AstScriptExample('price')] + [AstScriptExample('+')] [AstField(0, 'name', fkString)] IIdentifierNode = interface(IAstTypedNode) {$region 'private'} @@ -240,8 +241,8 @@ type end; [AstTag('Key')] - [AstDoc('An interned keyword literal.')] - [AstExample('["Key", "volume"]')] + [AstDoc('A keyword literal, typically used as record keys.')] + [AstScriptExample(':volume')] [AstField(0, 'name', fkString)] IKeywordNode = interface(IAstTypedNode) {$region 'private'} @@ -252,7 +253,8 @@ type [AstTag('Tuple')] [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)] ITupleNode = interface(IAstTypedNode) {$region 'private'} @@ -276,17 +278,8 @@ type [AstTag('If')] [AstDoc('Conditional ternary expression.')] - [ - AstExample( - ''' - ["If", - ["Call", ["Id", ">"], ["Tuple", [["Id", "x"], ["Const", 0]]]], - ["Const", "ok"], - ["Const", "fail"] - ] - ''' - ) - ] + [AstScriptExample('(if (> x 0) 1 -1)')] + [AstScriptExample('(if true "yes")')] [AstField(0, 'cond', fkNode)] [AstField(1, 'then', fkNode)] [AstField(2, 'else', fkNullableNode)] @@ -304,15 +297,11 @@ type [AstTag('Cond')] [AstDoc('Multi-branch condition. Pairs are [ [cond, branch], ... ].')] [ - AstExample( + AstScriptExample( ''' - ["Cond", - [ - [["Call", ["Id", "="], ["Tuple", [["Id", "s"], ["Const", 1]]]], ["Key", "up"]], - [["Call", ["Id", "="], ["Tuple", [["Id", "s"], ["Const", 2]]]], ["Key", "down"]] - ], - ["Key", "flat"] - ] + (? (> x 0) :positive + (< x 0) :negative + :zero) ''' ) ] @@ -328,17 +317,8 @@ type end; [AstTag('Fn')] - [AstDoc('Anonymous function definition (lambda).')] - [ - AstExample( - ''' - ["Fn", - ["Tuple", [["Id", "a"], ["Id", "b"]]], - ["Call", ["Id", "+"], ["Tuple", [["Id", "a"], ["Id", "b"]]]] - ] - ''' - ) - ] + [AstDoc('An anonymous function definition (lambda).')] + [AstScriptExample('(fn [a b] (+ a b))')] [AstField(0, 'params', fkTuple)] [AstField(1, 'body', fkNode)] ILambdaExpressionNode = interface(IFunctionDefinition) @@ -355,8 +335,9 @@ type end; [AstTag('Call')] - [AstDoc('Function or operator invocation.')] - [AstExample('["Call", ["Id", "abs"], ["Tuple", [["Const", -5]]]]')] + [AstDoc('A function or operator invocation.')] + [AstScriptExample('(abs -5)')] + [AstScriptExample('(SMA src 14)')] [AstField(0, 'callee', fkNode)] [AstField(1, 'args', fkTuple)] IFunctionCallNode = interface(IAstTypedNode) @@ -375,15 +356,14 @@ type end; [AstTag('Block')] - [AstDoc('Sequential execution of nodes.')] + [AstDoc('Sequential execution of nodes, returning the last result.')] [ - AstExample( + AstScriptExample( ''' - ["Block", ["Tuple", [ - ["Var", ["Id", "a"], ["Const", 1]], - ["Assign", ["Id", "a"], ["Call", ["Id", "+"], ["Tuple", [["Id", "a"], ["Const", 1]]]]], - ["Id", "a"] - ]]] + (do + (def x 10) + (assign x (* x 2)) + x) ''' ) ] @@ -396,8 +376,9 @@ type end; [AstTag('Var')] - [AstDoc('Variable declaration.')] - [AstExample('["Var", ["Id", "val"], ["Const", 0]]')] + [AstDoc('Declares a new variable in the current scope.')] + [AstScriptExample('(def x 10)')] + [AstScriptExample('(def y)')] [AstField(0, 'target', fkNode)] [AstField(1, 'init', fkNullableNode)] IVariableDeclarationNode = interface(IAstTypedNode) @@ -412,8 +393,8 @@ type end; [AstTag('Assign')] - [AstDoc('Variable assignment.')] - [AstExample('["Assign", ["Id", "x"], ["Const", 1]]')] + [AstDoc('Updates the value of an existing variable.')] + [AstScriptExample('(assign x 20)')] [AstField(0, 'target', fkNode)] [AstField(1, 'value', fkNode)] IAssignmentNode = interface(IAstTypedNode) @@ -426,15 +407,12 @@ type end; [AstTag('Macro')] - [AstDoc('Syntactic macro definition.')] + [AstDoc('Defines a compile-time syntactic macro.')] [ - AstExample( + AstScriptExample( ''' - ["Macro", - ["Id", "unless"], - ["Tuple", [["Id", "condition"], ["Id", "body"]]], - ["Quote", ["If", ["Id", "condition"], ["Nop"], ["Unquote", ["Id", "body"]]]] - ] + (defmacro unless [c b] + `(if ~c ... ~b)) ''' ) ] @@ -453,7 +431,8 @@ type end; [AstTag('Quote')] - [AstDoc('Suppresses evaluation of an expression.')] + [AstDoc('Quotes an expression to prevent immediate evaluation.')] + [AstScriptExample('`(+ 1 2)')] [AstField(0, 'expr', fkNode)] IQuasiquoteNode = interface(IAstNode) {$region 'private'} @@ -463,7 +442,8 @@ type end; [AstTag('Unquote')] - [AstDoc('Enables evaluation inside a quoted context.')] + [AstDoc('Evaluates an expression inside a quoted context.')] + [AstScriptExample('~x')] [AstField(0, 'expr', fkNode)] IUnquoteNode = interface(IAstNode) {$region 'private'} @@ -473,18 +453,19 @@ type end; [AstTag('Splice')] - [AstDoc('Splicing evaluation into a list.')] + [AstDoc('Unrolls a list into the surrounding quoted list.')] [AstField(0, 'expr', fkNode)] IUnquoteSplicingNode = interface(IAstNode) {$region 'private'} - function GetExpression: IQuasiquoteNode; + // BUGFIX: Should return IAstNode, not IQuasiquoteNode + function GetExpression: IAstNode; {$endregion} - property Expression: IQuasiquoteNode read GetExpression; + property Expression: IAstNode read GetExpression; end; [AstTag('Index')] - [AstDoc('Element access by position.')] - [AstExample('["Index", ["Id", "v"], ["Const", 0]]')] + [AstDoc('Positional access to a vector or series element.')] + [AstScriptExample('(get prices 0)')] [AstField(0, 'base', fkNode)] [AstField(1, 'index', fkNode)] IIndexerNode = interface(IAstTypedNode) @@ -497,8 +478,8 @@ type end; [AstTag('Member')] - [AstDoc('Field access by keyword.')] - [AstExample('["Member", ["Id", "rec"], ["Key", "price"]]')] + [AstDoc('Accesses a record field or stream member by key.')] + [AstScriptExample('(.price rec)')] [AstField(0, 'base', fkNode)] [AstField(1, 'member', fkNode)] IMemberAccessNode = interface(IAstTypedNode) @@ -511,17 +492,8 @@ type end; [AstTag('Record')] - [AstDoc('Defines a record literal.')] - [ - AstExample( - ''' - ["Record", ["Tuple", [ - ["Field", ["Key", "p"], ["Id", "p_val"]], - ["Field", ["Key", "q"], ["Const", 100]] - ]]] - ''' - ) - ] + [AstDoc('A literal key-value mapping.')] + [AstScriptExample('{:x 1 :y 2}')] [AstField(0, 'fields', fkTuple)] IRecordLiteralNode = interface(IAstTypedNode) {$region 'private'} @@ -535,8 +507,9 @@ type end; [AstTag('Series')] - [AstDoc('Stream schema definition.')] - [AstExample('["Series", ["Key", "Float"]]')] + [AstDoc('Defines a new data series schema.')] + [AstScriptExample('(new-series :Float)')] + [AstScriptExample('(new-series [[:p :Float] [:v :Ordinal]])')] [AstField(0, 'definition', fkNode)] ICreateSeriesNode = interface(IAstTypedNode) {$region 'private'} @@ -548,8 +521,9 @@ type end; [AstTag('Add')] - [AstDoc('Pushes data to a series.')] - [AstExample('["Add", ["Id", "s"], ["Const", 10]]')] + [AstDoc('Appends a value to a data series.')] + [AstScriptExample('(add-item s 10.5)')] + [AstScriptExample('(add-item s 10.5 100)')] [AstField(0, 'series', fkNode)] [AstField(1, 'value', fkNode)] [AstField(2, 'lookback', fkNullableNode)] @@ -565,8 +539,8 @@ type end; [AstTag('Count')] - [AstDoc('Series length.')] - [AstExample('["Count", ["Id", "src"]]')] + [AstDoc('Returns the current number of elements in a series.')] + [AstScriptExample('(count prices)')] [AstField(0, 'series', fkNode)] ISeriesLengthNode = interface(IAstTypedNode) {$region 'private'} @@ -576,8 +550,8 @@ type end; [AstTag('Recur')] - [AstDoc('Tail-recursive jump.')] - [AstExample('["Recur", ["Tuple", [["Call", ["Id", "-"], ["Tuple", [["Id", "i"], ["Const", 1]]]]]]]')] + [AstDoc('Tail-recursive call to the current function.')] + [AstScriptExample('(recur (- n 1))')] [AstField(0, 'args', fkTuple)] IRecurNode = interface(IAstTypedNode) {$region 'private'} @@ -589,18 +563,10 @@ type [AstTag('Pipe')] [AstDoc('Reactive pipeline from source streams to a transformation lambda.')] [ - AstExample( + AstScriptExample( ''' - ["Pipe", - ["Tuple", [ - ["Tuple", [["Id", "src"], ["Tuple", [["Key", "price"]]]]] - ]], - ["Fn", ["Tuple", [["Id", "p"]]], - ["Record", ["Tuple", [ - ["Field", ["Key", "res"], ["Call", ["Id", "*"], ["Tuple", [["Id", "p"], ["Const", 2]]]]] - ]]] - ] - ] + (pipe [[src [:val]]] + (fn [v] {:res (* v 2)})) ''' ) ] @@ -946,12 +912,12 @@ type TUnquoteSplicingNode = class(TAstNode, IUnquoteSplicingNode) private - FExpression: IQuasiquoteNode; - function GetExpression: IQuasiquoteNode; + FExpression: IAstNode; // Changed to IAstNode + function GetExpression: IAstNode; protected function GetKind: TAstNodeKind; override; public - constructor Create(const AExpression: IQuasiquoteNode; const AIdentity: IAstIdentity); + constructor Create(const AExpression: IAstNode; const AIdentity: IAstIdentity); function AsUnquoteSplicing: IUnquoteSplicingNode; override; end; @@ -1859,7 +1825,7 @@ end; { TUnquoteSplicingNode } -constructor TUnquoteSplicingNode.Create(const AExpression: IQuasiquoteNode; const AIdentity: IAstIdentity); +constructor TUnquoteSplicingNode.Create(const AExpression: IAstNode; const AIdentity: IAstIdentity); begin inherited Create(AIdentity); FExpression := AExpression; @@ -1869,7 +1835,7 @@ function TUnquoteSplicingNode.GetKind: TAstNodeKind; begin Result := akUnquoteSplicing; end; -function TUnquoteSplicingNode.GetExpression: IQuasiquoteNode; +function TUnquoteSplicingNode.GetExpression: IAstNode; begin Result := FExpression; end;