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
+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);