diff --git a/.gitignore b/.gitignore index 1a8ca5325..8385908be 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ htmlcov/ build/ venv/ .pypi-token +.coverage.* diff --git a/src/zxbc/zxbc.py b/src/zxbc/zxbc.py index cf7d96caf..766197dcb 100755 --- a/src/zxbc/zxbc.py +++ b/src/zxbc/zxbc.py @@ -3,6 +3,7 @@ import re import sys +from argparse import Namespace from io import StringIO import src.api.optimize @@ -61,7 +62,12 @@ def output(memory, ofile=None): ofile.write("%s\n" % m) -def main(args=None, emitter=None): +def save_config(options: Namespace) -> None: + if not gl.has_errors and options.save_config: + src.api.config.save_config_into_file(options.save_config, src.api.config.ConfigSections.ZXBC) + + +def main(args=None, emitter=None) -> int: """Entry point when executed from command line. zxbc can be used as python module. If so, bear in mind this function won't be executed unless explicitly called. @@ -124,6 +130,10 @@ def main(args=None, emitter=None): debug.__DEBUG__("exiting due to errors.") return 1 # Exit with errors + if options.parse_only: + save_config(options) + return gl.has_errors + # Emits data lines translator.emit_data_blocks() # Emits default constant strings @@ -216,8 +226,7 @@ def main(args=None, emitter=None): with open_file(OPTIONS.memory_map, "wt", "utf-8") as f: f.write(asmparse.MEMORY.memory_map) - if not gl.has_errors and options.save_config: - src.api.config.save_config_into_file(options.save_config, src.api.config.ConfigSections.ZXBC) + save_config(options) return gl.has_errors # Exit success diff --git a/src/zxbc/zxblex.py b/src/zxbc/zxblex.py index 0881cbf9c..f7e1fb00e 100755 --- a/src/zxbc/zxblex.py +++ b/src/zxbc/zxblex.py @@ -617,6 +617,8 @@ def t_ID(t): entry = api.global_.SYMBOL_TABLE.get_entry(t.value) if api.global_.SYMBOL_TABLE is not None else None if entry: t.type = callables.get(entry.class_, t.type) + elif is_label(t): + t.type = "LABEL" if t.type == "BIN": t.lexer.begin("bin") @@ -683,7 +685,7 @@ def find_column(token): return column -def is_label(token): +def is_label(token) -> bool: """Return whether the token is a label (an integer number or id at the beginning of a line. @@ -706,11 +708,23 @@ def is_label(token): i -= 1 column = c - i + if column != 0: + return False + + if token.type == "NUMBER": + return True + + i = token.lexpos + len(token.value) + while i < len(input): + if input[i] == ":": + return True + + if input[i] not in {" ", "\t"}: + break - if column == 0: - column += 1 + i += 1 - return column == 1 + return False lexer = lex.lex() diff --git a/src/zxbc/zxbparser.py b/src/zxbc/zxbparser.py index 0a7a67e1e..af649f479 100755 --- a/src/zxbc/zxbparser.py +++ b/src/zxbc/zxbparser.py @@ -1074,7 +1074,7 @@ def p_statement_call(p): p[0] = None elif len(p) == 2: entry = SYMBOL_TABLE.get_entry(p[1]) - if not entry or entry.class_ in (CLASS.label, CLASS.unknown): + if entry is not None and entry.class_ in (CLASS.label, CLASS.unknown): p[0] = make_label(p[1], p.lineno(1)) else: p[0] = make_sub_call(p[1], p.lineno(1), make_arg_list(None)) diff --git a/tests/functional/arch/zx48k/label_decl0.asm b/tests/functional/arch/zx48k/label_decl0.asm new file mode 100644 index 000000000..e8b53f627 --- /dev/null +++ b/tests/functional/arch/zx48k/label_decl0.asm @@ -0,0 +1,26 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: +.LABEL._MYLABEL: + jp .LABEL._MYLABEL + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx48k/label_decl0.bas b/tests/functional/arch/zx48k/label_decl0.bas new file mode 100644 index 000000000..b332503d5 --- /dev/null +++ b/tests/functional/arch/zx48k/label_decl0.bas @@ -0,0 +1,6 @@ + +REM labels needs to be declared at the beginning of line +REM with a trailing colon + +MYLABEL: + GOTO MYLABEL diff --git a/tests/functional/arch/zx48k/label_decl1.bas b/tests/functional/arch/zx48k/label_decl1.bas new file mode 100644 index 000000000..9c3d745bb --- /dev/null +++ b/tests/functional/arch/zx48k/label_decl1.bas @@ -0,0 +1,5 @@ + +REM Will fail because it lacks a trailing COLON + +WRONG_LABEL + GOTO WRONG_LABEL diff --git a/tests/functional/arch/zx48k/label_decl2.bas b/tests/functional/arch/zx48k/label_decl2.bas new file mode 100644 index 000000000..3941207f3 --- /dev/null +++ b/tests/functional/arch/zx48k/label_decl2.bas @@ -0,0 +1,5 @@ + +REM will fail because labels must be declared +REM at the begining of the line + +IF a > 10 THEN MYLABEL diff --git a/tests/functional/arch/zx48k/label_decl3.asm b/tests/functional/arch/zx48k/label_decl3.asm new file mode 100644 index 000000000..706f30e7e --- /dev/null +++ b/tests/functional/arch/zx48k/label_decl3.asm @@ -0,0 +1,89 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, 1 + ld hl, (_a - 1) + cp h + jp c, .LABEL._BREAK +.LABEL.__LABEL1: + ld a, 8 + call .core.__STOP + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +.LABEL._BREAK: + ld hl, _a + inc (hl) + ld hl, 0 + ld b, h + ld c, l + jp .core.__END_PROGRAM + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 31 "label_decl3.bas" + END diff --git a/tests/functional/arch/zx48k/label_decl3.bas b/tests/functional/arch/zx48k/label_decl3.bas new file mode 100644 index 000000000..9ecbcd594 --- /dev/null +++ b/tests/functional/arch/zx48k/label_decl3.bas @@ -0,0 +1,7 @@ + +DIM a As UByte + +IF a > 1 THEN GOTO BREAK +STOP + +BREAK: LET a = a + 1