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/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 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