Making AST node inmmutable - WIP
This commit is contained in:
+48
-34
@@ -284,13 +284,17 @@ begin
|
|||||||
// Define parameters
|
// Define parameters
|
||||||
for i := 0 to High(N.Parameters) do
|
for i := 0 to High(N.Parameters) do
|
||||||
begin
|
begin
|
||||||
var paramNode := N.Parameters[i];
|
var paramNode := N.Parameters[i]; // This is a TIdentifierNode
|
||||||
var slotIndex := FCurrentDescriptor.Define(paramNode.Name, TTypes.Unknown);
|
var slotIndex := FCurrentDescriptor.Define(paramNode.Name, TTypes.Unknown);
|
||||||
adr := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
|
adr := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
|
||||||
|
|
||||||
// Mutate the parameter node with its address
|
// --- Replace Node ---
|
||||||
(paramNode as TIdentifierNode).Address := adr;
|
// Create a new TBoundIdentifierNode (implementation is in Myc.Ast)
|
||||||
(paramNode as TAstNode).StaticType := TTypes.Unknown;
|
var boundParamNode := TBoundIdentifierNode.Create(paramNode.Name, adr);
|
||||||
|
// Set its type
|
||||||
|
(boundParamNode as TAstNode).StaticType := TTypes.Unknown;
|
||||||
|
// Replace it in the parameter array
|
||||||
|
N.Parameters[i] := boundParamNode;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Visit the body *within the new scope*
|
// Visit the body *within the new scope*
|
||||||
@@ -342,42 +346,50 @@ var
|
|||||||
symbol: TResolvedSymbol;
|
symbol: TResolvedSymbol;
|
||||||
adr: TResolvedAddress;
|
adr: TResolvedAddress;
|
||||||
begin
|
begin
|
||||||
symbol := FCurrentDescriptor.FindSymbol(Node.Name);
|
// We only bind nodes that are the base parser type.
|
||||||
adr := symbol.Address;
|
if Node.Kind = akIdentifier then
|
||||||
|
|
||||||
if adr.Kind = akLocalOrParent then
|
|
||||||
begin
|
begin
|
||||||
if (adr.ScopeDepth > 0) and (FUpvalueStack.Count > 0) then
|
symbol := FCurrentDescriptor.FindSymbol(Node.Name);
|
||||||
begin
|
adr := symbol.Address;
|
||||||
// --- Handle Upvalue ---
|
|
||||||
var upvalueMap := FUpvalueStack.Peek;
|
|
||||||
// Adjust address to be relative to the captured scope
|
|
||||||
dec(adr.ScopeDepth);
|
|
||||||
|
|
||||||
var upvalueIndex: Integer;
|
if adr.Kind = akUnresolved then
|
||||||
if not upvalueMap.TryGetValue(adr, upvalueIndex) then
|
raise Exception.CreateFmt('Undefined identifier: "%s"', [Node.Name]);
|
||||||
|
|
||||||
|
if adr.Kind = akLocalOrParent then
|
||||||
|
begin
|
||||||
|
if (adr.ScopeDepth > 0) and (FUpvalueStack.Count > 0) then
|
||||||
begin
|
begin
|
||||||
// This is a new upvalue for this lambda
|
// --- Handle Upvalue ---
|
||||||
upvalueIndex := upvalueMap.Count;
|
var upvalueMap := FUpvalueStack.Peek;
|
||||||
upvalueMap.Add(adr, upvalueIndex);
|
// Adjust address to be relative to the captured scope
|
||||||
|
dec(adr.ScopeDepth);
|
||||||
|
|
||||||
|
var upvalueIndex: Integer;
|
||||||
|
if not upvalueMap.TryGetValue(adr, upvalueIndex) then
|
||||||
|
begin
|
||||||
|
// This is a new upvalue for this lambda
|
||||||
|
upvalueIndex := upvalueMap.Count;
|
||||||
|
upvalueMap.Add(adr, upvalueIndex);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Create final upvalue address
|
||||||
|
adr := TResolvedAddress.Create(akUpvalue, 0, upvalueIndex);
|
||||||
end;
|
end;
|
||||||
|
// else: It's a local (ScopeDepth=0), adr is already correct.
|
||||||
// Mutate the node to point to the Upvalue slot
|
|
||||||
(Node as TIdentifierNode).Address := TResolvedAddress.Create(akUpvalue, 0, upvalueIndex);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
// --- Handle LocalOrParent ---
|
|
||||||
// Mutate the node to point to the Local/Parent slot
|
|
||||||
(Node as TIdentifierNode).Address := adr;
|
|
||||||
end;
|
end;
|
||||||
|
// else: It was already an upvalue (e.g. nested lambda), adr is correct.
|
||||||
|
|
||||||
(Node as TAstNode).StaticType := symbol.StaticType; // Set type from scope
|
// --- Replace Node ---
|
||||||
|
// Create the new TBoundIdentifierNode (implementation is in Myc.Ast)
|
||||||
|
Result := TBoundIdentifierNode.Create(Node.Name, adr);
|
||||||
|
// Copy static type from scope to the new node
|
||||||
|
(Result as TAstNode).StaticType := symbol.StaticType;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
raise Exception.CreateFmt('Undefined identifier: "%s"', [Node.Name]);
|
begin
|
||||||
|
// It's already an akBoundIdentifier (or other specialized type), just return it.
|
||||||
Result := Node;
|
Result := Node;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
|
function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
|
||||||
@@ -399,8 +411,10 @@ begin
|
|||||||
slotIndex := FCurrentDescriptor.Define(N.Identifier.Name, TTypes.Unknown);
|
slotIndex := FCurrentDescriptor.Define(N.Identifier.Name, TTypes.Unknown);
|
||||||
address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
|
address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
|
||||||
|
|
||||||
// 3. Mutate the Identifier node (which is NOT visited by inherited call)
|
// 3. --- Replace Node ---
|
||||||
(N.Identifier as TIdentifierNode).Address := address;
|
// Replace the TIdentifierNode with a new TBoundIdentifierNode
|
||||||
|
N.Identifier := TBoundIdentifierNode.Create(N.Identifier.Name, address);
|
||||||
|
// Set static type (Unknown) on the *new* node
|
||||||
(N.Identifier as TAstNode).StaticType := TTypes.Unknown; // TypeChecker will set this
|
(N.Identifier as TAstNode).StaticType := TTypes.Unknown; // TypeChecker will set this
|
||||||
|
|
||||||
// 4. Mutate this declaration node
|
// 4. Mutate this declaration node
|
||||||
|
|||||||
@@ -143,13 +143,15 @@ end;
|
|||||||
|
|
||||||
procedure TAstDumper.VisitIdentifier(const Node: IIdentifierNode);
|
procedure TAstDumper.VisitIdentifier(const Node: IIdentifierNode);
|
||||||
var
|
var
|
||||||
N: TIdentifierNode;
|
adr: TResolvedAddress;
|
||||||
begin
|
begin
|
||||||
N := (Node as TIdentifierNode);
|
if Node.Kind = akBoundIdentifier then
|
||||||
if N.Address.Kind <> akUnresolved then
|
adr := Node.AsBoundIdentifierNode.Address;
|
||||||
LogFmt('Identifier: %s -> %s', [N.Name, FormatAddress(N.Address)])
|
|
||||||
|
if adr.Kind <> akUnresolved then
|
||||||
|
LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress(adr)])
|
||||||
else
|
else
|
||||||
LogFmt('Identifier: %s (unbound)', [N.Name]);
|
LogFmt('Identifier: %s (unbound)', [Node.Name]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TAstDumper.VisitKeyword(const Node: IKeywordNode);
|
procedure TAstDumper.VisitKeyword(const Node: IKeywordNode);
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ begin
|
|||||||
for i := 0 to High(ArgValues) do
|
for i := 0 to High(ArgValues) do
|
||||||
begin
|
begin
|
||||||
// Parameters in a bound lambda must be bound identifiers.
|
// Parameters in a bound lambda must be bound identifiers.
|
||||||
adr.SlotIndex := (params[i] as TIdentifierNode).Address.SlotIndex; // Changed
|
adr.SlotIndex := params[i].AsBoundIdentifierNode.Address.SlotIndex; // Changed
|
||||||
lambdaScope[adr] := ArgValues[i];
|
lambdaScope[adr] := ArgValues[i];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -334,7 +334,7 @@ var
|
|||||||
itemValue, lookbackValue, seriesVar: TDataValue;
|
itemValue, lookbackValue, seriesVar: TDataValue;
|
||||||
lookback: Int64;
|
lookback: Int64;
|
||||||
begin
|
begin
|
||||||
seriesVar := FScope[(Node.Series as TIdentifierNode).Address]; // Changed
|
seriesVar := FScope[Node.Series.AsBoundIdentifierNode.Address]; // Changed
|
||||||
itemValue := Node.Value.Accept(Self);
|
itemValue := Node.Value.Accept(Self);
|
||||||
lookback := -1;
|
lookback := -1;
|
||||||
|
|
||||||
@@ -368,7 +368,7 @@ begin
|
|||||||
// Evaluate the new value.
|
// Evaluate the new value.
|
||||||
Result := Node.Value.Accept(Self);
|
Result := Node.Value.Accept(Self);
|
||||||
// Assign it. The scope's SetValues implementation now handles boxing correctly.
|
// Assign it. The scope's SetValues implementation now handles boxing correctly.
|
||||||
FScope[(Node.Identifier as TIdentifierNode).Address] := Result; // Changed
|
FScope[Node.Identifier.AsBoundIdentifierNode.Address] := Result; // Changed
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
|
function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
|
||||||
@@ -405,7 +405,7 @@ end;
|
|||||||
function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||||
begin
|
begin
|
||||||
// The scope's GetValues implementation now handles unboxing automatically.
|
// The scope's GetValues implementation now handles unboxing automatically.
|
||||||
Result := FScope[(Node as TIdentifierNode).Address]; // Changed
|
Result := FScope[Node.AsBoundIdentifierNode.Address]; // Changed
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
|
function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
|
||||||
@@ -548,8 +548,8 @@ begin
|
|||||||
Result := TDataValue.Void;
|
Result := TDataValue.Void;
|
||||||
|
|
||||||
// After binding, all declaration nodes are TVariableDeclarationNode
|
// After binding, all declaration nodes are TVariableDeclarationNode
|
||||||
boundNode := Node as TVariableDeclarationNode; // Changed
|
boundNode := Node as TVariableDeclarationNode;
|
||||||
address := (boundNode.Identifier as TIdentifierNode).Address; // Changed
|
address := boundNode.Identifier.AsBoundIdentifierNode.Address;
|
||||||
|
|
||||||
// Check the IsBoxed flag set by the binder.
|
// Check the IsBoxed flag set by the binder.
|
||||||
if boundNode.IsBoxed then
|
if boundNode.IsBoxed then
|
||||||
@@ -641,7 +641,7 @@ var
|
|||||||
seriesValue: TDataValue;
|
seriesValue: TDataValue;
|
||||||
len: Int64;
|
len: Int64;
|
||||||
begin
|
begin
|
||||||
seriesValue := FScope[(Node.Series as TIdentifierNode).Address]; // Changed
|
seriesValue := FScope[Node.Series.AsBoundIdentifierNode.Address]; // Changed
|
||||||
|
|
||||||
case seriesValue.Kind of
|
case seriesValue.Kind of
|
||||||
vkSeries: len := seriesValue.AsSeries.Count;
|
vkSeries: len := seriesValue.AsSeries.Count;
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ type
|
|||||||
IAddSeriesItemNode = interface;
|
IAddSeriesItemNode = interface;
|
||||||
ISeriesLengthNode = interface;
|
ISeriesLengthNode = interface;
|
||||||
IRecurNode = interface;
|
IRecurNode = interface;
|
||||||
INopNode = interface; // Added Nop
|
INopNode = interface;
|
||||||
|
IBoundIdentifierNode = interface;
|
||||||
|
|
||||||
// Defines the concrete kinds of AST nodes
|
// Defines the concrete kinds of AST nodes
|
||||||
TAstNodeKind = (
|
TAstNodeKind = (
|
||||||
@@ -65,7 +66,9 @@ type
|
|||||||
akAddSeriesItem,
|
akAddSeriesItem,
|
||||||
akSeriesLength,
|
akSeriesLength,
|
||||||
akRecur,
|
akRecur,
|
||||||
akNop // Added Nop
|
akNop,
|
||||||
|
// Internal node produced by compiler
|
||||||
|
akBoundIdentifier
|
||||||
);
|
);
|
||||||
|
|
||||||
// Helper for TAstNodeKind providing a string representation
|
// Helper for TAstNodeKind providing a string representation
|
||||||
@@ -156,6 +159,8 @@ type
|
|||||||
function AsRecur: IRecurNode;
|
function AsRecur: IRecurNode;
|
||||||
function AsNop: INopNode; // Added Nop
|
function AsNop: INopNode; // Added Nop
|
||||||
|
|
||||||
|
function AsBoundIdentifierNode: IBoundIdentifierNode;
|
||||||
|
|
||||||
property Kind: TAstNodeKind read GetKind;
|
property Kind: TAstNodeKind read GetKind;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -179,6 +184,13 @@ type
|
|||||||
property Name: string read GetName;
|
property Name: string read GetName;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
IBoundIdentifierNode = interface(IIdentifierNode)
|
||||||
|
{$region 'private'}
|
||||||
|
function GetAddress: TResolvedAddress;
|
||||||
|
{$endregion}
|
||||||
|
property Address: TResolvedAddress read GetAddress;
|
||||||
|
end;
|
||||||
|
|
||||||
// Represents a keyword literal in the AST (e.g., :foo)
|
// Represents a keyword literal in the AST (e.g., :foo)
|
||||||
IKeywordNode = interface(IAstNode)
|
IKeywordNode = interface(IAstNode)
|
||||||
{$region 'private'}
|
{$region 'private'}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ end;
|
|||||||
function TTypeChecker.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
|
function TTypeChecker.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
|
||||||
var
|
var
|
||||||
initType: IStaticType;
|
initType: IStaticType;
|
||||||
boundIdent: TIdentifierNode;
|
boundIdent: IIdentifierNode;
|
||||||
adr: TResolvedAddress;
|
adr: TResolvedAddress;
|
||||||
begin
|
begin
|
||||||
// 1. Visit children first (Identifier is leaf, Initializer is traversed)
|
// 1. Visit children first (Identifier is leaf, Initializer is traversed)
|
||||||
@@ -149,9 +149,9 @@ begin
|
|||||||
else
|
else
|
||||||
initType := TTypes.Void;
|
initType := TTypes.Void;
|
||||||
|
|
||||||
// 3. Get the address from the bound identifier.
|
// 3. Get the address from the bound identifier (SAFE CAST)
|
||||||
boundIdent := (Node.Identifier as TIdentifierNode);
|
boundIdent := Node.Identifier;
|
||||||
adr := boundIdent.Address;
|
adr := boundIdent.AsBoundIdentifierNode.Address;
|
||||||
|
|
||||||
// 4. Update the type in the scope descriptor (which was set to Unknown by the binder).
|
// 4. Update the type in the scope descriptor (which was set to Unknown by the binder).
|
||||||
FCurrentDescriptor.UpdateType(adr.SlotIndex, initType);
|
FCurrentDescriptor.UpdateType(adr.SlotIndex, initType);
|
||||||
|
|||||||
+43
-4
@@ -109,6 +109,8 @@ type
|
|||||||
function AsRecur: IRecurNode; virtual;
|
function AsRecur: IRecurNode; virtual;
|
||||||
function AsNop: INopNode; virtual; // Added Nop
|
function AsNop: INopNode; virtual; // Added Nop
|
||||||
|
|
||||||
|
function AsBoundIdentifierNode: IBoundIdentifierNode; virtual;
|
||||||
|
|
||||||
property StaticType: IStaticType read FStaticType write SetStaticType;
|
property StaticType: IStaticType read FStaticType write SetStaticType;
|
||||||
property Kind: TAstNodeKind read GetKind;
|
property Kind: TAstNodeKind read GetKind;
|
||||||
end;
|
end;
|
||||||
@@ -128,15 +130,24 @@ type
|
|||||||
TIdentifierNode = class(TAstNode, IIdentifierNode)
|
TIdentifierNode = class(TAstNode, IIdentifierNode)
|
||||||
private
|
private
|
||||||
FName: string;
|
FName: string;
|
||||||
FAddress: TResolvedAddress;
|
|
||||||
function GetName: string;
|
function GetName: string;
|
||||||
function GetKind: TAstNodeKind; override;
|
function GetKind: TAstNodeKind; override;
|
||||||
public
|
public
|
||||||
constructor Create(AName: string);
|
constructor Create(const AName: string);
|
||||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||||
function AsIdentifier: IIdentifierNode; override;
|
function AsIdentifier: IIdentifierNode; override;
|
||||||
property Name: string read FName; // Name ist immutable
|
property Name: string read FName; // Name ist immutable
|
||||||
property Address: TResolvedAddress read FAddress write FAddress;
|
end;
|
||||||
|
|
||||||
|
TBoundIdentifierNode = class(TIdentifierNode, IBoundIdentifierNode)
|
||||||
|
private
|
||||||
|
FAddress: TResolvedAddress;
|
||||||
|
function GetAddress: TResolvedAddress;
|
||||||
|
function GetKind: TAstNodeKind; override;
|
||||||
|
public
|
||||||
|
constructor Create(const AName: string; const AAddress: TResolvedAddress);
|
||||||
|
function AsBoundIdentifierNode: IBoundIdentifierNode; override;
|
||||||
|
property Address: TResolvedAddress read GetAddress;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TKeywordNode = class(TAstNode, IKeywordNode)
|
TKeywordNode = class(TAstNode, IKeywordNode)
|
||||||
@@ -724,6 +735,11 @@ begin
|
|||||||
raise ETypeException.Create('Node is not a BlockExpression');
|
raise ETypeException.Create('Node is not a BlockExpression');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TAstNode.AsBoundIdentifierNode: IBoundIdentifierNode;
|
||||||
|
begin
|
||||||
|
raise ETypeException.Create('Node is not a bound identifier');
|
||||||
|
end;
|
||||||
|
|
||||||
function TAstNode.AsConstant: IConstantNode;
|
function TAstNode.AsConstant: IConstantNode;
|
||||||
begin
|
begin
|
||||||
raise ETypeException.Create('Node is not a Constant');
|
raise ETypeException.Create('Node is not a Constant');
|
||||||
@@ -865,7 +881,7 @@ end;
|
|||||||
|
|
||||||
{ TIdentifierNode }
|
{ TIdentifierNode }
|
||||||
|
|
||||||
constructor TIdentifierNode.Create(AName: string);
|
constructor TIdentifierNode.Create(const AName: string);
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FName := AName;
|
FName := AName;
|
||||||
@@ -1629,4 +1645,27 @@ begin
|
|||||||
Result := akSeriesLength;
|
Result := akSeriesLength;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TBoundIdentifierNode }
|
||||||
|
|
||||||
|
constructor TBoundIdentifierNode.Create(const AName: string; const AAddress: TResolvedAddress);
|
||||||
|
begin
|
||||||
|
inherited Create(AName);
|
||||||
|
FAddress := AAddress;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TBoundIdentifierNode.AsBoundIdentifierNode: IBoundIdentifierNode;
|
||||||
|
begin
|
||||||
|
Result := Self;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TBoundIdentifierNode.GetAddress: TResolvedAddress;
|
||||||
|
begin
|
||||||
|
Result := FAddress;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TBoundIdentifierNode.GetKind: TAstNodeKind;
|
||||||
|
begin
|
||||||
|
Result := akBoundIdentifier;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
Reference in New Issue
Block a user