From 8b765487aed49aa1238a730901eb4cfe2375d0a4 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Sun, 21 Dec 2025 16:30:09 +0100 Subject: [PATCH] Implementing Pipes --- Src/AST/Myc.Ast.Evaluator.pas | 37 ++- Src/AST/Myc.Ast.Nodes.pas | 120 +++++++-- Src/AST/Myc.Ast.Script.pas | 408 ++++++++++++++--------------- Src/AST/Myc.Ast.Visitor.pas | 153 ++++++++++- Src/AST/Myc.Ast.pas | 126 ++------- Src/Data/Myc.Data.Stream.Pipes.pas | 7 +- 6 files changed, 515 insertions(+), 336 deletions(-) diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index 809b068..84d0304 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -35,7 +35,7 @@ type function VisitRecordField(const Node: IRecordFieldNode): TDataValue; virtual; function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; virtual; - function VisitCondExpression(const Node: ICondExpressionNode): TDataValue; virtual; // Replaces Ternary + function VisitCondExpression(const Node: ICondExpressionNode): TDataValue; virtual; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; virtual; function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; virtual; function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue; virtual; @@ -55,6 +55,12 @@ type function VisitRecurNode(const Node: IRecurNode): TDataValue; virtual; function VisitNop(const Node: INopNode): TDataValue; virtual; + // Pipe Support + function VisitPipeInput(const Node: IPipeInputNode): TDataValue; virtual; + function VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; virtual; + function VisitPipeInputList(const Node: IPipeInputList): TDataValue; virtual; + function VisitPipe(const Node: IPipeNode): TDataValue; virtual; + function IsTruthy(const AValue: TDataValue): Boolean; inline; // Returns a closure that can create the correct type of visitor for a lambda's body. @@ -627,4 +633,33 @@ begin Result := TDataValue.Void; end; +// --- Pipe Visitors Stubs --- + +function TEvaluatorVisitor.VisitPipe(const Node: IPipeNode): TDataValue; +begin + // Placeholder for Phase 4 (Runtime Integration) + // Here we will: + // 1. Resolve Input Streams from Scope + // 2. Build TPipeConfig + // 3. Compile Lambda to TDataValue.TFunc + // 4. Create Adapter + // 5. Create TPipeStream + raise EEvaluatorException.Create('Pipe execution is not yet implemented.'); +end; + +function TEvaluatorVisitor.VisitPipeInput(const Node: IPipeInputNode): TDataValue; +begin + Result := TDataValue.Void; +end; + +function TEvaluatorVisitor.VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; +begin + Result := TDataValue.Void; +end; + +function TEvaluatorVisitor.VisitPipeInputList(const Node: IPipeInputList): TDataValue; +begin + Result := TDataValue.Void; +end; + end. diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index a8fdaf6..36fb057 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -111,6 +111,12 @@ type IRecurNode = interface; INopNode = interface; + // Pipes + IPipeSelectorList = interface; + IPipeInputNode = interface; + IPipeInputList = interface; + IPipeNode = interface; + // Node Kinds & Helpers TAstNodeKind = ( akConstant, @@ -145,6 +151,7 @@ type // Pipes akPipe, akPipeInput, + akPipeSelectorList, akPipeInputList ); @@ -204,6 +211,12 @@ type function AsRecur: IRecurNode; function AsNop: INopNode; + // Pipes + function AsPipeInput: IPipeInputNode; + function AsPipeSelectorList: IPipeSelectorList; + function AsPipeInputList: IPipeInputList; + function AsPipe: IPipeNode; + property IsTyped: Boolean read GetIsTyped; property Kind: TAstNodeKind read GetKind; property Identity: IAstIdentity read GetIdentity; @@ -456,16 +469,18 @@ type property Series: IIdentifierNode read GetSeries; end; - // Represents a single input definition within a pipe: "btc [:Close]" - // - StreamSource: The identifier of the stream (e.g., "btc") - // - Selector: Optional keyword path to select a field (e.g., :Close) + // A list of selector keywords [ :A :B ] + IPipeSelectorList = interface(INodeList) + end; + + // Represents a single input definition within a pipe: "btc [:Close :Open]" IPipeInputNode = interface(IAstTypedNode) {$region 'private'} function GetStreamSource: IIdentifierNode; - function GetSelector: IKeywordNode; // Can be nil if the whole record is used + function GetSelectors: IPipeSelectorList; {$endregion} property StreamSource: IIdentifierNode read GetStreamSource; - property Selector: IKeywordNode read GetSelector; + property Selectors: IPipeSelectorList read GetSelectors; end; // A list of pipe inputs @@ -524,11 +539,11 @@ type function VisitRecurNode(const Node: IRecurNode): TDataValue; function VisitNop(const Node: INopNode): TDataValue; - // Pipes - // Not implemented yet. that's ok, it just needs to compile - // function VisitPipeInput(const Node: IPipeInputNode): TDataValue; - // function VisitPipeInputList(const Node: IPipeInputList): TDataValue; - // function VisitPipe(const Node: IPipeNode): TDataValue; + // Pipes + function VisitPipeInput(const Node: IPipeInputNode): TDataValue; + function VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; + function VisitPipeInputList(const Node: IPipeInputList): TDataValue; + function VisitPipe(const Node: IPipeNode): TDataValue; end; IEvaluatorVisitor = interface(IAstVisitor) @@ -579,6 +594,11 @@ type function AsRecur: IRecurNode; virtual; function AsNop: INopNode; virtual; + function AsPipeInput: IPipeInputNode; virtual; + function AsPipeSelectorList: IPipeSelectorList; virtual; + function AsPipeInputList: IPipeInputList; virtual; + function AsPipe: IPipeNode; virtual; + function AsTypedNode: IAstTypedNode; virtual; property IsTyped: Boolean read GetIsTyped; @@ -1048,22 +1068,31 @@ type function AsNop: INopNode; override; end; + TPipeSelectorList = class(TAstNodeList, IPipeSelectorList) + protected + function GetKind: TAstNodeKind; override; + public + function Accept(const Visitor: IAstVisitor): TDataValue; override; + function AsPipeSelectorList: IPipeSelectorList; override; + end; + TPipeInputNode = class(TAstTypedNode, IPipeInputNode) private FStreamSource: IIdentifierNode; - FSelector: IKeywordNode; + FSelectors: IPipeSelectorList; function GetStreamSource: IIdentifierNode; - function GetSelector: IKeywordNode; + function GetSelectors: IPipeSelectorList; protected function GetKind: TAstNodeKind; override; public constructor Create( const AStreamSource: IIdentifierNode; - const ASelector: IKeywordNode; + const ASelectors: IPipeSelectorList; const AIdentity: IAstIdentity; const AStaticType: IStaticType ); function Accept(const Visitor: IAstVisitor): TDataValue; override; + function AsPipeInput: IPipeInputNode; override; end; TPipeInputList = class(TAstNodeList, IPipeInputList) @@ -1071,6 +1100,7 @@ type function GetKind: TAstNodeKind; override; public function Accept(const Visitor: IAstVisitor): TDataValue; override; + function AsPipeInputList: IPipeInputList; override; end; TPipeNode = class(TAstTypedNode, IPipeNode) @@ -1089,6 +1119,7 @@ type const AStaticType: IStaticType ); function Accept(const Visitor: IAstVisitor): TDataValue; override; + function AsPipe: IPipeNode; override; end; implementation @@ -1375,6 +1406,23 @@ begin Result := nil; end; +function TAstNode.AsPipeInput: IPipeInputNode; +begin + Result := nil; +end; +function TAstNode.AsPipeSelectorList: IPipeSelectorList; +begin + Result := nil; +end; +function TAstNode.AsPipeInputList: IPipeInputList; +begin + Result := nil; +end; +function TAstNode.AsPipe: IPipeNode; +begin + Result := nil; +end; + { TAstTypedNode } constructor TAstTypedNode.Create(const AStaticType: IStaticType; const AIdentity: IAstIdentity); @@ -2366,23 +2414,45 @@ begin Inc(FIndex); end; +{ TPipeSelectorList } + +function TPipeSelectorList.Accept(const Visitor: IAstVisitor): TDataValue; +begin + Result := Visitor.VisitPipeSelectorList(Self); +end; + +function TPipeSelectorList.AsPipeSelectorList: IPipeSelectorList; +begin + Result := Self; +end; + +function TPipeSelectorList.GetKind: TAstNodeKind; +begin + Result := akPipeSelectorList; +end; + { TPipeInputNode } constructor TPipeInputNode.Create( const AStreamSource: IIdentifierNode; - const ASelector: IKeywordNode; + const ASelectors: IPipeSelectorList; const AIdentity: IAstIdentity; const AStaticType: IStaticType ); begin inherited Create(AStaticType, AIdentity); FStreamSource := AStreamSource; - FSelector := ASelector; + FSelectors := ASelectors; end; function TPipeInputNode.Accept(const Visitor: IAstVisitor): TDataValue; begin - // Result := Visitor.VisitPipeInput(Self); + Result := Visitor.VisitPipeInput(Self); +end; + +function TPipeInputNode.AsPipeInput: IPipeInputNode; +begin + Result := Self; end; function TPipeInputNode.GetKind: TAstNodeKind; @@ -2390,9 +2460,9 @@ begin Result := akPipeInput; end; -function TPipeInputNode.GetSelector: IKeywordNode; +function TPipeInputNode.GetSelectors: IPipeSelectorList; begin - Result := FSelector; + Result := FSelectors; end; function TPipeInputNode.GetStreamSource: IIdentifierNode; @@ -2404,7 +2474,12 @@ end; function TPipeInputList.Accept(const Visitor: IAstVisitor): TDataValue; begin - // Result := Visitor.VisitPipeInputList(Self); + Result := Visitor.VisitPipeInputList(Self); +end; + +function TPipeInputList.AsPipeInputList: IPipeInputList; +begin + Result := Self; end; function TPipeInputList.GetKind: TAstNodeKind; @@ -2428,7 +2503,12 @@ end; function TPipeNode.Accept(const Visitor: IAstVisitor): TDataValue; begin - // Result := Visitor.VisitPipe(Self); + Result := Visitor.VisitPipe(Self); +end; + +function TPipeNode.AsPipe: IPipeNode; +begin + Result := Self; end; function TPipeNode.GetInputs: IPipeInputList; diff --git a/Src/AST/Myc.Ast.Script.pas b/Src/AST/Myc.Ast.Script.pas index 3d54aad..7a36c30 100644 --- a/Src/AST/Myc.Ast.Script.pas +++ b/Src/AST/Myc.Ast.Script.pas @@ -9,7 +9,6 @@ uses Myc.Ast.Visitor; type - // Exception for parsing errors containing location information EParserException = class(EAstException) strict private FLine: Integer; @@ -20,67 +19,6 @@ type property Col: Integer read FCol; end; -var - // This BNF should always be kept up to date and valid. - BNF: String = - ''' - (* ---------------------------------------------------------------------- *) - (* ---- Main Productions (Start Symbols) ---- *) - (* ---------------------------------------------------------------------- *) - program ::= expression - expression ::= atom | list | record_literal | reader_macro - - (* ---------------------------------------------------------------------- *) - (* ---- Atoms (Basic Values) ---- *) - (* ---------------------------------------------------------------------- *) - atom ::= number | string | identifier | keyword - - (* ---------------------------------------------------------------------- *) - (* ---- Reader-Macros (Syntactic Sugar) ---- *) - (* ---------------------------------------------------------------------- *) - reader_macro ::= "'" expression (* (quote ...) *) - | "`" expression (* (quasiquote ...) *) - | "~" expression (* (unquote ...) *) - | "~@" expression (* (unquote-splicing ...) *) - - (* ---------------------------------------------------------------------- *) - (* ---- Lists (S-Expressions) ---- *) - (* ---------------------------------------------------------------------- *) - list ::= "(" list_content ")" - list_content ::= special_form | function_call - - special_form ::= "if" expression expression expression? - | "?" (expression expression)* expression (* cond1 branch1 ... else *) - | "def" identifier expression? - | "defmacro" identifier parameter_list expression - | "assign" identifier expression - | "fn" parameter_list expression - | "do" expression* - | "recur" expression* - | "get" expression expression - | dot_identifier expression - - function_call ::= expression expression* - - (* ---------------------------------------------------------------------- *) - (* ---- Parameter Lists and Records ---- *) - (* ---------------------------------------------------------------------- *) - parameter_list ::= "[" identifier* "]" - record_literal ::= "{" (keyword expression)* "}" - - (* ---------------------------------------------------------------------- *) - (* ---- Terminals (Lexer Tokens) ---- *) - (* ---------------------------------------------------------------------- *) - number ::= ["-"] digit+ ["." digit+] - string ::= '"' ( ? any char except \ or " ? | '\' ( '"' | '\' | 'n' | 'r' | 't' ) )* '"' - keyword ::= ":" identifier - dot_identifier ::= "." identifier - identifier ::= ? any sequence of chars not containing whitespace, '()[]{}'`~:; ? - digit ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" - '''; - -type - // Provides a high-level facade for parsing and printing the AST. TAstScript = record public class function Parse(const ASource: string): IAstNode; static; @@ -100,19 +38,17 @@ uses Myc.Ast.Identities; type - // --- Internal Parser Implementation --- - TTokenKind = ( - tkLeftParen, // ( - tkRightParen, // ) - tkLeftBracket, // [ - tkRightBracket, // ] - tkLeftBrace, // { - tkRightBrace, // } - tkQuote, // ' - tkBacktick, // ` - tkTilde, // ~ - tkAt, // @ + tkLeftParen, + tkRightParen, + tkLeftBracket, + tkRightBracket, + tkLeftBrace, + tkRightBrace, + tkQuote, + tkBacktick, + tkTilde, + tkAt, tkIdentifier, tkKeyword, tkNumber, @@ -157,7 +93,6 @@ type TExpr = record Token: TToken; Node: IAstNode; - Params: TArray; end; TParser = class @@ -168,18 +103,19 @@ type procedure ErrorFmt(const Msg: string; const Args: array of const); procedure Consume(AExpectedKind: TTokenKind); procedure NextToken; + function ParseList: IAstNode; function ParseRecordLiteral: IAstNode; - function ParseParameterList: TArray; + function ParseVector: IAstNode; function ParseExpression: TExpr; + + function ExtractIdentifiers(const Node: IAstNode; const Context: string): TArray; public constructor Create(const ASource: string); destructor Destroy; override; function Parse: IAstNode; end; - // --- Internal Printer Implementation --- - TPrettyPrintVisitor = class(TAstVisitor) private FBuilder: TStringBuilder; @@ -198,7 +134,6 @@ type function VisitIdentifier(const Node: IIdentifierNode): TVoid; override; function VisitKeyword(const Node: IKeywordNode): TVoid; override; - // List Visitors function VisitParameterList(const Node: IParameterList): TVoid; override; function VisitArgumentList(const Node: IArgumentList): TVoid; override; function VisitExpressionList(const Node: IExpressionList): TVoid; override; @@ -225,8 +160,14 @@ type function VisitSeriesLength(const Node: ISeriesLengthNode): TVoid; override; function VisitRecurNode(const Node: IRecurNode): TVoid; override; function VisitNop(const Node: INopNode): TVoid; override; + + function VisitPipeInput(const Node: IPipeInputNode): TVoid; override; + function VisitPipeSelectorList(const Node: IPipeSelectorList): TVoid; override; + function VisitPipeInputList(const Node: IPipeInputList): TVoid; override; + function VisitPipe(const Node: IPipeNode): TVoid; override; end; +{ TTokenKindHelper } function TTokenKindHelper.ToString: String; begin case Self of @@ -250,13 +191,13 @@ begin end; end; +{ TToken } function TToken.GetLocation: ISourceLocation; begin Result := TIdentities.Location(Line, Col); end; { EParserException } - constructor EParserException.Create(const AMessage: string; ALine, ACol: Integer); begin inherited CreateFmt('[Line %d, Col %d] %s', [ALine, ACol, AMessage]); @@ -265,7 +206,6 @@ begin end; { TLexer } - constructor TLexer.Create(const ASource: string); begin inherited Create; @@ -342,7 +282,7 @@ var builder: TStringBuilder; c: Char; begin - Advance; // Skip opening " + Advance; builder := TStringBuilder.Create; try while (FCurrentPos <= Length(FSource)) do @@ -350,18 +290,18 @@ begin c := Peek; if c = '"' then begin - Advance; // Consume closing " + Advance; Result := builder.ToString; exit; end; if c = '\' then begin - Advance; // Consume '\' + Advance; if FCurrentPos > Length(FSource) then Error('Syntax Error: String literal ends with an escape character.'); - c := Peek; // Get escaped char + c := Peek; case c of '"': builder.Append('"'); '\': builder.Append('\'); @@ -371,7 +311,7 @@ begin else builder.Append(c); end; - Advance; // Consume escaped char + Advance; end else begin @@ -494,9 +434,7 @@ begin end; end; -// ----------------------------------------------------------------------------- -// IMPLEMENTATION: Parser -// ----------------------------------------------------------------------------- +{ TParser } constructor TParser.Create(const ASource: string); begin @@ -533,30 +471,48 @@ begin NextToken; end; -function TParser.ParseParameterList: TArray; +function TParser.ParseVector: IAstNode; var - params: TList; + items: TList; + startLoc: ISourceLocation; begin + startLoc := FCurrentToken.GetLocation; Consume(tkLeftBracket); - params := TList.Create; + items := TList.Create; try while FCurrentToken.Kind <> tkRightBracket do begin if FCurrentToken.Kind = tkEOF then - Error('Syntax Error: Unexpected end of file.'); - if FCurrentToken.Kind <> tkIdentifier then - Error('Syntax Error: Expected identifier in parameter list.'); - - params.Add(TAst.Identifier(FCurrentToken.Text, FCurrentToken.GetLocation)); - NextToken; + Error('Syntax Error: Unexpected end of file in vector.'); + items.Add(ParseExpression.Node); end; - Result := params.ToArray; + + var id := TIdentities.List('[', ']', ' ', startLoc); + Result := TArgumentList.Create(items.ToArray, id); finally - params.Free; + items.Free; end; Consume(tkRightBracket); end; +function TParser.ExtractIdentifiers(const Node: IAstNode; const Context: string): TArray; +var + list: IArgumentList; + i: Integer; +begin + if Node.Kind <> akArgumentList then + ErrorFmt('%s expects a vector [...]', [Context]); + + list := Node.AsArgumentList; + SetLength(Result, list.Count); + for i := 0 to list.Count - 1 do + begin + if list[i].Kind <> akIdentifier then + ErrorFmt('%s vector must contain only identifiers.', [Context]); + Result[i] := list[i].AsIdentifier; + end; +end; + function TParser.ParseRecordLiteral: IAstNode; var fields: TList; @@ -589,7 +545,6 @@ begin fields.Add(TAst.RecordField(keyNode, fieldValue, keyToken.GetLocation)); end; - Result := TAst.RecordLiteral(fields.ToArray, startLoc); finally fields.Free; @@ -602,7 +557,6 @@ var elements: TList; head: TExpr; tailNodes: TArray; - initializer: IAstNode; startLoc: ISourceLocation; begin startLoc := FCurrentToken.GetLocation; @@ -620,25 +574,89 @@ begin end; head := elements[0]; - SetLength(tailNodes, elements.Count - 1); for var i := 0 to High(tailNodes) do tailNodes[i] := elements[i + 1].Node; if head.Token.Kind = tkIdentifier then begin - if SameText(head.Token.Text, 'if') then + if SameText(head.Token.Text, 'pipe') then begin - if not (Length(tailNodes) in [2, 3]) then - Error('Syntax Error in if statement.'); - var elseBranch: IAstNode := nil; + // (pipe [stream1 [:Key1 :Key2] stream2 [:Key3]] (fn ...)) + if Length(tailNodes) <> 2 then + Error('Syntax Error: ''pipe'' requires [inputs] vector and (fn) transformation.'); + + if tailNodes[0].Kind <> akArgumentList then + Error('Syntax Error: ''pipe'' first argument must be a vector [...].'); + var rawInputs := tailNodes[0].AsArgumentList; + if (rawInputs.Count mod 2) <> 0 then + Error('Syntax Error: Pipe inputs must be pairs of Identifier and Selector Vector (e.g. [btc [:Close]]).'); + + var pipeInputs := TList.Create; + try + var k := 0; + while k < rawInputs.Count do + begin + if rawInputs[k].Kind <> akIdentifier then + Error('Syntax Error: Pipe input stream must be an identifier.'); + + var selNode := rawInputs[k + 1]; + var streamId := rawInputs[k].AsIdentifier; + var selKeywords: TArray; + + // Strict format: Selector must be a vector [...] containing keywords + if selNode.Kind <> akArgumentList then + Error('Syntax Error: Pipe selector must be a vector of keywords (e.g. [:Close :High]).'); + + var selList := selNode.AsArgumentList; + if selList.Count = 0 then + Error('Syntax Error: Selector list cannot be empty.'); + + SetLength(selKeywords, selList.Count); + for var m := 0 to selList.Count - 1 do + begin + if selList[m].Kind <> akKeyword then + Error('Syntax Error: Selector vector must contain only keywords.'); + selKeywords[m] := selList[m].AsKeyword; + end; + + var selectorList := TAst.PipeSelectorList(selKeywords, selNode.Identity.Location); + pipeInputs.Add(TAst.PipeInput(streamId, selectorList, streamId.Identity.Location)); + + Inc(k, 2); + end; + + if tailNodes[1].Kind <> akLambdaExpression then + Error('Syntax Error: Pipe transformation must be a lambda (fn).'); + + Result := TAst.Pipe(pipeInputs.ToArray, tailNodes[1].AsLambdaExpression, startLoc); + finally + pipeInputs.Free; + end; + end + else if SameText(head.Token.Text, 'fn') then + begin + if Length(tailNodes) <> 2 then + Error('Syntax Error: ''fn'' requires a parameter list and a body.'); + var params := ExtractIdentifiers(tailNodes[0], 'fn'); + Result := TAst.LambdaExpr(params, tailNodes[1], startLoc); + end + else if SameText(head.Token.Text, 'defmacro') then + begin + if (Length(tailNodes) <> 3) or (elements[1].Token.Kind <> tkIdentifier) then + Error('Syntax Error: ''defmacro'' requires name, params, body.'); + var params := ExtractIdentifiers(tailNodes[1], 'defmacro'); + Result := TAst.MacroDef(tailNodes[0].AsIdentifier, params, tailNodes[2], startLoc); + end + else if SameText(head.Token.Text, 'if') then + begin + var elseNode: IAstNode := nil; if Length(tailNodes) = 3 then - elseBranch := tailNodes[2]; - Result := TAst.IfExpr(tailNodes[0], tailNodes[1], elseBranch, startLoc); + elseNode := tailNodes[2]; + Result := TAst.IfExpr(tailNodes[0], tailNodes[1], elseNode, startLoc); end else if SameText(head.Token.Text, '?') then begin - // (? test1 branch1 test2 branch2 ... else) var count := Length(tailNodes); if (count < 1) or ((count mod 2) = 0) then Error('Syntax Error: ''?'' (cond) requires an odd number of arguments.'); @@ -653,39 +671,15 @@ begin end else if SameText(head.Token.Text, 'def') then begin - if not (Length(tailNodes) in [1, 2]) then - Error('Syntax Error: ''def'' requires a target and an optional initializer.'); - initializer := nil; + var initializer: IAstNode := nil; if Length(tailNodes) = 2 then initializer := tailNodes[1]; Result := TAst.VarDecl(tailNodes[0], initializer, startLoc); end - else if SameText(head.Token.Text, 'defmacro') then - begin - if (Length(tailNodes) <> 3) or (elements[1].Token.Kind <> tkIdentifier) then - Error('Syntax Error: ''defmacro'' requires a name, a parameter list, and a body.'); - - var macroName := tailNodes[0].AsIdentifier; - var macroParams := elements[2].Params; // Parameter list parsing happens in ParseExpression for brackets - - if tailNodes[2].Kind <> akQuasiquote then - Error('Syntax Error: Expected a quasiquote as macro body.'); - - Result := TAst.MacroDef(macroName, macroParams, tailNodes[2], startLoc); - end else if SameText(head.Token.Text, 'assign') then begin - if Length(tailNodes) <> 2 then - Error('Syntax Error: ''assign'' requires exactly 2 arguments.'); Result := TAst.Assign(tailNodes[0], tailNodes[1], startLoc); end - else if SameText(head.Token.Text, 'fn') then - begin - if Length(tailNodes) <> 2 then - Error('Syntax Error: ''fn'' requires a parameter list and a body.'); - // elements[1] is the parameter list node which contains the parsed Params array - Result := TAst.LambdaExpr(elements[1].Params, tailNodes[1], startLoc); - end else if SameText(head.Token.Text, 'do') then begin Result := TAst.Block(tailNodes, startLoc) @@ -696,57 +690,32 @@ begin end else if SameText(head.Token.Text, 'get') then begin - if Length(tailNodes) <> 2 then - Error('Syntax Error: ''get'' requires exactly 2 arguments (base and index).'); Result := TAst.Indexer(tailNodes[0], tailNodes[1], startLoc) end - // --- NEW: Support for new-series --- else if SameText(head.Token.Text, 'new-series') then begin - if Length(tailNodes) <> 1 then - Error('Syntax Error: ''new-series'' requires exactly 1 argument (definition string).'); - - // Extract definition string from constant node if (tailNodes[0].Kind = akConstant) and (tailNodes[0].AsConstant.Value.Kind = vkText) then Result := TAst.CreateSeries(tailNodes[0].AsConstant.Value.AsText, startLoc) else Error('Syntax Error: ''new-series'' argument must be a string literal.'); end - // --- NEW: Support for add-item --- else if SameText(head.Token.Text, 'add-item') then begin - if not (Length(tailNodes) in [2, 3]) then - Error('Syntax Error: ''add-item'' requires 2 or 3 arguments (series, value, [lookback]).'); - var lookback: IAstNode := nil; if Length(tailNodes) = 3 then lookback := tailNodes[2]; - - if tailNodes[0].Kind <> akIdentifier then - Error('Syntax Error: ''add-item'' first argument must be a series identifier.'); - Result := TAst.AddSeriesItem(tailNodes[0].AsIdentifier, tailNodes[1], lookback, startLoc); end - // --- NEW: Support for count (SeriesLength) --- else if SameText(head.Token.Text, 'count') then begin - if Length(tailNodes) <> 1 then - Error('Syntax Error: ''count'' requires exactly 1 argument (series).'); - - if tailNodes[0].Kind <> akIdentifier then - Error('Syntax Error: ''count'' argument must be a series identifier.'); - Result := TAst.SeriesLength(tailNodes[0].AsIdentifier, startLoc); end else if (Length(head.Token.Text) > 1) and (head.Token.Text.StartsWith('.')) then begin - if Length(tailNodes) <> 1 then - ErrorFmt( - 'Syntax Error: Member access (e.g., ''%s'') requires exactly 1 argument (the base object).', - [head.Token.Text] - ); Result := TAst.MemberAccess(tailNodes[0], TAst.Keyword(head.Token.Text.Substring(1), head.Token.GetLocation), startLoc); - end; + end + else + Result := nil; end; if Result = nil then @@ -755,26 +724,28 @@ begin finally elements.Free; end; - Consume(tkRightParen); end; function TParser.ParseExpression: TExpr; var + startLoc: ISourceLocation; i64: Int64; dbl: Double; - expr: TExpr; - startLoc: ISourceLocation; begin Result.Token := FCurrentToken; startLoc := FCurrentToken.GetLocation; case FCurrentToken.Kind of + tkLeftBracket: + begin + Result.Node := ParseVector; + exit; + end; tkBacktick: begin NextToken; - expr := ParseExpression; - Result.Node := TAst.Quasiquote(expr.Node, startLoc); + Result.Node := TAst.Quasiquote(ParseExpression.Node, startLoc); exit; end; tkTilde: @@ -783,26 +754,20 @@ begin if FCurrentToken.Kind = tkAt then begin NextToken; - expr := ParseExpression; - Result.Node := TAst.UnquoteSplicing(expr.Node.AsQuasiquote, startLoc); + Result.Node := TAst.UnquoteSplicing(ParseExpression.Node.AsQuasiquote, startLoc); end else - begin - expr := ParseExpression; - Result.Node := TAst.Unquote(expr.Node, startLoc); - end; + Result.Node := TAst.Unquote(ParseExpression.Node, startLoc); exit; end; tkQuote: begin NextToken; - expr := ParseExpression; - Result.Node := TAst.FunctionCall(TAst.Identifier('quote', startLoc), [expr.Node], startLoc); + Result.Node := TAst.FunctionCall(TAst.Identifier('quote', startLoc), [ParseExpression.Node], startLoc); exit; end; end; - Result.Token := FCurrentToken; case FCurrentToken.Kind of tkNumber: begin @@ -811,7 +776,7 @@ begin else if TryStrToFloat(FCurrentToken.Text, dbl, TFormatSettings.Invariant) then Result.Node := TAst.Constant(dbl, startLoc) else - ErrorFmt('Syntax Error: Invalid number format "%s"', [FCurrentToken.Text]); + Error('Invalid number'); NextToken; end; tkString: @@ -833,11 +798,9 @@ begin NextToken; end; tkLeftParen: Result.Node := ParseList; - tkLeftBracket: Result.Params := ParseParameterList; tkLeftBrace: Result.Node := ParseRecordLiteral; - tkEOF: {nop}; else - ErrorFmt('Syntax Error: Unexpected token %s', [FCurrentToken.Kind.ToString]); + Error('Unexpected token'); end; end; @@ -845,7 +808,7 @@ function TParser.Parse: IAstNode; begin var expr := ParseExpression; if FCurrentToken.Kind <> tkEOF then - Error('Syntax Error: Unexpected characters after end of expression.'); + Error('Unexpected chars after end.'); Result := expr.Node; end; @@ -861,7 +824,7 @@ end; destructor TPrettyPrintVisitor.Destroy; begin FBuilder.Free; - inherited Destroy; + inherited; end; function TPrettyPrintVisitor.GetResult: string; @@ -871,12 +834,12 @@ end; procedure TPrettyPrintVisitor.Indent; begin - inc(FIndentLevel, 2); + Inc(FIndentLevel, 2); end; procedure TPrettyPrintVisitor.Unindent; begin - dec(FIndentLevel, 2); + Dec(FIndentLevel, 2); end; procedure TPrettyPrintVisitor.Append(const S: string); @@ -897,15 +860,56 @@ begin Result := TDataValue.Void; end; -function TPrettyPrintVisitor.VisitConstant(const Node: IConstantNode): TVoid; -var - val: TDataValue; +function TPrettyPrintVisitor.VisitPipeInput(const Node: IPipeInputNode): TVoid; begin - val := Node.Value; - if val.Kind = vkText then - Append('"' + val.AsText + '"') + Node.StreamSource.Accept(Self); + Append(' '); + // Printer Logic for Vector syntax + Node.Selectors.Accept(Self); // Selectors is IPipeSelectorList which is visited as list +end; + +function TPrettyPrintVisitor.VisitPipeSelectorList(const Node: IPipeSelectorList): TVoid; +begin + Append('['); + for var i := 0 to Node.Count - 1 do + begin + if i > 0 then + Append(' '); + Node[i].Accept(Self); + end; + Append(']'); +end; + +function TPrettyPrintVisitor.VisitPipeInputList(const Node: IPipeInputList): TVoid; +begin + Append('['); + for var i := 0 to Node.Count - 1 do + begin + if i > 0 then + Append(' '); + Node[i].Accept(Self); + end; + Append(']'); +end; + +function TPrettyPrintVisitor.VisitPipe(const Node: IPipeNode): TVoid; +begin + Append('(pipe '); + Node.Inputs.Accept(Self); + Indent; + NewLine; + Node.Transformation.Accept(Self); + Unindent; + NewLine; + Append(')'); +end; + +function TPrettyPrintVisitor.VisitConstant(const Node: IConstantNode): TVoid; +begin + if Node.Value.Kind = vkText then + Append('"' + Node.Value.AsText + '"') else - Append(val.ToString); + Append(Node.Value.ToString); end; function TPrettyPrintVisitor.VisitIdentifier(const Node: IIdentifierNode): TVoid; @@ -918,7 +922,10 @@ begin Append(':' + Node.Value.Name); end; -// --- List Visitors Implementation --- +function TPrettyPrintVisitor.VisitNop(const Node: INopNode): TVoid; +begin + Append('...'); +end; function TPrettyPrintVisitor.VisitParameterList(const Node: IParameterList): TVoid; begin @@ -934,10 +941,9 @@ end; function TPrettyPrintVisitor.VisitArgumentList(const Node: IArgumentList): TVoid; begin - // Argument list does not have brackets of its own in Lisp call syntax for var i := 0 to Node.Count - 1 do begin - Append(' '); // Space before each argument + Append(' '); Node[i].Accept(Self); end; end; @@ -973,8 +979,6 @@ begin Node.Value.Accept(Self); end; -// --- Node Visitors --- - function TPrettyPrintVisitor.VisitIfExpression(const Node: IIfExpressionNode): TVoid; begin Append('(if '); @@ -1003,7 +1007,6 @@ begin Append(' '); pair.Branch.Accept(Self); end; - // Else NewLine; Node.ElseBranch.Accept(Self); Unindent; @@ -1063,10 +1066,9 @@ begin Node.Arguments[0].Accept(Self); exit; end; - Append('('); Node.Callee.Accept(Self); - Node.Arguments.Accept(Self); // Prints separated by space + Node.Arguments.Accept(Self); Append(')'); end; @@ -1135,7 +1137,6 @@ begin Append('{}'); exit; end; - Append('{'); Node.Fields.Accept(Self); Append('}'); @@ -1160,11 +1161,6 @@ begin Append(')'); end; -function TPrettyPrintVisitor.VisitNop(const Node: INopNode): TVoid; -begin - Append('...'); -end; - function TPrettyPrintVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TVoid; begin Append('(count '); diff --git a/Src/AST/Myc.Ast.Visitor.pas b/Src/AST/Myc.Ast.Visitor.pas index eca479b..ad41787 100644 --- a/Src/AST/Myc.Ast.Visitor.pas +++ b/Src/AST/Myc.Ast.Visitor.pas @@ -26,7 +26,7 @@ type function IAstVisitor.VisitRecordField = DoVisitRecordField; function IAstVisitor.VisitIfExpression = DoVisitIfExpression; - function IAstVisitor.VisitCondExpression = DoVisitCondExpression; // Updated + function IAstVisitor.VisitCondExpression = DoVisitCondExpression; function IAstVisitor.VisitLambdaExpression = DoVisitLambdaExpression; function IAstVisitor.VisitFunctionCall = DoVisitFunctionCall; function IAstVisitor.VisitMacroExpansionNode = DoVisitMacroExpansionNode; @@ -46,6 +46,12 @@ type function IAstVisitor.VisitRecurNode = DoVisitRecurNode; function IAstVisitor.VisitNop = DoVisitNop; + // Pipe Support + function IAstVisitor.VisitPipeInput = DoVisitPipeInput; + function IAstVisitor.VisitPipeSelectorList = DoVisitPipeSelectorList; + function IAstVisitor.VisitPipeInputList = DoVisitPipeInputList; + function IAstVisitor.VisitPipe = DoVisitPipe; + // Private bridge method implementations function DoVisitConstant(const Node: IConstantNode): TDataValue; function DoVisitIdentifier(const Node: IIdentifierNode): TDataValue; @@ -58,7 +64,7 @@ type function DoVisitRecordField(const Node: IRecordFieldNode): TDataValue; function DoVisitIfExpression(const Node: IIfExpressionNode): TDataValue; - function DoVisitCondExpression(const Node: ICondExpressionNode): TDataValue; // Updated + function DoVisitCondExpression(const Node: ICondExpressionNode): TDataValue; function DoVisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; function DoVisitFunctionCall(const Node: IFunctionCallNode): TDataValue; function DoVisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue; @@ -78,6 +84,11 @@ type function DoVisitRecurNode(const Node: IRecurNode): TDataValue; function DoVisitNop(const Node: INopNode): TDataValue; + function DoVisitPipeInput(const Node: IPipeInputNode): TDataValue; + function DoVisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; + function DoVisitPipeInputList(const Node: IPipeInputList): TDataValue; + function DoVisitPipe(const Node: IPipeNode): TDataValue; + protected // Visit a node. function Accept(const Node: IAstNode): T; virtual; @@ -94,7 +105,7 @@ type function VisitRecordField(const Node: IRecordFieldNode): T; virtual; function VisitIfExpression(const Node: IIfExpressionNode): T; virtual; - function VisitCondExpression(const Node: ICondExpressionNode): T; virtual; // Updated + function VisitCondExpression(const Node: ICondExpressionNode): T; virtual; function VisitLambdaExpression(const Node: ILambdaExpressionNode): T; virtual; function VisitFunctionCall(const Node: IFunctionCallNode): T; virtual; function VisitMacroExpansionNode(const Node: IMacroExpansionNode): T; virtual; @@ -113,6 +124,11 @@ type function VisitSeriesLength(const Node: ISeriesLengthNode): T; virtual; function VisitRecurNode(const Node: IRecurNode): T; virtual; function VisitNop(const Node: INopNode): T; virtual; + + function VisitPipeInput(const Node: IPipeInputNode): T; virtual; + function VisitPipeSelectorList(const Node: IPipeSelectorList): T; virtual; + function VisitPipeInputList(const Node: IPipeInputList): T; virtual; + function VisitPipe(const Node: IPipeNode): T; virtual; end; TAstTransformer = class abstract(TAstVisitor) @@ -128,7 +144,7 @@ type function VisitRecordField(const Node: IRecordFieldNode): IAstNode; override; function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override; - function VisitCondExpression(const Node: ICondExpressionNode): IAstNode; override; // Updated + function VisitCondExpression(const Node: ICondExpressionNode): IAstNode; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override; function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override; function VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; override; @@ -147,6 +163,11 @@ type function VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode; override; function VisitRecurNode(const Node: IRecurNode): IAstNode; override; function VisitNop(const Node: INopNode): IAstNode; override; + + function VisitPipeInput(const Node: IPipeInputNode): IAstNode; override; + function VisitPipeSelectorList(const Node: IPipeSelectorList): IAstNode; override; + function VisitPipeInputList(const Node: IPipeInputList): IAstNode; override; + function VisitPipe(const Node: IPipeNode): IAstNode; override; end; TVoid = record @@ -281,6 +302,23 @@ begin Result := TDataValue.FromGeneric(VisitNop(Node)); end; +function TAstVisitor.DoVisitPipeInput(const Node: IPipeInputNode): TDataValue; +begin + Result := TDataValue.FromGeneric(VisitPipeInput(Node)); +end; +function TAstVisitor.DoVisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; +begin + Result := TDataValue.FromGeneric(VisitPipeSelectorList(Node)); +end; +function TAstVisitor.DoVisitPipeInputList(const Node: IPipeInputList): TDataValue; +begin + Result := TDataValue.FromGeneric(VisitPipeInputList(Node)); +end; +function TAstVisitor.DoVisitPipe(const Node: IPipeNode): TDataValue; +begin + Result := TDataValue.FromGeneric(VisitPipe(Node)); +end; + // --- Default implementations (analogous to TAstVisitor) --- function TAstVisitor.VisitConstant(const Node: IConstantNode): T; @@ -471,6 +509,34 @@ begin Result := Default(T); end; +function TAstVisitor.VisitPipeInput(const Node: IPipeInputNode): T; +begin + Result := Default(T); + Accept(Node.StreamSource); + Accept(Node.Selectors); // Now visits SelectorList +end; + +function TAstVisitor.VisitPipeSelectorList(const Node: IPipeSelectorList): T; +begin + Result := Default(T); + for var Item in Node do + Accept(Item); +end; + +function TAstVisitor.VisitPipeInputList(const Node: IPipeInputList): T; +begin + Result := Default(T); + for var Item in Node do + Accept(Item); +end; + +function TAstVisitor.VisitPipe(const Node: IPipeNode): T; +begin + Result := Default(T); + Accept(Node.Inputs); + Accept(Node.Transformation); +end; + { TAstTransformer } // --- Base Virtual Implementations --- @@ -881,4 +947,83 @@ begin Result := TRecurNode.Create(newArgs, Node.StaticType, Node.Identity); end; +function TAstTransformer.VisitPipeInput(const Node: IPipeInputNode): IAstNode; +var + newSource: IIdentifierNode; + newSels: IPipeSelectorList; +begin + newSource := Accept(Node.StreamSource).AsIdentifier; + newSels := Accept(Node.Selectors).AsPipeSelectorList; + + if (newSource = Node.StreamSource) and (newSels = Node.Selectors) then + Result := Node + else + Result := TAst.PipeInput(newSource, newSels, Node.Identity.Location); +end; + +function TAstTransformer.VisitPipeSelectorList(const Node: IPipeSelectorList): IAstNode; +var + hasChanged: Boolean; + newItems: TArray; + item, newItem: IKeywordNode; + i: Integer; +begin + hasChanged := False; + SetLength(newItems, Node.Count); + for i := 0 to Node.Count - 1 do + begin + item := Node[i]; + newItem := Accept(item).AsKeyword; + newItems[i] := newItem; + if item <> newItem then + hasChanged := True; + end; + + if not hasChanged then + Result := Node + else + // Manually reconstruct using helper + Result := TAst.PipeSelectorList(newItems, Node.Identity.Location); +end; + +function TAstTransformer.VisitPipeInputList(const Node: IPipeInputList): IAstNode; +var + hasChanged: Boolean; + newItems: TArray; + item, newItem: IPipeInputNode; + i: Integer; +begin + hasChanged := False; + SetLength(newItems, Node.Count); + for i := 0 to Node.Count - 1 do + begin + item := Node[i]; + newItem := Accept(item).AsPipeInput; // Explicit cast via new helper + newItems[i] := newItem; + if item <> newItem then + hasChanged := True; + end; + + if not hasChanged then + Result := Node + else + // Using TList implementation directly implies we are creating the list structure + // Since IPipeInputList is just INodeList, we reuse TPipeInputList + Result := TPipeInputList.Create(newItems, Node.Identity); +end; + +function TAstTransformer.VisitPipe(const Node: IPipeNode): IAstNode; +var + newInputs: IPipeInputList; + newTrans: ILambdaExpressionNode; +begin + newInputs := Accept(Node.Inputs).AsPipeInputList; + newTrans := Accept(Node.Transformation).AsLambdaExpression; + + if (newInputs = Node.Inputs) and (newTrans = Node.Transformation) then + Result := Node + else + Result := TAst.Pipe(Node.Identity, newInputs, newTrans, Node.StaticType); +end; + end. diff --git a/Src/AST/Myc.Ast.pas b/Src/AST/Myc.Ast.pas index 915a9a8..e07de2f 100644 --- a/Src/AST/Myc.Ast.pas +++ b/Src/AST/Myc.Ast.pas @@ -1,6 +1,3 @@ -//================================================================================================== -//== FULL UNIT START: Myc.Ast (from Myc.Ast.pas) -//================================================================================================== unit Myc.Ast; interface @@ -35,68 +32,36 @@ type ARegisterLibraries: Boolean = False ): IExecutionScope; static; - // ============================================================================= - // 1. NODES WITH DATA IDENTITIES (Identifier, Constant, Keyword, Definition) - // ============================================================================= - - // --- IDENTIFIERS --- - + // ... (Keep existing methods: Identifier, Constant, Keyword, CreateSeries, Nop, IfExpr, CondExpr, LambdaExpr, MacroDef, Quotes, Call, etc.) ... // [Creation] Raw Name -> New INamedIdentity class function Identifier(const AName: string; const Loc: ISourceLocation = nil): IIdentifierNode; overload; static; - - // [Transformation] Reuse INamedIdentity class function Identifier( const Identity: INamedIdentity; const Address: TResolvedAddress; const AStaticType: IStaticType = nil ): IIdentifierNode; overload; static; - // --- CONSTANTS --- - - // [Creation] Raw Value -> New IConstantIdentity class function Constant(const AValue: TDataValue; const Loc: ISourceLocation = nil): IConstantNode; overload; static; - class function Constant(const AValue: String; const Loc: ISourceLocation = nil): IConstantNode; overload; static; - - // [Transformation] Reuse IConstantIdentity class function Constant(const Identity: IConstantIdentity; const AStaticType: IStaticType = nil): IConstantNode; overload; static; - // --- KEYWORDS --- - - // [Creation] Raw Name -> New IKeywordIdentity class function Keyword(const AName: string; const Loc: ISourceLocation = nil): IKeywordNode; overload; static; - - // [Transformation] Reuse IKeywordIdentity class function Keyword(const Identity: IKeywordIdentity): IKeywordNode; overload; static; - // --- DEFINITIONS (CreateSeries) --- - - // [Creation] Raw Definition String -> New IDefinitionIdentity class function CreateSeries(const ADefinition: String; const Loc: ISourceLocation = nil): ICreateSeriesNode; overload; static; - - // [Transformation] Reuse IDefinitionIdentity class function CreateSeries( const Identity: IDefinitionIdentity; const AStaticType: IStaticType = nil ): ICreateSeriesNode; overload; static; - // ============================================================================= - // 2. STRUCTURAL NODES (Identity is just Location / Marker) - // ============================================================================= - // Creation: Generates new TStructuralIdentity(Loc) - // Transformation: Reuses IAstIdentity passed as first param - - // --- NOP --- class function Nop(const Loc: ISourceLocation = nil): IAstNode; overload; static; class function Nop(const Identity: IAstIdentity; const AStaticType: IStaticType = nil): IAstNode; overload; static; - // --- IF --- class function IfExpr( const ACondition: IAstNode; const AThenBranch, AElseBranch: IAstNode; const Loc: ISourceLocation = nil ): IIfExpressionNode; overload; static; - class function IfExpr( const Identity: IAstIdentity; const ACondition: IAstNode; @@ -104,34 +69,28 @@ type const AStaticType: IStaticType = nil ): IIfExpressionNode; overload; static; - // --- COND (Previously Ternary) --- class function CondExpr( const APairs: TArray; const AElseBranch: IAstNode; const Loc: ISourceLocation = nil ): ICondExpressionNode; overload; static; - class function CondExpr( const Identity: IAstIdentity; const APairs: TArray; const AElseBranch: IAstNode; const AStaticType: IStaticType = nil ): ICondExpressionNode; overload; static; - - // Convenience wrapper for Ternary operator behavior using CondExpr class function TernaryExpr( const ACondition: IAstNode; const AThenBranch, AElseBranch: IAstNode; const Loc: ISourceLocation = nil ): ICondExpressionNode; overload; static; - // --- LAMBDA --- class function LambdaExpr( const AParameters: TArray; const ABody: IAstNode; const Loc: ISourceLocation = nil ): ILambdaExpressionNode; overload; static; - class function LambdaExpr( const Identity: IAstIdentity; const AParameters: IParameterList; @@ -144,14 +103,12 @@ type const AStaticType: IStaticType = nil ): ILambdaExpressionNode; overload; static; - // --- MACRO DEF --- class function MacroDef( const AName: IIdentifierNode; const AParameters: TArray; const ABody: IAstNode; const Loc: ISourceLocation = nil ): IMacroDefinitionNode; overload; static; - class function MacroDef( const Identity: IAstIdentity; const AName: IIdentifierNode; @@ -159,7 +116,6 @@ type const ABody: IAstNode ): IMacroDefinitionNode; overload; static; - // --- QUOTES --- class function Quasiquote(const AExpression: IAstNode; const Loc: ISourceLocation = nil): IQuasiquoteNode; overload; static; class function Quasiquote(const Identity: IAstIdentity; const AExpression: IAstNode): IQuasiquoteNode; overload; static; @@ -175,13 +131,11 @@ type const AExpression: IQuasiquoteNode ): IUnquoteSplicingNode; overload; static; - // --- CALL --- class function FunctionCall( const ACallee: IAstNode; const AArguments: TArray; const Loc: ISourceLocation = nil ): IFunctionCallNode; overload; static; - class function FunctionCall( const Identity: IAstIdentity; const ACallee: IAstNode; @@ -192,47 +146,39 @@ type const AIsTargetPure: Boolean = False ): IFunctionCallNode; overload; static; - // --- MACRO EXPANSION --- class function MacroExpansionNode( const AOriginalCallNode: IFunctionCallNode; const AExpandedBody: IAstNode; const Loc: ISourceLocation = nil ): IMacroExpansionNode; overload; static; - class function MacroExpansionNode( const Identity: IAstIdentity; const AOriginalCallNode: IFunctionCallNode; const AExpandedBody: IAstNode ): IMacroExpansionNode; overload; static; - // --- RECUR --- class function Recur(const AArguments: array of IAstNode; const Loc: ISourceLocation = nil): IRecurNode; overload; static; - class function Recur( const Identity: IAstIdentity; const AArguments: IArgumentList; const AStaticType: IStaticType = nil ): IRecurNode; overload; static; - // --- BLOCK --- class function Block( const AExpressions: array of IAstNode; const Loc: ISourceLocation = nil ): IBlockExpressionNode; overload; static; - class function Block( const Identity: IAstIdentity; const AExpressions: IExpressionList; const AStaticType: IStaticType = nil ): IBlockExpressionNode; overload; static; - // --- VAR DECL --- class function VarDecl( const AIdentifier: IAstNode; AInitializer: IAstNode = nil; const Loc: ISourceLocation = nil ): IVariableDeclarationNode; overload; static; - class function VarDecl( const Identity: IAstIdentity; const AIdentifier: IAstNode; @@ -241,36 +187,29 @@ type const AIsBoxed: Boolean = False ): IVariableDeclarationNode; overload; static; - // --- ASSIGN --- class function Assign(const ATarget, AValue: IAstNode; const Loc: ISourceLocation = nil): IAssignmentNode; overload; static; - class function Assign( const Identity: IAstIdentity; const ATarget, AValue: IAstNode; const AStaticType: IStaticType = nil ): IAssignmentNode; overload; static; - // --- INDEXER --- class function Indexer( const ABase: IAstNode; const AIndex: IAstNode; const Loc: ISourceLocation = nil ): IIndexerNode; overload; static; - class function Indexer( const Identity: IAstIdentity; - const ABase: IAstNode; - const AIndex: IAstNode; + const ABase, AIndex: IAstNode; const AStaticType: IStaticType = nil ): IIndexerNode; overload; static; - // --- MEMBER ACCESS --- class function MemberAccess( const ABase: IAstNode; const AMember: IKeywordNode; const Loc: ISourceLocation = nil ): IMemberAccessNode; overload; static; - class function MemberAccess( const Identity: IAstIdentity; const ABase: IAstNode; @@ -278,14 +217,11 @@ type const AStaticType: IStaticType = nil ): IMemberAccessNode; overload; static; - // --- RECORD FIELD & LITERAL --- class function RecordField( const AKey: IKeywordNode; const AValue: IAstNode; const Loc: ISourceLocation = nil ): IRecordFieldNode; overload; static; - - // Neuer Overload für Identitäts-Erhalt class function RecordField( const Identity: IAstIdentity; const AKey: IKeywordNode; @@ -296,7 +232,6 @@ type const AFields: TArray; const Loc: ISourceLocation = nil ): IRecordLiteralNode; overload; static; - class function RecordLiteral( const Identity: IAstIdentity; const AFields: IRecordFieldList; @@ -305,15 +240,12 @@ type const AStaticType: IStaticType = nil ): IRecordLiteralNode; overload; static; - // --- SERIES OPS --- - class function AddSeriesItem( const ASeries: IIdentifierNode; const AValue: IAstNode; const ALookback: IAstNode = nil; const Loc: ISourceLocation = nil ): IAddSeriesItemNode; overload; static; - class function AddSeriesItem( const Identity: IAstIdentity; const ASeries: IIdentifierNode; @@ -323,18 +255,21 @@ type ): IAddSeriesItemNode; overload; static; class function SeriesLength(const ASeries: IIdentifierNode; const Loc: ISourceLocation = nil): ISeriesLengthNode; overload; static; - class function SeriesLength( const Identity: IAstIdentity; const ASeries: IIdentifierNode; const AStaticType: IStaticType = nil ): ISeriesLengthNode; overload; static; - // --- PIPES --- + // --- PIPES (Updated) --- + // Helper: Create a SelectorList from an array of KeywordNodes + class function PipeSelectorList(const AKeywords: TArray; const Loc: ISourceLocation = nil): IPipeSelectorList; static; + + // Updated PipeInput: Accepts IPipeSelectorList instead of single Keyword class function PipeInput( const AStream: IIdentifierNode; - const ASelector: IKeywordNode = nil; + const ASelectors: IPipeSelectorList; const Loc: ISourceLocation = nil ): IPipeInputNode; static; @@ -350,7 +285,6 @@ type const ATransformation: ILambdaExpressionNode; const AStaticType: IStaticType = nil ): IPipeNode; overload; static; - end; implementation @@ -358,6 +292,8 @@ implementation uses System.Generics.Defaults; +// ... (Keep existing implementation for other methods) ... + { TAst } class constructor TAst.Create; @@ -393,9 +329,7 @@ begin end; end; -// ============================================================================= -// DATA NODES -// ============================================================================= +// ... (Keep existing Node Factory Methods) ... class function TAst.Identifier(const AName: string; const Loc: ISourceLocation): IIdentifierNode; begin @@ -428,7 +362,6 @@ begin else constType := TTypes.Unknown; end; - var id := TIdentities.Constant(AValue, Loc); Result := TConstantNode.Create(id, constType); end; @@ -476,10 +409,6 @@ begin ); end; -// ============================================================================= -// STRUCTURAL NODES -// ============================================================================= - class function TAst.Nop(const Loc: ISourceLocation): IAstNode; begin var id := TIdentities.Structural(Loc); @@ -543,10 +472,8 @@ begin end; class function TAst.TernaryExpr(const ACondition, AThenBranch, AElseBranch: IAstNode; const Loc: ISourceLocation): ICondExpressionNode; -var - pair: TCondPair; begin - pair := TCondPair.Create(ACondition, AThenBranch); + var pair := TCondPair.Create(ACondition, AThenBranch); Result := TAst.CondExpr([pair], AElseBranch, Loc); end; @@ -558,13 +485,10 @@ class function TAst.LambdaExpr( begin var listId := TIdentities.List('[', ']', ' ', Loc); var paramList := TParameterList.Create(AParameters, listId); - var id := TIdentities.Structural(Loc); - var body := ABody; if body = nil then body := Block([]); - Result := TLambdaExpressionNode.Create(paramList, body, TTypes.Unknown, nil, nil, nil, False, False, id); end; @@ -603,7 +527,6 @@ class function TAst.MacroDef( begin var listId := TIdentities.List('[', ']', ' ', Loc); var paramList := TParameterList.Create(AParameters, listId); - var id := TIdentities.Structural(Loc); Result := TMacroDefinitionNode.Create(AName, paramList, ABody, id); end; @@ -659,7 +582,6 @@ class function TAst.FunctionCall( begin var listId := TIdentities.List('', '', ' ', Loc); var argList := TArgumentList.Create(AArguments, listId); - var id := TIdentities.Structural(Loc); Result := TFunctionCallNode.Create(ACallee, argList, TTypes.Unknown, False, nil, False, id); end; @@ -714,10 +636,8 @@ begin SetLength(args, Length(AArguments)); for i := 0 to High(AArguments) do args[i] := AArguments[i]; - var listId := TIdentities.List('', '', ' ', Loc); var argList := TArgumentList.Create(args, listId); - var id := TIdentities.Structural(Loc); Result := TRecurNode.Create(argList, TTypes.Void, id); end; @@ -741,10 +661,8 @@ begin SetLength(exprs, Length(AExpressions)); for i := 0 to High(AExpressions) do exprs[i] := AExpressions[i]; - var listId := TIdentities.List('', '', ' ', Loc); var exprList := TExpressionList.Create(exprs, listId); - var id := TIdentities.Structural(Loc); Result := TBlockExpressionNode.Create(exprList, TTypes.Unknown, id); end; @@ -863,7 +781,6 @@ class function TAst.RecordLiteral(const AFields: TArray; const begin var listId := TIdentities.List('{', '}', ' ', Loc); var fieldList := TRecordFieldList.Create(AFields, listId); - var id := TIdentities.Structural(Loc); Result := TRecordLiteralNode.Create(fieldList, nil, nil, TTypes.Unknown, id); end; @@ -940,11 +857,20 @@ end; // --- PIPES --- -class function TAst.PipeInput(const AStream: IIdentifierNode; const ASelector: IKeywordNode; const Loc: ISourceLocation): IPipeInputNode; +class function TAst.PipeSelectorList(const AKeywords: TArray; const Loc: ISourceLocation): IPipeSelectorList; +begin + var listId := TIdentities.List('[', ']', ' ', Loc); + Result := TPipeSelectorList.Create(AKeywords, listId); +end; + +class function TAst.PipeInput( + const AStream: IIdentifierNode; + const ASelectors: IPipeSelectorList; + const Loc: ISourceLocation +): IPipeInputNode; begin var id := TIdentities.Structural(Loc); - // Erzeugt Node aus Myc.Ast.Nodes - Result := TPipeInputNode.Create(AStream, ASelector, id, TTypes.Unknown); + Result := TPipeInputNode.Create(AStream, ASelectors, id, TTypes.Unknown); end; class function TAst.Pipe( @@ -957,7 +883,6 @@ begin var inputList := TPipeInputList.Create(AInputs, listId); var id := TIdentities.Structural(Loc); - // Erzeugt Node aus Myc.Ast.Nodes Result := TPipeNode.Create(inputList, ATransformation, id, TTypes.Unknown); end; @@ -979,6 +904,3 @@ begin end; end. - //================================================================================================== -//== FULL UNIT END: Myc.Ast -//================================================================================================== diff --git a/Src/Data/Myc.Data.Stream.Pipes.pas b/Src/Data/Myc.Data.Stream.Pipes.pas index d962bfa..c99fb3d 100644 --- a/Src/Data/Myc.Data.Stream.Pipes.pas +++ b/Src/Data/Myc.Data.Stream.Pipes.pas @@ -1,4 +1,5 @@ -(* +(*TODO + ; Record-Streams sind Record-Series, an die "Pipes" andocken können. ; Es handelt sich um ein reaktives Producer-Consumer-Pattern. @@ -11,7 +12,6 @@ (def btc-sma (pipe [btc [:Close]] (fn [price] - ; KEIN emit mehr. Wir geben einfach die Map zurück. ; Die Engine nimmt diesen Rückgabewert und publiziert ihn. {:ma (sma (get price 0))} ) @@ -34,9 +34,10 @@ (def btc-buy-only (pipe [btc [:Close] btc-sma [:ma]] (fn [price ma] - ; Ein IF ohne ELSE. + ; Ein IF ohne ELSE -> optionaler Typ ; Wenn die Bedingung FALSE ist, ist das Ergebnis der Funktion "Void". ; Die Engine erkennt "Void" und sendet NICHTS an die Observer. + ; Die ermöglich Aggregation. (if (> (get price 0) (get ma 0)) {:signal :buy}) )