Consume the COLON token: a line can hold several statements
The token has existed since the scanner was written and nothing read it, so 10 PRINT A$ : REM ... was a parse error. Leading separators are consumed before each statement and an empty statement yields a NULL leaf rather than an error, so a trailing colon and a run of them are both legal. BASIC 7.0 scopes everything after THEN to the condition, which the reference had no opinion about because it never got here. The rule is not "skip when false": the rest of the line belongs to whichever arm was written last. A whole FOR/NEXT on one line still does not loop -- block skipping works by source line. Recorded in TODO.md as what group A has to fix first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
32
src/parser.c
32
src/parser.c
@@ -150,12 +150,36 @@ static akerr_ErrorContext *unary(akbasic_Parser *obj, akbasic_ASTLeaf **dest);
|
||||
static akerr_ErrorContext *exponent(akbasic_Parser *obj, akbasic_ASTLeaf **dest);
|
||||
static akerr_ErrorContext *function_call(akbasic_Parser *obj, akbasic_ASTLeaf **dest);
|
||||
|
||||
bool akbasic_parser_skip_separators(akbasic_Parser *obj)
|
||||
{
|
||||
while ( akbasic_parser_match1(obj, AKBASIC_TOK_COLON) ) {
|
||||
/*
|
||||
* A run of them, so `10 PRINT 1 :: PRINT 2` and a trailing colon both
|
||||
* work. An empty statement is not an error on a C128 and is not one
|
||||
* here.
|
||||
*/
|
||||
}
|
||||
return !akbasic_parser_is_at_end(obj);
|
||||
}
|
||||
|
||||
akerr_ErrorContext *akbasic_parser_parse(akbasic_Parser *obj, akbasic_ASTLeaf **dest)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
|
||||
FAIL_ZERO_RETURN(errctx, (obj != NULL && dest != NULL), AKERR_NULLPOINTER,
|
||||
"NULL argument in parse");
|
||||
*dest = NULL;
|
||||
/*
|
||||
* Statements are separated by colons. The scanner has emitted the token
|
||||
* since the port began and nothing consumed it, so a line with two
|
||||
* statements on it was a parse error -- TODO.md section 4.
|
||||
*
|
||||
* Nothing after the separators is a line that ended in one. That is not an
|
||||
* error, so the caller gets a NULL leaf and skips it.
|
||||
*/
|
||||
if ( !akbasic_parser_skip_separators(obj) ) {
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
PASS(errctx, akbasic_parser_command(obj, dest));
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
@@ -196,7 +220,13 @@ akerr_ErrorContext *akbasic_parser_command(akbasic_Parser *obj, akbasic_ASTLeaf
|
||||
* when there is one and it will not parse.
|
||||
*/
|
||||
righttoken = akbasic_parser_peek(obj);
|
||||
if ( righttoken != NULL && righttoken->tokentype != AKBASIC_TOK_UNDEFINED ) {
|
||||
if ( righttoken != NULL && righttoken->tokentype != AKBASIC_TOK_UNDEFINED &&
|
||||
righttoken->tokentype != AKBASIC_TOK_COLON ) {
|
||||
/*
|
||||
* A colon ends the statement, so `PRINT : PRINT "X"` is a bare PRINT
|
||||
* followed by another statement rather than a PRINT of whatever a colon
|
||||
* evaluates to.
|
||||
*/
|
||||
PASS(errctx, akbasic_parser_expression(obj, &right));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user