AST Identities

This commit is contained in:
Michael Schimmel
2025-11-25 18:45:47 +01:00
parent 85ef043b04
commit 0b7a60e338
6 changed files with 804 additions and 190 deletions
+395
View File
@@ -0,0 +1,395 @@
unit Myc.Ast.Identities;
interface
uses
System.SysUtils,
Myc.Data.Value,
Myc.Data.Keyword;
type
// Forward declarations
INamedIdentity = interface;
IConstantIdentity = interface;
IKeywordIdentity = interface;
IDefinitionIdentity = interface;
// Represents a position in the source code.
ISourceLocation = interface
function GetLine: Integer;
function GetCol: Integer;
function ToString: string;
property Line: Integer read GetLine;
property Col: Integer read GetCol;
end;
// Discriminator enum for identity types.
TAstIdentityKind = (
ikStructural, // Pure location, no extra data (e.g. If, Block)
ikNamed, // Identifier, MacroDef (Name + Location)
ikConstant, // Literals (Value + Location)
ikKeyword, // Keywords (Keyword + Location)
ikDefinition // Complex definitions (DefString + Location)
);
// Base interface for the "Green Tree" nodes.
// Holds the raw syntax data and optional source location.
IAstIdentity = interface
{$region 'private'}
function GetKind: TAstIdentityKind;
function GetLocation: ISourceLocation;
{$endregion}
function ToString: string;
// Explicit casting methods (Fail-Fast)
function AsNamed: INamedIdentity;
function AsConstant: IConstantIdentity;
function AsKeyword: IKeywordIdentity;
function AsDefinition: IDefinitionIdentity;
property Kind: TAstIdentityKind read GetKind;
// Optional: Returns nil if the node has no source mapping (e.g. generated code)
property Location: ISourceLocation read GetLocation;
end;
INamedIdentity = interface(IAstIdentity)
{$region 'private'}
function GetName: string;
{$endregion}
property Name: string read GetName;
end;
IConstantIdentity = interface(IAstIdentity)
{$region 'private'}
function GetValue: TDataValue;
{$endregion}
property Value: TDataValue read GetValue;
end;
IKeywordIdentity = interface(IAstIdentity)
{$region 'private'}
function GetValue: IKeyword;
{$endregion}
property Value: IKeyword read GetValue;
end;
IDefinitionIdentity = interface(IAstIdentity)
{$region 'private'}
function GetDefinition: string;
{$endregion}
property Definition: string read GetDefinition;
end;
// Factory and static access.
TIdentities = record
public
class function Location(ALine, ACol: Integer): ISourceLocation; static;
class function Structural(const ALoc: ISourceLocation = nil): IAstIdentity; static;
class function Identifier(const AName: string; const ALoc: ISourceLocation = nil): INamedIdentity; static;
class function Constant(const AValue: TDataValue; const ALoc: ISourceLocation = nil): IConstantIdentity; static;
class function Keyword(const AValue: IKeyword; const ALoc: ISourceLocation = nil): IKeywordIdentity; static;
class function Definition(const ADef: string; const ALoc: ISourceLocation = nil): IDefinitionIdentity; static;
end;
implementation
type
TSourceLocation = class(TInterfacedObject, ISourceLocation)
private
FLine, FCol: Integer;
function GetLine: Integer;
function GetCol: Integer;
public
constructor Create(ALine, ACol: Integer);
function ToString: string; override;
end;
// Abstract base implementation providing the casting logic
TAbstractIdentity = class(TInterfacedObject, IAstIdentity)
private
FLocation: ISourceLocation;
function GetKind: TAstIdentityKind; virtual; abstract;
function GetLocation: ISourceLocation;
public
constructor Create(const ALoc: ISourceLocation);
function ToString: string; override;
function AsNamed: INamedIdentity; virtual;
function AsConstant: IConstantIdentity; virtual;
function AsKeyword: IKeywordIdentity; virtual;
function AsDefinition: IDefinitionIdentity; virtual;
end;
TStructuralIdentity = class(TAbstractIdentity)
protected
function GetKind: TAstIdentityKind; override;
end;
TNamedIdentity = class(TAbstractIdentity, INamedIdentity)
private
FName: string;
function GetName: string;
protected
function GetKind: TAstIdentityKind; override;
public
constructor Create(const AName: string; const ALoc: ISourceLocation);
function ToString: string; override;
function AsNamed: INamedIdentity; override;
end;
TConstantIdentity = class(TAbstractIdentity, IConstantIdentity)
private
FValue: TDataValue;
function GetValue: TDataValue;
protected
function GetKind: TAstIdentityKind; override;
public
constructor Create(const AValue: TDataValue; const ALoc: ISourceLocation);
function ToString: string; override;
function AsConstant: IConstantIdentity; override;
end;
TKeywordIdentity = class(TAbstractIdentity, IKeywordIdentity)
private
FValue: IKeyword;
function GetValue: IKeyword;
protected
function GetKind: TAstIdentityKind; override;
public
constructor Create(const AValue: IKeyword; const ALoc: ISourceLocation);
function ToString: string; override;
function AsKeyword: IKeywordIdentity; override;
end;
TDefinitionIdentity = class(TAbstractIdentity, IDefinitionIdentity)
private
FDefinition: string;
function GetDefinition: string;
protected
function GetKind: TAstIdentityKind; override;
public
constructor Create(const ADef: string; const ALoc: ISourceLocation);
function ToString: string; override;
function AsDefinition: IDefinitionIdentity; override;
end;
{ TSourceLocation }
constructor TSourceLocation.Create(ALine, ACol: Integer);
begin
inherited Create;
FLine := ALine;
FCol := ACol;
end;
function TSourceLocation.GetCol: Integer;
begin
Result := FCol;
end;
function TSourceLocation.GetLine: Integer;
begin
Result := FLine;
end;
function TSourceLocation.ToString: string;
begin
Result := Format('Line %d, Col %d', [FLine, FCol]);
end;
{ TAbstractIdentity }
constructor TAbstractIdentity.Create(const ALoc: ISourceLocation);
begin
inherited Create;
FLocation := ALoc;
end;
function TAbstractIdentity.GetLocation: ISourceLocation;
begin
Result := FLocation;
end;
function TAbstractIdentity.ToString: string;
begin
if Assigned(FLocation) then
Result := FLocation.ToString
else
Result := '<unknown location>';
end;
function TAbstractIdentity.AsNamed: INamedIdentity;
begin
raise EInvalidCast.Create('Identity is not a Named Identity');
end;
function TAbstractIdentity.AsConstant: IConstantIdentity;
begin
raise EInvalidCast.Create('Identity is not a Constant Identity');
end;
function TAbstractIdentity.AsKeyword: IKeywordIdentity;
begin
raise EInvalidCast.Create('Identity is not a Keyword Identity');
end;
function TAbstractIdentity.AsDefinition: IDefinitionIdentity;
begin
raise EInvalidCast.Create('Identity is not a Definition Identity');
end;
{ TStructuralIdentity }
function TStructuralIdentity.GetKind: TAstIdentityKind;
begin
Result := ikStructural;
end;
{ TNamedIdentity }
constructor TNamedIdentity.Create(const AName: string; const ALoc: ISourceLocation);
begin
inherited Create(ALoc);
FName := AName;
end;
function TNamedIdentity.GetKind: TAstIdentityKind;
begin
Result := ikNamed;
end;
function TNamedIdentity.GetName: string;
begin
Result := FName;
end;
function TNamedIdentity.AsNamed: INamedIdentity;
begin
Result := Self;
end;
function TNamedIdentity.ToString: string;
begin
Result := Format('Name: "%s" (%s)', [FName, inherited ToString]);
end;
{ TConstantIdentity }
constructor TConstantIdentity.Create(const AValue: TDataValue; const ALoc: ISourceLocation);
begin
inherited Create(ALoc);
FValue := AValue;
end;
function TConstantIdentity.GetKind: TAstIdentityKind;
begin
Result := ikConstant;
end;
function TConstantIdentity.GetValue: TDataValue;
begin
Result := FValue;
end;
function TConstantIdentity.AsConstant: IConstantIdentity;
begin
Result := Self;
end;
function TConstantIdentity.ToString: string;
begin
Result := Format('Const: %s (%s)', [FValue.ToString, inherited ToString]);
end;
{ TKeywordIdentity }
constructor TKeywordIdentity.Create(const AValue: IKeyword; const ALoc: ISourceLocation);
begin
inherited Create(ALoc);
FValue := AValue;
end;
function TKeywordIdentity.GetKind: TAstIdentityKind;
begin
Result := ikKeyword;
end;
function TKeywordIdentity.GetValue: IKeyword;
begin
Result := FValue;
end;
function TKeywordIdentity.AsKeyword: IKeywordIdentity;
begin
Result := Self;
end;
function TKeywordIdentity.ToString: string;
begin
Result := Format('Keyword: :%s (%s)', [FValue.Name, inherited ToString]);
end;
{ TDefinitionIdentity }
constructor TDefinitionIdentity.Create(const ADef: string; const ALoc: ISourceLocation);
begin
inherited Create(ALoc);
FDefinition := ADef;
end;
function TDefinitionIdentity.GetKind: TAstIdentityKind;
begin
Result := ikDefinition;
end;
function TDefinitionIdentity.GetDefinition: string;
begin
Result := FDefinition;
end;
function TDefinitionIdentity.AsDefinition: IDefinitionIdentity;
begin
Result := Self;
end;
function TDefinitionIdentity.ToString: string;
begin
Result := Format('Def: "%s" (%s)', [FDefinition, inherited ToString]);
end;
{ TIdentities }
class function TIdentities.Location(ALine, ACol: Integer): ISourceLocation;
begin
Result := TSourceLocation.Create(ALine, ACol);
end;
class function TIdentities.Structural(const ALoc: ISourceLocation): IAstIdentity;
begin
Result := TStructuralIdentity.Create(ALoc);
end;
class function TIdentities.Identifier(const AName: string; const ALoc: ISourceLocation): INamedIdentity;
begin
Result := TNamedIdentity.Create(AName, ALoc);
end;
class function TIdentities.Constant(const AValue: TDataValue; const ALoc: ISourceLocation): IConstantIdentity;
begin
Result := TConstantIdentity.Create(AValue, ALoc);
end;
class function TIdentities.Keyword(const AValue: IKeyword; const ALoc: ISourceLocation): IKeywordIdentity;
begin
Result := TKeywordIdentity.Create(AValue, ALoc);
end;
class function TIdentities.Definition(const ADef: string; const ALoc: ISourceLocation): IDefinitionIdentity;
begin
Result := TDefinitionIdentity.Create(ADef, ALoc);
end;
end.
+41 -34
View File
@@ -15,6 +15,25 @@ 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);
@@ -28,6 +47,7 @@ type
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);
@@ -54,12 +74,10 @@ type
function GetEntries: TArray<TCompilerError>;
end;
// 1. The abstract base class for all Compiler related exceptions.
// Catching this will catch compilation failures AND internal logic errors.
// Base class for all Compiler related exceptions.
EAstException = class(Exception);
// 2. The concrete exception thrown at the END of a compilation pass if errors occurred.
// It wraps the accumulated log.
// The concrete exception thrown at the END of a compilation pass if errors occurred.
ECompilationFailed = class(EAstException)
private
FErrors: TArray<TCompilerError>;
@@ -69,10 +87,6 @@ type
property Errors: TArray<TCompilerError> read FErrors;
end;
// 3. (Optional) Exception for critical internal bugs (e.g. implementation defects).
// This should ideally never happen in production.
EInternalCompilerError = class(EAstException);
// --- AST Interfaces ---
IAstVisitor = interface;
@@ -129,7 +143,6 @@ type
akNop
);
// Helper for TAstNodeKind providing a string representation
TAstNodeKindHelper = record helper for TAstNodeKind
public
function ToString: string;
@@ -137,7 +150,6 @@ type
// --- Concrete Type Definitions ---
// A single field in a record literal
TRecordFieldLiteral = record
Key: IKeywordNode;
Value: IAstNode;
@@ -174,6 +186,7 @@ type
{$region 'private'}
function GetKind: TAstNodeKind;
function GetIsTyped: Boolean;
function GetIdentity: IAstIdentity;
{$endregion}
function Accept(const Visitor: IAstVisitor): TDataValue;
@@ -205,6 +218,7 @@ type
property IsTyped: Boolean read GetIsTyped;
property Kind: TAstNodeKind read GetKind;
property Identity: IAstIdentity read GetIdentity;
end;
IAstTypedNode = interface(IAstNode)
@@ -214,7 +228,6 @@ type
property StaticType: IStaticType read GetStaticType;
end;
// The abstract definition of a callable function body
IFunctionDefinition = interface(IAstTypedNode)
{$region 'private'}
function GetBody: IAstNode;
@@ -227,7 +240,6 @@ type
end;
INopNode = interface(IAstTypedNode)
// A placeholder node for the UI (e.g., drag target).
end;
IConstantNode = interface(IAstTypedNode)
@@ -246,12 +258,10 @@ type
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;
@@ -281,27 +291,17 @@ type
{$region 'private'}
function GetParameters: TArray<IIdentifierNode>;
function GetBody: IAstNode;
// The structural layout. Always present after binding.
function GetLayout: IScopeLayout;
// The runtime descriptor. Can be nil during early compilation phases.
function GetDescriptor: IScopeDescriptor;
function GetUpvalues: TArray<TResolvedAddress>;
function GetHasNestedLambdas: Boolean;
// GetIsPure is inherited from IFunctionDefinition
{$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;
// IsPure is inherited
end;
IFunctionCallNode = interface(IAstTypedNode)
@@ -319,18 +319,15 @@ type
property IsTargetPure: Boolean read GetIsTargetPure;
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>;
@@ -376,7 +373,6 @@ type
property Body: IAstNode read GetBody;
end;
// Represents a quasiquoted expression, e.g., `(a b)
IQuasiquoteNode = interface(IAstNode)
{$region 'private'}
function GetExpression: IAstNode;
@@ -384,7 +380,6 @@ type
property Expression: IAstNode read GetExpression;
end;
// Represents an unquoted expression, e.g., ~x
IUnquoteNode = interface(IAstNode)
{$region 'private'}
function GetExpression: IAstNode;
@@ -392,7 +387,6 @@ type
property Expression: IAstNode read GetExpression;
end;
// Represents an unquote-splicing expression, e.g., ~@y
IUnquoteSplicingNode = interface(IAstNode)
{$region 'private'}
function GetExpression: IQuasiquoteNode;
@@ -418,7 +412,6 @@ type
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>;
@@ -459,7 +452,6 @@ type
function Execute(const RootNode: IAstNode): TDataValue;
end;
// A factory for creating visitors
TEvaluatorFactory = reference to function(const Scope: IExecutionScope): IEvaluatorVisitor;
implementation
@@ -496,14 +488,29 @@ begin
end;
function TCompilerError.ToString: string;
var
location: string;
loc: ISourceLocation;
begin
// Simplified string representation for quick debugging
// 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;
Result := Result + Message;
// 3. Assemble
Result := Result + location + Message;
end;
{ TCompilerLog }
+364 -154
View File
File diff suppressed because it is too large Load Diff