416 lines
15 KiB
ObjectPascal
416 lines
15 KiB
ObjectPascal
unit Myc.Ast.Nodes;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils,
|
|
System.Generics.Collections,
|
|
Myc.Data.Scalar,
|
|
Myc.Data.Value,
|
|
Myc.Data.Keyword,
|
|
Myc.Ast.Scope,
|
|
Myc.Ast.Types;
|
|
|
|
type
|
|
// --- Forward Declarations to break cycles ---
|
|
IAstVisitor = interface;
|
|
IAstNode = interface;
|
|
IIdentifierNode = interface;
|
|
IConstantNode = interface;
|
|
IKeywordNode = interface;
|
|
IBinaryExpressionNode = interface;
|
|
IUnaryExpressionNode = interface;
|
|
IIfExpressionNode = interface;
|
|
ITernaryExpressionNode = interface;
|
|
ILambdaExpressionNode = interface;
|
|
IFunctionCallNode = interface;
|
|
IMacroExpansionNode = interface;
|
|
IBlockExpressionNode = interface;
|
|
IVariableDeclarationNode = interface;
|
|
IAssignmentNode = interface;
|
|
IMacroDefinitionNode = interface;
|
|
IQuasiquoteNode = interface;
|
|
IUnquoteNode = interface;
|
|
IUnquoteSplicingNode = interface;
|
|
IIndexerNode = interface;
|
|
IMemberAccessNode = interface;
|
|
IRecordLiteralNode = interface;
|
|
ICreateSeriesNode = interface;
|
|
IAddSeriesItemNode = interface;
|
|
ISeriesLengthNode = interface;
|
|
IRecurNode = interface;
|
|
INopNode = interface;
|
|
IAstTypedNode = interface;
|
|
|
|
// Defines the concrete kinds of AST nodes
|
|
TAstNodeKind = (
|
|
akConstant,
|
|
akIdentifier,
|
|
akKeyword,
|
|
akBinaryExpression,
|
|
akUnaryExpression,
|
|
akIfExpression,
|
|
akTernaryExpression,
|
|
akLambdaExpression,
|
|
akFunctionCall,
|
|
akMacroExpansion,
|
|
akBlockExpression,
|
|
akVariableDeclaration,
|
|
akAssignment,
|
|
akMacroDefinition,
|
|
akQuasiquote,
|
|
akUnquote,
|
|
akUnquoteSplicing,
|
|
akIndexer,
|
|
akMemberAccess,
|
|
akRecordLiteral,
|
|
akCreateSeries,
|
|
akAddSeriesItem,
|
|
akSeriesLength,
|
|
akRecur,
|
|
akNop
|
|
);
|
|
|
|
// Helper for TAstNodeKind providing a string representation
|
|
TAstNodeKindHelper = record helper for TAstNodeKind
|
|
public
|
|
function ToString: string;
|
|
end;
|
|
|
|
// --- Concrete Type Definitions ---
|
|
|
|
// A single field in a record literal
|
|
TRecordFieldLiteral = record
|
|
Key: IKeywordNode;
|
|
Value: IAstNode;
|
|
constructor Create(const AKey: IKeywordNode; const AValue: IAstNode);
|
|
end;
|
|
|
|
IAstVisitor = interface
|
|
function VisitConstant(const Node: IConstantNode): TDataValue;
|
|
function VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
|
function VisitKeyword(const Node: IKeywordNode): TDataValue;
|
|
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
|
|
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
|
|
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
|
|
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
|
|
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
|
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
|
|
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
|
|
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
|
|
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
|
function VisitAssignment(const Node: IAssignmentNode): TDataValue;
|
|
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
|
|
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
|
|
function VisitUnquote(const Node: IUnquoteNode): TDataValue;
|
|
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
|
|
function VisitIndexer(const Node: IIndexerNode): TDataValue;
|
|
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
|
|
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
|
|
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
|
|
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
|
|
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
|
|
function VisitRecurNode(const Node: IRecurNode): TDataValue;
|
|
function VisitNop(const Node: INopNode): TDataValue; // Added Nop
|
|
end;
|
|
|
|
IAstNode = interface(IInterface)
|
|
{$region 'private'}
|
|
function GetKind: TAstNodeKind;
|
|
function GetIsTyped: Boolean;
|
|
{$endregion}
|
|
function Accept(const Visitor: IAstVisitor): TDataValue;
|
|
|
|
function AsConstant: IConstantNode;
|
|
function AsIdentifier: IIdentifierNode;
|
|
function AsKeyword: IKeywordNode;
|
|
function AsBinaryExpression: IBinaryExpressionNode;
|
|
function AsUnaryExpression: IUnaryExpressionNode;
|
|
function AsIfExpression: IIfExpressionNode;
|
|
function AsTernaryExpression: ITernaryExpressionNode;
|
|
function AsLambdaExpression: ILambdaExpressionNode;
|
|
function AsFunctionCall: IFunctionCallNode;
|
|
function AsMacroExpansion: IMacroExpansionNode;
|
|
function AsBlockExpression: IBlockExpressionNode;
|
|
function AsVariableDeclaration: IVariableDeclarationNode;
|
|
function AsAssignment: IAssignmentNode;
|
|
function AsMacroDefinition: IMacroDefinitionNode;
|
|
function AsQuasiquote: IQuasiquoteNode;
|
|
function AsUnquote: IUnquoteNode;
|
|
function AsUnquoteSplicing: IUnquoteSplicingNode;
|
|
function AsIndexer: IIndexerNode;
|
|
function AsMemberAccess: IMemberAccessNode;
|
|
function AsRecordLiteral: IRecordLiteralNode;
|
|
function AsCreateSeries: ICreateSeriesNode;
|
|
function AsAddSeriesItem: IAddSeriesItemNode;
|
|
function AsSeriesLength: ISeriesLengthNode;
|
|
function AsRecur: IRecurNode;
|
|
function AsNop: INopNode;
|
|
|
|
function AsTypedNode: IAstTypedNode;
|
|
|
|
property IsTyped: Boolean read GetIsTyped;
|
|
property Kind: TAstNodeKind read GetKind;
|
|
end;
|
|
|
|
IAstTypedNode = interface(IAstNode)
|
|
{$region 'private'}
|
|
function GetStaticType: IStaticType;
|
|
{$endregion}
|
|
property StaticType: IStaticType read GetStaticType;
|
|
end;
|
|
|
|
INopNode = interface(IAstNode)
|
|
// A placeholder node for the UI (e.g., drag target).
|
|
// This node is illegal during compilation.
|
|
end;
|
|
|
|
IConstantNode = interface(IAstTypedNode)
|
|
// Represents a constant value in the AST (Scalar, Text, or Void).
|
|
{$region 'private'}
|
|
function GetValue: TDataValue;
|
|
{$endregion}
|
|
property Value: TDataValue read GetValue;
|
|
end;
|
|
|
|
IIdentifierNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetAddress: TResolvedAddress;
|
|
function GetName: string;
|
|
{$endregion}
|
|
property Address: TResolvedAddress read GetAddress;
|
|
property Name: string read GetName;
|
|
end;
|
|
|
|
// Represents a keyword literal in the AST (e.g., :foo)
|
|
IKeywordNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetValue: IKeyword;
|
|
{$endregion}
|
|
// The interned keyword object
|
|
property Value: IKeyword read GetValue;
|
|
end;
|
|
|
|
IBinaryExpressionNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetLeft: IAstNode;
|
|
function GetOperator: TScalar.TBinaryOp;
|
|
function GetRight: IAstNode;
|
|
{$endregion}
|
|
property Left: IAstNode read GetLeft;
|
|
property Operator: TScalar.TBinaryOp read GetOperator;
|
|
property Right: IAstNode read GetRight;
|
|
end;
|
|
|
|
IUnaryExpressionNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetOperator: TScalar.TUnaryOp;
|
|
function GetRight: IAstNode;
|
|
{$endregion}
|
|
property Operator: TScalar.TUnaryOp read GetOperator;
|
|
property Right: IAstNode read GetRight;
|
|
end;
|
|
|
|
IIfExpressionNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetCondition: IAstNode;
|
|
function GetThenBranch: IAstNode;
|
|
function GetElseBranch: IAstNode;
|
|
{$endregion}
|
|
property Condition: IAstNode read GetCondition;
|
|
property ThenBranch: IAstNode read GetThenBranch;
|
|
property ElseBranch: IAstNode read GetElseBranch;
|
|
end;
|
|
|
|
ITernaryExpressionNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetCondition: IAstNode;
|
|
function GetThenBranch: IAstNode;
|
|
function GetElseBranch: IAstNode;
|
|
{$endregion}
|
|
property Condition: IAstNode read GetCondition;
|
|
property ThenBranch: IAstNode read GetThenBranch;
|
|
property ElseBranch: IAstNode read GetElseBranch;
|
|
end;
|
|
|
|
ILambdaExpressionNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetParameters: TArray<IIdentifierNode>;
|
|
function GetBody: IAstNode;
|
|
{$endregion}
|
|
property Parameters: TArray<IIdentifierNode> read GetParameters;
|
|
property Body: IAstNode read GetBody;
|
|
end;
|
|
|
|
IFunctionCallNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetCallee: IAstNode;
|
|
function GetArguments: TArray<IAstNode>;
|
|
{$endregion}
|
|
property Callee: IAstNode read GetCallee;
|
|
property Arguments: TArray<IAstNode> read GetArguments;
|
|
end;
|
|
|
|
// A node representing a macro call.
|
|
IMacroExpansionNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetCallNode: IFunctionCallNode;
|
|
function GetExpandedBody: IAstNode;
|
|
{$endregion}
|
|
// The AST fragment that resulted from the macro expansion.
|
|
property CallNode: IFunctionCallNode read GetCallNode;
|
|
property ExpandedBody: IAstNode read GetExpandedBody;
|
|
end;
|
|
|
|
// A node representing a tail-recursive call.
|
|
IRecurNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetArguments: TArray<IAstNode>;
|
|
{$endregion}
|
|
property Arguments: TArray<IAstNode> read GetArguments;
|
|
end;
|
|
|
|
IBlockExpressionNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetExpressions: TArray<IAstNode>;
|
|
{$endregion}
|
|
property Expressions: TArray<IAstNode> read GetExpressions;
|
|
end;
|
|
|
|
IVariableDeclarationNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetIdentifier: IIdentifierNode;
|
|
function GetInitializer: IAstNode;
|
|
{$endregion}
|
|
property Identifier: IIdentifierNode read GetIdentifier;
|
|
property Initializer: IAstNode read GetInitializer;
|
|
end;
|
|
|
|
IAssignmentNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetIdentifier: IIdentifierNode;
|
|
function GetValue: IAstNode;
|
|
{$endregion}
|
|
property Identifier: IIdentifierNode read GetIdentifier;
|
|
property Value: IAstNode read GetValue;
|
|
end;
|
|
|
|
IMacroDefinitionNode = interface(IAstNode)
|
|
{$region 'private'}
|
|
function GetName: IIdentifierNode;
|
|
function GetParameters: TArray<IIdentifierNode>;
|
|
function GetBody: IAstNode;
|
|
{$endregion}
|
|
property Name: IIdentifierNode read GetName;
|
|
property Parameters: TArray<IIdentifierNode> read GetParameters;
|
|
property Body: IAstNode read GetBody;
|
|
end;
|
|
|
|
// Represents a quasiquoted expression, e.g., `(a b)
|
|
IQuasiquoteNode = interface(IAstNode)
|
|
{$region 'private'}
|
|
function GetExpression: IAstNode;
|
|
{$endregion}
|
|
property Expression: IAstNode read GetExpression;
|
|
end;
|
|
|
|
// Represents an unquoted expression, e.g., ~x
|
|
IUnquoteNode = interface(IAstNode)
|
|
{$region 'private'}
|
|
function GetExpression: IAstNode;
|
|
{$endregion}
|
|
property Expression: IAstNode read GetExpression;
|
|
end;
|
|
|
|
// Represents an unquote-splicing expression, e.g., ~@y
|
|
IUnquoteSplicingNode = interface(IAstNode)
|
|
{$region 'private'}
|
|
function GetExpression: IQuasiquoteNode;
|
|
{$endregion}
|
|
property Expression: IQuasiquoteNode read GetExpression;
|
|
end;
|
|
|
|
IIndexerNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetBase: IAstNode;
|
|
function GetIndex: IAstNode;
|
|
{$endregion}
|
|
property Base: IAstNode read GetBase;
|
|
property Index: IAstNode read GetIndex;
|
|
end;
|
|
|
|
IMemberAccessNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetBase: IAstNode;
|
|
function GetMember: IKeywordNode;
|
|
{$endregion}
|
|
property Base: IAstNode read GetBase;
|
|
property Member: IKeywordNode read GetMember;
|
|
end;
|
|
|
|
// Represents a record literal, e.g., {:a 1 :b 2}
|
|
IRecordLiteralNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetFields: TArray<TRecordFieldLiteral>;
|
|
{$endregion}
|
|
property Fields: TArray<TRecordFieldLiteral> read GetFields;
|
|
end;
|
|
|
|
ICreateSeriesNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetDefinition: String;
|
|
{$endregion}
|
|
property Definition: String read GetDefinition;
|
|
end;
|
|
|
|
IAddSeriesItemNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetSeries: IIdentifierNode;
|
|
function GetValue: IAstNode;
|
|
function GetLookback: IAstNode;
|
|
{$endregion}
|
|
property Series: IIdentifierNode read GetSeries;
|
|
property Value: IAstNode read GetValue;
|
|
property Lookback: IAstNode read GetLookback;
|
|
end;
|
|
|
|
ISeriesLengthNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetSeries: IIdentifierNode;
|
|
{$endregion}
|
|
property Series: IIdentifierNode read GetSeries;
|
|
end;
|
|
|
|
IEvaluatorVisitor = interface(IAstVisitor)
|
|
function Execute(const RootNode: IAstNode): TDataValue;
|
|
end;
|
|
|
|
// A factory for creating visitors, primarily used by the debugger subsystem.
|
|
TEvaluatorFactory = reference to function(const Scope: IExecutionScope): IEvaluatorVisitor;
|
|
|
|
implementation
|
|
|
|
uses
|
|
System.Classes,
|
|
System.Rtti,
|
|
System.StrUtils;
|
|
|
|
{ TAstNodeKindHelper }
|
|
|
|
function TAstNodeKindHelper.ToString: string;
|
|
begin
|
|
Result := TRttiEnumerationType.GetName<TAstNodeKind>(Self);
|
|
Assert(StartsText('ak', Result));
|
|
Result := Result.Substring(2);
|
|
end;
|
|
|
|
{ TRecordFieldLiteral }
|
|
|
|
constructor TRecordFieldLiteral.Create(const AKey: IKeywordNode; const AValue: IAstNode);
|
|
begin
|
|
Key := AKey;
|
|
Value := AValue;
|
|
end;
|
|
|
|
end.
|