Refactoring node to be immutable

This commit is contained in:
Michael Schimmel
2025-11-05 15:24:54 +01:00
parent 25984fe61a
commit b98e7d98e6
6 changed files with 50 additions and 94 deletions
+3 -3
View File
@@ -256,7 +256,7 @@ begin
// --- Replace Node --- // --- Replace Node ---
// Create a new TBoundIdentifierNode (implementation is in Myc.Ast) // Create a new TBoundIdentifierNode (implementation is in Myc.Ast)
var boundParamNode := TAst.BoundIdentifier(paramNode.Name, adr); var boundParamNode := TAst.Identifier(paramNode.Name, adr);
// Replace it in the parameter array // Replace it in the parameter array
N.Parameters[i] := boundParamNode; N.Parameters[i] := boundParamNode;
end; end;
@@ -345,7 +345,7 @@ begin
// --- Replace Node --- // --- Replace Node ---
// Create the new TBoundIdentifierNode (implementation is in Myc.Ast) // Create the new TBoundIdentifierNode (implementation is in Myc.Ast)
Result := TAst.BoundIdentifier(Node.Name, adr); Result := TAst.Identifier(Node.Name, adr);
end end
else else
begin begin
@@ -375,7 +375,7 @@ begin
// 3. --- Replace Node --- // 3. --- Replace Node ---
// Replace the TIdentifierNode with a new TBoundIdentifierNode // Replace the TIdentifierNode with a new TBoundIdentifierNode
N.Identifier := TAst.BoundIdentifier(N.Identifier.Name, address); N.Identifier := TAst.Identifier(N.Identifier.Name, address);
// 4. Mutate this declaration node // 4. Mutate this declaration node
N.IsBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node); N.IsBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node);
+1 -2
View File
@@ -145,8 +145,7 @@ procedure TAstDumper.VisitIdentifier(const Node: IIdentifierNode);
var var
adr: TResolvedAddress; adr: TResolvedAddress;
begin begin
if Node.Kind = akBoundIdentifier then adr := Node.AsIdentifier.Address;
adr := Node.AsBoundIdentifierNode.Address;
if adr.Kind <> akUnresolved then if adr.Kind <> akUnresolved then
LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress(adr)]) LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress(adr)])
+6 -6
View File
@@ -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].AsBoundIdentifierNode.Address.SlotIndex; // Changed adr.SlotIndex := params[i].AsIdentifier.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.AsBoundIdentifierNode.Address]; // Changed seriesVar := FScope[Node.Series.AsIdentifier.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.AsBoundIdentifierNode.Address] := Result; // Changed FScope[Node.Identifier.AsIdentifier.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.AsBoundIdentifierNode.Address]; // Changed Result := FScope[Node.AsIdentifier.Address];
end; end;
function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue; function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
@@ -549,7 +549,7 @@ begin
// After binding, all declaration nodes are TVariableDeclarationNode // After binding, all declaration nodes are TVariableDeclarationNode
boundNode := Node as TVariableDeclarationNode; boundNode := Node as TVariableDeclarationNode;
address := boundNode.Identifier.AsBoundIdentifierNode.Address; address := boundNode.Identifier.AsIdentifier.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.AsBoundIdentifierNode.Address]; // Changed seriesValue := FScope[Node.Series.AsIdentifier.Address]; // Changed
case seriesValue.Kind of case seriesValue.Kind of
vkSeries: len := seriesValue.AsSeries.Count; vkSeries: len := seriesValue.AsSeries.Count;
+3 -12
View File
@@ -39,7 +39,6 @@ type
ISeriesLengthNode = interface; ISeriesLengthNode = interface;
IRecurNode = interface; IRecurNode = interface;
INopNode = interface; INopNode = interface;
IBoundIdentifierNode = interface;
IAstTypedNode = interface; IAstTypedNode = interface;
// Defines the concrete kinds of AST nodes // Defines the concrete kinds of AST nodes
@@ -68,9 +67,7 @@ type
akAddSeriesItem, akAddSeriesItem,
akSeriesLength, akSeriesLength,
akRecur, akRecur,
akNop, akNop
// Internal node produced by compiler
akBoundIdentifier
); );
// Helper for TAstNodeKind providing a string representation // Helper for TAstNodeKind providing a string representation
@@ -162,7 +159,6 @@ type
function AsRecur: IRecurNode; function AsRecur: IRecurNode;
function AsNop: INopNode; function AsNop: INopNode;
function AsBoundIdentifierNode: IBoundIdentifierNode;
function AsTypedNode: IAstTypedNode; function AsTypedNode: IAstTypedNode;
property IsTyped: Boolean read GetIsTyped; property IsTyped: Boolean read GetIsTyped;
@@ -191,16 +187,11 @@ type
IIdentifierNode = interface(IAstTypedNode) IIdentifierNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetAddress: TResolvedAddress;
function GetName: string; function GetName: string;
{$endregion} {$endregion}
property Name: string read GetName;
end;
IBoundIdentifierNode = interface(IIdentifierNode)
{$region 'private'}
function GetAddress: TResolvedAddress;
{$endregion}
property Address: TResolvedAddress read GetAddress; property Address: TResolvedAddress read GetAddress;
property Name: string read GetName;
end; end;
// Represents a keyword literal in the AST (e.g., :foo) // Represents a keyword literal in the AST (e.g., :foo)
+8 -8
View File
@@ -111,10 +111,10 @@ begin
// Get the type from the descriptor (which was populated by Binder/RTL) // Get the type from the descriptor (which was populated by Binder/RTL)
symbol := FCurrentDescriptor.FindSymbol(Node.Name); symbol := FCurrentDescriptor.FindSymbol(Node.Name);
adr := Node.AsBoundIdentifierNode.Address; adr := Node.AsIdentifier.Address;
// Create a new node, copying the address and assigning the inferred type // Create a new node, copying the address and assigning the inferred type
Result := TAst.BoundIdentifier(Node.Name, adr, symbol.StaticType); Result := TAst.Identifier(Node.Name, adr, symbol.StaticType);
end; end;
function TTypeChecker.VisitRecurNode(const Node: IRecurNode): IAstNode; function TTypeChecker.VisitRecurNode(const Node: IRecurNode): IAstNode;
@@ -153,14 +153,14 @@ begin
// 3. Get the address from the bound identifier (SAFE CAST) // 3. Get the address from the bound identifier (SAFE CAST)
boundIdent := Node.Identifier; boundIdent := Node.Identifier;
adr := boundIdent.AsBoundIdentifierNode.Address; adr := boundIdent.AsIdentifier.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).
if initType.Kind <> stUnknown then if initType.Kind <> stUnknown then
FCurrentDescriptor.UpdateType(adr.SlotIndex, initType); FCurrentDescriptor.UpdateType(adr.SlotIndex, initType);
// 5. Create the new (typed) identifier node // 5. Create the new (typed) identifier node
newIdent := TAst.BoundIdentifier(boundIdent.Name, adr, initType); newIdent := TAst.Identifier(boundIdent.Name, adr, initType);
// 6. Create the new VariableDeclaration node // 6. Create the new VariableDeclaration node
Result := TVariableDeclarationNode.Create(newIdent.AsIdentifier, newInitializer, initType); Result := TVariableDeclarationNode.Create(newIdent.AsIdentifier, newInitializer, initType);
@@ -192,11 +192,11 @@ begin
// with the new, inferred type. This enables recursion. // with the new, inferred type. This enables recursion.
if (targetType.Kind = stUnknown) and (sourceType.Kind <> stUnknown) then if (targetType.Kind = stUnknown) and (sourceType.Kind <> stUnknown) then
begin begin
adr := newIdent.AsBoundIdentifierNode.Address; adr := newIdent.AsIdentifier.Address;
FCurrentDescriptor.UpdateType(adr.SlotIndex, sourceType); FCurrentDescriptor.UpdateType(adr.SlotIndex, sourceType);
// Re-create the identifier node *with the new type* // Re-create the identifier node *with the new type*
newIdent := TAst.BoundIdentifier(newIdent.AsIdentifier.Name, adr, sourceType); newIdent := TAst.Identifier(newIdent.AsIdentifier.Name, adr, sourceType);
targetType := sourceType; targetType := sourceType;
end; end;
@@ -229,8 +229,8 @@ begin
// Parameters are leaves, but we must *replace* them with typed versions // Parameters are leaves, but we must *replace* them with typed versions
// (even if they are just TTypes.Unknown for now, for type inference placeholders) // (even if they are just TTypes.Unknown for now, for type inference placeholders)
var paramIdent := boundNode.Parameters[i]; var paramIdent := boundNode.Parameters[i];
var paramAdr := paramIdent.AsBoundIdentifierNode.Address; var paramAdr := paramIdent.AsIdentifier.Address;
var newParam := TAst.BoundIdentifier(paramIdent.Name, paramAdr); var newParam := TAst.Identifier(paramIdent.Name, paramAdr);
newParams[i] := newParam; newParams[i] := newParam;
paramTypes[i] := TTypes.Unknown; paramTypes[i] := TTypes.Unknown;
+29 -63
View File
@@ -36,7 +36,12 @@ type
class function Constant(const AValue: TDataValue; const AStaticType: IStaticType = nil): IConstantNode; overload; static; class function Constant(const AValue: TDataValue; const AStaticType: IStaticType = nil): IConstantNode; overload; static;
class function Constant(const AValue: String): IConstantNode; overload; static; class function Constant(const AValue: String): IConstantNode; overload; static;
class function Keyword(const AName: string): IKeywordNode; static; class function Keyword(const AName: string): IKeywordNode; static;
class function Identifier(AName: string; const AStaticType: IStaticType = nil): IIdentifierNode; static; class function Identifier(AName: string; const AStaticType: IStaticType = nil): IIdentifierNode; overload; static;
class function Identifier(
AName: string;
const Address: TResolvedAddress;
const AStaticType: IStaticType = nil
): IIdentifierNode; overload; static;
class function BinaryExpr( class function BinaryExpr(
ALeft: IAstNode; ALeft: IAstNode;
AOperator: TScalar.TBinaryOp; AOperator: TScalar.TBinaryOp;
@@ -112,11 +117,6 @@ type
): IAddSeriesItemNode; static; ): IAddSeriesItemNode; static;
class function SeriesLength(const ASeries: IIdentifierNode; const AStaticType: IStaticType = nil): ISeriesLengthNode; static; class function SeriesLength(const ASeries: IIdentifierNode; const AStaticType: IStaticType = nil): ISeriesLengthNode; static;
class function BoundIdentifier(
AName: string;
const Address: TResolvedAddress;
const AStaticType: IStaticType = nil
): IBoundIdentifierNode; static;
end; end;
// Common base class for AST nodes to reduce boilerplate. // Common base class for AST nodes to reduce boilerplate.
@@ -155,7 +155,6 @@ 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;
function AsTypedNode: IAstTypedNode; virtual; function AsTypedNode: IAstTypedNode; virtual;
property IsTyped: Boolean read GetIsTyped; property IsTyped: Boolean read GetIsTyped;
@@ -271,25 +270,17 @@ type
TIdentifierNode = class(TAstTypedNode, IIdentifierNode) TIdentifierNode = class(TAstTypedNode, IIdentifierNode)
private private
FAddress: TResolvedAddress;
FName: string; FName: string;
function GetAddress: TResolvedAddress;
function GetName: string; function GetName: string;
function GetKind: TAstNodeKind; override; function GetKind: TAstNodeKind; override;
public public
constructor Create(const AName: string; const AStaticType: IStaticType); constructor Create(const AName: string; const AAddress: TResolvedAddress; const AStaticType: IStaticType);
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;
end;
TBoundIdentifierNode = class(TIdentifierNode, IBoundIdentifierNode)
private
FAddress: TResolvedAddress;
function GetAddress: TResolvedAddress;
function GetKind: TAstNodeKind; override;
public
constructor Create(const AName: string; const AAddress: TResolvedAddress; const AStaticType: IStaticType);
function AsBoundIdentifierNode: IBoundIdentifierNode; override;
property Address: TResolvedAddress read GetAddress; property Address: TResolvedAddress read GetAddress;
property Name: string read FName;
end; end;
TKeywordNode = class(TAstTypedNode, IKeywordNode) TKeywordNode = class(TAstTypedNode, IKeywordNode)
@@ -701,21 +692,6 @@ begin
FLibraries.Free; FLibraries.Free;
end; end;
class function TAst.BoundIdentifier(
AName: string;
const Address: TResolvedAddress;
const AStaticType: IStaticType = nil
): IBoundIdentifierNode;
begin
Result :=
TBoundIdentifierNode.Create(
AName,
Address,
if AStaticType <> nil then AStaticType
else TTypes.Unknown
);
end;
class procedure TAst.RegisterLibrary(const AProc: TRegisterLibraryProc); class procedure TAst.RegisterLibrary(const AProc: TRegisterLibraryProc);
begin begin
FLibraries.Add(AProc); FLibraries.Add(AProc);
@@ -741,6 +717,18 @@ begin
Result := Result :=
TIdentifierNode.Create( TIdentifierNode.Create(
AName, AName,
Default(TResolvedAddress),
if AStaticType <> nil then AStaticType
else TTypes.Unknown
);
end;
class function TAst.Identifier(AName: string; const Address: TResolvedAddress; const AStaticType: IStaticType = nil): IIdentifierNode;
begin
Result :=
TIdentifierNode.Create(
AName,
Address,
if AStaticType <> nil then AStaticType if AStaticType <> nil then AStaticType
else TTypes.Unknown else TTypes.Unknown
); );
@@ -971,11 +959,6 @@ 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');
@@ -1146,10 +1129,11 @@ end;
{ TIdentifierNode } { TIdentifierNode }
constructor TIdentifierNode.Create(const AName: string; const AStaticType: IStaticType); constructor TIdentifierNode.Create(const AName: string; const AAddress: TResolvedAddress; const AStaticType: IStaticType);
begin begin
inherited Create(AStaticType); inherited Create(AStaticType);
FName := AName; FName := AName;
FAddress := AAddress;
end; end;
function TIdentifierNode.Accept(const Visitor: IAstVisitor): TDataValue; function TIdentifierNode.Accept(const Visitor: IAstVisitor): TDataValue;
@@ -1162,6 +1146,11 @@ begin
Result := Self; Result := Self;
end; end;
function TIdentifierNode.GetAddress: TResolvedAddress;
begin
Result := FAddress;
end;
function TIdentifierNode.GetName: string; function TIdentifierNode.GetName: string;
begin begin
Result := FName; Result := FName;
@@ -1926,27 +1915,4 @@ begin
Result := akSeriesLength; Result := akSeriesLength;
end; end;
{ TBoundIdentifierNode }
constructor TBoundIdentifierNode.Create(const AName: string; const AAddress: TResolvedAddress; const AStaticType: IStaticType);
begin
inherited Create(AName, AStaticType);
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.