Compiler errors
This commit is contained in:
@@ -16,9 +16,6 @@ uses
|
||||
Myc.Ast;
|
||||
|
||||
type
|
||||
// Exception specific to the binding phase (e.g. undefined symbols, invalid identifiers)
|
||||
EBinderException = class(EAstException);
|
||||
|
||||
IAstBinder = interface(IAstVisitor)
|
||||
function Execute(const RootNode: IAstNode; out Layout: IScopeLayout): IAstNode;
|
||||
end;
|
||||
@@ -40,6 +37,7 @@ type
|
||||
FArgTypes: TArray<IStaticType>;
|
||||
FFunctionRegistry: IFunctionDefinitionRegistry;
|
||||
FBoxedDeclarations: THashSet<IVariableDeclarationNode>;
|
||||
FLog: ICompilerLog;
|
||||
|
||||
// Counts lambdas encountered to determine if a lambda has nested children
|
||||
FLambdaCounter: Integer;
|
||||
@@ -63,7 +61,8 @@ type
|
||||
constructor Create(
|
||||
const AParentLayout: IScopeLayout;
|
||||
const AFunctionRegistry: IFunctionDefinitionRegistry;
|
||||
const AArgTypes: TArray<IStaticType>
|
||||
const AArgTypes: TArray<IStaticType>;
|
||||
const ALog: ICompilerLog
|
||||
);
|
||||
destructor Destroy; override;
|
||||
|
||||
@@ -73,6 +72,7 @@ type
|
||||
const ParentLayout: IScopeLayout;
|
||||
const RootNode: IAstNode;
|
||||
out Layout: IScopeLayout;
|
||||
const ALog: ICompilerLog;
|
||||
const AFunctionRegistry: IFunctionDefinitionRegistry = nil;
|
||||
const AArgTypes: TArray<IStaticType> = nil
|
||||
): IAstNode; static;
|
||||
@@ -110,7 +110,8 @@ end;
|
||||
constructor TAstBinder.Create(
|
||||
const AParentLayout: IScopeLayout;
|
||||
const AFunctionRegistry: IFunctionDefinitionRegistry;
|
||||
const AArgTypes: TArray<IStaticType>
|
||||
const AArgTypes: TArray<IStaticType>;
|
||||
const ALog: ICompilerLog
|
||||
);
|
||||
begin
|
||||
inherited Create;
|
||||
@@ -120,6 +121,7 @@ begin
|
||||
|
||||
FFunctionRegistry := AFunctionRegistry;
|
||||
FArgTypes := AArgTypes;
|
||||
FLog := ALog;
|
||||
FBoxedDeclarations := nil;
|
||||
FLambdaCounter := 0;
|
||||
end;
|
||||
@@ -135,11 +137,12 @@ class function TAstBinder.Bind(
|
||||
const ParentLayout: IScopeLayout;
|
||||
const RootNode: IAstNode;
|
||||
out Layout: IScopeLayout;
|
||||
const ALog: ICompilerLog;
|
||||
const AFunctionRegistry: IFunctionDefinitionRegistry = nil;
|
||||
const AArgTypes: TArray<IStaticType> = nil
|
||||
): IAstNode;
|
||||
begin
|
||||
var binder := TAstBinder.Create(ParentLayout, AFunctionRegistry, AArgTypes);
|
||||
var binder := TAstBinder.Create(ParentLayout, AFunctionRegistry, AArgTypes, ALog);
|
||||
try
|
||||
Result := binder.Execute(RootNode, Layout);
|
||||
finally
|
||||
@@ -239,7 +242,13 @@ begin
|
||||
if Node.Address.Kind = akUnresolved then
|
||||
begin
|
||||
if not ResolveSymbol(Node.Name, physAddr) then
|
||||
raise EBinderException.CreateFmt('Undefined identifier: "%s"', [Node.Name]);
|
||||
begin
|
||||
// Log error but don't crash. Return unresolved node (Poison Pill).
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError(Format('Undefined identifier: "%s"', [Node.Name]), Node);
|
||||
Result := Node;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if physAddr.ScopeDepth > 0 then
|
||||
begin
|
||||
@@ -264,12 +273,28 @@ var
|
||||
isBoxed: Boolean;
|
||||
begin
|
||||
var identifier := Node.Target.AsIdentifier;
|
||||
if not IsValidIdentifier(identifier.Name) then
|
||||
raise EBinderException.CreateFmt('Invalid identifier name: "%s".', [identifier.Name]);
|
||||
|
||||
// Check if the identifier is already defined in the current scope builder to avoid Scope Asserts.
|
||||
if not IsValidIdentifier(identifier.Name) then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError(Format('Invalid identifier name: "%s".', [identifier.Name]), Node);
|
||||
// Continue to try processing the initializer
|
||||
end;
|
||||
|
||||
// Check for duplicates before defining
|
||||
if FCurrentBuilder.FindSlot(identifier.Name) >= 0 then
|
||||
raise EBinderException.CreateFmt('Variable "%s" is already defined in this scope.', [identifier.Name]);
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError(Format('Variable "%s" is already defined in this scope.', [identifier.Name]), Node);
|
||||
|
||||
// Do NOT define the slot to avoid scope corruption.
|
||||
// Visit initializer to catch errors there, then return original node or dummy.
|
||||
if Assigned(Node.Initializer) then
|
||||
Accept(Node.Initializer);
|
||||
|
||||
Result := Node;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
slot := FCurrentBuilder.Define(identifier.Name);
|
||||
addr := TResolvedAddress.Create(akLocalOrParent, 0, slot);
|
||||
@@ -300,6 +325,7 @@ begin
|
||||
|
||||
if Assigned(FFunctionRegistry) and (newValue <> nil) and (newValue.Kind = akLambdaExpression) then
|
||||
begin
|
||||
// Only register if it resolved correctly
|
||||
if newIdent.AsIdentifier.Address.Kind = akLocalOrParent then
|
||||
FFunctionRegistry.Register(newIdent.AsIdentifier.Address, newValue.AsLambdaExpression);
|
||||
end;
|
||||
@@ -338,18 +364,33 @@ begin
|
||||
SetLength(newParams, Length(Node.Parameters));
|
||||
for i := 0 to High(Node.Parameters) do
|
||||
begin
|
||||
// Parameter names must also be checked for uniqueness, though usually guaranteed by Parser/AST construction
|
||||
if FCurrentBuilder.FindSlot(Node.Parameters[i].Name) >= 0 then
|
||||
raise EBinderException.CreateFmt('Duplicate parameter name "%s".', [Node.Parameters[i].Name]);
|
||||
var paramName := Node.Parameters[i].Name;
|
||||
|
||||
// Check duplicate parameters
|
||||
if FCurrentBuilder.FindSlot(paramName) >= 0 then
|
||||
begin
|
||||
if Assigned(FLog) then
|
||||
FLog.AddError(Format('Duplicate parameter name "%s".', [paramName]), Node);
|
||||
// Skip defining this parameter to avoid scope crash, but create a dummy binding
|
||||
// so index alignment isn't completely broken if we were to continue harder.
|
||||
// Here we just skip definition.
|
||||
end
|
||||
else
|
||||
FCurrentBuilder.Define(paramName);
|
||||
|
||||
// Note: We still create a valid (or attempted) identifier node mapping
|
||||
// even if definition failed, to keep AST shape consistent.
|
||||
slot := FCurrentBuilder.FindSlot(paramName); // might return <0 or previous slot if duplicate
|
||||
if slot < 0 then
|
||||
slot := 0; // Fallback for duplicates
|
||||
|
||||
slot := FCurrentBuilder.Define(Node.Parameters[i].Name);
|
||||
addr := TResolvedAddress.Create(akLocalOrParent, 0, slot);
|
||||
|
||||
paramType := TTypes.Unknown;
|
||||
if (parentBuilder.Parent = nil) and (FArgTypes <> nil) and (i < Length(FArgTypes)) then
|
||||
paramType := FArgTypes[i];
|
||||
|
||||
newParams[i] := TAst.Identifier(Node.Parameters[i].Name, addr, paramType);
|
||||
newParams[i] := TAst.Identifier(paramName, addr, paramType);
|
||||
end;
|
||||
|
||||
newBody := Accept(Node.Body);
|
||||
|
||||
Reference in New Issue
Block a user