599 lines
20 KiB
ObjectPascal
599 lines
20 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 ---
|
|
IAstNode = interface;
|
|
|
|
// --- Identity & Provenance ---
|
|
|
|
/// Represents the immutable identity or provenance of an AST node.
|
|
/// This allows tracking where a node came from (Parser, Generator, Macro, etc.)
|
|
/// without coupling the AST to specific source formats.
|
|
IAstIdentity = interface
|
|
['{5C496755-721D-4793-9526-7E2E63357719}']
|
|
function ToString: string;
|
|
end;
|
|
|
|
/// Specific identity for nodes originating from source code text.
|
|
ISourceLocation = interface(IAstIdentity)
|
|
['{98226687-F323-49B0-8D38-61803B225973}']
|
|
function GetLine: Integer;
|
|
function GetCol: Integer;
|
|
property Line: Integer read GetLine;
|
|
property Col: Integer read GetCol;
|
|
end;
|
|
|
|
// --- Error Handling Infrastructure ---
|
|
|
|
TCompilerErrorLevel = (elHint, elWarning, elError);
|
|
|
|
TCompilerError = record
|
|
Level: TCompilerErrorLevel;
|
|
Message: string;
|
|
Node: IAstNode; // Context node where the error occurred
|
|
constructor Create(ALevel: TCompilerErrorLevel; const AMessage: string; const ANode: IAstNode = nil);
|
|
function ToString: string;
|
|
end;
|
|
|
|
ICompilerLog = interface
|
|
['{8E5F2C10-4B3A-49F1-9C2D-7A8B5E6F4D3C}']
|
|
procedure Add(ALevel: TCompilerErrorLevel; const AMessage: string; const ANode: IAstNode = nil);
|
|
procedure AddError(const AMessage: string; const ANode: IAstNode = nil);
|
|
procedure AddWarning(const AMessage: string; const ANode: IAstNode = nil);
|
|
|
|
function HasErrors: Boolean;
|
|
function GetEntryCount: Integer;
|
|
function GetEntries: TArray<TCompilerError>;
|
|
|
|
property Entries: TArray<TCompilerError> read GetEntries;
|
|
end;
|
|
|
|
// Standard implementation of the log
|
|
TCompilerLog = class(TInterfacedObject, ICompilerLog)
|
|
private
|
|
FEntries: TList<TCompilerError>;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Add(ALevel: TCompilerErrorLevel; const AMessage: string; const ANode: IAstNode = nil);
|
|
procedure AddError(const AMessage: string; const ANode: IAstNode = nil);
|
|
procedure AddWarning(const AMessage: string; const ANode: IAstNode = nil);
|
|
function HasErrors: Boolean;
|
|
function GetEntryCount: Integer;
|
|
function GetEntries: TArray<TCompilerError>;
|
|
end;
|
|
|
|
// Base class for all Compiler related exceptions.
|
|
EAstException = class(Exception);
|
|
|
|
// The concrete exception thrown at the END of a compilation pass if errors occurred.
|
|
ECompilationFailed = class(EAstException)
|
|
private
|
|
FErrors: TArray<TCompilerError>;
|
|
public
|
|
constructor Create(const AErrors: TArray<TCompilerError>);
|
|
function ToString: string; override;
|
|
property Errors: TArray<TCompilerError> read FErrors;
|
|
end;
|
|
|
|
// --- AST Interfaces ---
|
|
|
|
IAstVisitor = interface;
|
|
IFunctionDefinition = interface;
|
|
IIdentifierNode = interface;
|
|
IConstantNode = interface;
|
|
IKeywordNode = 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,
|
|
akIfExpression,
|
|
akTernaryExpression,
|
|
akLambdaExpression,
|
|
akFunctionCall,
|
|
akMacroExpansion,
|
|
akBlockExpression,
|
|
akVariableDeclaration,
|
|
akAssignment,
|
|
akMacroDefinition,
|
|
akQuasiquote,
|
|
akUnquote,
|
|
akUnquoteSplicing,
|
|
akIndexer,
|
|
akMemberAccess,
|
|
akRecordLiteral,
|
|
akCreateSeries,
|
|
akAddSeriesItem,
|
|
akSeriesLength,
|
|
akRecur,
|
|
akNop
|
|
);
|
|
|
|
TAstNodeKindHelper = record helper for TAstNodeKind
|
|
public
|
|
function ToString: string;
|
|
end;
|
|
|
|
// --- Concrete Type Definitions ---
|
|
|
|
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 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;
|
|
end;
|
|
|
|
IAstNode = interface
|
|
{$region 'private'}
|
|
function GetKind: TAstNodeKind;
|
|
function GetIsTyped: Boolean;
|
|
function GetIdentity: IAstIdentity;
|
|
{$endregion}
|
|
function Accept(const Visitor: IAstVisitor): TDataValue;
|
|
|
|
function AsConstant: IConstantNode;
|
|
function AsIdentifier: IIdentifierNode;
|
|
function AsKeyword: IKeywordNode;
|
|
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;
|
|
property Identity: IAstIdentity read GetIdentity;
|
|
end;
|
|
|
|
IAstTypedNode = interface(IAstNode)
|
|
{$region 'private'}
|
|
function GetStaticType: IStaticType;
|
|
{$endregion}
|
|
property StaticType: IStaticType read GetStaticType;
|
|
end;
|
|
|
|
IFunctionDefinition = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetBody: IAstNode;
|
|
function GetParameters: TArray<IIdentifierNode>;
|
|
function GetIsPure: Boolean;
|
|
{$endregion}
|
|
property Body: IAstNode read GetBody;
|
|
property Parameters: TArray<IIdentifierNode> read GetParameters;
|
|
property IsPure: Boolean read GetIsPure;
|
|
end;
|
|
|
|
INopNode = interface(IAstTypedNode)
|
|
end;
|
|
|
|
IConstantNode = interface(IAstTypedNode)
|
|
{$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;
|
|
|
|
IKeywordNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetValue: IKeyword;
|
|
{$endregion}
|
|
property Value: IKeyword read GetValue;
|
|
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(IFunctionDefinition)
|
|
{$region 'private'}
|
|
function GetParameters: TArray<IIdentifierNode>;
|
|
function GetBody: IAstNode;
|
|
function GetLayout: IScopeLayout;
|
|
function GetDescriptor: IScopeDescriptor;
|
|
function GetUpvalues: TArray<TResolvedAddress>;
|
|
function GetHasNestedLambdas: Boolean;
|
|
{$endregion}
|
|
property Parameters: TArray<IIdentifierNode> read GetParameters;
|
|
property Body: IAstNode read GetBody;
|
|
property Layout: IScopeLayout read GetLayout;
|
|
property Descriptor: IScopeDescriptor read GetDescriptor;
|
|
property Upvalues: TArray<TResolvedAddress> read GetUpvalues;
|
|
property HasNestedLambdas: Boolean read GetHasNestedLambdas;
|
|
end;
|
|
|
|
IFunctionCallNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetCallee: IAstNode;
|
|
function GetArguments: TArray<IAstNode>;
|
|
function GetIsTailCall: Boolean;
|
|
function GetStaticTarget: TDataValue.TFunc;
|
|
function GetIsTargetPure: Boolean;
|
|
{$endregion}
|
|
property Callee: IAstNode read GetCallee;
|
|
property Arguments: TArray<IAstNode> read GetArguments;
|
|
property IsTailCall: Boolean read GetIsTailCall;
|
|
property StaticTarget: TDataValue.TFunc read GetStaticTarget;
|
|
property IsTargetPure: Boolean read GetIsTargetPure;
|
|
end;
|
|
|
|
IMacroExpansionNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetCallNode: IFunctionCallNode;
|
|
function GetExpandedBody: IAstNode;
|
|
{$endregion}
|
|
property CallNode: IFunctionCallNode read GetCallNode;
|
|
property ExpandedBody: IAstNode read GetExpandedBody;
|
|
end;
|
|
|
|
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 GetTarget: IAstNode;
|
|
function GetInitializer: IAstNode;
|
|
function GetIsBoxed: Boolean;
|
|
{$endregion}
|
|
property Target: IAstNode read GetTarget;
|
|
property Initializer: IAstNode read GetInitializer;
|
|
property IsBoxed: Boolean read GetIsBoxed;
|
|
end;
|
|
|
|
IAssignmentNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetTarget: IAstNode;
|
|
function GetValue: IAstNode;
|
|
{$endregion}
|
|
property Target: IAstNode read GetTarget;
|
|
property Value: IAstNode read GetValue;
|
|
end;
|
|
|
|
IMacroDefinitionNode = interface(IAstTypedNode)
|
|
{$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;
|
|
|
|
IQuasiquoteNode = interface(IAstNode)
|
|
{$region 'private'}
|
|
function GetExpression: IAstNode;
|
|
{$endregion}
|
|
property Expression: IAstNode read GetExpression;
|
|
end;
|
|
|
|
IUnquoteNode = interface(IAstNode)
|
|
{$region 'private'}
|
|
function GetExpression: IAstNode;
|
|
{$endregion}
|
|
property Expression: IAstNode read GetExpression;
|
|
end;
|
|
|
|
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;
|
|
|
|
IRecordLiteralNode = interface(IAstTypedNode)
|
|
{$region 'private'}
|
|
function GetFields: TArray<TRecordFieldLiteral>;
|
|
function GetGenericDefinition: IGenericRecordDefinition;
|
|
function GetScalarDefinition: IScalarRecordDefinition;
|
|
{$endregion}
|
|
property Fields: TArray<TRecordFieldLiteral> read GetFields;
|
|
property GenericDefinition: IGenericRecordDefinition read GetGenericDefinition;
|
|
property ScalarDefinition: IScalarRecordDefinition read GetScalarDefinition;
|
|
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;
|
|
|
|
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;
|
|
|
|
{ TCompilerError }
|
|
|
|
constructor TCompilerError.Create(ALevel: TCompilerErrorLevel; const AMessage: string; const ANode: IAstNode);
|
|
begin
|
|
Level := ALevel;
|
|
Message := AMessage;
|
|
Node := ANode;
|
|
end;
|
|
|
|
function TCompilerError.ToString: string;
|
|
var
|
|
location: string;
|
|
loc: ISourceLocation;
|
|
begin
|
|
// 1. Try to extract location from Node Identity
|
|
location := '';
|
|
if Assigned(Node) and Assigned(Node.Identity) then
|
|
begin
|
|
if Supports(Node.Identity, ISourceLocation, loc) then
|
|
location := Format('[Line %d, Col %d] ', [loc.Line, loc.Col])
|
|
else
|
|
location := Format('[%s] ', [Node.Identity.ToString]);
|
|
end;
|
|
|
|
// 2. Prefix with Level
|
|
case Level of
|
|
elHint: Result := '[Hint] ';
|
|
elWarning: Result := '[Warning] ';
|
|
elError: Result := '[Error] ';
|
|
end;
|
|
|
|
// 3. Assemble
|
|
Result := Result + location + Message;
|
|
end;
|
|
|
|
{ TCompilerLog }
|
|
|
|
constructor TCompilerLog.Create;
|
|
begin
|
|
inherited Create;
|
|
FEntries := TList<TCompilerError>.Create;
|
|
end;
|
|
|
|
destructor TCompilerLog.Destroy;
|
|
begin
|
|
FEntries.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TCompilerLog.Add(ALevel: TCompilerErrorLevel; const AMessage: string; const ANode: IAstNode);
|
|
begin
|
|
FEntries.Add(TCompilerError.Create(ALevel, AMessage, ANode));
|
|
end;
|
|
|
|
procedure TCompilerLog.AddError(const AMessage: string; const ANode: IAstNode);
|
|
begin
|
|
Add(elError, AMessage, ANode);
|
|
end;
|
|
|
|
procedure TCompilerLog.AddWarning(const AMessage: string; const ANode: IAstNode);
|
|
begin
|
|
Add(elWarning, AMessage, ANode);
|
|
end;
|
|
|
|
function TCompilerLog.HasErrors: Boolean;
|
|
var
|
|
entry: TCompilerError;
|
|
begin
|
|
Result := False;
|
|
for entry in FEntries do
|
|
if entry.Level = elError then
|
|
Exit(True);
|
|
end;
|
|
|
|
function TCompilerLog.GetEntryCount: Integer;
|
|
begin
|
|
Result := FEntries.Count;
|
|
end;
|
|
|
|
function TCompilerLog.GetEntries: TArray<TCompilerError>;
|
|
begin
|
|
Result := FEntries.ToArray;
|
|
end;
|
|
|
|
{ ECompilationFailed }
|
|
|
|
constructor ECompilationFailed.Create(const AErrors: TArray<TCompilerError>);
|
|
var
|
|
msg: string;
|
|
begin
|
|
if Length(AErrors) > 0 then
|
|
msg := Format('Compilation failed with %d error(s). First: %s', [Length(AErrors), AErrors[0].Message])
|
|
else
|
|
msg := 'Compilation failed with unspecified errors.';
|
|
inherited Create(msg);
|
|
FErrors := AErrors;
|
|
end;
|
|
|
|
function ECompilationFailed.ToString: string;
|
|
var
|
|
sb: TStringBuilder;
|
|
err: TCompilerError;
|
|
begin
|
|
sb := TStringBuilder.Create;
|
|
try
|
|
sb.AppendLine(Message);
|
|
for err in FErrors do
|
|
begin
|
|
sb.Append(' - ');
|
|
sb.AppendLine(err.ToString);
|
|
end;
|
|
Result := sb.ToString;
|
|
finally
|
|
sb.Free;
|
|
end;
|
|
end;
|
|
|
|
end.
|