Compiler errors

This commit is contained in:
Michael Schimmel
2025-11-25 18:11:04 +01:00
parent 0a1df4e9fe
commit 85ef043b04
5 changed files with 464 additions and 103 deletions
+167 -8
View File
@@ -12,9 +12,70 @@ uses
Myc.Ast.Types;
type
// --- Forward Declarations to break cycles ---
IAstVisitor = interface;
// --- Forward Declarations ---
IAstNode = interface;
// --- 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
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;
// 1. The abstract base class for all Compiler related exceptions.
// Catching this will catch compilation failures AND internal logic errors.
EAstException = class(Exception);
// 2. The concrete exception thrown at the END of a compilation pass if errors occurred.
// It wraps the accumulated log.
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;
// 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;
IFunctionDefinition = interface;
IIdentifierNode = interface;
IConstantNode = interface;
@@ -158,7 +219,7 @@ type
{$region 'private'}
function GetBody: IAstNode;
function GetParameters: TArray<IIdentifierNode>;
function GetIsPure: Boolean; // (* ADDED *)
function GetIsPure: Boolean;
{$endregion}
property Body: IAstNode read GetBody;
property Parameters: TArray<IIdentifierNode> read GetParameters;
@@ -249,7 +310,7 @@ type
function GetArguments: TArray<IAstNode>;
function GetIsTailCall: Boolean;
function GetStaticTarget: TDataValue.TFunc;
function GetIsTargetPure: Boolean; // (* ADDED *)
function GetIsTargetPure: Boolean;
{$endregion}
property Callee: IAstNode read GetCallee;
property Arguments: TArray<IAstNode> read GetArguments;
@@ -401,10 +462,6 @@ type
// A factory for creating visitors
TEvaluatorFactory = reference to function(const Scope: IExecutionScope): IEvaluatorVisitor;
// Base class for ALL compiler exceptions.
// Renamed from IAstException to EAstException to follow standard Delphi naming convention.
EAstException = class(Exception);
implementation
uses
@@ -429,4 +486,106 @@ begin
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;
begin
// Simplified string representation for quick debugging
case Level of
elHint: Result := '[Hint] ';
elWarning: Result := '[Warning] ';
elError: Result := '[Error] ';
end;
Result := Result + 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.