ITupleNode signature change

This commit is contained in:
Michael Schimmel
2026-01-06 11:37:18 +01:00
parent 264314cd93
commit 40ed51aef8
23 changed files with 340 additions and 270 deletions
+1 -1
View File
@@ -918,7 +918,7 @@ begin
try
if rootNode.Kind = akBlockExpression then
begin
for var expr in rootNode.AsBlockExpression.Expressions.ToArray do
for var expr in rootNode.AsBlockExpression.Expressions.Elements do
begin
if (expr.Kind = akVariableDeclaration) then
begin
+2 -1
View File
@@ -2,7 +2,8 @@
* Unterstütze mich bei der Entwicklung unter Embarcadero Delphi.
* Ich bin ein sehr erfahrener Softwareentwickler. Fasse dich kurz und nutze Fachsprache.
* Wir nutzen immer die neueste Delphi-Version, aktuell ist das Delphi 12.3 Athens.
* Du brauchst mich nicht zu loben.
* Wir nutzen immer die neueste Delphi-Version, aktuell ist das Delphi 13.
+3 -3
View File
@@ -135,14 +135,14 @@ end;
function TPurityAnalyzer.VisitTuple(const Node: IAstNode): Boolean;
var
i: Integer;
T: ITupleNode;
begin
T := Node.AsTuple;
// A tuple is pure if ALL its elements are pure.
for i := 0 to T.Count - 1 do
// Optimization: Use Elements array directly
for var item in T.Elements do
begin
if not IsNodePure(T.Items[i]) then
if not IsNodePure(item) then
exit(False);
end;
Result := True;
+6 -5
View File
@@ -195,7 +195,6 @@ function TUpvalueAnalyzer.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
L: ILambdaExpressionNode;
i: Integer;
paramTuple: ITupleNode;
begin
L := Node.AsLambdaExpression;
@@ -205,11 +204,13 @@ begin
// 2. Register parameters (they mask outer variables)
// We pass 'nil' as the node because we currently don't box parameters,
// but we must ensure Resolve() finds them so we don't accidentally box a shadowed variable.
paramTuple := L.Parameters;
for i := 0 to paramTuple.Count - 1 do
// Optimized: Access Elements array directly
var paramElements := L.Parameters.Elements;
for i := 0 to High(paramElements) do
begin
if paramTuple.Items[i].Kind = akIdentifier then
FCurrentScope.Define(paramTuple.Items[i].AsIdentifier.Name, nil);
if paramElements[i].Kind = akIdentifier then
FCurrentScope.Define(paramElements[i].AsIdentifier.Name, nil);
end;
// 3. Visit Body
+7 -7
View File
@@ -343,7 +343,6 @@ var
startCount: Integer;
hasNested: Boolean;
paramIdentity: INamedIdentity;
paramsTuple: ITupleNode;
paramNode: IAstNode;
begin
L := Node.AsLambdaExpression;
@@ -359,12 +358,12 @@ begin
try
FCurrentBuilder.Define('<self>');
paramsTuple := L.Parameters;
SetLength(newParams, paramsTuple.Count);
var paramElements := L.Parameters.Elements;
SetLength(newParams, Length(paramElements));
for i := 0 to paramsTuple.Count - 1 do
for i := 0 to High(paramElements) do
begin
paramNode := paramsTuple.Items[i];
paramNode := paramElements[i];
if paramNode.Kind <> akIdentifier then
begin
@@ -454,14 +453,15 @@ begin
// 1. Keyword as Function Logic
if C.Callee.Kind = akKeyword then
begin
if C.Arguments.Count <> 1 then
var args := C.Arguments.Elements;
if Length(args) <> 1 then
begin
if Assigned(FLog) then
FLog.AddError(Format('Keyword access :%s requires exactly one argument.', [C.Callee.AsKeyword.Value.Name]), Node);
end
else
begin
var memberAccess := TAst.MemberAccess(Node.Identity, Accept(C.Arguments.Items[0]), C.Callee.AsKeyword);
var memberAccess := TAst.MemberAccess(Node.Identity, Accept(args[0]), C.Callee.AsKeyword);
Result := memberAccess;
exit;
end;
+23 -20
View File
@@ -196,9 +196,11 @@ var
begin
newList := TList<IAstNode>.Create;
try
for i := 0 to ANodes.Count - 1 do
// Optimization: Access Elements array directly
var elements := ANodes.Elements;
for i := 0 to High(elements) do
begin
node := ANodes.Items[i];
node := elements[i];
if node.Kind = akUnquoteSplicing then
begin
var spliceExpr := node.AsUnquoteSplicing.Expression;
@@ -218,15 +220,15 @@ begin
// Unification: Lists are now Tuples.
if nodeToSplice.Kind = akTuple then
begin
var tuple := nodeToSplice.AsTuple;
for var k := 0 to tuple.Count - 1 do
newList.Add(tuple.Items[k]);
var tupleElements := nodeToSplice.AsTuple.Elements;
for var k := 0 to High(tupleElements) do
newList.Add(tupleElements[k]);
end
else if nodeToSplice.Kind = akBlockExpression then
begin
var blkTuple := nodeToSplice.AsBlockExpression.Expressions;
for var k := 0 to blkTuple.Count - 1 do
newList.Add(blkTuple.Items[k]);
var blkElements := nodeToSplice.AsBlockExpression.Expressions.Elements;
for var k := 0 to High(blkElements) do
newList.Add(blkElements[k]);
end
else
begin
@@ -290,15 +292,14 @@ var
newBody: IAstNode;
newName: string;
i: Integer;
paramsTuple: ITupleNode;
begin
L := Node.AsLambdaExpression;
paramsTuple := L.Parameters;
SetLength(newParams, paramsTuple.Count);
var paramsElements := L.Parameters.Elements;
SetLength(newParams, Length(paramsElements));
for i := 0 to paramsTuple.Count - 1 do
for i := 0 to High(paramsElements) do
begin
var param := paramsTuple.Items[i].AsIdentifier; // Binder ensures params are Identifiers
var param := paramsElements[i].AsIdentifier; // Binder ensures params are Identifiers
newName := Gensym(param.Name);
newParams[i] := TAst.Identifier(newName, param.Identity.Location);
end;
@@ -497,16 +498,18 @@ begin
if macroDef <> nil then
begin
var expansionScope := TScope.CreateScope(FInitialScope, nil, nil);
var paramsTuple := macroDef.Parameters;
var argsTuple := C.Arguments;
if argsTuple.Count <> paramsTuple.Count then
raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, paramsTuple.Count]);
// Optimization: Access elements directly
var paramsElements := macroDef.Parameters.Elements;
var argsElements := C.Arguments.Elements;
for i := 0 to paramsTuple.Count - 1 do
if Length(argsElements) <> Length(paramsElements) then
raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, Length(paramsElements)]);
for i := 0 to High(paramsElements) do
begin
var paramName := paramsTuple.Items[i].AsIdentifier.Name;
expansionScope.Define(paramName, TDataValue.FromIntf<IAstNode>(argsTuple.Items[i]));
var paramName := paramsElements[i].AsIdentifier.Name;
expansionScope.Define(paramName, TDataValue.FromIntf<IAstNode>(argsElements[i]));
end;
var expandedBody := TExpansionVisitor.Expand(expansionScope, macroDef.Body.AsQuasiquote.Expression, FMacroEvaluator);
+6 -3
View File
@@ -162,11 +162,14 @@ begin
funcName := calleeIdent.Name;
// 3. Check if all argument types are statically known
// Optimization: Use Elements array directly
var argsElements := newArgs.Elements;
allTypesKnown := True;
SetLength(argTypes, newArgs.Count);
for i := 0 to newArgs.Count - 1 do
SetLength(argTypes, Length(argsElements));
for i := 0 to High(argsElements) do
begin
argTypes[i] := newArgs.Items[i].AsTypedNode.StaticType;
argTypes[i] := argsElements[i].AsTypedNode.StaticType;
if argTypes[i].Kind = stUnknown then
begin
allTypesKnown := False;
+15 -7
View File
@@ -123,19 +123,22 @@ var
i: Integer;
item, newItem: IAstNode;
hasChanged: Boolean;
elements: TArray<IAstNode>;
begin
B := Node.AsBlockExpression;
isContextTail := FIsTailStack.Peek;
exprsTuple := B.Expressions;
elements := exprsTuple.Elements;
SetLength(newExprs, exprsTuple.Count);
var count := Length(elements);
SetLength(newExprs, count);
hasChanged := False;
var nTail := exprsTuple.Count - 1;
var nTail := count - 1;
for i := 0 to nTail do
begin
item := exprsTuple.Items[i];
item := elements[i];
// Only the last expression in the block inherits the tail position status
FNextIsTail := isContextTail and (i = nTail);
@@ -150,9 +153,11 @@ begin
if not hasChanged then
Result := Node
else
begin
// Create new Tuple for expressions
Result := TAst.Block(Node.Identity, TAst.Tuple(B.Expressions.Identity, newExprs), B.StaticType);
end;
end;
function TAstTCO.VisitIfExpression(const Node: IAstNode): IAstNode;
var
@@ -324,6 +329,7 @@ var
i: Integer;
hasChanged: Boolean;
savedNextIsTail: Boolean;
elements: TArray<IAstNode>;
begin
T := Node.AsTuple;
savedNextIsTail := FNextIsTail; // Zustand sichern
@@ -332,13 +338,15 @@ begin
FNextIsTail := False;
hasChanged := False;
SetLength(newElements, T.Count);
elements := T.Elements;
var count := Length(elements);
SetLength(newElements, count);
try
for i := 0 to T.Count - 1 do
for i := 0 to count - 1 do
begin
newElements[i] := Accept(T.Items[i]);
if newElements[i] <> T.Items[i] then
newElements[i] := Accept(elements[i]);
if newElements[i] <> elements[i] then
hasChanged := True;
end;
finally
+53 -46
View File
@@ -314,9 +314,11 @@ var
newDim: TArray<Integer>;
finalType: IStaticType;
elementsArray: TArray<IAstNode>;
begin
T := Node.AsTuple;
var count := T.Count;
elementsArray := T.Elements;
var count := Length(elementsArray);
SetLength(newElements, count);
SetLength(elementTypes, count);
@@ -325,7 +327,7 @@ begin
// Recursively type-check all elements first to determine their static types.
for i := 0 to count - 1 do
begin
newElements[i] := Accept(T.Items[i]);
newElements[i] := Accept(elementsArray[i]);
if newElements[i].IsTyped then
elementTypes[i] := newElements[i].AsTypedNode.StaticType
else
@@ -503,8 +505,7 @@ function TTypeChecker.VisitBlockExpression(const Node: IAstNode): IAstNode;
var
newBlock: IBlockExpressionNode;
blockType: IStaticType;
exprs: ITupleNode;
i: Integer;
exprs: TArray<IAstNode>;
begin
var transformedNode := inherited VisitBlockExpression(Node);
@@ -512,30 +513,25 @@ begin
raise ECompilationFailed.Create([TCompilerError.Create(elError, 'Internal Error: Block transformation returned nil.', Node)]);
newBlock := transformedNode.AsBlockExpression;
exprs := newBlock.Expressions;
exprs := newBlock.Expressions.Elements;
if exprs.Count > 0 then
if Length(exprs) > 0 then
begin
for i := 0 to exprs.Count - 1 do
begin
if exprs.Items[i] = nil then
raise ECompilationFailed.Create(
[TCompilerError.Create(elError, Format('Internal Error: Block expression #%d transformed to nil.', [i]), Node)]);
end;
blockType := exprs.Items[exprs.Count - 1].AsTypedNode.StaticType;
// The type of the block is the type of the last expression
blockType := exprs[High(exprs)].AsTypedNode.StaticType;
end
else
begin
blockType := TTypes.Void;
end;
Result := TAst.Block(Node.Identity, exprs, blockType);
Result := TAst.Block(Node.Identity, newBlock.Expressions, blockType);
end;
function TTypeChecker.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
L: ILambdaExpressionNode;
newParams: TArray<IIdentifierNode>;
newParams: TArray<IAstNode>;
newBody: IAstNode;
bodyType, methodType: IStaticType;
paramTypes: TArray<IStaticType>;
@@ -545,7 +541,7 @@ var
paramIdent: IIdentifierNode;
injectedType: IStaticType;
paramsTuple: ITupleNode;
paramsAsNodes: TArray<IAstNode>;
paramsElements: TArray<IAstNode>;
begin
L := Node.AsLambdaExpression;
@@ -566,12 +562,13 @@ begin
FCurrentContext := TTypeContext.Create(FCurrentContext, L.Layout, upvalueTypes, nil);
try
paramsTuple := L.Parameters;
SetLength(newParams, paramsTuple.Count);
SetLength(paramTypes, paramsTuple.Count);
paramsElements := paramsTuple.Elements;
SetLength(newParams, Length(paramsElements));
SetLength(paramTypes, Length(paramsElements));
for i := 0 to paramsTuple.Count - 1 do
for i := 0 to High(paramsElements) do
begin
paramIdent := paramsTuple.Items[i].AsIdentifier;
paramIdent := paramsElements[i].AsIdentifier;
injectedType := paramIdent.AsTypedNode.StaticType;
if injectedType.Kind = stUnknown then
@@ -597,11 +594,7 @@ begin
temp.Free;
end;
SetLength(paramsAsNodes, Length(newParams));
for i := 0 to High(newParams) do
paramsAsNodes[i] := newParams[i];
var paramList := TAst.Tuple(L.Parameters.Identity, paramsAsNodes);
var paramList := TAst.Tuple(L.Parameters.Identity, newParams);
Result :=
TAst.LambdaExpr(Node.Identity, paramList, newBody, L.Layout, finalDescriptor, L.Upvalues, L.HasNestedLambdas, L.IsPure, methodType);
@@ -617,17 +610,19 @@ var
bestSig: IMethodSignature;
match: Boolean;
newArgs: ITupleNode;
argsElements: TArray<IAstNode>;
begin
newCall := inherited VisitFunctionCall(Node).AsFunctionCall;
var newCallee := newCall.Callee;
newArgs := newCall.Arguments;
argsElements := newArgs.Elements;
SetLength(argTypes, newArgs.Count);
SetLength(argTypes, Length(argsElements));
hasUnknownArgs := False;
for i := 0 to newArgs.Count - 1 do
for i := 0 to High(argsElements) do
begin
argTypes[i] := newArgs.Items[i].AsTypedNode.StaticType;
argTypes[i] := argsElements[i].AsTypedNode.StaticType;
if argTypes[i].Kind = stUnknown then
hasUnknownArgs := True;
end;
@@ -761,18 +756,20 @@ var
valType: IStaticType;
key: IKeyword;
newFields: ITupleNode;
fieldsElements: TArray<IAstNode>;
begin
R := Node.AsRecordLiteral;
newFields := Visit(R.Fields).AsTuple;
fieldsElements := newFields.Elements;
var count := newFields.Count;
var count := Length(fieldsElements);
SetLength(fieldTypes, count);
SetLength(scalarFieldTypes, count);
isScalar := True;
for i := 0 to count - 1 do
begin
var field := newFields.Items[i].AsRecordField;
var field := fieldsElements[i].AsRecordField;
key := field.Key.Value;
valType := field.Value.AsTypedNode.StaticType;
@@ -865,17 +862,22 @@ var
selectorsNode: ITupleNode;
newSelectors: TList<IAstNode>;
streamType: IStaticType;
rawInputsElements: TArray<IAstNode>;
rawTupleElements: TArray<IAstNode>;
selectorsElements: TArray<IAstNode>;
lambdaParamsElements: TArray<IAstNode>;
begin
P := Node.AsPipe;
rawInputs := P.Inputs; // This is the Tuple of Tuples [[id [sel]] ...]
rawInputsElements := rawInputs.Elements;
newInputs := TList<IAstNode>.Create;
paramTypes := TList<IStaticType>.Create;
try
// 1. Iterate over input definitions
for i := 0 to rawInputs.Count - 1 do
for i := 0 to High(rawInputsElements) do
begin
inputPair := rawInputs.Items[i];
inputPair := rawInputsElements[i];
// Validate Structure: [StreamSource, [Selectors]]
if inputPair.Kind <> akTuple then
@@ -886,7 +888,9 @@ begin
end;
rawTuple := inputPair.AsTuple;
if rawTuple.Count <> 2 then
rawTupleElements := rawTuple.Elements;
if Length(rawTupleElements) <> 2 then
begin
if Assigned(FLog) then
FLog.AddError('Pipe input vector must have exactly 2 elements.', inputPair);
@@ -894,15 +898,15 @@ begin
end;
// Element 0: Stream Identifier
if rawTuple.Items[0].Kind <> akIdentifier then
if rawTupleElements[0].Kind <> akIdentifier then
begin
if Assigned(FLog) then
FLog.AddError('Pipe source must be an identifier.', rawTuple.Items[0]);
FLog.AddError('Pipe source must be an identifier.', rawTupleElements[0]);
continue;
end;
// Resolve Stream Identifier (Type Binding)
streamNode := Accept(rawTuple.Items[0]).AsIdentifier;
streamNode := Accept(rawTupleElements[0]).AsIdentifier;
streamType := streamNode.StaticType;
// Validate Stream Type
@@ -919,19 +923,21 @@ begin
end;
// Element 1: Selector Vector
if rawTuple.Items[1].Kind <> akTuple then
if rawTupleElements[1].Kind <> akTuple then
begin
if Assigned(FLog) then
FLog.AddError('Pipe selectors must be a vector of keywords.', rawTuple.Items[1]);
FLog.AddError('Pipe selectors must be a vector of keywords.', rawTupleElements[1]);
continue;
end;
selectorsNode := rawTuple.Items[1].AsTuple;
selectorsNode := rawTupleElements[1].AsTuple;
selectorsElements := selectorsNode.Elements;
newSelectors := TList<IAstNode>.Create;
try
// Iterate Selectors
for k := 0 to selectorsNode.Count - 1 do
for k := 0 to High(selectorsElements) do
begin
var selItem := selectorsNode.Items[k];
var selItem := selectorsElements[k];
if selItem.Kind <> akKeyword then
begin
if Assigned(FLog) then
@@ -974,23 +980,24 @@ begin
// 2. Process Transformation Lambda
lambda := P.Transformation;
lambdaParamsElements := lambda.Parameters.Elements;
if lambda.Parameters.Count <> paramTypes.Count then
if Length(lambdaParamsElements) <> paramTypes.Count then
begin
if Assigned(FLog) then
FLog.AddError(
Format(
'Pipe lambda expects %d parameters (one per selector), but got %d.',
[paramTypes.Count, lambda.Parameters.Count]
[paramTypes.Count, Length(lambdaParamsElements)]
),
lambda
);
end;
SetLength(newParams, lambda.Parameters.Count);
for k := 0 to lambda.Parameters.Count - 1 do
SetLength(newParams, Length(lambdaParamsElements));
for k := 0 to High(lambdaParamsElements) do
begin
var oldP := lambda.Parameters.Items[k].AsIdentifier;
var oldP := lambdaParamsElements[k].AsIdentifier;
var typeToInject :=
if k < paramTypes.Count then paramTypes[k]
else TTypes.Unknown;
+13 -9
View File
@@ -300,9 +300,11 @@ var
argTypes: TArray<string>;
i: Integer;
args: ITupleNode;
argsElements: TArray<IAstNode>;
begin
C := Node.AsFunctionCall;
args := C.Arguments;
argsElements := args.Elements;
LogFmt(
'FunctionCall (IsTailCall: %s, StaticTarget: %s, IsTargetPure: %s)',
@@ -317,11 +319,11 @@ begin
if Assigned(C.StaticTarget) then
begin
Indent;
SetLength(argTypes, args.Count);
for i := 0 to args.Count - 1 do
SetLength(argTypes, Length(argsElements));
for i := 0 to High(argsElements) do
begin
if args.Items[i].IsTyped then
argTypes[i] := args.Items[i].AsTypedNode.StaticType.ToString
if argsElements[i].IsTyped then
argTypes[i] := argsElements[i].AsTypedNode.StaticType.ToString
else
argTypes[i] := 'Untyped';
end;
@@ -332,7 +334,7 @@ begin
Indent;
Log('Callee:');
Visit(C.Callee);
LogFmt('Arguments (%d):', [args.Count]);
LogFmt('Arguments (%d):', [Length(argsElements)]);
Visit(args);
Unindent;
end;
@@ -469,7 +471,7 @@ var
R: IRecordLiteralNode;
begin
R := Node.AsRecordLiteral;
LogFmt('RecordLiteral (%d fields)', [R.Fields.Count], Node);
LogFmt('RecordLiteral (%d fields)', [Length(R.Fields.Elements)], Node);
Indent;
Visit(R.Fields);
Unindent;
@@ -527,15 +529,17 @@ function TAstDumper.VisitTuple(const Node: IAstNode): TVoid;
var
T: ITupleNode;
i: Integer;
elements: TArray<IAstNode>;
begin
T := Node.AsTuple;
LogFmt('Tuple (%d elements)', [T.Count], Node);
elements := T.Elements;
LogFmt('Tuple (%d elements)', [Length(elements)], Node);
Indent;
for i := 0 to T.Count - 1 do
for i := 0 to High(elements) do
begin
LogFmt('Item %d:', [i]);
Indent;
Visit(T.Items[i]);
Visit(elements[i]);
Unindent;
end;
Unindent;
+46 -42
View File
@@ -179,10 +179,12 @@ function TEvaluatorVisitor.VisitTuple(const N: ITupleNode): TDataValue;
var
elements: TArray<TDataValue>;
i: Integer;
astElements: TArray<IAstNode>;
begin
SetLength(elements, N.Count);
for i := 0 to N.Count - 1 do
elements[i] := Visit(N.Items[i]);
astElements := N.Elements;
SetLength(elements, Length(astElements));
for i := 0 to High(astElements) do
elements[i] := Visit(astElements[i]);
// Uses the implicit operator: TArray<TDataValue> -> TDataValue (vkTuple)
Result := elements;
@@ -194,6 +196,7 @@ var
i: Integer;
closureScope: IExecutionScope;
visitorFactory: TEvaluatorFactory;
paramsElements: TArray<IAstNode>;
begin
if Length(N.Upvalues) > 0 then
begin
@@ -210,7 +213,7 @@ begin
visitorFactory := CreateVisitorFactory();
var descriptor := N.Descriptor;
var params := N.Parameters;
paramsElements := N.Parameters.Elements;
var [unsafe] closure: TDataValue.TFunc;
closure :=
@@ -220,17 +223,17 @@ begin
bodyVisitor: IAstVisitor;
k: Integer;
begin
if (Length(ArgValues) <> params.Count) then
if (Length(ArgValues) <> Length(paramsElements)) then
raise EEvaluatorException.Create('Arg mismatch');
lambdaScope := TScope.CreateScope(closureScope, descriptor, capturedCells);
// Self-reference for recursion (Slot 0)
lambdaScope.SetValues(TResolvedAddress.Create(akLocalOrParent, 0, 0), TDataValue(closure));
for k := 0 to params.Count - 1 do
for k := 0 to High(paramsElements) do
begin
// Parameters are guaranteed to be Identifiers by the Binder
var paramNode := params.Items[k].AsIdentifier;
var paramNode := paramsElements[k].AsIdentifier;
lambdaScope[paramNode.Address] := ArgValues[k];
end;
@@ -245,12 +248,12 @@ var
calleeValue: TDataValue;
argValues: TArray<TDataValue>;
i: Integer;
argsTuple: ITupleNode;
argsElements: TArray<IAstNode>;
begin
argsTuple := N.Arguments;
SetLength(argValues, argsTuple.Count);
for i := 0 to argsTuple.Count - 1 do
argValues[i] := Visit(argsTuple.Items[i]);
argsElements := N.Arguments.Elements;
SetLength(argValues, Length(argsElements));
for i := 0 to High(argsElements) do
argValues[i] := Visit(argsElements[i]);
if Assigned(N.StaticTarget) then
begin
@@ -278,12 +281,12 @@ function TEvaluatorVisitor.VisitRecurNode(const N: IRecurNode): TDataValue;
var
argValues: TArray<TDataValue>;
i: Integer;
argsTuple: ITupleNode;
argsElements: TArray<IAstNode>;
begin
argsTuple := N.Arguments;
SetLength(argValues, argsTuple.Count);
for i := 0 to argsTuple.Count - 1 do
argValues[i] := Visit(argsTuple.Items[i]);
argsElements := N.Arguments.Elements;
SetLength(argValues, Length(argsElements));
for i := 0 to High(argsElements) do
argValues[i] := Visit(argsElements[i]);
// The "self" function is always at Slot 0
var callee := FScope[TResolvedAddress.Create(akLocalOrParent, 0, 0)];
@@ -292,13 +295,13 @@ end;
function TEvaluatorVisitor.VisitBlockExpression(const N: IBlockExpressionNode): TDataValue;
var
exprs: ITupleNode;
exprs: TArray<IAstNode>;
i: Integer;
begin
exprs := N.Expressions;
exprs := N.Expressions.Elements;
Result := TDataValue.Void;
for i := 0 to exprs.Count - 1 do
Result := Visit(exprs.Items[i]);
for i := 0 to High(exprs) do
Result := Visit(exprs[i]);
end;
function TEvaluatorVisitor.VisitIfExpression(const N: IIfExpressionNode): TDataValue;
@@ -397,16 +400,16 @@ end;
function TEvaluatorVisitor.VisitRecordLiteral(const N: IRecordLiteralNode): TDataValue;
var
i: Integer;
fieldsTuple: ITupleNode;
fieldsElements: TArray<IAstNode>;
begin
fieldsElements := N.Fields.Elements;
if Assigned(N.ScalarDefinition) then
begin
var vals: TArray<TScalar.TValue>;
fieldsTuple := N.Fields;
SetLength(vals, fieldsTuple.Count);
for i := 0 to fieldsTuple.Count - 1 do
SetLength(vals, Length(fieldsElements));
for i := 0 to High(fieldsElements) do
begin
var field := fieldsTuple.Items[i].AsRecordField;
var field := fieldsElements[i].AsRecordField;
vals[i] := Visit(field.Value).AsScalar.Value;
end;
Result := TScalarRecord.Create(N.ScalarDefinition, vals);
@@ -414,11 +417,10 @@ begin
else
begin
var fields: TArray<TPair<IKeyword, TDataValue>>;
fieldsTuple := N.Fields;
SetLength(fields, fieldsTuple.Count);
for i := 0 to fieldsTuple.Count - 1 do
SetLength(fields, Length(fieldsElements));
for i := 0 to High(fieldsElements) do
begin
var field := fieldsTuple.Items[i].AsRecordField;
var field := fieldsElements[i].AsRecordField;
fields[i] := TPair<IKeyword, TDataValue>.Create(field.Key.Value, Visit(field.Value));
end;
Result := TGenericRecord<TDataValue>.Create(fields);
@@ -460,24 +462,26 @@ var
sourceSeries: IScalarRecordSeries;
lambdaFunc: TDataValue.TFunc;
inputsTuple: ITupleNode;
inputsElements: TArray<IAstNode>;
entryTuple: ITupleNode;
entryElements: TArray<IAstNode>;
sourceId: IIdentifierNode;
selectorsTuple: ITupleNode;
selectorsElements: TArray<IAstNode>;
begin
inputsTuple := N.Inputs;
inputsElements := N.Inputs.Elements;
SetLength(sources, inputsTuple.Count);
SetLength(config, inputsTuple.Count);
SetLength(sources, Length(inputsElements));
SetLength(config, Length(inputsElements));
for i := 0 to inputsTuple.Count - 1 do
for i := 0 to High(inputsElements) do
begin
// Unpack: [Source, [Selectors]]
// Note: Structure is guaranteed by TypeChecker
entryTuple := inputsTuple.Items[i].AsTuple;
entryTuple := inputsElements[i].AsTuple;
entryElements := entryTuple.Elements;
sourceId := entryTuple.Items[0].AsIdentifier;
selectorsTuple := entryTuple.Items[1].AsTuple;
sourceId := entryElements[0].AsIdentifier;
selectorsElements := entryElements[1].AsTuple.Elements;
// Resolve Source Stream from Scope
// The Binder has already linked sourceId to its variable address
@@ -491,10 +495,10 @@ begin
var srcDef := sourceSeries.Def;
// Build Selectors Config
SetLength(config[i], selectorsTuple.Count);
for var k := 0 to selectorsTuple.Count - 1 do
SetLength(config[i], Length(selectorsElements));
for var k := 0 to High(selectorsElements) do
begin
var key := selectorsTuple.Items[k].AsKeyword.Value;
var key := selectorsElements[k].AsKeyword.Value;
var idx := srcDef.IndexOf(key);
// Index check already done in TypeChecker, but good for safety
if idx < 0 then
+19 -8
View File
@@ -202,13 +202,18 @@ var
T: ITupleNode;
elemsArray: TJSONArray;
i: Integer;
elements: TArray<IAstNode>;
begin
T := Node.AsTuple;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Tuple'));
elemsArray := TJSONArray.Create;
for i := 0 to T.Count - 1 do
elemsArray.Add(Visit(T.Items[i]));
// Updated: Use Elements array directly
elements := T.Elements;
for i := 0 to High(elements) do
elemsArray.Add(Visit(elements[i]));
Result.AddPair('Elements', elemsArray);
end;
@@ -611,7 +616,8 @@ var
begin
identNodes := TList<IIdentifierNode>.Create;
try
for node in JsonToNode(AObj.GetValue('Parameters')).AsTuple.ToArray do
// Updated: Use Elements instead of ToArray (or iterate if ToArray is missing)
for node in JsonToNode(AObj.GetValue('Parameters')).AsTuple.Elements do
identNodes.Add(node.AsIdentifier);
Result := TAst.LambdaExpr(identNodes.ToArray, JsonToNode(AObj.GetValue('Body')));
finally
@@ -626,7 +632,8 @@ var
begin
identNodes := TList<IIdentifierNode>.Create;
try
for node in JsonToNode(AObj.GetValue('Parameters')).AsTuple.ToArray do
// Updated: Use Elements
for node in JsonToNode(AObj.GetValue('Parameters')).AsTuple.Elements do
identNodes.Add(node.AsIdentifier);
Result :=
TAst.MacroDef(
@@ -656,7 +663,8 @@ end;
function TJsonAstConverter.JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode;
begin
Result := TAst.FunctionCall(JsonToNode(AObj.GetValue('Callee')), JsonToNode(AObj.GetValue('Arguments')).AsTuple.ToArray);
// Updated: Use Elements
Result := TAst.FunctionCall(JsonToNode(AObj.GetValue('Callee')), JsonToNode(AObj.GetValue('Arguments')).AsTuple.Elements);
end;
function TJsonAstConverter.JsonToMacroExpansionNode(const AObj: TJSONObject): IMacroExpansionNode;
@@ -667,17 +675,20 @@ begin
callee := JsonToNode(AObj.GetValue('Callee'));
expandedBody := JsonToNode(AObj.GetValue('ExpandedBody'));
argsTuple := JsonToNode(AObj.GetValue('Arguments')).AsTuple;
Result := TAst.MacroExpansionNode(TAst.FunctionCall(callee, argsTuple.ToArray), expandedBody);
// Updated: Use Elements
Result := TAst.MacroExpansionNode(TAst.FunctionCall(callee, argsTuple.Elements), expandedBody);
end;
function TJsonAstConverter.JsonToRecurNode(const AObj: TJSONObject): IRecurNode;
begin
Result := TAst.Recur(JsonToNode(AObj.GetValue('Arguments')).AsTuple.ToArray);
// Updated: Use Elements
Result := TAst.Recur(JsonToNode(AObj.GetValue('Arguments')).AsTuple.Elements);
end;
function TJsonAstConverter.JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode;
begin
Result := TAst.Block(JsonToNode(AObj.GetValue('Expressions')).AsTuple.ToArray);
// Updated: Use Elements
Result := TAst.Block(JsonToNode(AObj.GetValue('Expressions')).AsTuple.Elements);
end;
function TJsonAstConverter.JsonToVarDeclNode(const AObj: TJSONObject): IVariableDeclarationNode;
+5 -20
View File
@@ -263,12 +263,9 @@ type
ITupleNode = interface(IAstTypedNode)
{$region 'private'}
function GetCount: Integer;
function GetItems(Idx: Integer): IAstNode;
function GetElements: TArray<IAstNode>;
{$endregion}
function ToArray: TArray<IAstNode>;
property Count: Integer read GetCount;
property Items[Idx: Integer]: IAstNode read GetItems; default;
property Elements: TArray<IAstNode> read GetElements;
end;
IIfExpressionNode = interface(IAstTypedNode)
@@ -582,11 +579,9 @@ type
TTupleNode = class(TAstTypedNode, ITupleNode)
private
FElements: TArray<IAstNode>;
function GetCount: Integer;
function GetItems(Idx: Integer): IAstNode;
function GetElements: TArray<IAstNode>;
protected
function GetKind: TAstNodeKind; override;
function ToArray: TArray<IAstNode>;
public
constructor Create(const AElements: TArray<IAstNode>; const AIdentity: IAstIdentity; const AStaticType: IStaticType);
function AsTuple: ITupleNode; override;
@@ -1372,14 +1367,9 @@ begin
Result := Self;
end;
function TTupleNode.GetCount: Integer;
function TTupleNode.GetElements: TArray<IAstNode>;
begin
Result := Length(FElements);
end;
function TTupleNode.GetItems(Idx: Integer): IAstNode;
begin
Result := FElements[Idx];
Result := FElements;
end;
function TTupleNode.GetKind: TAstNodeKind;
@@ -1387,11 +1377,6 @@ begin
Result := akTuple;
end;
function TTupleNode.ToArray: TArray<IAstNode>;
begin
Result := FElements;
end;
{ TCreateSeriesNode }
constructor TCreateSeriesNode.Create(const AIdentity: IDefinitionIdentity; const AStaticType: IStaticType);
+8 -4
View File
@@ -127,12 +127,14 @@ var
i: Integer;
item, transformed: IAstNode;
list: TList<IAstNode>;
elements: TArray<IAstNode>;
begin
list := TList<IAstNode>.Create;
try
for i := 0 to Source.Count - 1 do
elements := Source.Elements;
for i := 0 to High(elements) do
begin
item := Source.Items[i];
item := elements[i];
transformed := Accept(item);
if Assigned(transformed) then
list.Add(transformed);
@@ -335,14 +337,16 @@ var
item, transformed: IAstNode;
i: Integer;
hasChanged: Boolean;
elements: TArray<IAstNode>;
begin
T := Node.AsTuple;
list := TList<IAstNode>.Create;
hasChanged := False;
try
for i := 0 to T.Count - 1 do
elements := T.Elements;
for i := 0 to High(elements) do
begin
item := T.Items[i];
item := elements[i];
transformed := Accept(item);
if transformed <> item then
+18 -21
View File
@@ -198,12 +198,13 @@ var
i: Integer;
begin
T := N.AsTuple;
var elements := T.Elements;
Append('[');
for i := 0 to T.Count - 1 do
for i := 0 to High(elements) do
begin
if i > 0 then
Append(' ');
Visit(T.Items[i]);
Visit(elements[i]);
end;
Append(']');
end;
@@ -315,26 +316,25 @@ function TPrettyPrintVisitor.VisitFunctionCall(const N: IAstNode): TVoid;
var
C: IFunctionCallNode;
i: Integer;
args: ITupleNode;
begin
C := N.AsFunctionCall;
args := C.Arguments;
var args := C.Arguments.Elements;
// Special case for 'quote'
if (C.Callee.Kind = akIdentifier) and (C.Callee.AsIdentifier.Name = 'quote') and (args.Count = 1) then
if (C.Callee.Kind = akIdentifier) and (C.Callee.AsIdentifier.Name = 'quote') and (Length(args) = 1) then
begin
Append('''');
Visit(args.Items[0]);
Visit(args[0]);
exit;
end;
Append('(');
Visit(C.Callee);
// Manually iterate arguments tuple to avoid printing [...] inside ()
for i := 0 to args.Count - 1 do
for i := 0 to High(args) do
begin
Append(' ');
Visit(args.Items[i]);
Visit(args[i]);
end;
Append(')');
end;
@@ -348,16 +348,15 @@ function TPrettyPrintVisitor.VisitRecurNode(const N: IAstNode): TVoid;
var
R: IRecurNode;
i: Integer;
args: ITupleNode;
begin
R := N.AsRecur;
args := R.Arguments;
var args := R.Arguments.Elements;
Append('(recur');
// Manually iterate arguments
for i := 0 to args.Count - 1 do
for i := 0 to High(args) do
begin
Append(' ');
Visit(args.Items[i]);
Visit(args[i]);
end;
Append(')');
end;
@@ -365,18 +364,17 @@ end;
function TPrettyPrintVisitor.VisitBlockExpression(const N: IAstNode): TVoid;
var
B: IBlockExpressionNode;
exprs: ITupleNode;
i: Integer;
begin
B := N.AsBlockExpression;
exprs := B.Expressions;
var exprs := B.Expressions.Elements;
Append('(do');
Indent;
// Manually iterate expressions tuple for newlines
for i := 0 to exprs.Count - 1 do
for i := 0 to High(exprs) do
begin
NewLine;
Visit(exprs.Items[i]);
Visit(exprs[i]);
end;
Unindent;
NewLine;
@@ -437,22 +435,21 @@ end;
function TPrettyPrintVisitor.VisitRecordLiteral(const N: IAstNode): TVoid;
var
R: IRecordLiteralNode;
fields: ITupleNode;
i: Integer;
begin
R := N.AsRecordLiteral;
fields := R.Fields;
if fields.Count = 0 then
var fields := R.Fields.Elements;
if Length(fields) = 0 then
begin
Append('{}');
exit;
end;
Append('{');
Indent;
for i := 0 to fields.Count - 1 do
for i := 0 to High(fields) do
begin
NewLine;
Visit(fields.Items[i]);
Visit(fields[i]);
end;
Unindent;
NewLine;
+6 -4
View File
@@ -453,12 +453,14 @@ begin
ErrorFmt('%s expects a vector [...]', [Context]);
tuple := Node.AsTuple;
SetLength(Result, tuple.Count);
for i := 0 to tuple.Count - 1 do
// Updated to use Elements property
var elements := tuple.Elements;
SetLength(Result, Length(elements));
for i := 0 to High(elements) do
begin
if tuple.Items[i].Kind <> akIdentifier then
if elements[i].Kind <> akIdentifier then
ErrorFmt('%s vector must contain only identifiers.', [Context]);
Result[i] := tuple.Items[i].AsIdentifier;
Result[i] := elements[i].AsIdentifier;
end;
end;
+10 -8
View File
@@ -539,11 +539,14 @@ var
begin
T := N.AsTuple;
hasChanged := False;
SetLength(newElements, T.Count);
for i := 0 to T.Count - 1 do
// Updated: Use Elements directly
var oldElements := T.Elements;
SetLength(newElements, Length(oldElements));
for i := 0 to High(oldElements) do
begin
item := T.Items[i];
item := oldElements[i];
newItem := Visit(item);
newElements[i] := newItem;
if item <> newItem then
@@ -553,7 +556,7 @@ begin
if not hasChanged then
Result := N
else
Result := TAst.Tuple(N.Identity, newElements, N.AsTypedNode.StaticType);
Result := TAst.Tuple(N.Identity, newElements, T.AsTypedNode.StaticType);
end;
{ TAstVisitor (Walker) }
@@ -731,12 +734,11 @@ begin
end;
function TAstVisitor.WalkTuple(const N: IAstNode): TVoid;
var
i: Integer;
begin
// Updated: Iterate directly via Elements
var t := N.AsTuple;
for i := 0 to t.Count - 1 do
Visit(t.Items[i]);
for var item in t.Elements do
Visit(item);
end;
end.
+10 -5
View File
@@ -335,6 +335,7 @@ var
i: Integer;
argView: TAstViewNode;
opLabel: TTextNode;
argsElements: TArray<IAstNode>;
begin
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1);
@@ -342,19 +343,23 @@ begin
OwnerNode.Orientation := loHorizontal;
OwnerNode.Alignment := laCenter;
if FNode.Arguments.Count = 1 then
// Optimization: Access Elements directly
argsElements := FNode.Arguments.Elements;
var argCount := Length(argsElements);
if argCount = 1 then
begin
opLabel := OwnerNode.AddLabel(OwnerNode, FCalleeName);
opLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
opLabel.Color := TAlphaColors.Darkorange;
opLabel.Margins := TMarginRect.Create(2, 0, 2, 0);
argView := visu.CallAccept(FNode.Arguments.Items[0]);
argView := visu.CallAccept(argsElements[0]);
FArgumentNodes.Add(argView);
end
else
begin
for i := 0 to FNode.Arguments.Count - 1 do
for i := 0 to argCount - 1 do
begin
if i > 0 then
begin
@@ -365,12 +370,12 @@ begin
opLabel.Margins := TMarginRect.Create(0, 0, 0, 0);
end;
argView := visu.CallAccept(FNode.Arguments.Items[i]);
argView := visu.CallAccept(argsElements[i]);
FArgumentNodes.Add(argView);
end;
end;
if FNode.Arguments.Count = 0 then
if argCount = 0 then
begin
opLabel := OwnerNode.AddLabel(OwnerNode, FCalleeName);
opLabel.FontSettings.Font.Style := [TFontStyle.fsBold];
+7 -3
View File
@@ -47,6 +47,7 @@ var
i: Integer;
childView: TAstViewNode;
lbl: TTextNode;
elements: TArray<IAstNode>;
begin
// Tuples act as lists, using horizontal flow (e.g. [1 2 3] or [[src [:sel]]])
OwnerNode.Frameless := True;
@@ -60,18 +61,21 @@ begin
visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth);
for i := 0 to FNode.Count - 1 do
// Optimization: Access Elements array directly
elements := FNode.Elements;
for i := 0 to High(elements) do
begin
// Standard spacing between elements
if i > 0 then
OwnerNode.AddLabel(OwnerNode, ' ');
childView := visu.CallAccept(FNode.Items[i]);
childView := visu.CallAccept(elements[i]);
FChildNodes.Add(childView);
end;
// Visual hint for empty tuples
if FNode.Count = 0 then
if Length(elements) = 0 then
begin
OwnerNode.AddLabel(OwnerNode, ' '); // spacer
end;
+19 -14
View File
@@ -13,14 +13,15 @@ uses
type
// --- Abstract List Base ---
// Implements IReorderable to support drag & drop reordering within the virtual tree
TNodeListHandler<T: IAstNode> = class(TBaseNodeHandler<INodeList<T>>, IReorderable)
// Implements IReorderable to support drag & drop reordering within the virtual tree.
// Now operates on the untyped ITupleNode.
TNodeListHandler = class(TBaseNodeHandler<ITupleNode>, IReorderable)
protected
FChildNodes: TList<TAstViewNode>;
function GetOrientation: TLayoutOrientation; virtual; abstract;
function GetAlignment: TLayoutAlignment; virtual;
public
constructor Create(const ANode: INodeList<T>);
constructor Create(const ANode: ITupleNode);
destructor Destroy; override;
procedure BuildUI(OwnerNode: TAstViewNode); override;
function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override; abstract;
@@ -32,32 +33,33 @@ type
implementation
{ TNodeListHandler<T> }
{ TNodeListHandler }
constructor TNodeListHandler<T>.Create(const ANode: INodeList<T>);
constructor TNodeListHandler.Create(const ANode: ITupleNode);
begin
inherited Create(ANode);
FChildNodes := TList<TAstViewNode>.Create;
end;
destructor TNodeListHandler<T>.Destroy;
destructor TNodeListHandler.Destroy;
begin
FChildNodes.Free;
inherited;
end;
function TNodeListHandler<T>.GetAlignment: TLayoutAlignment;
function TNodeListHandler.GetAlignment: TLayoutAlignment;
begin
Result := TLayoutAlignment.laCenter;
end;
procedure TNodeListHandler<T>.BuildUI(OwnerNode: TAstViewNode);
procedure TNodeListHandler.BuildUI(OwnerNode: TAstViewNode);
var
visu: IAstVisualizer;
i: Integer;
childView: TAstViewNode;
openTok, closeTok, sep: string;
listId: IListIdentity;
elements: TArray<IAstNode>;
begin
if (FNode.Identity <> nil) and (FNode.Identity.Kind = ikList) then
begin
@@ -83,19 +85,22 @@ begin
if openTok <> '' then
OwnerNode.AddLabel(OwnerNode, openTok);
for i := 0 to FNode.Count - 1 do
// Optimization: Access Elements array directly from ITupleNode
elements := FNode.Elements;
for i := 0 to High(elements) do
begin
childView := visu.CallAccept(FNode[i]);
childView := visu.CallAccept(elements[i]);
FChildNodes.Add(childView);
if (i < FNode.Count - 1) and (sep <> '') and (sep <> ' ') then
if (i < High(elements)) and (sep <> '') and (sep <> ' ') then
OwnerNode.AddLabel(OwnerNode, sep);
end;
if closeTok <> '' then
OwnerNode.AddLabel(OwnerNode, closeTok);
if FNode.Count = 0 then
if Length(elements) = 0 then
begin
if (openTok = '') and (closeTok = '') then
begin
@@ -107,7 +112,7 @@ end;
// IReorderable Implementation
function TNodeListHandler<T>.CanDrag(Child: TVisualNode): Boolean;
function TNodeListHandler.CanDrag(Child: TVisualNode): Boolean;
begin
// Only allow dragging if the child is one of our managed ViewNodes
if Child is TAstViewNode then
@@ -116,7 +121,7 @@ begin
Result := False;
end;
procedure TNodeListHandler<T>.MoveChild(SourceIndex, TargetIndex: Integer);
procedure TNodeListHandler.MoveChild(SourceIndex, TargetIndex: Integer);
var
item: TAstViewNode;
begin
+2 -1
View File
@@ -119,7 +119,8 @@ begin
if Node.Kind = akBlockExpression then
begin
block := Node.AsBlockExpression;
for child in block.Expressions.ToArray do
// Updated: Use Elements instead of ToArray
for child in block.Expressions.Elements do
begin
Result := FindFirstVarName(child);
if Result <> '' then
+31 -20
View File
@@ -23,6 +23,7 @@ type
[TestCase('Integer', '42,42')]
[TestCase('Negative', '-10,-10')]
[TestCase('Zero', '0,0')]
[TestCase('Nop', '...')]
procedure Parser_Number_Integer(const Source: string; Expected: Int64);
[Test]
@@ -129,6 +130,12 @@ var
node: IAstNode;
begin
node := Parse(Source);
if Source = '...' then
begin
Assert.AreEqual<TAstNodeKind>(TAstNodeKind.akNop, node.Kind);
Exit;
end;
Assert.AreEqual<TAstNodeKind>(TAstNodeKind.akConstant, node.Kind);
Assert.AreEqual<TScalar.TKind>(TScalar.TKind.Ordinal, node.AsConstant.Value.AsScalar.Kind);
Assert.AreEqual<Int64>(Expected, node.AsConstant.Value.AsScalar.Value.AsInt64);
@@ -209,11 +216,11 @@ begin
call := node.AsFunctionCall;
Assert.AreEqual<string>('add', call.Callee.AsIdentifier.Name);
// Use Count instead of Length
Assert.AreEqual<Integer>(2, call.Arguments.Count);
// Use Length(Elements) instead of Count
Assert.AreEqual<Integer>(2, Length(call.Arguments.Elements));
Assert.AreEqual<Int64>(1, call.Arguments[0].AsConstant.Value.AsScalar.Value.AsInt64);
Assert.AreEqual<Int64>(2, call.Arguments[1].AsConstant.Value.AsScalar.Value.AsInt64);
Assert.AreEqual<Int64>(1, call.Arguments.Elements[0].AsConstant.Value.AsScalar.Value.AsInt64);
Assert.AreEqual<Int64>(2, call.Arguments.Elements[1].AsConstant.Value.AsScalar.Value.AsInt64);
end;
procedure TTestMycAstScript.Parser_RecordLiteral;
@@ -226,14 +233,15 @@ begin
Assert.AreEqual<TAstNodeKind>(TAstNodeKind.akRecordLiteral, node.Kind);
rec := node.AsRecordLiteral;
Assert.AreEqual<Integer>(2, rec.Fields.Count);
// Use Length(Elements) instead of Count
Assert.AreEqual<Integer>(2, Length(rec.Fields.Elements));
// FIX: Cast elements to RecordField to access Key/Value
Assert.AreEqual<string>('a', rec.Fields[0].AsRecordField.Key.Value.Name);
Assert.AreEqual<Int64>(1, rec.Fields[0].AsRecordField.Value.AsConstant.Value.AsScalar.Value.AsInt64);
Assert.AreEqual<string>('a', rec.Fields.Elements[0].AsRecordField.Key.Value.Name);
Assert.AreEqual<Int64>(1, rec.Fields.Elements[0].AsRecordField.Value.AsConstant.Value.AsScalar.Value.AsInt64);
Assert.AreEqual<string>('b', rec.Fields[1].AsRecordField.Key.Value.Name);
Assert.AreEqual<Int64>(2, rec.Fields[1].AsRecordField.Value.AsConstant.Value.AsScalar.Value.AsInt64);
Assert.AreEqual<string>('b', rec.Fields.Elements[1].AsRecordField.Key.Value.Name);
Assert.AreEqual<Int64>(2, rec.Fields.Elements[1].AsRecordField.Value.AsConstant.Value.AsScalar.Value.AsInt64);
end;
procedure TTestMycAstScript.Parser_RecordLiteral_Empty;
@@ -242,7 +250,8 @@ var
begin
node := Parse('{}');
Assert.AreEqual<TAstNodeKind>(TAstNodeKind.akRecordLiteral, node.Kind);
Assert.AreEqual<Integer>(0, node.AsRecordLiteral.Fields.Count);
// Use Length(Elements) instead of Count
Assert.AreEqual<Integer>(0, Length(node.AsRecordLiteral.Fields.Elements));
end;
// --- Special Forms ---
@@ -271,10 +280,11 @@ begin
Assert.AreEqual<TAstNodeKind>(TAstNodeKind.akLambdaExpression, node.Kind);
lam := node.AsLambdaExpression;
Assert.AreEqual<Integer>(2, lam.Parameters.Count);
// Use Length(Elements) instead of Count
Assert.AreEqual<Integer>(2, Length(lam.Parameters.Elements));
// FIX: Cast tuple items to Identifier to access Name
Assert.AreEqual<string>('a', lam.Parameters[0].AsIdentifier.Name);
Assert.AreEqual<string>('b', lam.Parameters[1].AsIdentifier.Name);
Assert.AreEqual<string>('a', lam.Parameters.Elements[0].AsIdentifier.Name);
Assert.AreEqual<string>('b', lam.Parameters.Elements[1].AsIdentifier.Name);
Assert.AreEqual<TAstNodeKind>(TAstNodeKind.akFunctionCall, lam.Body.Kind);
end;
@@ -322,8 +332,9 @@ begin
call := node.AsFunctionCall;
Assert.AreEqual<string>('quote', call.Callee.AsIdentifier.Name);
Assert.AreEqual<Integer>(1, call.Arguments.Count);
Assert.AreEqual<string>('foo', call.Arguments[0].AsIdentifier.Name);
// Use Length(Elements) instead of Count
Assert.AreEqual<Integer>(1, Length(call.Arguments.Elements));
Assert.AreEqual<string>('foo', call.Arguments.Elements[0].AsIdentifier.Name);
end;
procedure TTestMycAstScript.Parser_Quasiquote_CreatesNode;
@@ -360,27 +371,27 @@ end;
procedure TTestMycAstScript.Parser_Error_UnbalancedParens_MissingRight;
begin
Assert.WillRaise(procedure begin Parse('(a b'); end);
Assert.WillRaise(procedure begin Parse('(a b'); end, EParserException);
end;
procedure TTestMycAstScript.Parser_Error_UnbalancedParens_ExtraRight;
begin
Assert.WillRaise(procedure begin Parse('a )'); end);
Assert.WillRaise(procedure begin Parse('a )'); end, EParserException);
end;
procedure TTestMycAstScript.Parser_Error_UnterminatedString;
begin
Assert.WillRaise(procedure begin Parse('"hello'); end);
Assert.WillRaise(procedure begin Parse('"hello'); end, EParserException);
end;
procedure TTestMycAstScript.Parser_Error_EmptyList;
begin
Assert.WillRaise(procedure begin Parse('()'); end);
Assert.WillRaise(procedure begin Parse('()'); end, EParserException);
end;
procedure TTestMycAstScript.Parser_Error_Record_MissingValue;
begin
Assert.WillRaise(procedure begin Parse('{:a 1 :b}'); end);
Assert.WillRaise(procedure begin Parse('{:a 1 :b}'); end, EParserException);
end;
end.
+30 -18
View File
@@ -104,8 +104,8 @@ end;
function TTestAstBinder.Unwrap(const Node: IAstNode): IAstNode;
begin
if (Node.Kind = akBlockExpression) and (Node.AsBlockExpression.Expressions.Count = 1) then
Result := Node.AsBlockExpression.Expressions[0]
if (Node.Kind = akBlockExpression) and (Length(Node.AsBlockExpression.Expressions.Elements) = 1) then
Result := Node.AsBlockExpression.Expressions.Elements[0]
else
Result := Node;
end;
@@ -132,7 +132,7 @@ begin
block := bound.AsBlockExpression;
// Check definition of 'b'
decl := block.Expressions[1].AsVariableDeclaration;
decl := block.Expressions.Elements[1].AsVariableDeclaration;
initIdent := decl.Initializer.AsIdentifier;
// 'a' is Slot 0. 'b' is Slot 1.
@@ -177,7 +177,7 @@ begin
Assert.IsFalse(log.HasErrors);
block := bound.AsBlockExpression;
ident := block.Expressions[1].AsIdentifier;
ident := block.Expressions.Elements[1].AsIdentifier;
Assert.AreEqual<TAddressKind>(akLocalOrParent, ident.Address.Kind);
Assert.AreEqual<Integer>(0, ident.Address.ScopeDepth);
Assert.AreEqual<Integer>(0, ident.Address.SlotIndex);
@@ -192,14 +192,15 @@ var
innerUsage: IIdentifierNode;
log: ICompilerLog;
begin
root :=
TAst.Block([TAst.VarDecl(TAst.Identifier('x'), TAst.Constant(1)), TAst.LambdaExpr([TAst.Identifier('x')], TAst.Identifier('x'))]);
// Convert array to tuple for lambda params
var paramTuple := TAst.Tuple([TAst.Identifier('x')]);
root := TAst.Block([TAst.VarDecl(TAst.Identifier('x'), TAst.Constant(1)), TAst.LambdaExpr(nil, paramTuple, TAst.Identifier('x'))]);
bound := Bind(root, layout, log);
Assert.IsFalse(log.HasErrors);
block := bound.AsBlockExpression;
innerLambda := block.Expressions[1].AsLambdaExpression;
innerLambda := block.Expressions.Elements[1].AsLambdaExpression;
innerUsage := innerLambda.Body.AsIdentifier;
Assert.AreEqual<Integer>(1, innerUsage.Address.SlotIndex); // Parameter x (Slot 1 because <self> is Slot 0)
end;
@@ -211,7 +212,8 @@ var
log: ICompilerLog;
begin
// (do (fn [] (def a 1)) a) -> 'a' is inside lambda, not visible outside
root := TAst.Block([TAst.LambdaExpr([], TAst.VarDecl(TAst.Identifier('a'), TAst.Constant(1))), TAst.Identifier('a')]);
var emptyParams := TAst.Tuple([]);
root := TAst.Block([TAst.LambdaExpr(nil, emptyParams, TAst.VarDecl(TAst.Identifier('a'), TAst.Constant(1))), TAst.Identifier('a')]);
Bind(root, layout, log);
@@ -230,14 +232,16 @@ var
lambda: ILambdaExpressionNode;
log: ICompilerLog;
begin
root := TAst.LambdaExpr([TAst.Identifier('p1')], TAst.Nop);
// Use Tuple factory for params
var params := TAst.Tuple([TAst.Identifier('p1')]);
root := TAst.LambdaExpr(nil, params, TAst.Nop);
bound := Unwrap(Bind(root, layout, log));
Assert.IsFalse(log.HasErrors);
lambda := bound.AsLambdaExpression;
// FIX: Cast element to Identifier to access Address
Assert.AreEqual<Integer>(1, lambda.Parameters[0].AsIdentifier.Address.SlotIndex); // Slot 0 is reserved for <self>
Assert.AreEqual<Integer>(1, lambda.Parameters.Elements[0].AsIdentifier.Address.SlotIndex); // Slot 0 is reserved for <self>
end;
procedure TTestAstBinder.Test_MultipleParameters_AreBoundCorrectly;
@@ -247,15 +251,17 @@ var
lambda: ILambdaExpressionNode;
log: ICompilerLog;
begin
root := TAst.LambdaExpr([TAst.Identifier('a'), TAst.Identifier('b')], TAst.Nop);
// Use Tuple factory for params
var params := TAst.Tuple([TAst.Identifier('a'), TAst.Identifier('b')]);
root := TAst.LambdaExpr(nil, params, TAst.Nop);
bound := Unwrap(Bind(root, layout, log));
Assert.IsFalse(log.HasErrors);
lambda := bound.AsLambdaExpression;
// FIX: Cast elements to Identifier
Assert.AreEqual<Integer>(1, lambda.Parameters[0].AsIdentifier.Address.SlotIndex);
Assert.AreEqual<Integer>(2, lambda.Parameters[1].AsIdentifier.Address.SlotIndex);
Assert.AreEqual<Integer>(1, lambda.Parameters.Elements[0].AsIdentifier.Address.SlotIndex);
Assert.AreEqual<Integer>(2, lambda.Parameters.Elements[1].AsIdentifier.Address.SlotIndex);
end;
// ------------------------------------------------------------------------------------------------
@@ -271,13 +277,14 @@ var
bodyIdent: IIdentifierNode;
log: ICompilerLog;
begin
root := TAst.Block([TAst.VarDecl(TAst.Identifier('x'), TAst.Constant(99)), TAst.LambdaExpr([], TAst.Identifier('x'))]);
var emptyParams := TAst.Tuple([]);
root := TAst.Block([TAst.VarDecl(TAst.Identifier('x'), TAst.Constant(99)), TAst.LambdaExpr(nil, emptyParams, TAst.Identifier('x'))]);
bound := Bind(root, layout, log);
Assert.IsFalse(log.HasErrors);
block := bound.AsBlockExpression;
lambda := block.Expressions[1].AsLambdaExpression;
lambda := block.Expressions.Elements[1].AsLambdaExpression;
bodyIdent := lambda.Body.AsIdentifier;
// Inside the lambda, 'x' is accessed via an Upvalue
@@ -293,16 +300,20 @@ var
midLambda, innerLambda: ILambdaExpressionNode;
log: ICompilerLog;
begin
var emptyParams := TAst.Tuple([]);
root :=
TAst.Block(
[TAst.VarDecl(TAst.Identifier('top'), TAst.Constant(100)), TAst.LambdaExpr([], TAst.LambdaExpr([], TAst.Identifier('top')))]
[
TAst.VarDecl(TAst.Identifier('top'), TAst.Constant(100)),
TAst.LambdaExpr(nil, emptyParams, TAst.LambdaExpr(nil, emptyParams, TAst.Identifier('top')))
]
);
bound := Bind(root, layout, log);
Assert.IsFalse(log.HasErrors);
outerBlock := bound.AsBlockExpression;
midLambda := outerBlock.Expressions[1].AsLambdaExpression;
midLambda := outerBlock.Expressions.Elements[1].AsLambdaExpression;
innerLambda := midLambda.Body.AsLambdaExpression;
// Inner lambda accesses 'top' via upvalue
@@ -316,7 +327,8 @@ var
lambda: ILambdaExpressionNode;
log: ICompilerLog;
begin
root := TAst.LambdaExpr([], TAst.Identifier('<self>'));
var emptyParams := TAst.Tuple([]);
root := TAst.LambdaExpr(nil, emptyParams, TAst.Identifier('<self>'));
bound := Unwrap(Bind(root, layout, log));
Assert.IsFalse(log.HasErrors);