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