This commit is contained in:
Michael Schimmel
2025-10-30 20:27:44 +01:00
parent 798aa08f02
commit 0526ec8a24
17 changed files with 193 additions and 137 deletions
+30 -19
View File
@@ -259,8 +259,15 @@ begin
// externally evaluate the expression using the injected evaluator
value := FEvaluate(expr);
// Allow unquoting keywords
if value.Kind in [vkScalar, vkText, vkVoid, vkKeyword] then
Result := TDataValue.FromIntf<IAstNode>(TAst.Constant(value))
begin
// vkKeyword needs to be handled differently than other constants
if value.Kind = vkKeyword then
Result := TDataValue.FromIntf<IAstNode>(TAst.Keyword(value.AsKeyword.Name))
else
Result := TDataValue.FromIntf<IAstNode>(TAst.Constant(value));
end
else
raise Exception.CreateFmt('Cannot unquote complex value of type %s at compile time.', [value.Kind.ToString]);
end;
@@ -290,8 +297,8 @@ end;
function TExpansionVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
begin
// This node type does not support splicing, so we use the default
// TAstTransformer implementation which just transforms child values.
// Record literals do not support splicing.
// We just use the default TAstTransformer implementation which visits child values.
Result := inherited VisitRecordLiteral(Node);
end;
@@ -500,6 +507,7 @@ var
i: Integer;
begin
// --- Transformation: Keyword-as-Function ---
// Check if the callee is a keyword literal
if (Node.Callee is TKeywordNode) then
begin
var keywordNode := (Node.Callee as TKeywordNode);
@@ -508,18 +516,17 @@ begin
// 1. Validate argument count
if Length(Node.Arguments) <> 1 then
raise ETypeException
.CreateFmt('Keyword :%s expects exactly one argument (the record), but got %d', [keywordName, Length(Node.Arguments)]);
.CreateFmt('Keyword :%s expects exactly one argument (the record/map), but got %d', [keywordName, Length(Node.Arguments)]);
// 2. Bind the base (the record)
// 2. Bind the base (the record/map)
FNextIsTail := False; // Accessing a member is not a tail call
var baseNode := Accept(Node.Arguments[0]).AsIntf<IAstNode>;
// 3. Create a synthetic IMemberAccessNode
var memberIdentifier := TAst.Identifier(keywordName);
var memberAccessNode := TAst.MemberAccess(baseNode, memberIdentifier);
var memberAccessNode := TAst.MemberAccess(baseNode, TAst.Keyword(keywordName));
// 4. Re-bind the synthetic node by calling Accept (which dispatches to VisitMemberAccess)
// This ensures type checking and type inference for member access is done in one place.
// This ensures type checking and type inference for member access is centralized.
Result := Accept(memberAccessNode);
exit;
end;
@@ -925,19 +932,19 @@ var
begin
baseNode := Accept(Node.Base).AsIntf<IAstNode>;
baseType := (baseNode as TAstNode).StaticType;
var memberName := Node.Member.Name;
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]);
raise ETypeException.CreateFmt('Member access requires a record or record series, but got %s', [baseType.ToString]);
fieldIndex := baseType.Definition.IndexOf(memberName);
// 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', [memberName, baseType.ToString]);
raise ETypeException.CreateFmt('Member "%s" not found in type %s', [Node.Member.Value.Name, baseType.ToString]);
var fieldType := TTypes.FromScalarKind(baseType.Definition.Fields[fieldIndex].Kind);
var fieldType := TTypes.FromScalarKind(baseType.Definition.Fields[fieldIndex].Value);
if baseType.Kind = TStaticTypeKind.stRecord then
elemType := fieldType
@@ -953,7 +960,7 @@ function TAstBinder.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataVal
var
i: Integer;
boundFields: TArray<TRecordFieldLiteral>;
defFields: TArray<TScalarRecordField>;
defFields: TArray<TScalarRecordDefinition.TField>;
def: TScalarRecordDefinition;
staticType: IStaticType;
boundNode: IRecordLiteralNode;
@@ -964,18 +971,21 @@ begin
SetLength(boundFields, Length(Node.Fields));
SetLength(defFields, Length(Node.Fields));
// We assume this is a TScalarRecord (Path A) until proven otherwise.
// The "dual path" logic for stDictionary is not yet implemented.
for i := 0 to High(Node.Fields) do
begin
valNode := Accept(Node.Fields[i].Value).AsIntf<IAstNode>;
valType := (valNode as TAstNode).StaticType;
// Records can only store scalar values
// Path A: Records can only store scalar values
if not (valType.Kind in [stOrdinal, stFloat]) then
raise ETypeException.CreateFmt(
'Record fields must be scalar (Ordinal or Float), but field "%s" is %s',
[Node.Fields[i].Name, valType.ToString]);
'Record fields must be scalar (Ordinal or Float), but field ":%s" is %s',
[Node.Fields[i].Key.Value.Name, valType.ToString]);
boundFields[i] := TRecordFieldLiteral.Create(Node.Fields[i].Name, valNode);
boundFields[i] := TRecordFieldLiteral.Create(Node.Fields[i].Key, valNode);
var scalarKind: TScalar.TKind;
if valType.Kind = stOrdinal then
@@ -983,7 +993,8 @@ begin
else
scalarKind := TScalar.TKind.Float;
defFields[i] := TScalarRecordField.Create(Node.Fields[i].Name, scalarKind);
// Create the definition field using the Keyword's name
defFields[i] := TScalarRecordField.Create(Node.Fields[i].Key.Value, scalarKind);
end;
def := TScalarRecordDefinition.Create(defFields);
+1 -1
View File
@@ -237,7 +237,7 @@ end;
function TDebugEvaluatorVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
begin
AppendLine(Format('MemberAccess (Member: %s) {', [Node.Member.Name]));
AppendLine(Format('MemberAccess (Member: %s) {', [Node.Member.Value.Name]));
Indent;
try
Result := inherited VisitMemberAccess(Node);
+1 -1
View File
@@ -449,7 +449,7 @@ begin
Indent;
for field in Node.Fields do
begin
LogFmt('Field :%s', [field.Name]);
LogFmt('Field :%s', [field.Key.Value.Name]);
Indent;
field.Value.Accept(Self);
Unindent;
+2 -4
View File
@@ -437,14 +437,12 @@ end;
function TEvaluatorVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
var
baseValue: TDataValue;
memberName: string;
begin
baseValue := Node.Base.Accept(Self);
memberName := Node.Member.Name;
case baseValue.Kind of
vkRecordSeries: Result := TDataValue.FromSeries(baseValue.AsRecordSeries.CreateMemberSeries(memberName));
vkRecord: Result := baseValue.AsRecord.Items[memberName];
vkRecordSeries: Result := TDataValue.FromSeries(baseValue.AsRecordSeries.CreateMemberSeries(Node.Member.Value));
vkRecord: Result := baseValue.AsRecord[Node.Member.Value];
else
raise EArgumentException.Create('Member access operator `.` is not supported for this value type.');
end;
+7 -3
View File
@@ -578,7 +578,7 @@ begin
field.Value.Accept(Self); // This pushes the value JSON onto the stack
fieldObj := TJSONObject.Create;
fieldObj.AddPair('Name', TJSONString.Create(field.Name));
fieldObj.AddPair('Name', TJSONString.Create(field.Key.Value.Name));
fieldObj.AddPair('Value', FJsonObjectStack.Pop);
fieldsArray.Add(fieldObj);
end;
@@ -900,7 +900,7 @@ end;
function TJsonAstConverter.JsonToMemberAccessNode(const AObj: TJSONObject): IMemberAccessNode;
begin
Result := TAst.MemberAccess(JsonToNode(AObj.GetValue('Base')), JsonToIdentifierNode(AObj.GetValue('Member') as TJSONObject));
Result := TAst.MemberAccess(JsonToNode(AObj.GetValue('Base')), JsonToKeywordNode(AObj.GetValue('Member') as TJSONObject));
end;
function TJsonAstConverter.JsonToRecordLiteralNode(const AObj: TJSONObject): IRecordLiteralNode;
@@ -915,7 +915,11 @@ begin
for i := 0 to fieldsArray.Count - 1 do
begin
fieldObj := fieldsArray.Items[i] as TJSONObject;
fields[i] := TRecordFieldLiteral.Create(fieldObj.GetValue<string>('Name'), JsonToNode(fieldObj.GetValue('Value')));
fields[i] :=
TRecordFieldLiteral.Create(
TKeywordNode.Create(TKeywordRegistry.Intern(fieldObj.GetValue<string>('Name'))),
JsonToNode(fieldObj.GetValue('Value'))
);
end;
Result := TAst.RecordLiteral(fields);
end;
+6 -6
View File
@@ -55,9 +55,9 @@ type
// A single field in a record literal
TRecordFieldLiteral = record
Name: string; // The field name (from keyword :name)
Key: IKeywordNode;
Value: IAstNode;
constructor Create(const AName: string; const AValue: IAstNode);
constructor Create(const AKey: IKeywordNode; const AValue: IAstNode);
end;
IAstVisitor = interface
@@ -264,10 +264,10 @@ type
IMemberAccessNode = interface(IAstNode)
{$region 'private'}
function GetBase: IAstNode;
function GetMember: IIdentifierNode;
function GetMember: IKeywordNode;
{$endregion}
property Base: IAstNode read GetBase;
property Member: IIdentifierNode read GetMember;
property Member: IKeywordNode read GetMember;
end;
// Represents a record literal, e.g., {:a 1 :b 2}
@@ -335,9 +335,9 @@ end;
{ TRecordFieldLiteral }
constructor TRecordFieldLiteral.Create(const AName: string; const AValue: IAstNode);
constructor TRecordFieldLiteral.Create(const AKey: IKeywordNode; const AValue: IAstNode);
begin
Name := AName;
Key := AKey;
Value := AValue;
end;
+3 -3
View File
@@ -376,7 +376,7 @@ begin
raise Exception.Create('Syntax Error: Missing value for key ' + fieldName + ' in record literal.');
fieldValue := ParseExpression.Node;
fields.Add(TRecordFieldLiteral.Create(fieldName, fieldValue));
fields.Add(TRecordFieldLiteral.Create(TKeywordNode.Create(TKeywordRegistry.Intern(fieldName)), fieldValue));
end;
Result := TAst.RecordLiteral(fields.ToArray);
@@ -490,7 +490,7 @@ begin
else if SameText(head.Token.Text, 'get') then
Result := TAst.Indexer(tailNodes[0], tailNodes[1])
else if (Length(head.Token.Text) > 1) and (head.Token.Text.StartsWith('.')) then
Result := TAst.MemberAccess(tailNodes[0], TAst.Identifier(head.Token.Text.Substring(1)));
Result := TAst.MemberAccess(tailNodes[0], TAst.Keyword(head.Token.Text.Substring(1)));
end;
// If no special form matched, treat it as a generic function call.
@@ -916,7 +916,7 @@ begin
for field in Node.Fields do
begin
NewLine;
Append(':' + field.Name);
Append(':' + field.Key.Value.Name);
Append(' ');
field.Value.Accept(Self);
end;
+3 -2
View File
@@ -386,7 +386,8 @@ begin
for i := 0 to High(Self.FDefinition.Fields) do
begin
if (Self.FDefinition.Fields[i].Name <> otherDef.Fields[i].Name) or (Self.FDefinition.Fields[i].Kind <> otherDef.Fields[i].Kind) then
// Use fast interface comparison for Keys
if (Self.FDefinition.Fields[i].Key <> otherDef.Fields[i].Key) or (Self.FDefinition.Fields[i].Value <> otherDef.Fields[i].Value) then
exit(False);
end;
@@ -400,7 +401,7 @@ begin
Result := GetKind.ToString + '{';
for i := 0 to High(FDefinition.Fields) do
begin
Result := Result + FDefinition.Fields[i].Name + ': ' + FDefinition.Fields[i].Kind.ToString;
Result := Result + FDefinition.Fields[i].Key.Name + ': ' + FDefinition.Fields[i].Value.ToString;
if i < High(FDefinition.Fields) then
Result := Result + ', ';
end;
+7 -3
View File
@@ -319,7 +319,7 @@ end;
function TAstTransformer.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
begin
var base := Accept(Node.Base).AsIntf<IAstNode>;
var member := Node.Member;
var member := Accept(Node.Member).AsIntf<IKeywordNode>;
if (base = Node.Base) and (member = Node.Member) then
Result := TDataValue.FromIntf<IMemberAccessNode>(Node)
else
@@ -331,6 +331,7 @@ var
i: Integer;
hasChanged: Boolean;
newFields: TArray<TRecordFieldLiteral>;
newKey: IKeywordNode;
newValue: IAstNode;
begin
hasChanged := False;
@@ -338,10 +339,13 @@ begin
for i := 0 to High(Node.Fields) do
begin
newKey := Accept(Node.Fields[i].Key).AsIntf<IKeywordNode>;
newValue := Accept(Node.Fields[i].Value).AsIntf<IAstNode>;
if newValue <> Node.Fields[i].Value then
if (newKey <> Node.Fields[i].Key) or (newValue <> Node.Fields[i].Value) then
hasChanged := True;
newFields[i] := TRecordFieldLiteral.Create(Node.Fields[i].Name, newValue);
newFields[i] := TRecordFieldLiteral.Create(newKey, newValue);
end;
if hasChanged then
+15 -13
View File
@@ -58,7 +58,7 @@ type
class function Assign(const AIdentifier: IIdentifierNode; const AValue: IAstNode): IAssignmentNode; static;
class function AssignResult(const AValue: IAstNode): IAssignmentNode; static; deprecated;
class function Indexer(const ABase: IAstNode; const AIndex: IAstNode): IIndexerNode; static;
class function MemberAccess(const ABase: IAstNode; const AMember: IIdentifierNode): IMemberAccessNode; static;
class function MemberAccess(const ABase: IAstNode; const AMember: IKeywordNode): IMemberAccessNode; static;
class function RecordLiteral(const AFields: TArray<TRecordFieldLiteral>): IRecordLiteralNode; static;
class function CreateSeries(const ADefinition: String): ICreateSeriesNode; static;
class function AddSeriesItem(
@@ -104,7 +104,7 @@ type
FValue: IKeyword;
function GetValue: IKeyword;
public
constructor Create(const AName: string);
constructor Create(const AValue: IKeyword);
function Accept(const Visitor: IAstVisitor): TDataValue; override;
property Value: IKeyword read FValue;
end;
@@ -292,11 +292,11 @@ type
TMemberAccessNode = class(TAstNode, IMemberAccessNode)
private
FBase: IAstNode;
FMember: IIdentifierNode;
FMember: IKeywordNode;
function GetBase: IAstNode;
function GetMember: IIdentifierNode;
function GetMember: IKeywordNode;
public
constructor Create(const ABase: IAstNode; const AMember: IIdentifierNode);
constructor Create(const ABase: IAstNode; const AMember: IKeywordNode);
function Accept(const Visitor: IAstVisitor): TDataValue; override;
end;
@@ -349,14 +349,15 @@ constructor TConstantNode.Create(const AValue: TDataValue);
begin
inherited Create;
// Validate that only allowed constant kinds are used.
if not (AValue.Kind in [vkScalar, vkText, vkVoid]) then
raise EArgumentException.Create('IConstantNode only supports Scalar, Text, and Void values.');
if not (AValue.Kind in [vkScalar, vkText, vkVoid, vkKeyword]) then
raise EArgumentException.Create('IConstantNode only supports Scalar, Text, Void and Keyword values.');
FValue := AValue;
case AValue.Kind of
vkScalar: StaticType := TTypes.FromScalarKind(AValue.AsScalar.Kind);
vkText: StaticType := TTypes.Text;
vkVoid: StaticType := TTypes.Void;
vkKeyword: StaticType := TTypes.Keyword;
else
StaticType := TTypes.Unknown; // Should not happen due to validation above
end;
@@ -392,11 +393,12 @@ end;
{ TKeywordNode }
constructor TKeywordNode.Create(const AName: string);
constructor TKeywordNode.Create(const AValue: IKeyword);
begin
inherited Create;
FValue := TKeywordRegistry.Intern(AName);
FValue := AValue;
StaticType := TTypes.Keyword;
FValue := AValue;
end;
function TKeywordNode.Accept(const Visitor: IAstVisitor): TDataValue;
@@ -788,7 +790,7 @@ end;
{ TMemberAccessNode }
constructor TMemberAccessNode.Create(const ABase: IAstNode; const AMember: IIdentifierNode);
constructor TMemberAccessNode.Create(const ABase: IAstNode; const AMember: IKeywordNode);
begin
inherited Create;
FBase := ABase;
@@ -805,7 +807,7 @@ begin
Result := FBase;
end;
function TMemberAccessNode.GetMember: IIdentifierNode;
function TMemberAccessNode.GetMember: IKeywordNode;
begin
Result := FMember;
end;
@@ -992,7 +994,7 @@ end;
class function TAst.Keyword(const AName: string): IKeywordNode;
begin
Result := TKeywordNode.Create(AName);
Result := TKeywordNode.Create(TKeywordRegistry.Intern(AName));
end;
class function TAst.LambdaExpr(const AParameters: TArray<IIdentifierNode>; const ABody: IAstNode): ILambdaExpressionNode;
@@ -1009,7 +1011,7 @@ begin
Result := TMacroDefinitionNode.Create(AName, AParameters, ABody);
end;
class function TAst.MemberAccess(const ABase: IAstNode; const AMember: IIdentifierNode): IMemberAccessNode;
class function TAst.MemberAccess(const ABase: IAstNode; const AMember: IKeywordNode): IMemberAccessNode;
begin
Result := TMemberAccessNode.Create(ABase, AMember);
end;
+74 -3
View File
@@ -11,7 +11,9 @@ uses
type
// The runtime representation of an interned keyword
IKeyword = interface(IInterface)
function GetIdx: Integer;
function GetName: string;
property Idx: Integer read GetIdx;
property Name: string read GetName;
end;
@@ -22,9 +24,11 @@ type
TKeyword = class(TInterfacedObject, IKeyword)
private
FName: string;
FIdx: Integer;
function GetName: string;
function GetIdx: Integer;
public
constructor Create(const AName: string);
constructor Create(const AName: string; AIdx: Integer);
end;
strict private
class var
@@ -38,14 +42,38 @@ type
class function Intern(const AName: string): IKeyword; static;
end;
TKeywordMapping<T> = record
type
TField = record
Key: IKeyword;
Value: T;
public
constructor Create(const AKey: IKeyword; const AValue: T);
end;
private
FMap: TArray<Integer>;
FFields: TArray<TField>;
FFirst, FLast: Integer;
public
constructor Create(const AFields: TArray<TField>);
function IndexOf(const Key: IKeyword): Integer;
property Fields: TArray<TField> read FFields;
end;
implementation
{ TKeywordRegistry.TKeyword }
constructor TKeywordRegistry.TKeyword.Create(const AName: string);
constructor TKeywordRegistry.TKeyword.Create(const AName: string; AIdx: Integer);
begin
inherited Create;
FName := AName;
FIdx := AIdx;
end;
function TKeywordRegistry.TKeyword.GetIdx: Integer;
begin
Result := FIdx;
end;
function TKeywordRegistry.TKeyword.GetName: string;
@@ -72,7 +100,7 @@ begin
try
if not FRegistry.TryGetValue(AName, Result) then
begin
Result := TKeyword.Create(AName);
Result := TKeyword.Create(AName, FRegistry.Count);
FRegistry.Add(AName, Result);
end;
finally
@@ -80,4 +108,47 @@ begin
end;
end;
{ TKeywordMapping }
constructor TKeywordMapping<T>.Create(const AFields: TArray<TField>);
begin
FFields := AFields;
FFirst := 0;
FLast := -1;
if Length(FFields) = 0 then
exit;
FFirst := FFields[0].Key.Idx;
FLast := FFirst;
for var i := 1 to High(FFields) do
begin
var idx := FFields[i].Key.Idx;
if FFirst > idx then
FFirst := idx;
if FLast < idx then
FLast := idx;
end;
SetLength(FMap, FLast - FFirst + 1);
for var i := 0 to High(FMap) do
FMap[i] := -1;
for var i := 0 to High(FFields) do
FMap[FFirst + FFields[i].Key.Idx] := i;
end;
function TKeywordMapping<T>.IndexOf(const Key: IKeyword): Integer;
begin
var idx := Key.Idx - FFirst;
if (idx < 0) or (Idx > High(FMap)) then
exit(-1);
Result := FMap[idx];
end;
constructor TKeywordMapping<T>.TField.Create(const AKey: IKeyword; const AValue: T);
begin
Key := AKey;
Value := AValue;
end;
end.
+8 -4
View File
@@ -24,8 +24,9 @@ type
implementation
uses
System.Generics.Collections,
Myc.Data.Decimal,
System.Generics.Collections;
Myc.Data.Keyword;
{ TRttiAstHelper }
@@ -35,7 +36,8 @@ var
fieldsArray: TJSONArray;
i: Integer;
fieldObj: TJSONObject;
kindStr: string;
kindStr, fieldName: string;
fieldKey: IKeyword;
fields: TArray<TScalarRecordField>;
begin
jsonValue := TJSONObject.ParseJSONValue(AJson);
@@ -55,9 +57,11 @@ begin
if not Assigned(fieldObj) then
continue;
fields[i].Name := fieldObj.GetValue<string>('name');
fieldName := fieldObj.GetValue<string>('name');
fieldKey := TKeywordRegistry.Intern(fieldName);
kindStr := fieldObj.GetValue<string>('kind');
fields[i].Kind := TScalar.TKind(GetEnumValue(TypeInfo(TScalar.TKind), kindStr));
fields[i] := TScalarRecordField.Create(fieldKey, TScalar.TKind(GetEnumValue(TypeInfo(TScalar.TKind), kindStr)));
end;
Result := TScalarRecordDefinition.Create(fields);
finally
+16 -54
View File
@@ -5,7 +5,8 @@ interface
uses
System.SysUtils,
Myc.Data.Decimal,
Myc.Data.Series;
Myc.Data.Series,
Myc.Data.Keyword;
{$SCOPEDENUMS ON}
@@ -91,20 +92,8 @@ type
TScalarTuple = TArray<TScalar>;
// A field definition for a scalar record.
TScalarRecordField = record
Name: String;
Kind: TScalar.TKind;
constructor Create(const AName: String; AKind: TScalar.TKind);
end;
TScalarRecordDefinition = record
private
FFields: TArray<TScalarRecordField>;
public
constructor Create(const AFields: TArray<TScalarRecordField>);
function IndexOf(const Name: String): Integer;
property Fields: TArray<TScalarRecordField> read FFields;
end;
TScalarRecordField = TKeywordMapping<TScalar.TKind>.TField;
TScalarRecordDefinition = TKeywordMapping<TScalar.TKind>;
// A record of scalar values, based on a definition.
TScalarRecord = record
@@ -112,10 +101,10 @@ type
constructor Create(const ADef: TScalarRecordDefinition; const AFields: TArray<TScalar.TValue>);
function GetDef: TScalarRecordDefinition; inline;
function GetFields: TArray<TScalar.TValue>; inline;
function GetItems(const Name: String): TScalar;
function GetKeys(const Key: IKeyword): TScalar;
property Def: TScalarRecordDefinition read GetDef;
property Fields: TArray<TScalar.TValue> read GetFields;
property Items[const Name: String]: TScalar read GetItems; default;
property Keys[const Key: IKeyword]: TScalar read GetKeys; default;
strict private
FDef: TScalarRecordDefinition;
FFields: TArray<TScalar.TValue>;
@@ -158,7 +147,7 @@ type
function GetTotalCount: Int64;
{$endregion}
procedure Add(const Item: TScalarRecord; Lookback: Int64 = -1);
function CreateMemberSeries(const Field: String): ISeries;
function CreateMemberSeries(const Key: IKeyword): ISeries;
property Count: Int64 read GetCount;
property Def: TScalarRecordDefinition read GetDef;
property Items[Idx: Integer]: TScalarRecord read GetItems; default;
@@ -192,7 +181,7 @@ type
public
constructor Create(const ADef: TScalarRecordDefinition);
procedure Add(const Item: TScalarRecord; Lookback: Int64 = -1);
function CreateMemberSeries(const Field: String): ISeries;
function CreateMemberSeries(const Key: IKeyword): ISeries;
property Count: Int64 read GetCount;
property Def: TScalarRecordDefinition read GetDef;
property Items[Idx: Integer]: TScalarRecord read GetItems; default;
@@ -528,14 +517,6 @@ begin
Items := AItems;
end;
{ TScalarRecordField }
constructor TScalarRecordField.Create(const AName: String; AKind: TScalar.TKind);
begin
Name := AName;
Kind := AKind;
end;
{ TScalarRecord }
constructor TScalarRecord.Create(const ADef: TScalarRecordDefinition; const AFields: TArray<TScalar.TValue>);
@@ -555,34 +536,15 @@ begin
Result := FFields;
end;
function TScalarRecord.GetItems(const Name: String): TScalar;
function TScalarRecord.GetKeys(const Key: IKeyword): TScalar;
var
idx: Integer;
begin
idx := FDef.IndexOf(Name);
idx := FDef.IndexOf(Key);
if idx < 0 then
raise EArgumentException.CreateFmt('Field "%s" not found in record definition.', [Name]);
raise EArgumentException.CreateFmt('Field ":%s" not found in record definition.', [Key.Name]);
Result.Create(FDef.Fields[idx].Kind, FFields[idx]);
end;
{ TScalarRecordDefinition }
constructor TScalarRecordDefinition.Create(const AFields: TArray<TScalarRecordField>);
begin
FFields := AFields;
end;
function TScalarRecordDefinition.IndexOf(const Name: String): Integer;
var
i: Integer;
begin
for i := 0 to High(FFields) do
begin
if (CompareText(FFields[i].Name, Name) = 0) then
exit(i);
end;
Result := -1;
Result.Create(FDef.Fields[idx].Value, FFields[idx]);
end;
{ TScalarRecordSeries }
@@ -600,11 +562,11 @@ begin
inc(FTotalCount);
end;
function TScalarRecordSeries.CreateMemberSeries(const Field: String): ISeries;
function TScalarRecordSeries.CreateMemberSeries(const Key: IKeyword): ISeries;
begin
var elem := FDef.IndexOf(Field);
var elem := FDef.IndexOf(Key);
if elem < 0 then
raise EArgumentException.CreateFmt('Field "%s" not found in record definition.', [Field]);
raise EArgumentException.CreateFmt('Field ":%s" not found in record definition.', [Key.Name]);
Result := TMemberSeries.Create(Self, elem);
end;
@@ -686,7 +648,7 @@ begin
inherited Create;
FRecordSeries := ARecordSeries;
FRecordSeries._AddRef;
FKind := FRecordSeries.FDef.FFields[AElementIdx].Kind;
FKind := FRecordSeries.FDef.Fields[AElementIdx].Value;
FOffset := sizeof(TScalar.TValue) * AElementIdx;
end;
+8 -8
View File
@@ -82,12 +82,12 @@ type
class function FromRecordSeries(const AValue: IRecordSeries): TDataValue; static; inline;
class function FromRecord(const [ref] AValue: TScalarRecord): TDataValue; static; inline;
class function FromSeries(const AValue: ISeries): TDataValue; static; inline;
class function FromKeyword(const AValue: IKeyword): TDataValue; static; inline; // Added
class function FromKeyword(const AValue: IKeyword): TDataValue; static; inline;
function AsScalar: TScalar; inline;
function AsMethod: TFunc; inline;
function AsText: String; inline;
function AsKeyword: IKeyword; inline; // Added
function AsKeyword: IKeyword; inline;
function AsRecordSeries: IRecordSeries; inline;
function AsRecord: TScalarRecord; inline;
function AsSeries: ISeries; inline;
@@ -264,7 +264,7 @@ begin
TInterlocked.CompareExchange(FScalar.Value.AsInt64, NewValue.AsScalar.Value.AsInt64, Expected.AsScalar.Value.AsInt64)
= Expected.AsScalar.Value.AsInt64;
vkText, vkKeyword, vkSeries, vkRecordSeries, vkRecord, vkManagedObject, vkMethod, vkInterface, vkGeneric: // Added vkKeyword
vkText, vkKeyword, vkSeries, vkRecordSeries, vkRecord, vkManagedObject, vkMethod, vkInterface, vkGeneric:
Result := IntfCompareExchange(FInterface, NewValue.FInterface, Expected.FInterface) = Expected.FInterface;
end;
end;
@@ -369,7 +369,7 @@ begin
end;
vkPointer: Result := TInterlocked.Exchange(FScalar.Value.AsInt64, NewValue.AsScalar.Value.AsInt64);
vkText, vkKeyword, vkSeries, vkRecordSeries, vkRecord, vkManagedObject, vkMethod, vkInterface, vkGeneric: // Added vkKeyword
vkText, vkKeyword, vkSeries, vkRecordSeries, vkRecord, vkManagedObject, vkMethod, vkInterface, vkGeneric:
begin
Result.FKind := FKind;
Result.FInterface := IntfExchange(FInterface, NewValue.FInterface);
@@ -386,7 +386,7 @@ begin
case FKind of
vkScalar: Result := FScalar.ToString;
vkText: Result := '"' + AsText + '"';
vkKeyword: Result := ':' + AsKeyword.Name; // Added
vkKeyword: Result := ':' + AsKeyword.Name;
vkSeries:
begin
var series := AsSeries;
@@ -404,7 +404,7 @@ begin
begin
if not first then
sb.Append(',');
sb.Append(field.Name);
sb.Append(field.Key.Name);
first := False;
end;
sb.Append('}');
@@ -425,7 +425,7 @@ begin
begin
if not first then
sb.Append(',');
sb.Append(field.Name);
sb.Append(field.Key.Name);
first := False;
end;
sb.Append('}');
@@ -490,7 +490,7 @@ begin
vkVoid: Result := 'Void';
vkScalar: Result := 'Scalar';
vkText: Result := 'Text';
vkKeyword: Result := 'Keyword'; // Added
vkKeyword: Result := 'Keyword';
vkSeries: Result := 'Series';
vkRecordSeries: Result := 'RecordSeries';
vkRecord: Result := 'Record';