From b88189ed334a4d4aa7967f12e43f9851eaaee594 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 20:39:04 +0000 Subject: [PATCH 1/2] Allows abbreviating ENDPROC as "EN.", in both compilers. "EN." is also an abbreviation of ENDIF, which is tried first: if there is an IF open it closes the IF, as before, and if not, the parser now tries ENDPROC. The cross compiler already did this, as a loop error from ENDIF only fails the current alternative. The native E_POP_IF now does the same: when there is no IF or ELSE to close it fails, so the parser continues with the next statements, and sets the error to report if none matches to "bad loop". As errors are now reported after trying all the alternatives, the column of these errors is the same in both compilers. Costs 23 bytes in the native compilers. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NASRhH2Xg6HmeBUZTSPhwM --- manual.md | 7 +++++- src/actions.asm | 20 +++++++++++++++++- src/parse.asm | 7 ++++-- src/syntax/basic.syn | 2 +- testsuite/tests/err-endif-proc.bas | 4 ++++ testsuite/tests/err-endif-proc.chk | 4 ++++ testsuite/tests/err-endproc.bas | 3 +++ testsuite/tests/err-endproc.chk | 4 ++++ testsuite/tests/proc-abbrev.bas | 34 ++++++++++++++++++++++++++++++ testsuite/tests/proc-abbrev.chk | 12 +++++++++++ 10 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 testsuite/tests/err-endif-proc.bas create mode 100644 testsuite/tests/err-endif-proc.chk create mode 100644 testsuite/tests/err-endproc.bas create mode 100644 testsuite/tests/err-endproc.chk create mode 100644 testsuite/tests/proc-abbrev.bas create mode 100644 testsuite/tests/proc-abbrev.chk diff --git a/manual.md b/manual.md index ac5caef..8b91017 100644 --- a/manual.md +++ b/manual.md @@ -1383,12 +1383,17 @@ Control Statements **Define A Subroutine.** **PROC _name_ _var1_ .../ PR.** -**ENDPROC / ENDP.** +**ENDPROC / EN.** `PROC` statement starts the definition of a subroutine that can be called via `EXEC` or `@`. + `EN.` is also an abbreviation of + `ENDIF`: if there is an `IF` open, + `EN.` closes the `IF`, if not, it + closes the `PROC`. + You can pass a list of integer variables separated by spaces after the `PROC` name to specify a number diff --git a/src/actions.asm b/src/actions.asm index eecdf8c..33e0d1b 100644 --- a/src/actions.asm +++ b/src/actions.asm @@ -44,7 +44,7 @@ ; From parser.asm .import parser_error, parser_skipws, parser_emit_byte, parser_inc_opos ; From error.asm - .importzp ERR_LOOP + .importzp ERR_LOOP, parse_err .ifdef FASTBASIC_FP ; Exported only in Floating Point version @@ -801,6 +801,24 @@ move: .endproc .proc E_POP_IF + ; Check that there is an IF or ELSE to close. If not, don't give the + ; error now: fail, so the parser tries the next statements (ENDIF and + ; ENDPROC share the "EN." abbreviation), and report a loop error only + ; if none matches. The cross compiler does the same. + ldy loop_sp + beq no_if + lda loop_stk-3, y + .assert LT_ELSE = LT_IF + 1, error, "LT_ELSE must be LT_IF + 1" + ; C=0 on entry, so this is A - LT_IF: 0 for IF, 1 for ELSE + sbc #LT_IF-1 + cmp #2 + bcc is_if +no_if: + lda #ERR_LOOP + sta parse_err + sec + rts +is_if: ; Patch IF/ELSE with current position lda #LT_ELSE check_elif: diff --git a/src/parse.asm b/src/parse.asm index 3ea14c8..fe02aba 100644 --- a/src/parse.asm +++ b/src/parse.asm @@ -22,7 +22,7 @@ .export parser_start, parser_error, parser_skipws, parser_emit_byte, parser_inc_opos ; Parser state .exportzp bptr, bpos, bmax, linenum, buf_ptr - .exportzp loop_sp, var_sp + .exportzp loop_sp, var_sp, parse_err ; Output state .exportzp opos ; From actions.asm @@ -51,6 +51,7 @@ buf_ptr:.res 2 bmax: .res 1 pptr: .res 2 +parse_err: .res 1 ; Error to report if the statement does not parse ; This variables are cleared in one loop: zp_clear_start: @@ -260,6 +261,8 @@ ucase_end: lda #0 parse_start: ; Parse statement, A=0 on input + ldx #ERR_PARSE + stx parse_err ldx #<(PARSE_START-1) ldy #>(PARSE_START-1) @@ -449,7 +452,7 @@ go_ploop: : jmp ploop_line set_parse_error: - ldy #ERR_PARSE + ldy parse_err jmp parser_error ; vi:syntax=asm_ca65 diff --git a/src/syntax/basic.syn b/src/syntax/basic.syn index 2790658..bebc567 100644 --- a/src/syntax/basic.syn +++ b/src/syntax/basic.syn @@ -525,7 +525,7 @@ STATEMENT: statement "INC" VAR_WORD_LVALUE_SADDR emit TOK_INC "DEc" VAR_WORD_LVALUE_SADDR emit TOK_DEC "PRoc" E_LABEL_CREATE emit { TOK_JUMP } E_LABEL_DEF emit LT_PROC_2 E_PUSH_LT OPT_PROC_VAR - "ENDProc" E_POP_PROC_2 emit TOK_RET E_POP_PROC_DATA + "ENdproc" E_POP_PROC_2 emit TOK_RET E_POP_PROC_DATA EXEC_CALL E_LABEL_CREATE OPT_EXEC_PARAM emit { TOK_CALL } E_DO_EXEC "DAta" DATA_VAR DATA_END "END" emit TOK_END diff --git a/testsuite/tests/err-endif-proc.bas b/testsuite/tests/err-endif-proc.bas new file mode 100644 index 0000000..efdf45e --- /dev/null +++ b/testsuite/tests/err-endif-proc.bas @@ -0,0 +1,4 @@ +' ENDIF without IF inside a PROC +PROC P + E. +ENDPROC diff --git a/testsuite/tests/err-endif-proc.chk b/testsuite/tests/err-endif-proc.chk new file mode 100644 index 0000000..5d5c816 --- /dev/null +++ b/testsuite/tests/err-endif-proc.chk @@ -0,0 +1,4 @@ +Name: Check detection of ENDIF without IF inside PROC +Test: compile-error +Error: bad loop at line 3 column 4 +Error-pos: 3:4 diff --git a/testsuite/tests/err-endproc.bas b/testsuite/tests/err-endproc.bas new file mode 100644 index 0000000..032fc8a --- /dev/null +++ b/testsuite/tests/err-endproc.bas @@ -0,0 +1,3 @@ +' ENDPROC without PROC, abbreviated +? 1 +EN. diff --git a/testsuite/tests/err-endproc.chk b/testsuite/tests/err-endproc.chk new file mode 100644 index 0000000..7d5d782 --- /dev/null +++ b/testsuite/tests/err-endproc.chk @@ -0,0 +1,4 @@ +Name: Check detection of ENDPROC without PROC +Test: compile-error +Error: bad loop at line 3 column 3 +Error-pos: 3:3 diff --git a/testsuite/tests/proc-abbrev.bas b/testsuite/tests/proc-abbrev.bas new file mode 100644 index 0000000..6815912 --- /dev/null +++ b/testsuite/tests/proc-abbrev.bas @@ -0,0 +1,34 @@ +' Test abbreviations of ENDPROC and ENDIF +PROC P1 X + IF X > 1 + ? "BIG "; + EN. + ? X +EN. +PROC P2 X + IF X = 1 + ? "ONE" + ELIF X = 2 + ? "TWO" + ELSE + ? "MANY" + E. +END. +PROC P3 X + DO + IF X > 2 THEN EXIT + INC X + LOOP + IF X = 3 + ? "THREE" + ENDI. +ENDP. +PROC P4:? "P4":EN. +? "Start" +@P1 1:@P1 5 +@P2 1:@P2 2:@P2 3 +@P3 0 +@P4 +IF 1 + ? "IF" +EN. diff --git a/testsuite/tests/proc-abbrev.chk b/testsuite/tests/proc-abbrev.chk new file mode 100644 index 0000000..66f3735 --- /dev/null +++ b/testsuite/tests/proc-abbrev.chk @@ -0,0 +1,12 @@ +Name: Test abbreviations of ENDPROC and ENDIF +Test: run +Output: +Start +1 +BIG 5 +ONE +TWO +MANY +THREE +P4 +IF From dd2e003388f37f516dbfdbef39ea6ce6c7129369 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 05:01:58 +0000 Subject: [PATCH 2/2] Fixes test runner build with newer MinGW, which declares strndup. The Windows fallback was a static "strndup", which conflicts with the declaration in newer MinGW , failing the Windows CI. Use another name for it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NASRhH2Xg6HmeBUZTSPhwM --- testsuite/src/fbtest.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/testsuite/src/fbtest.c b/testsuite/src/fbtest.c index 2e49031..2522b94 100644 --- a/testsuite/src/fbtest.c +++ b/testsuite/src/fbtest.c @@ -29,14 +29,16 @@ #ifdef _WIN32 # define PATH_SEP "\\" -// Implement missing "strndup" -static char *strndup(const char *s, size_t n) +// Implement "strndup", missing in older MinGW versions. Use another name, as +// newer MinGW versions declare it in . +static char *fbtest_strndup(const char *s, size_t n) { char *ret = malloc(n + 1); strncpy(ret, s, n); ret[n] = 0; return ret; -}; +} +# define strndup fbtest_strndup #else # define PATH_SEP "/" #endif