Implementing Pipes

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