AST Identities
This commit is contained in:
@@ -13,6 +13,7 @@ uses
|
||||
Myc.Ast.Scope,
|
||||
Myc.Ast.Types,
|
||||
Myc.Ast.Compiler.Binder.Upvalues,
|
||||
Myc.Ast.Identities, // Wichtig für AsNamed
|
||||
Myc.Ast;
|
||||
|
||||
type
|
||||
@@ -156,7 +157,7 @@ begin
|
||||
|
||||
Result := Accept(RootNode);
|
||||
if not Assigned(Result) then
|
||||
Result := TAst.Block([]);
|
||||
Result := TAst.Block([], nil);
|
||||
|
||||
Layout := FCurrentBuilder.Build;
|
||||
end;
|
||||
@@ -168,12 +169,10 @@ begin
|
||||
if Name.IsEmpty then
|
||||
exit(False);
|
||||
|
||||
// Support standard identifiers and hygienic macro names (e.g. loop#1)
|
||||
for c in Name do
|
||||
if not (c.IsLetterOrDigit or (c = '_') or (c = '-') or (c = '#')) then
|
||||
exit(False);
|
||||
|
||||
// First char check (cannot be digit unless it's a pure number which is tokenized differently)
|
||||
c := Name[1];
|
||||
if not (c.IsLetter or (c = '_') or (c = '#')) then
|
||||
exit(False);
|
||||
@@ -187,7 +186,6 @@ var
|
||||
depth: Integer;
|
||||
slot: Integer;
|
||||
begin
|
||||
// Start search in current builder
|
||||
layout := FCurrentBuilder;
|
||||
depth := 0;
|
||||
|
||||
@@ -238,12 +236,14 @@ function TAstBinder.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
|
||||
var
|
||||
physAddr: TResolvedAddress;
|
||||
upvalueIndex: Integer;
|
||||
identity: INamedIdentity;
|
||||
begin
|
||||
identity := Node.Identity.AsNamed; // Type-safe extraction of identity
|
||||
|
||||
if Node.Address.Kind = akUnresolved then
|
||||
begin
|
||||
if not ResolveSymbol(Node.Name, physAddr) then
|
||||
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;
|
||||
@@ -252,13 +252,12 @@ begin
|
||||
|
||||
if physAddr.ScopeDepth > 0 then
|
||||
begin
|
||||
// It's an upvalue (from a parent scope). Capture it.
|
||||
upvalueIndex := CaptureUpvalue(physAddr);
|
||||
Result := TAst.Identifier(Node.Name, TResolvedAddress.Create(akUpvalue, 0, upvalueIndex), TTypes.Unknown);
|
||||
// Recreate identifier using the SAME identity but new address
|
||||
Result := TAst.Identifier(identity, TResolvedAddress.Create(akUpvalue, 0, upvalueIndex), TTypes.Unknown);
|
||||
end
|
||||
else
|
||||
// It's local.
|
||||
Result := TAst.Identifier(Node.Name, physAddr, TTypes.Unknown);
|
||||
Result := TAst.Identifier(identity, physAddr, TTypes.Unknown);
|
||||
end
|
||||
else
|
||||
Result := Node;
|
||||
@@ -271,24 +270,23 @@ var
|
||||
newInit: IAstNode;
|
||||
newIdent: IIdentifierNode;
|
||||
isBoxed: Boolean;
|
||||
identifier: IIdentifierNode;
|
||||
identIdentity: INamedIdentity;
|
||||
begin
|
||||
var identifier := Node.Target.AsIdentifier;
|
||||
identifier := Node.Target.AsIdentifier;
|
||||
identIdentity := identifier.Identity.AsNamed;
|
||||
|
||||
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
|
||||
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);
|
||||
|
||||
@@ -304,10 +302,13 @@ begin
|
||||
else
|
||||
newInit := nil;
|
||||
|
||||
newIdent := TAst.Identifier(identifier.Name, addr, TTypes.Unknown);
|
||||
// Recreate identifier with bound address using original identity
|
||||
newIdent := TAst.Identifier(identIdentity, addr, TTypes.Unknown);
|
||||
|
||||
isBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node);
|
||||
|
||||
Result := TAst.VarDecl(newIdent, newInit, TTypes.Unknown, isBoxed);
|
||||
// Recreate variable decl using original identity
|
||||
Result := TAst.VarDecl(Node.Identity, newIdent, newInit, TTypes.Unknown, isBoxed);
|
||||
|
||||
if Assigned(FFunctionRegistry) and (newInit <> nil) and (newInit.Kind = akLambdaExpression) then
|
||||
FFunctionRegistry.Register(addr, newInit.AsLambdaExpression);
|
||||
@@ -321,11 +322,10 @@ begin
|
||||
newIdent := Accept(Node.Target);
|
||||
newValue := Accept(Node.Value);
|
||||
|
||||
Result := TAst.Assign(newIdent.AsIdentifier, newValue, TTypes.Unknown);
|
||||
Result := TAst.Assign(Node.Identity, newIdent, newValue, TTypes.Unknown);
|
||||
|
||||
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;
|
||||
@@ -343,15 +343,13 @@ var
|
||||
capturedMap: TUpvalueMap;
|
||||
sortedPairs: TArray<TPair<TResolvedAddress, Integer>>;
|
||||
upvaluesList: TArray<TResolvedAddress>;
|
||||
|
||||
startCount: Integer;
|
||||
hasNested: Boolean;
|
||||
paramIdentity: INamedIdentity;
|
||||
begin
|
||||
// 1. Count logic to determine if this lambda has nested lambdas
|
||||
startCount := FLambdaCounter;
|
||||
Inc(FLambdaCounter); // Count myself
|
||||
Inc(FLambdaCounter);
|
||||
|
||||
// 2. Push Scope
|
||||
parentBuilder := FCurrentBuilder;
|
||||
FCurrentBuilder := TScope.CreateBuilder(parentBuilder);
|
||||
|
||||
@@ -364,25 +362,21 @@ begin
|
||||
SetLength(newParams, Length(Node.Parameters));
|
||||
for i := 0 to High(Node.Parameters) do
|
||||
begin
|
||||
var paramName := Node.Parameters[i].Name;
|
||||
var paramNode := Node.Parameters[i];
|
||||
var paramName := paramNode.Name;
|
||||
paramIdentity := paramNode.Identity.AsNamed;
|
||||
|
||||
// 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
|
||||
slot := FCurrentBuilder.FindSlot(paramName);
|
||||
if slot < 0 then
|
||||
slot := 0; // Fallback for duplicates
|
||||
slot := 0;
|
||||
|
||||
addr := TResolvedAddress.Create(akLocalOrParent, 0, slot);
|
||||
|
||||
@@ -390,7 +384,8 @@ begin
|
||||
if (parentBuilder.Parent = nil) and (FArgTypes <> nil) and (i < Length(FArgTypes)) then
|
||||
paramType := FArgTypes[i];
|
||||
|
||||
newParams[i] := TAst.Identifier(paramName, addr, paramType);
|
||||
// Rebind parameter identifier using original identity
|
||||
newParams[i] := TAst.Identifier(paramIdentity, addr, paramType);
|
||||
end;
|
||||
|
||||
newBody := Accept(Node.Body);
|
||||
@@ -421,18 +416,30 @@ begin
|
||||
FUpvalueStack.Pop;
|
||||
end;
|
||||
|
||||
// 3. Calculate hasNested
|
||||
// If FLambdaCounter increased by more than just 1 (myself), children were visited.
|
||||
hasNested := FLambdaCounter > (startCount + 1);
|
||||
|
||||
Result := TAst.LambdaExpr(newParams, newBody, finalLayout, nil, upvaluesList, hasNested, Node.IsPure);
|
||||
// Rebuild Lambda using original identity
|
||||
Result :=
|
||||
TAst.LambdaExpr(
|
||||
Node.Identity,
|
||||
newParams,
|
||||
newBody,
|
||||
finalLayout,
|
||||
nil, // Descriptor is created in TypeChecker
|
||||
upvaluesList,
|
||||
hasNested,
|
||||
Node.IsPure
|
||||
);
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
|
||||
begin
|
||||
if Node.Callee.Kind = akKeyword then
|
||||
begin
|
||||
var memberAccess := TAst.MemberAccess(Accept(Node.Arguments[0]), Node.Callee.AsKeyword);
|
||||
// Transform keyword call (:key obj) into member access (.key obj)
|
||||
// Note: The new MemberAccess node gets the identity of the original FunctionCall
|
||||
// The member keyword retains its own identity.
|
||||
var memberAccess := TAst.MemberAccess(Node.Identity, Accept(Node.Arguments[0]), Node.Callee.AsKeyword);
|
||||
Result := memberAccess;
|
||||
exit;
|
||||
end;
|
||||
|
||||
Reference in New Issue
Block a user