Fix five defects the port uncovered in the reference parser and scanner

Chained subtraction and exponentiation fold left instead of abandoning the
rest of the line -- `1 - 2 - 3` gave -1 and dropped the `- 3`. A unary leaf's
operand moves to .left, so a negative literal is one argument again and a
second argument no longer overwrites it. An operator in a line's final column
survives. Hex literals reach the parser whole. A leading zero is padding, not
a radix.

Item 16 stays pinned: the fix works and costs an upstream golden case, which
is a decision rather than a defect.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 11:35:14 -04:00
parent f24201fdef
commit 1eb8f9ffaa
14 changed files with 272 additions and 167 deletions

View File

@@ -349,10 +349,15 @@ akerr_ErrorContext *akbasic_parser_relation(akbasic_Parser *obj, akbasic_ASTLeaf
}
/*
* subtraction and exponent return after one operator where addition,
* multiplication and division loop. That asymmetry is the reference's, not a
* transcription slip: `1 - 2 - 3` parses as `1 - (2 - 3)` there. Reproduced,
* because the golden corpus encodes the observed associativity.
* Loops, where the reference returns after one operator (basicparser.go:329).
* That asymmetry was reproduced faithfully at first and is TODO.md section 6
* item 12: `1 - 2 - 3` computed `1 - 2` and abandoned the rest of the line,
* which the statement loop then picked up as a second statement and discarded.
* A wrong answer, silently -- the highest-impact item on that list.
*
* Left-associative, matching addition, multiplication and division here and
* matching a C128, which evaluates a run of same-precedence operators left to
* right.
*/
static akerr_ErrorContext *subtraction(akbasic_Parser *obj, akbasic_ASTLeaf **dest)
{
@@ -363,15 +368,16 @@ static akerr_ErrorContext *subtraction(akbasic_Parser *obj, akbasic_ASTLeaf **de
akbasic_Token *operator_ = NULL;
PASS(errctx, addition(obj, &left));
if ( akbasic_parser_match1(obj, AKBASIC_TOK_MINUS) ) {
while ( akbasic_parser_match1(obj, AKBASIC_TOK_MINUS) ) {
PASS(errctx, akbasic_parser_previous(obj, &operator_));
PASS(errctx, addition(obj, &right));
if ( expr != NULL ) {
left = expr;
}
PASS(errctx, akbasic_parser_new_leaf(obj, &expr));
PASS(errctx, akbasic_leaf_new_binary(expr, left, operator_->tokentype, right));
*dest = expr;
SUCCEED_RETURN(errctx);
}
*dest = left;
*dest = (expr != NULL ? expr : left);
SUCCEED_RETURN(errctx);
}
@@ -469,15 +475,21 @@ static akerr_ErrorContext *exponent(akbasic_Parser *obj, akbasic_ASTLeaf **dest)
akbasic_Token *operator_ = NULL;
PASS(errctx, function_call(obj, &left));
if ( akbasic_parser_match1(obj, AKBASIC_TOK_CARAT) ) {
/*
* Loops for the same reason subtraction does -- `2 ^ 3 ^ 2` abandoned the
* second operator before this. Left-associative, which is a C128: 2^3^2 is
* 64 there, not the 512 a right-associative reading gives.
*/
while ( akbasic_parser_match1(obj, AKBASIC_TOK_CARAT) ) {
PASS(errctx, akbasic_parser_previous(obj, &operator_));
PASS(errctx, function_call(obj, &right));
if ( expr != NULL ) {
left = expr;
}
PASS(errctx, akbasic_parser_new_leaf(obj, &expr));
PASS(errctx, akbasic_leaf_new_binary(expr, left, operator_->tokentype, right));
*dest = expr;
SUCCEED_RETURN(errctx);
}
*dest = left;
*dest = (expr != NULL ? expr : left);
SUCCEED_RETURN(errctx);
}