Compiler exceptions
This commit is contained in:
@@ -8,13 +8,16 @@ uses
|
||||
System.Generics.Collections,
|
||||
Myc.Data.Scalar,
|
||||
Myc.Data.Value,
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Nodes, // Provides EAstException
|
||||
Myc.Ast.Visitor,
|
||||
Myc.Ast.Scope,
|
||||
Myc.Ast.Types,
|
||||
Myc.Ast;
|
||||
|
||||
type
|
||||
// Exception specific to AST lowering/rewriting errors
|
||||
ELoweringException = class(EAstException);
|
||||
|
||||
IAstLowerer = interface(IAstVisitor)
|
||||
function Execute(const RootNode: IAstNode): IAstNode;
|
||||
end;
|
||||
@@ -27,6 +30,8 @@ type
|
||||
FBinaryOperators: TDictionary<string, TScalar.TBinaryOp>;
|
||||
FUnaryOperators: TDictionary<string, TScalar.TUnaryOp>;
|
||||
protected
|
||||
// Implement Visit methods here when specific lowering logic is added
|
||||
// (e.g. converting operator function calls to specific node types if added in future)
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
@@ -47,7 +52,7 @@ constructor TAstLowerer.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
// Operator folding maps
|
||||
// Operator folding maps (Reserved for future static optimization)
|
||||
FBinaryOperators := TDictionary<string, TScalar.TBinaryOp>.Create;
|
||||
|
||||
FBinaryOperators.Add('+', TScalar.TBinaryOp.Add);
|
||||
@@ -76,12 +81,20 @@ end;
|
||||
class function TAstLowerer.Lower(const RootNode: IAstNode): IAstNode;
|
||||
begin
|
||||
var lowerer := TAstLowerer.Create as IAstLowerer;
|
||||
Result := lowerer.Execute(RootNode);
|
||||
try
|
||||
Result := lowerer.Execute(RootNode);
|
||||
except
|
||||
on E: Exception do
|
||||
raise ELoweringException.Create('Internal Error during AST Lowering: ' + E.Message);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAstLowerer.Execute(const RootNode: IAstNode): IAstNode;
|
||||
begin
|
||||
Result := Accept(RootNode); // Use IAstNode-returning Accept
|
||||
if not Assigned(RootNode) then
|
||||
exit(nil);
|
||||
|
||||
Result := Accept(RootNode);
|
||||
if not Assigned(Result) then
|
||||
Result := TAst.Block([]);
|
||||
end;
|
||||
|
||||
@@ -15,6 +15,9 @@ uses
|
||||
Myc.Ast;
|
||||
|
||||
type
|
||||
// Exception specific to macro expansion errors
|
||||
EMacroException = class(EAstException);
|
||||
|
||||
IAstMacroExpander = interface(IAstVisitor)
|
||||
function Execute(const RootNode: IAstNode): IAstNode;
|
||||
end;
|
||||
@@ -306,13 +309,13 @@ begin
|
||||
if value.Kind in [vkScalar, vkText, vkVoid] then
|
||||
Result := TAst.Constant(value)
|
||||
else
|
||||
raise Exception.CreateFmt('Cannot unquote complex runtime value of type %s at compile time.', [value.Kind.ToString]);
|
||||
raise EMacroException.CreateFmt('Cannot unquote complex runtime value of type %s at compile time.', [value.Kind.ToString]);
|
||||
end;
|
||||
|
||||
function TExpansionVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
|
||||
begin
|
||||
// ~@ can only be handled within a list context (TransformAndSpliceNodes)
|
||||
raise Exception.Create('Unquote-splicing (`~@`) can only appear inside a list form.');
|
||||
raise EMacroException.Create('Unquote-splicing (`~@`) can only appear inside a list form.');
|
||||
end;
|
||||
|
||||
function TExpansionVisitor.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
|
||||
@@ -443,7 +446,7 @@ begin
|
||||
|
||||
var params := macroDef.Parameters;
|
||||
if Length(Node.Arguments) <> Length(params) then
|
||||
raise Exception.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, Length(params)]);
|
||||
raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, Length(params)]);
|
||||
|
||||
// 2. Populate scope with UN-EVALUATED AST nodes (Code as Data)
|
||||
for i := 0 to High(params) do
|
||||
@@ -470,12 +473,12 @@ end;
|
||||
function TMacroExpander.VisitUnquote(const Node: IUnquoteNode): IAstNode;
|
||||
begin
|
||||
// Unquotes outside of a macro definition/expansion phase are invalid syntax
|
||||
raise Exception.Create('Unquote (`~`) can only be used inside a quasiquote.');
|
||||
raise EMacroException.Create('Unquote (`~`) can only be used inside a quasiquote.');
|
||||
end;
|
||||
|
||||
function TMacroExpander.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
|
||||
begin
|
||||
raise Exception.Create('Unquote-splicing (`~@`) can only be used inside a quasiquote.');
|
||||
raise EMacroException.Create('Unquote-splicing (`~@`) can only be used inside a quasiquote.');
|
||||
end;
|
||||
|
||||
{ TMacroRegistry }
|
||||
|
||||
@@ -16,6 +16,9 @@ uses
|
||||
Myc.Ast.Compiler.Binder;
|
||||
|
||||
type
|
||||
// Exception specific to specialization errors (e.g. internal compilation failures)
|
||||
ESpecializerException = class(EAstException);
|
||||
|
||||
IAstSpecializer = interface(IAstVisitor)
|
||||
function Execute(const RootNode: IAstNode): IAstNode;
|
||||
end;
|
||||
@@ -90,6 +93,12 @@ constructor TStaticSpecializer.Create(
|
||||
);
|
||||
begin
|
||||
inherited Create;
|
||||
if not Assigned(AMonomorphCache) then
|
||||
raise ESpecializerException.Create('MonomorphCache cannot be nil.');
|
||||
if not Assigned(AFunctionRegistry) then
|
||||
raise ESpecializerException.Create('FunctionRegistry cannot be nil.');
|
||||
// CompileFunc can be nil if no recursion is supported, but usually required.
|
||||
|
||||
FMonomorphCache := AMonomorphCache;
|
||||
FFunctionRegistry := AFunctionRegistry;
|
||||
FCompileFunc := ACompileFunc;
|
||||
@@ -178,7 +187,6 @@ begin
|
||||
if FMonomorphCache.TryGetFunction(key, specializedMethod) then
|
||||
begin
|
||||
// 4a. Cache Hit (Environment)
|
||||
// Propagate IsPure flag from cache to AST node
|
||||
Result :=
|
||||
TAst.FunctionCall(
|
||||
newCallee,
|
||||
@@ -198,7 +206,6 @@ begin
|
||||
// 5a. Cache Hit (RTL)
|
||||
FMonomorphCache.Add(key, specializedMethod);
|
||||
|
||||
// Propagate IsPure flag from RTL definition to AST node
|
||||
Result :=
|
||||
TAst.FunctionCall(
|
||||
newCallee,
|
||||
@@ -228,17 +235,21 @@ begin
|
||||
end;
|
||||
|
||||
// 6a. Compile func with KNOWN TYPES
|
||||
// This recursively triggers Bind -> Check -> Specialize -> Purity Inference for the callee body!
|
||||
if not Assigned(FCompileFunc) then
|
||||
raise ESpecializerException.Create('Cannot specialize user function: Compiler callback is missing.');
|
||||
|
||||
var compiled := FCompileFunc(funcDef, argTypes);
|
||||
|
||||
// Safety check: The recursive compiler MUST produce a valid function pointer
|
||||
if not Assigned(compiled.Func) then
|
||||
raise ESpecializerException.CreateFmt('Internal Error: Failed to compile specialization for "%s"', [funcName]);
|
||||
|
||||
// 6b. Store in cache
|
||||
// Get the return type from the *full function type* returned by Compile
|
||||
var returnType := compiled.StaticType.Signatures[0].ReturnType;
|
||||
specializedMethod := TSpecializedMethod.Create(compiled.Func, returnType, compiled.IsPure);
|
||||
FMonomorphCache.Add(key, specializedMethod);
|
||||
|
||||
// 6c. Return the new node
|
||||
// Propagate the inferred IsPure flag to the AST node
|
||||
Result := TAst.FunctionCall(newCallee, newArgs, returnType, Node.IsTailCall, compiled.Func, compiled.IsPure);
|
||||
exit;
|
||||
end;
|
||||
|
||||
@@ -14,6 +14,9 @@ uses
|
||||
Myc.Ast;
|
||||
|
||||
type
|
||||
// Exception specific to optimization/TCO errors
|
||||
EOptimizerException = class(EAstException);
|
||||
|
||||
IAstTCO = interface(IAstVisitor)
|
||||
function Execute(const RootNode: IAstNode): IAstNode;
|
||||
end;
|
||||
@@ -180,7 +183,6 @@ begin
|
||||
begin
|
||||
// Rebuild using factory.
|
||||
// IMPORTANT: Pass Layout AND Descriptor (TCO runs after TypeChecker).
|
||||
// (* FIX: Pass Node.IsPure to preserve it *)
|
||||
Result :=
|
||||
TAst.LambdaExpr(
|
||||
newParams,
|
||||
@@ -200,7 +202,7 @@ var
|
||||
newArgs: TArray<IAstNode>;
|
||||
begin
|
||||
if not FIsTailStack.Peek then
|
||||
raise Exception.Create('''recur'' can only be used in a tail position.');
|
||||
raise EOptimizerException.Create('''recur'' can only be used in a tail position.');
|
||||
|
||||
// Arguments are not in tail position
|
||||
FNextIsTail := False;
|
||||
@@ -254,7 +256,7 @@ begin
|
||||
Node.StaticType,
|
||||
isTailCall,
|
||||
Node.StaticTarget, // Preserve the static target from Specializer
|
||||
Node.IsTargetPure // (* FIX: Preserve the purity flag from Specializer! *)
|
||||
Node.IsTargetPure // Preserve purity flag
|
||||
);
|
||||
end;
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ uses
|
||||
Myc.Ast.Types;
|
||||
|
||||
type
|
||||
// Exception specific to scope resolution and definition errors
|
||||
EScopeException = class(Exception);
|
||||
|
||||
IExecutionScope = interface;
|
||||
IScopeDescriptor = interface;
|
||||
IScopeLayout = interface;
|
||||
@@ -357,7 +360,7 @@ function TScopeBuilder.Define(const Name: string): Integer;
|
||||
begin
|
||||
// Rule: Shadowing / Redefinition in the same scope is forbidden.
|
||||
if FMap.ContainsKey(Name) then
|
||||
raise Exception.CreateFmt('Variable "%s" is already defined in this scope.', [Name]);
|
||||
raise EScopeException.CreateFmt('Variable "%s" is already defined in this scope.', [Name]);
|
||||
|
||||
// Allocate a new slot
|
||||
Result := FNextSlot;
|
||||
@@ -598,7 +601,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
else
|
||||
raise EInvalidOpException.Create('Cannot capture an unresolved address.');
|
||||
raise EScopeException.Create('Cannot capture an unresolved address.');
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -613,7 +616,7 @@ begin
|
||||
// NOTE: Runtime redefinition is generally restricted in this dynamic Define
|
||||
// (typically used for Root/Global definitions).
|
||||
if FNameToIndex.ContainsKey(id) then
|
||||
raise Exception.CreateFmt('Variable "%s" is already defined in this scope.', [Name]);
|
||||
raise EScopeException.CreateFmt('Variable "%s" is already defined in this scope.', [Name]);
|
||||
|
||||
index := Length(FValues);
|
||||
SetLength(FValues, index + 1);
|
||||
@@ -734,7 +737,7 @@ begin
|
||||
Result := item.Value;
|
||||
end;
|
||||
else
|
||||
raise EInvalidOpException.Create('Cannot get value for an unresolved address.');
|
||||
raise EScopeException.Create('Cannot get value for an unresolved address.');
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -810,7 +813,7 @@ begin
|
||||
pItem.Value := Value;
|
||||
end;
|
||||
else
|
||||
raise EInvalidOpException.Create('Cannot set value for an unresolved address.');
|
||||
raise EScopeException.Create('Cannot set value for an unresolved address.');
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ uses
|
||||
Myc.Data.Keyword;
|
||||
|
||||
type
|
||||
// Exception for atomic type errors (promotion failures, invalid operations)
|
||||
// Inherits from Exception directly to keep Types unit independent of Nodes/AST.
|
||||
ETypeException = class(Exception);
|
||||
|
||||
IStaticType = interface;
|
||||
|
||||
// Defines the categories of types available in the language.
|
||||
@@ -79,8 +83,6 @@ type
|
||||
function GetHashCode: Integer;
|
||||
end;
|
||||
|
||||
ETypeException = class(Exception);
|
||||
|
||||
// Factory and Flyweight access for static types.
|
||||
// Use this record to create or access all IStaticType instances.
|
||||
TTypes = record
|
||||
|
||||
Reference in New Issue
Block a user