Give argument lists their own link, not each argument's .right

An identifier's subscript list, a unary's operand and a binary's right-hand
side all lived in the same field the argument chain used, so ABS(-9),
MOD(A#, B# + 12) and any array reference in a parameter list were counted as
extra arguments and refused. Subscript lists move to .expr and arguments now
chain through a dedicated .next.

This is the third time the same collision was fixed; the first two moved it
along rather than removing it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 11:58:15 -04:00
parent 5b7b7d2ed9
commit f4007d80d4
14 changed files with 133 additions and 55 deletions

View File

@@ -329,10 +329,10 @@ static akerr_ErrorContext *collect_subscripts(akbasic_Environment *obj, akbasic_
akbasic_Value *tval = NULL;
*count = 0;
if ( lval->right != NULL &&
lval->right->leaftype == AKBASIC_LEAF_ARGUMENTLIST &&
lval->right->operator_ == AKBASIC_TOK_ARRAY_SUBSCRIPT ) {
for ( expr = lval->right->right; expr != NULL; expr = expr->right ) {
if ( lval->expr != NULL &&
lval->expr->leaftype == AKBASIC_LEAF_ARGUMENTLIST &&
lval->expr->operator_ == AKBASIC_TOK_ARRAY_SUBSCRIPT ) {
for ( expr = lval->expr->right; expr != NULL; expr = expr->next ) {
FAIL_ZERO_RETURN(errctx, (*count < AKBASIC_MAX_ARRAY_DEPTH), AKBASIC_ERR_BOUNDS,
"More than %d array subscripts", AKBASIC_MAX_ARRAY_DEPTH);
PASS(errctx, akbasic_runtime_evaluate(obj->runtime, expr, &tval));

View File

@@ -49,6 +49,7 @@ akerr_ErrorContext *akbasic_leaf_init(akbasic_ASTLeaf *obj, akbasic_LeafType lea
obj->left = NULL;
obj->right = NULL;
obj->expr = NULL;
obj->next = NULL;
obj->identifier[0] = '\0';
obj->literal_int = 0;
obj->literal_float = 0.0;
@@ -96,6 +97,7 @@ static akerr_ErrorContext *clone_into(akbasic_ASTLeaf *self, akbasic_LeafPool *p
PASS(errctx, clone_into(self->left, pool, &copy->left));
PASS(errctx, clone_into(self->right, pool, &copy->right));
PASS(errctx, clone_into(self->expr, pool, &copy->expr));
PASS(errctx, clone_into(self->next, pool, &copy->next));
*dest = copy;
SUCCEED_RETURN(errctx);
@@ -124,12 +126,12 @@ akbasic_ASTLeaf *akbasic_leaf_first_argument(akbasic_ASTLeaf *self)
akbasic_ASTLeaf *akbasic_leaf_first_subscript(akbasic_ASTLeaf *self)
{
if ( self == NULL ||
self->right == NULL ||
self->right->leaftype != AKBASIC_LEAF_ARGUMENTLIST ||
self->right->operator_ != AKBASIC_TOK_ARRAY_SUBSCRIPT ) {
self->expr == NULL ||
self->expr->leaftype != AKBASIC_LEAF_ARGUMENTLIST ||
self->expr->operator_ != AKBASIC_TOK_ARRAY_SUBSCRIPT ) {
return NULL;
}
return self->right->right;
return self->expr->right;
}
bool akbasic_leaf_is_identifier(akbasic_ASTLeaf *self)

View File

@@ -292,10 +292,16 @@ akerr_ErrorContext *akbasic_parser_argument_list(akbasic_Parser *obj, akbasic_To
arglist->operator_ = arglisttype;
PASS(errctx, akbasic_parser_expression(obj, &arglist->right));
/*
* Siblings chain through .next, never through .right. Every leaf type that
* can be an argument already uses .right for something of its own -- a
* unary's operand, a binary's right-hand side -- and reusing it here is what
* made ABS(-9) count as two arguments. See akbasic_ASTLeaf.next.
*/
expr = arglist->right;
while ( expr != NULL && akbasic_parser_match1(obj, AKBASIC_TOK_COMMA) ) {
PASS(errctx, akbasic_parser_expression(obj, &expr->right));
expr = expr->right;
PASS(errctx, akbasic_parser_expression(obj, &expr->next));
expr = expr->next;
}
if ( !akbasic_parser_match1(obj, AKBASIC_TOK_RIGHT_PAREN) && requireparens ) {
FAIL_RETURN(errctx, AKBASIC_ERR_SYNTAX, "Unbalanced parenthesis");
@@ -523,7 +529,7 @@ static akerr_ErrorContext *exponent(akbasic_Parser *obj, akbasic_ASTLeaf **dest)
SUCCEED_RETURN(errctx);
}
/* Count the .right-joined chain hanging off an argument list. */
/* Count the arguments hanging off an argument list. */
static int arglist_length(akbasic_ASTLeaf *arglist)
{
akbasic_ASTLeaf *leaf = NULL;
@@ -532,7 +538,7 @@ static int arglist_length(akbasic_ASTLeaf *arglist)
if ( arglist == NULL ) {
return 0;
}
for ( leaf = arglist->right; leaf != NULL; leaf = leaf->right ) {
for ( leaf = arglist->right; leaf != NULL; leaf = leaf->next ) {
count += 1;
}
return count;
@@ -624,15 +630,15 @@ akerr_ErrorContext *akbasic_parser_primary(akbasic_Parser *obj, akbasic_ASTLeaf
break;
case AKBASIC_TOK_IDENTIFIER_INT:
PASS(errctx, akbasic_leaf_new_identifier(expr, AKBASIC_LEAF_IDENTIFIER_INT, previous->lexeme));
PASS(errctx, akbasic_parser_argument_list(obj, AKBASIC_TOK_ARRAY_SUBSCRIPT, true, &expr->right));
PASS(errctx, akbasic_parser_argument_list(obj, AKBASIC_TOK_ARRAY_SUBSCRIPT, true, &expr->expr));
break;
case AKBASIC_TOK_IDENTIFIER_FLOAT:
PASS(errctx, akbasic_leaf_new_identifier(expr, AKBASIC_LEAF_IDENTIFIER_FLOAT, previous->lexeme));
PASS(errctx, akbasic_parser_argument_list(obj, AKBASIC_TOK_ARRAY_SUBSCRIPT, true, &expr->right));
PASS(errctx, akbasic_parser_argument_list(obj, AKBASIC_TOK_ARRAY_SUBSCRIPT, true, &expr->expr));
break;
case AKBASIC_TOK_IDENTIFIER_STRING:
PASS(errctx, akbasic_leaf_new_identifier(expr, AKBASIC_LEAF_IDENTIFIER_STRING, previous->lexeme));
PASS(errctx, akbasic_parser_argument_list(obj, AKBASIC_TOK_ARRAY_SUBSCRIPT, true, &expr->right));
PASS(errctx, akbasic_parser_argument_list(obj, AKBASIC_TOK_ARRAY_SUBSCRIPT, true, &expr->expr));
break;
case AKBASIC_TOK_FUNCTION:
case AKBASIC_TOK_IDENTIFIER:

View File

@@ -69,8 +69,8 @@ akerr_ErrorContext *akbasic_parse_draw(akbasic_Parser *parser, akbasic_ASTLeaf *
"DRAW expected a color source and a coordinate");
tail = arglist->right;
while ( tail->right != NULL ) {
tail = tail->right;
while ( tail->next != NULL ) {
tail = tail->next;
}
for ( ;; ) {
@@ -82,16 +82,16 @@ akerr_ErrorContext *akbasic_parse_draw(akbasic_Parser *parser, akbasic_ASTLeaf *
}
FAIL_ZERO_RETURN(errctx, akbasic_parser_match1(parser, AKBASIC_TOK_COMMAND),
AKBASIC_ERR_SYNTAX, "DRAW expected TO");
PASS(errctx, akbasic_parser_expression(parser, &tail->right));
FAIL_ZERO_RETURN(errctx, (tail->right != NULL), AKBASIC_ERR_SYNTAX,
PASS(errctx, akbasic_parser_expression(parser, &tail->next));
FAIL_ZERO_RETURN(errctx, (tail->next != NULL), AKBASIC_ERR_SYNTAX,
"DRAW expected X after TO");
tail = tail->right;
tail = tail->next;
FAIL_ZERO_RETURN(errctx, akbasic_parser_match1(parser, AKBASIC_TOK_COMMA),
AKBASIC_ERR_SYNTAX, "DRAW expected TO X,Y");
PASS(errctx, akbasic_parser_expression(parser, &tail->right));
FAIL_ZERO_RETURN(errctx, (tail->right != NULL), AKBASIC_ERR_SYNTAX,
PASS(errctx, akbasic_parser_expression(parser, &tail->next));
FAIL_ZERO_RETURN(errctx, (tail->next != NULL), AKBASIC_ERR_SYNTAX,
"DRAW expected Y after TO X,");
tail = tail->right;
tail = tail->next;
}
PASS(errctx, akbasic_parser_new_leaf(parser, &expr));
@@ -174,7 +174,7 @@ akerr_ErrorContext *akbasic_parse_def(akbasic_Parser *parser, akbasic_ASTLeaf **
FAIL_ZERO_RETURN(errctx, (arglist != NULL), AKBASIC_ERR_SYNTAX,
"Expected argument list (identifier names)");
for ( walk = arglist->right; walk != NULL; walk = walk->right ) {
for ( walk = arglist->right; walk != NULL; walk = walk->next ) {
FAIL_ZERO_RETURN(errctx,
(walk->leaftype == AKBASIC_LEAF_IDENTIFIER_STRING ||
walk->leaftype == AKBASIC_LEAF_IDENTIFIER_INT ||
@@ -321,12 +321,12 @@ akerr_ErrorContext *akbasic_parse_read(akbasic_Parser *parser, akbasic_ASTLeaf *
"Expected identifier");
PASS(errctx, akbasic_leaf_clone(expr, &env->readLeafPool, &env->readIdentifierLeaves[i]));
/*
* A cloned identifier keeps its .right chain, which for READ is the
* *next* identifier, not a subscript. Sever it so evaluating this leaf
* cannot walk into its sibling.
* A cloned identifier keeps its sibling chain, which for READ is the
* *next* identifier. Sever it so evaluating this leaf cannot walk into
* its sibling.
*/
env->readIdentifierLeaves[i]->right = NULL;
expr = expr->right;
env->readIdentifierLeaves[i]->next = NULL;
expr = expr->next;
}
env->readReturnLine = env->lineno + 1;
@@ -350,7 +350,7 @@ akerr_ErrorContext *akbasic_parse_data(akbasic_Parser *parser, akbasic_ASTLeaf *
PASS(errctx, akbasic_parser_argument_list(parser, AKBASIC_TOK_FUNCTION_ARGUMENT, false, &arglist));
FAIL_ZERO_RETURN(errctx, (arglist != NULL && arglist->right != NULL), AKBASIC_ERR_SYNTAX,
"Expected literal");
for ( expr = arglist->right; expr != NULL; expr = expr->right ) {
for ( expr = arglist->right; expr != NULL; expr = expr->next ) {
FAIL_ZERO_RETURN(errctx, akbasic_leaf_is_literal(expr), AKBASIC_ERR_SYNTAX,
"Expected literal");
}

View File

@@ -308,15 +308,17 @@ static akerr_ErrorContext *evaluate_identifier(akbasic_Runtime *obj, akbasic_AST
int subscriptcount = 0;
/*
* A .right hanging off an identifier is an array subscript only when it is
* an ARRAY_SUBSCRIPT argument list; anything else belongs to the enclosing
* expression and must not be followed.
* An identifier's subscript list hangs off .expr, deliberately clear of
* .right -- which is where an argument list chains its arguments, and where
* INPUT's parse handler would otherwise collide with it. Checked for the
* ARRAY_SUBSCRIPT operator anyway, because .expr means something else on the
* leaf types that use it for grouping.
*/
texpr = expr->right;
texpr = expr->expr;
if ( texpr != NULL &&
texpr->leaftype == AKBASIC_LEAF_ARGUMENTLIST &&
texpr->operator_ == AKBASIC_TOK_ARRAY_SUBSCRIPT ) {
for ( texpr = texpr->right; texpr != NULL; texpr = texpr->right ) {
for ( texpr = texpr->right; texpr != NULL; texpr = texpr->next ) {
FAIL_ZERO_RETURN(errctx, (subscriptcount < AKBASIC_MAX_ARRAY_DEPTH),
AKBASIC_ERR_BOUNDS,
"More than %d array subscripts", AKBASIC_MAX_ARRAY_DEPTH);
@@ -611,8 +613,8 @@ akerr_ErrorContext *akbasic_runtime_user_function(akbasic_Runtime *obj, akbasic_
obj->environment = fndef->environment;
PASS(errctx, akbasic_environment_assign(fndef->environment, argptr, argvalue, &unused));
obj->environment = callerenv;
leafptr = leafptr->right;
argptr = argptr->right;
leafptr = leafptr->next;
argptr = argptr->next;
}
obj->environment = fndef->environment;

View File

@@ -68,7 +68,7 @@ static akerr_ErrorContext *collect_numbers(akbasic_Runtime *obj, akbasic_ASTLeaf
values[*count] = (value->valuetype == AKBASIC_TYPE_FLOAT)
? value->floatval : (double)value->intval;
*count += 1;
arg = arg->right;
arg = arg->next;
}
SUCCEED_RETURN(errctx);
}

View File

@@ -224,9 +224,9 @@ akerr_ErrorContext *akbasic_cmd_dim(akbasic_Runtime *obj, akbasic_ASTLeaf *expr,
(void)lval; (void)rval;
FAIL_ZERO_RETURN(errctx,
(expr != NULL && expr->right != NULL && expr->right->right != NULL &&
expr->right->right->leaftype == AKBASIC_LEAF_ARGUMENTLIST &&
expr->right->right->operator_ == AKBASIC_TOK_ARRAY_SUBSCRIPT &&
(expr != NULL && expr->right != NULL && expr->right->expr != NULL &&
expr->right->expr->leaftype == AKBASIC_LEAF_ARGUMENTLIST &&
expr->right->expr->operator_ == AKBASIC_TOK_ARRAY_SUBSCRIPT &&
akbasic_leaf_is_identifier(expr->right)),
AKBASIC_ERR_SYNTAX, "Expected DIM IDENTIFIER(DIMENSIONS, ...)");
@@ -234,7 +234,7 @@ akerr_ErrorContext *akbasic_cmd_dim(akbasic_Runtime *obj, akbasic_ASTLeaf *expr,
FAIL_ZERO_RETURN(errctx, (variable != NULL), AKBASIC_ERR_UNDEFINED,
"Unable to get variable for identifier %s", expr->right->identifier);
for ( walk = expr->right->right->right; walk != NULL; walk = walk->right ) {
for ( walk = expr->right->expr->right; walk != NULL; walk = walk->next ) {
FAIL_ZERO_RETURN(errctx, (sizecount < AKBASIC_MAX_ARRAY_DEPTH), AKBASIC_ERR_BOUNDS,
"More than %d array dimensions", AKBASIC_MAX_ARRAY_DEPTH);
PASS(errctx, akbasic_runtime_evaluate(obj, walk, &size));
@@ -281,14 +281,14 @@ akerr_ErrorContext *akbasic_cmd_poke(akbasic_Runtime *obj, akbasic_ASTLeaf *expr
FAIL_NONZERO_RETURN(errctx, (addrval->valuetype != AKBASIC_TYPE_INTEGER), AKBASIC_ERR_TYPE,
"POKE expected INTEGER, INTEGER");
FAIL_ZERO_RETURN(errctx,
(arg->right != NULL &&
(arg->right->leaftype == AKBASIC_LEAF_LITERAL_INT ||
arg->right->leaftype == AKBASIC_LEAF_IDENTIFIER_INT)),
(arg->next != NULL &&
(arg->next->leaftype == AKBASIC_LEAF_LITERAL_INT ||
arg->next->leaftype == AKBASIC_LEAF_IDENTIFIER_INT)),
AKBASIC_ERR_SYNTAX, "POKE expected INTEGER, INTEGER");
FAIL_ZERO_RETURN(errctx, (addrval->intval != 0), AKBASIC_ERR_VALUE,
"POKE got NIL pointer or uninitialized variable");
PASS(errctx, akbasic_runtime_evaluate(obj, arg->right, &byteval));
PASS(errctx, akbasic_runtime_evaluate(obj, arg->next, &byteval));
target = (uint8_t *)(uintptr_t)addrval->intval;
*target = (uint8_t)byteval->intval;
SUCCEED_TRUE(obj, dest);
@@ -761,7 +761,7 @@ akerr_ErrorContext *akbasic_cmd_data(akbasic_Runtime *obj, akbasic_ASTLeaf *expr
FAIL_ZERO_RETURN(errctx, (expr != NULL && expr->right != NULL), AKERR_NULLPOINTER,
"NIL expression or argument list");
for ( literal = expr->right->right; literal != NULL; literal = literal->right ) {
for ( literal = expr->right->right; literal != NULL; literal = literal->next ) {
if ( env->readIdentifierIdx >= AKBASIC_MAX_LEAVES ) {
break;
}

View File

@@ -312,7 +312,7 @@ static akerr_ErrorContext *nth_arg(akbasic_Runtime *obj, akbasic_ASTLeaf *expr,
FAIL_ZERO_RETURN(errctx, (expr != NULL), AKERR_NULLPOINTER, "NIL leaf");
arg = akbasic_leaf_first_argument(expr);
for ( i = 0; i < n && arg != NULL; i++ ) {
arg = arg->right;
arg = arg->next;
}
FAIL_ZERO_RETURN(errctx, (arg != NULL), AKBASIC_ERR_SYNTAX,
"%s is missing argument %d", fname, n + 1);

View File

@@ -99,7 +99,7 @@ static akerr_ErrorContext *collect_numbers(akbasic_Runtime *obj, akbasic_ASTLeaf
values[*count] = (double)value->intval;
}
*count += 1;
arg = arg->right;
arg = arg->next;
}
SUCCEED_RETURN(errctx);
}
@@ -557,7 +557,7 @@ static akerr_ErrorContext *shape_variable(akbasic_Runtime *obj, akbasic_ASTLeaf
PASS(errctx, akbasic_environment_get(obj->environment, arg->identifier, dest));
FAIL_ZERO_RETURN(errctx, (*dest != NULL), AKBASIC_ERR_UNDEFINED,
"%s could not reach the variable %s", verb, arg->identifier);
*rest = arg->right;
*rest = arg->next;
SUCCEED_RETURN(errctx);
}
@@ -584,7 +584,7 @@ akerr_ErrorContext *akbasic_cmd_sshape(akbasic_Runtime *obj, akbasic_ASTLeaf *ex
coords[count] = (value->valuetype == AKBASIC_TYPE_FLOAT)
? value->floatval : (double)value->intval;
count += 1;
arg = arg->right;
arg = arg->next;
}
FAIL_ZERO_RETURN(errctx, (count >= 2), AKBASIC_ERR_SYNTAX,
"SSHAPE expected A$, X1, Y1");
@@ -636,7 +636,7 @@ akerr_ErrorContext *akbasic_cmd_gshape(akbasic_Runtime *obj, akbasic_ASTLeaf *ex
coords[count] = (value->valuetype == AKBASIC_TYPE_FLOAT)
? value->floatval : (double)value->intval;
count += 1;
arg = arg->right;
arg = arg->next;
}
x = (count >= 1) ? coords[0] : obj->gfx.x;
y = (count >= 2) ? coords[1] : obj->gfx.y;