Keywords as basic scalar type

Script enhancements
This commit is contained in:
Michael Schimmel
2025-11-01 00:56:59 +01:00
parent 689dede600
commit 957171f089
7 changed files with 498 additions and 78 deletions
+75 -24
View File
@@ -161,6 +161,15 @@ type
property Definition: IScalarRecordDefinition read FDefinition;
end;
// Represents a bound record literal that maps to a generic (non-scalar) record
TBoundGenericRecordLiteralNode = class(TRecordLiteralNode)
private
FDefinition: IGenericRecordDefinition;
public
constructor Create(const AFields: TArray<TRecordFieldLiteral>; const ADef: IGenericRecordDefinition);
property Definition: IGenericRecordDefinition read FDefinition;
end;
implementation
uses
@@ -340,6 +349,13 @@ begin
FIsTailCall := AIsTailCall;
end;
{ TBoundGenericRecordLiteralNode }
constructor TBoundGenericRecordLiteralNode.Create(const AFields: TArray<TRecordFieldLiteral>; const ADef: IGenericRecordDefinition);
begin
inherited Create(AFields);
FDefinition := ADef;
end;
{ TBoundRecordLiteralNode }
constructor TBoundRecordLiteralNode.Create(const AFields: TArray<TRecordFieldLiteral>; const ADef: IScalarRecordDefinition);
begin
@@ -932,20 +948,35 @@ begin
elemType := TTypes.Unknown;
if (baseType.Kind <> TStaticTypeKind.stUnknown) then
begin
if (baseType.Kind <> TStaticTypeKind.stRecord) and (baseType.Kind <> TStaticTypeKind.stRecordSeries) then
raise ETypeException.CreateFmt('Member access requires a record or record series, but got %s', [baseType.ToString]);
if (baseType.Kind = TStaticTypeKind.stRecord) or (baseType.Kind = TStaticTypeKind.stRecordSeries) then
begin
// --- SALAR PATH ---
fieldIndex := baseType.Definition.IndexOf(Node.Member.Value);
if fieldIndex < 0 then
raise ETypeException.CreateFmt('Member "%s" not found in type %s', [Node.Member.Value.Name, baseType.ToString]);
// Use IndexOf(string) which correctly delegates to IndexOf(IKeyword)
fieldIndex := baseType.Definition.IndexOf(Node.Member.Value);
if fieldIndex < 0 then
raise ETypeException.CreateFmt('Member "%s" not found in type %s', [Node.Member.Value.Name, baseType.ToString]);
var fieldType := TTypes.FromScalarKind(baseType.Definition.Fields[fieldIndex].Value);
var fieldType := TTypes.FromScalarKind(baseType.Definition.Fields[fieldIndex].Value);
if baseType.Kind = TStaticTypeKind.stRecord then
elemType := fieldType
else // stRecordSeries
elemType := TTypes.CreateSeries(fieldType);
end
else if (baseType.Kind = TStaticTypeKind.stGenericRecord) then
begin
// --- GENERIC PATH ---
var genDef := baseType.GenericDefinition;
fieldIndex := genDef.IndexOf(Node.Member.Value);
if fieldIndex < 0 then
raise ETypeException.CreateFmt('Member "%s" not found in type %s', [Node.Member.Value.Name, baseType.ToString]);
if baseType.Kind = TStaticTypeKind.stRecord then
elemType := fieldType
else // stRecordSeries
elemType := TTypes.CreateSeries(fieldType);
// Type is stored directly in the generic definition
elemType := genDef.Fields[fieldIndex].Value;
end
else
begin
raise ETypeException.CreateFmt('Member access requires a record type, but got %s', [baseType.ToString]);
end;
end;
var boundNode := TAst.MemberAccess(baseNode, Node.Member);
@@ -956,24 +987,25 @@ function TAstBinder.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataVal
var
i: Integer;
boundFields: TArray<TRecordFieldLiteral>;
defFields: TArray<TScalarRecordField>;
scalarDefFields: TArray<TScalarRecordField>; // Renamed
def: IScalarRecordDefinition;
staticType: IStaticType;
boundNode: IRecordLiteralNode;
valNode: IAstNode;
valType: IStaticType;
scalarKind: TScalar.TKind;
allScalar: Boolean; // Added
begin
FNextIsTail := False;
SetLength(boundFields, Length(Node.Fields));
SetLength(defFields, Length(Node.Fields));
SetLength(scalarDefFields, Length(Node.Fields)); // Renamed
allScalar := True; // Added
for i := 0 to High(Node.Fields) do
begin
valNode := Accept(Node.Fields[i].Value).AsIntf<IAstNode>;
valType := (valNode as TAstNode).StaticType;
// Path A: Records can now store Ordinal, Float, or Keyword
// Check if this field fits the scalar path
if (valType.Kind = stOrdinal) then
scalarKind := TScalar.TKind.Ordinal
else if (valType.Kind = stFloat) then
@@ -981,21 +1013,40 @@ begin
else if (valType.Kind = stKeyword) then
scalarKind := TScalar.TKind.Keyword
else
raise ETypeException.CreateFmt(
'Record fields must be scalar (Ordinal, Float, or Keyword), but field ":%s" is %s',
[Node.Fields[i].Key.Value.Name, valType.ToString]);
begin
allScalar := False; // It's a generic record
scalarKind := TScalar.TKind.Ordinal; // Dummy value, won't be used
end;
boundFields[i] := TRecordFieldLiteral.Create(Node.Fields[i].Key, valNode);
// Create the definition field using the Keyword and its TScalar.TKind
defFields[i] := TScalarRecordField.Create(Node.Fields[i].Key.Value, scalarKind);
// Conditionally fill the scalar definition array
if allScalar then
scalarDefFields[i] := TScalarRecordField.Create(Node.Fields[i].Key.Value, scalarKind);
end;
def := TScalarRecordRegistry.Intern(defFields);
staticType := TTypes.CreateRecord(def);
boundNode := TBoundRecordLiteralNode.Create(boundFields, def);
// Now, create the correct bound node based on the flag
if allScalar then
begin
// --- EXISTING SCALAR PATH ---
def := TScalarRecordRegistry.Intern(scalarDefFields);
staticType := TTypes.CreateRecord(def);
var boundNode := TBoundRecordLiteralNode.Create(boundFields, def); // Old bound node
Result := SetType(TDataValue.FromIntf<IRecordLiteralNode>(boundNode), staticType);
end
else
begin
// --- NEW GENERIC PATH ---
var genDefFields: TArray<TPair<IKeyword, IStaticType>>;
SetLength(genDefFields, Length(boundFields));
for i := 0 to High(boundFields) do
genDefFields[i] := TPair<IKeyword, IStaticType>.Create(boundFields[i].Key.Value, (boundFields[i].Value as TAstNode).StaticType);
Result := SetType(TDataValue.FromIntf<IRecordLiteralNode>(boundNode), staticType);
var genDef := TGenericRecordRegistry.Intern(genDefFields);
staticType := TTypes.CreateGenericRecord(genDef);
var genBoundNode := TBoundGenericRecordLiteralNode.Create(boundFields, genDef);
Result := SetType(TDataValue.FromIntf<IRecordLiteralNode>(genBoundNode), staticType);
end;
end;
function TAstBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;