From b98e7d98e61fa316920211c1a4766593ce41d77f Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Wed, 5 Nov 2025 15:24:54 +0100 Subject: [PATCH] Refactoring node to be immutable --- Src/AST/Myc.Ast.Binding.pas | 6 +-- Src/AST/Myc.Ast.Dumper.pas | 3 +- Src/AST/Myc.Ast.Evaluator.pas | 12 ++--- Src/AST/Myc.Ast.Nodes.pas | 15 ++---- Src/AST/Myc.Ast.TypeChecker.pas | 16 +++--- Src/AST/Myc.Ast.pas | 92 +++++++++++---------------------- 6 files changed, 50 insertions(+), 94 deletions(-) diff --git a/Src/AST/Myc.Ast.Binding.pas b/Src/AST/Myc.Ast.Binding.pas index 97e0f3e..b18e1c7 100644 --- a/Src/AST/Myc.Ast.Binding.pas +++ b/Src/AST/Myc.Ast.Binding.pas @@ -256,7 +256,7 @@ begin // --- Replace Node --- // 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 N.Parameters[i] := boundParamNode; end; @@ -345,7 +345,7 @@ begin // --- Replace Node --- // Create the new TBoundIdentifierNode (implementation is in Myc.Ast) - Result := TAst.BoundIdentifier(Node.Name, adr); + Result := TAst.Identifier(Node.Name, adr); end else begin @@ -375,7 +375,7 @@ begin // 3. --- Replace Node --- // 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 N.IsBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node); diff --git a/Src/AST/Myc.Ast.Dumper.pas b/Src/AST/Myc.Ast.Dumper.pas index adf96d1..6bebd60 100644 --- a/Src/AST/Myc.Ast.Dumper.pas +++ b/Src/AST/Myc.Ast.Dumper.pas @@ -145,8 +145,7 @@ procedure TAstDumper.VisitIdentifier(const Node: IIdentifierNode); var adr: TResolvedAddress; begin - if Node.Kind = akBoundIdentifier then - adr := Node.AsBoundIdentifierNode.Address; + adr := Node.AsIdentifier.Address; if adr.Kind <> akUnresolved then LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress(adr)]) diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index 47226ed..a230e28 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -234,7 +234,7 @@ begin for i := 0 to High(ArgValues) do begin // 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]; end; @@ -334,7 +334,7 @@ var itemValue, lookbackValue, seriesVar: TDataValue; lookback: Int64; begin - seriesVar := FScope[Node.Series.AsBoundIdentifierNode.Address]; // Changed + seriesVar := FScope[Node.Series.AsIdentifier.Address]; // Changed itemValue := Node.Value.Accept(Self); lookback := -1; @@ -368,7 +368,7 @@ begin // Evaluate the new value. Result := Node.Value.Accept(Self); // 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; function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue; @@ -405,7 +405,7 @@ end; function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue; begin // The scope's GetValues implementation now handles unboxing automatically. - Result := FScope[Node.AsBoundIdentifierNode.Address]; // Changed + Result := FScope[Node.AsIdentifier.Address]; end; function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue; @@ -549,7 +549,7 @@ begin // After binding, all declaration nodes are TVariableDeclarationNode boundNode := Node as TVariableDeclarationNode; - address := boundNode.Identifier.AsBoundIdentifierNode.Address; + address := boundNode.Identifier.AsIdentifier.Address; // Check the IsBoxed flag set by the binder. if boundNode.IsBoxed then @@ -641,7 +641,7 @@ var seriesValue: TDataValue; len: Int64; begin - seriesValue := FScope[Node.Series.AsBoundIdentifierNode.Address]; // Changed + seriesValue := FScope[Node.Series.AsIdentifier.Address]; // Changed case seriesValue.Kind of vkSeries: len := seriesValue.AsSeries.Count; diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index 5a87337..c9fdd49 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -39,7 +39,6 @@ type ISeriesLengthNode = interface; IRecurNode = interface; INopNode = interface; - IBoundIdentifierNode = interface; IAstTypedNode = interface; // Defines the concrete kinds of AST nodes @@ -68,9 +67,7 @@ type akAddSeriesItem, akSeriesLength, akRecur, - akNop, - // Internal node produced by compiler - akBoundIdentifier + akNop ); // Helper for TAstNodeKind providing a string representation @@ -162,7 +159,6 @@ type function AsRecur: IRecurNode; function AsNop: INopNode; - function AsBoundIdentifierNode: IBoundIdentifierNode; function AsTypedNode: IAstTypedNode; property IsTyped: Boolean read GetIsTyped; @@ -191,16 +187,11 @@ type IIdentifierNode = interface(IAstTypedNode) {$region 'private'} + function GetAddress: TResolvedAddress; function GetName: string; {$endregion} - property Name: string read GetName; - end; - - IBoundIdentifierNode = interface(IIdentifierNode) - {$region 'private'} - function GetAddress: TResolvedAddress; - {$endregion} property Address: TResolvedAddress read GetAddress; + property Name: string read GetName; end; // Represents a keyword literal in the AST (e.g., :foo) diff --git a/Src/AST/Myc.Ast.TypeChecker.pas b/Src/AST/Myc.Ast.TypeChecker.pas index c2f9990..1c2d531 100644 --- a/Src/AST/Myc.Ast.TypeChecker.pas +++ b/Src/AST/Myc.Ast.TypeChecker.pas @@ -111,10 +111,10 @@ begin // Get the type from the descriptor (which was populated by Binder/RTL) 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 - Result := TAst.BoundIdentifier(Node.Name, adr, symbol.StaticType); + Result := TAst.Identifier(Node.Name, adr, symbol.StaticType); end; function TTypeChecker.VisitRecurNode(const Node: IRecurNode): IAstNode; @@ -153,14 +153,14 @@ begin // 3. Get the address from the bound identifier (SAFE CAST) 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). if initType.Kind <> stUnknown then FCurrentDescriptor.UpdateType(adr.SlotIndex, initType); // 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 Result := TVariableDeclarationNode.Create(newIdent.AsIdentifier, newInitializer, initType); @@ -192,11 +192,11 @@ begin // with the new, inferred type. This enables recursion. if (targetType.Kind = stUnknown) and (sourceType.Kind <> stUnknown) then begin - adr := newIdent.AsBoundIdentifierNode.Address; + adr := newIdent.AsIdentifier.Address; FCurrentDescriptor.UpdateType(adr.SlotIndex, sourceType); // 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; end; @@ -229,8 +229,8 @@ begin // Parameters are leaves, but we must *replace* them with typed versions // (even if they are just TTypes.Unknown for now, for type inference placeholders) var paramIdent := boundNode.Parameters[i]; - var paramAdr := paramIdent.AsBoundIdentifierNode.Address; - var newParam := TAst.BoundIdentifier(paramIdent.Name, paramAdr); + var paramAdr := paramIdent.AsIdentifier.Address; + var newParam := TAst.Identifier(paramIdent.Name, paramAdr); newParams[i] := newParam; paramTypes[i] := TTypes.Unknown; diff --git a/Src/AST/Myc.Ast.pas b/Src/AST/Myc.Ast.pas index d99e306..9ab747a 100644 --- a/Src/AST/Myc.Ast.pas +++ b/Src/AST/Myc.Ast.pas @@ -36,7 +36,12 @@ type class function Constant(const AValue: TDataValue; const AStaticType: IStaticType = nil): IConstantNode; overload; static; class function Constant(const AValue: String): IConstantNode; overload; 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( ALeft: IAstNode; AOperator: TScalar.TBinaryOp; @@ -112,11 +117,6 @@ type ): IAddSeriesItemNode; 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; // Common base class for AST nodes to reduce boilerplate. @@ -155,7 +155,6 @@ type function AsRecur: IRecurNode; virtual; function AsNop: INopNode; virtual; // Added Nop - function AsBoundIdentifierNode: IBoundIdentifierNode; virtual; function AsTypedNode: IAstTypedNode; virtual; property IsTyped: Boolean read GetIsTyped; @@ -271,25 +270,17 @@ type TIdentifierNode = class(TAstTypedNode, IIdentifierNode) private + FAddress: TResolvedAddress; FName: string; + function GetAddress: TResolvedAddress; function GetName: string; function GetKind: TAstNodeKind; override; 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 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 Name: string read FName; end; TKeywordNode = class(TAstTypedNode, IKeywordNode) @@ -701,21 +692,6 @@ begin FLibraries.Free; 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); begin FLibraries.Add(AProc); @@ -741,6 +717,18 @@ begin Result := TIdentifierNode.Create( 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 else TTypes.Unknown ); @@ -971,11 +959,6 @@ begin raise ETypeException.Create('Node is not a BlockExpression'); end; -function TAstNode.AsBoundIdentifierNode: IBoundIdentifierNode; -begin - raise ETypeException.Create('Node is not a bound identifier'); -end; - function TAstNode.AsConstant: IConstantNode; begin raise ETypeException.Create('Node is not a Constant'); @@ -1146,10 +1129,11 @@ end; { TIdentifierNode } -constructor TIdentifierNode.Create(const AName: string; const AStaticType: IStaticType); +constructor TIdentifierNode.Create(const AName: string; const AAddress: TResolvedAddress; const AStaticType: IStaticType); begin inherited Create(AStaticType); FName := AName; + FAddress := AAddress; end; function TIdentifierNode.Accept(const Visitor: IAstVisitor): TDataValue; @@ -1162,6 +1146,11 @@ begin Result := Self; end; +function TIdentifierNode.GetAddress: TResolvedAddress; +begin + Result := FAddress; +end; + function TIdentifierNode.GetName: string; begin Result := FName; @@ -1926,27 +1915,4 @@ begin Result := akSeriesLength; 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.