Implementing Pipes
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
|
||||||
//==================================================================================================
|
|
||||||
|
|||||||
@@ -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})
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user