Files
MycLib/Src/AST/Myc.Ast.Nodes.pas
T
2025-11-04 22:10:11 +01:00

451 lines
16 KiB
ObjectPascal

unit Myc.Ast.Nodes;
interface
uses
System.SysUtils,
System.Generics.Collections,
Myc.Data.Scalar,
Myc.Data.Value,
Myc.Data.Keyword,
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;
IBoundIdentifierNode = 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,
// Internal node produced by compiler
akBoundIdentifier
);
// Helper for TAstNodeKind providing a string representation
TAstNodeKindHelper = record helper for TAstNodeKind
public
function ToString: string;
end;
// --- Concrete Type Definitions ---
// Defines how an identifier's address was resolved.
TAddressKind = (akUnresolved, akLocalOrParent, akUpvalue);
TResolvedAddress = record
Kind: TAddressKind;
ScopeDepth: Integer;
SlotIndex: Integer;
constructor Create(AKind: TAddressKind; AScopeDepth, ASlotIndex: Integer);
class operator Initialize(out Dest: TResolvedAddress);
public
class operator Equal(const A: TResolvedAddress; B: TResolvedAddress): Boolean;
end;
// 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;
{$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 AsBoundIdentifierNode: IBoundIdentifierNode;
function AsTypedNode: IAstTypedNode;
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(IAstNode)
// 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(IAstNode)
{$region 'private'}
function GetName: string;
{$endregion}
property Name: string read GetName;
end;
IBoundIdentifierNode = interface(IIdentifierNode)
{$region 'private'}
function GetAddress: TResolvedAddress;
{$endregion}
property Address: TResolvedAddress read GetAddress;
end;
// Represents a keyword literal in the AST (e.g., :foo)
IKeywordNode = interface(IAstNode)
{$region 'private'}
function GetValue: IKeyword;
{$endregion}
// The interned keyword object
property Value: IKeyword read GetValue;
end;
IBinaryExpressionNode = interface(IAstNode)
{$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(IAstNode)
{$region 'private'}
function GetOperator: TScalar.TUnaryOp;
function GetRight: IAstNode;
{$endregion}
property Operator: TScalar.TUnaryOp read GetOperator;
property Right: IAstNode read GetRight;
end;
IIfExpressionNode = interface(IAstNode)
{$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(IAstNode)
{$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(IAstNode)
{$region 'private'}
function GetParameters: TArray<IIdentifierNode>;
function GetBody: IAstNode;
{$endregion}
property Parameters: TArray<IIdentifierNode> read GetParameters;
property Body: IAstNode read GetBody;
end;
IFunctionCallNode = interface(IAstNode)
{$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(IFunctionCallNode)
{$region 'private'}
function GetExpandedBody: IAstNode;
{$endregion}
// The AST fragment that resulted from the macro expansion.
property ExpandedBody: IAstNode read GetExpandedBody;
end;
// A node representing a tail-recursive call.
IRecurNode = interface(IAstNode)
{$region 'private'}
function GetArguments: TArray<IAstNode>;
{$endregion}
property Arguments: TArray<IAstNode> read GetArguments;
end;
IBlockExpressionNode = interface(IAstNode)
{$region 'private'}
function GetExpressions: TArray<IAstNode>;
{$endregion}
property Expressions: TArray<IAstNode> read GetExpressions;
end;
IVariableDeclarationNode = interface(IAstNode)
{$region 'private'}
function GetIdentifier: IIdentifierNode;
function GetInitializer: IAstNode;
{$endregion}
property Identifier: IIdentifierNode read GetIdentifier;
property Initializer: IAstNode read GetInitializer;
end;
IAssignmentNode = interface(IAstNode)
{$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(IAstNode)
{$region 'private'}
function GetBase: IAstNode;
function GetIndex: IAstNode;
{$endregion}
property Base: IAstNode read GetBase;
property Index: IAstNode read GetIndex;
end;
IMemberAccessNode = interface(IAstNode)
{$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(IAstNode)
{$region 'private'}
function GetFields: TArray<TRecordFieldLiteral>;
{$endregion}
property Fields: TArray<TRecordFieldLiteral> read GetFields;
end;
ICreateSeriesNode = interface(IAstNode)
{$region 'private'}
function GetDefinition: String;
{$endregion}
property Definition: String read GetDefinition;
end;
IAddSeriesItemNode = interface(IAstNode)
{$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(IAstNode)
{$region 'private'}
function GetSeries: IIdentifierNode;
{$endregion}
property Series: IIdentifierNode read GetSeries;
end;
IEvaluatorVisitor = interface(IAstVisitor)
function Execute(const RootNode: IAstNode): TDataValue;
end;
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;
{ TResolvedAddress }
constructor TResolvedAddress.Create(AKind: TAddressKind; AScopeDepth, ASlotIndex: Integer);
begin
Kind := AKind;
ScopeDepth := AScopeDepth;
SlotIndex := ASlotIndex;
end;
class operator TResolvedAddress.Equal(const A: TResolvedAddress; B: TResolvedAddress): Boolean;
begin
Result := (A.Kind = B.Kind) and (A.ScopeDepth = B.ScopeDepth) and (A.SlotIndex = B.SlotIndex);
end;
class operator TResolvedAddress.Initialize(out Dest: TResolvedAddress);
begin
Dest.Kind := akUnresolved;
Dest.ScopeDepth := -1;
Dest.SlotIndex := -1;
end;
{ TRecordFieldLiteral }
constructor TRecordFieldLiteral.Create(const AKey: IKeywordNode; const AValue: IAstNode);
begin
Key := AKey;
Value := AValue;
end;
end.