Keywords
This commit is contained in:
+30
-19
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user