Finished node immutability

This commit is contained in:
Michael Schimmel
2025-11-05 18:33:29 +01:00
parent 1f0eef7698
commit 5003cfd899
5 changed files with 71 additions and 80 deletions
-20
View File
@@ -91,26 +91,6 @@ var
left, right: IAstNode; left, right: IAstNode;
nodeType: IStaticType; nodeType: IStaticType;
begin begin
// Get the type *before* replacing the node (it's an IAstTypedNode)
nodeType := Node.AsTypedNode.StaticType;
// --- Transformation: Keyword-as-Function ---
if Node.Callee.Kind = akKeyword then
begin
var keywordNode := Node.Callee.AsKeyword;
if Length(Node.Arguments) <> 1 then
raise EArgumentException.CreateFmt(
'Keyword :%s expects exactly one argument (the record/map), but got %d',
[keywordNode.Value.Name, Length(Node.Arguments)]);
// Visit the base node
var baseNode := Accept(Node.Arguments[0]);
// Create the new MemberAccess node, preserving the type
Result := TAst.MemberAccess(baseNode, keywordNode, nodeType);
exit;
end;
// --- Optimization: Operator Folding --- // --- Optimization: Operator Folding ---
if Node.Callee.Kind = akIdentifier then if Node.Callee.Kind = akIdentifier then
begin begin
+4
View File
@@ -362,8 +362,12 @@ type
IRecordLiteralNode = interface(IAstTypedNode) IRecordLiteralNode = interface(IAstTypedNode)
{$region 'private'} {$region 'private'}
function GetFields: TArray<TRecordFieldLiteral>; function GetFields: TArray<TRecordFieldLiteral>;
function GetGenericDefinition: IGenericRecordDefinition;
function GetScalarDefinition: IScalarRecordDefinition;
{$endregion} {$endregion}
property Fields: TArray<TRecordFieldLiteral> read GetFields; property Fields: TArray<TRecordFieldLiteral> read GetFields;
property GenericDefinition: IGenericRecordDefinition read GetGenericDefinition;
property ScalarDefinition: IScalarRecordDefinition read GetScalarDefinition;
end; end;
ICreateSeriesNode = interface(IAstTypedNode) ICreateSeriesNode = interface(IAstTypedNode)
+2 -4
View File
@@ -540,8 +540,7 @@ begin
begin begin
def := TScalarRecordRegistry.Intern(scalarDefFields); def := TScalarRecordRegistry.Intern(scalarDefFields);
staticType := TTypes.CreateRecord(def); staticType := TTypes.CreateRecord(def);
Result := TRecordLiteralNode.Create(newFields, staticType); Result := TAst.RecordLiteral(newFields, def, nil, staticType);
(Result as TRecordLiteralNode).Definition := def;
end end
else else
begin begin
@@ -552,8 +551,7 @@ begin
var genDef := TGenericRecordRegistry.Intern(genDefFields); var genDef := TGenericRecordRegistry.Intern(genDefFields);
staticType := TTypes.CreateGenericRecord(genDef); staticType := TTypes.CreateGenericRecord(genDef);
Result := TGenericRecordLiteralNode.Create(newFields, staticType); Result := TAst.RecordLiteral(newFields, nil, genDef, staticType);
(Result as TGenericRecordLiteralNode).GenericDefinition := genDef;
end; end;
end; end;
+6 -14
View File
@@ -697,20 +697,18 @@ end;
function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
var var
N: IRecordLiteralNode; // Use interface
i: Integer; i: Integer;
newFields: TArray<TRecordFieldLiteral>; newFields: TArray<TRecordFieldLiteral>;
hasChanged: Boolean; hasChanged: Boolean;
begin begin
N := Node; SetLength(newFields, Length(Node.Fields));
SetLength(newFields, Length(N.Fields));
hasChanged := False; hasChanged := False;
for i := 0 to High(N.Fields) do for i := 0 to High(Node.Fields) do
begin begin
newFields[i].Key := Accept(N.Fields[i].Key).AsKeyword; newFields[i].Key := Accept(Node.Fields[i].Key).AsKeyword;
newFields[i].Value := Accept(N.Fields[i].Value); newFields[i].Value := Accept(Node.Fields[i].Value);
if (newFields[i].Key <> N.Fields[i].Key) or (newFields[i].Value <> N.Fields[i].Value) then if (newFields[i].Key <> Node.Fields[i].Key) or (newFields[i].Value <> Node.Fields[i].Value) then
hasChanged := True; hasChanged := True;
end; end;
@@ -720,13 +718,7 @@ begin
begin begin
// Rebuild the node, preserving its specific type and definitions // Rebuild the node, preserving its specific type and definitions
// Use TAst factory // Use TAst factory
Result := TAst.RecordLiteral(newFields, N.StaticType); Result := TAst.RecordLiteral(newFields, Node.ScalarDefinition, Node.GenericDefinition, Node.StaticType);
// Copy runtime properties (casts are valid)
if N is TGenericRecordLiteralNode then
(Result as TGenericRecordLiteralNode).GenericDefinition := (N as TGenericRecordLiteralNode).GenericDefinition
else
(Result as TRecordLiteralNode).Definition := (N as TRecordLiteralNode).Definition;
end; end;
end; end;
+59 -42
View File
@@ -120,6 +120,8 @@ type
): IMemberAccessNode; static; ): IMemberAccessNode; static;
class function RecordLiteral( class function RecordLiteral(
const AFields: TArray<TRecordFieldLiteral>; const AFields: TArray<TRecordFieldLiteral>;
const AScalarDefinition: IScalarRecordDefinition = nil;
const AGenericDefinition: IGenericRecordDefinition = nil;
const AStaticType: IStaticType = nil const AStaticType: IStaticType = nil
): IRecordLiteralNode; static; ): IRecordLiteralNode; static;
class function CreateSeries(const ADefinition: String; const AStaticType: IStaticType = nil): ICreateSeriesNode; static; class function CreateSeries(const ADefinition: String; const AStaticType: IStaticType = nil): ICreateSeriesNode; static;
@@ -133,6 +135,14 @@ type
end; end;
implementation
uses
System.Generics.Defaults;
type
// --- Concrete Class Definitions moved to implementation ---
// Common base class for AST nodes to reduce boilerplate. // Common base class for AST nodes to reduce boilerplate.
TAstNode = class(TInterfacedObject, IAstNode) TAstNode = class(TInterfacedObject, IAstNode)
private private
@@ -186,38 +196,6 @@ type
property StaticType: IStaticType read GetStaticType; property StaticType: IStaticType read GetStaticType;
end; end;
// TRecordLiteralNode is still needed in the interface
// for TGenericRecordLiteralNode
TRecordLiteralNode = class(TAstTypedNode, IRecordLiteralNode)
private
FFields: TArray<TRecordFieldLiteral>;
FDefinition: IScalarRecordDefinition;
function GetFields: TArray<TRecordFieldLiteral>;
function GetKind: TAstNodeKind; override;
public
constructor Create(const AFields: TArray<TRecordFieldLiteral>; const AStaticType: IStaticType);
function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsRecordLiteral: IRecordLiteralNode; override;
property Fields: TArray<TRecordFieldLiteral> read FFields;
property Definition: IScalarRecordDefinition read FDefinition write FDefinition;
end;
TGenericRecordLiteralNode = class(TRecordLiteralNode, IRecordLiteralNode)
private
FGenericDefinition: IGenericRecordDefinition;
public
constructor Create(const AFields: TArray<TRecordFieldLiteral>; const AStaticType: IStaticType);
property GenericDefinition: IGenericRecordDefinition read FGenericDefinition write FGenericDefinition;
end;
implementation
uses
System.Generics.Defaults;
type
// --- Concrete Class Definitions moved to implementation ---
TLambdaExpressionNode = class(TAstTypedNode, ILambdaExpressionNode) TLambdaExpressionNode = class(TAstTypedNode, ILambdaExpressionNode)
private private
FParameters: TArray<IIdentifierNode>; FParameters: TArray<IIdentifierNode>;
@@ -580,6 +558,31 @@ type
property Series: IIdentifierNode read FSeries; property Series: IIdentifierNode read FSeries;
end; end;
// TRecordLiteralNode is still needed in the interface
// for TGenericRecordLiteralNode
TRecordLiteralNode = class(TAstTypedNode, IRecordLiteralNode)
private
FFields: TArray<TRecordFieldLiteral>;
FScalarDefinition: IScalarRecordDefinition;
FGenericDefinition: IGenericRecordDefinition;
function GetFields: TArray<TRecordFieldLiteral>;
function GetGenericDefinition: IGenericRecordDefinition;
function GetKind: TAstNodeKind; override;
function GetScalarDefinition: IScalarRecordDefinition;
public
constructor Create(
const AFields: TArray<TRecordFieldLiteral>;
const AScalarDefinition: IScalarRecordDefinition;
const AGenericDefinition: IGenericRecordDefinition;
const AStaticType: IStaticType
);
function Accept(const Visitor: IAstVisitor): TDataValue; override;
function AsRecordLiteral: IRecordLiteralNode; override;
property Fields: TArray<TRecordFieldLiteral> read FFields;
property ScalarDefinition: IScalarRecordDefinition read GetScalarDefinition;
property GenericDefinition: IGenericRecordDefinition read GetGenericDefinition;
end;
TNopNode = class(TAstNode, INopNode) TNopNode = class(TAstNode, INopNode)
private private
function GetKind: TAstNodeKind; override; function GetKind: TAstNodeKind; override;
@@ -882,14 +885,18 @@ begin
); );
end; end;
class function TAst.RecordLiteral(const AFields: TArray<TRecordFieldLiteral>; const AStaticType: IStaticType = nil): IRecordLiteralNode; class function TAst.RecordLiteral(
const AFields: TArray<TRecordFieldLiteral>;
const AScalarDefinition: IScalarRecordDefinition = nil;
const AGenericDefinition: IGenericRecordDefinition = nil;
const AStaticType: IStaticType = nil
): IRecordLiteralNode;
begin begin
// By default, the parser creates the generic (most flexible) node type.
// The TypeChecker (Phase 3) is responsible for "lowering" this to a
// TRecordLiteralNode if it determines all fields are scalar.
Result := Result :=
TGenericRecordLiteralNode.Create( TRecordLiteralNode.Create(
AFields, AFields,
AScalarDefinition,
AGenericDefinition,
if AStaticType <> nil then AStaticType if AStaticType <> nil then AStaticType
else TTypes.Unknown else TTypes.Unknown
); );
@@ -1870,10 +1877,17 @@ end;
{ TRecordLiteralNode } { TRecordLiteralNode }
constructor TRecordLiteralNode.Create(const AFields: TArray<TRecordFieldLiteral>; const AStaticType: IStaticType); constructor TRecordLiteralNode.Create(
const AFields: TArray<TRecordFieldLiteral>;
const AScalarDefinition: IScalarRecordDefinition;
const AGenericDefinition: IGenericRecordDefinition;
const AStaticType: IStaticType
);
begin begin
inherited Create(AStaticType); inherited Create(AStaticType);
FFields := AFields; FFields := AFields;
FGenericDefinition := AGenericDefinition;
FScalarDefinition := AScalarDefinition;
end; end;
function TRecordLiteralNode.Accept(const Visitor: IAstVisitor): TDataValue; function TRecordLiteralNode.Accept(const Visitor: IAstVisitor): TDataValue;
@@ -1891,16 +1905,19 @@ begin
Result := FFields; Result := FFields;
end; end;
function TRecordLiteralNode.GetGenericDefinition: IGenericRecordDefinition;
begin
Result := FGenericDefinition;
end;
function TRecordLiteralNode.GetKind: TAstNodeKind; function TRecordLiteralNode.GetKind: TAstNodeKind;
begin begin
Result := akRecordLiteral; Result := akRecordLiteral;
end; end;
{ TGenericRecordLiteralNode } function TRecordLiteralNode.GetScalarDefinition: IScalarRecordDefinition;
constructor TGenericRecordLiteralNode.Create(const AFields: TArray<TRecordFieldLiteral>; const AStaticType: IStaticType);
begin begin
inherited Create(AFields, AStaticType); Result := FScalarDefinition;
end; end;
{ TCreateSeriesNode } { TCreateSeriesNode }