From 8d46895dd30bcce644cd6a492586f1f24f56599a Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Wed, 13 Nov 2019 21:14:29 -0500 Subject: [PATCH] lib/parse: decimal ending with a whitespace are now valid Also, make empty strings be parsed as invalid by parseDecimal. --- apps/basic/glue.asm | 1 + apps/lib/parse.asm | 23 +++++++------ apps/lib/util.asm | 7 ++++ apps/zasm/parse.asm | 3 ++ apps/zasm/tok.asm | 7 ---- tools/tests/unit/test_parse_z.asm | 54 ++++++++++++++++++------------- 6 files changed, 57 insertions(+), 38 deletions(-) diff --git a/apps/basic/glue.asm b/apps/basic/glue.asm index e7216bc..c5eab79 100644 --- a/apps/basic/glue.asm +++ b/apps/basic/glue.asm @@ -12,6 +12,7 @@ jp basStart +.inc "lib/util.asm" .inc "lib/parse.asm" .equ BAS_RAMSTART USER_RAMSTART .inc "basic/main.asm" diff --git a/apps/lib/parse.asm b/apps/lib/parse.asm index c51aad2..7907faa 100644 --- a/apps/lib/parse.asm +++ b/apps/lib/parse.asm @@ -1,6 +1,3 @@ -; *** Requirements *** -; None -; ; *** Code *** ; Parse the decimal char at A and extract it's 0-9 numerical value. Put the @@ -15,31 +12,34 @@ ; Parse string at (HL) as a decimal value and return value in IX under the ; same conditions as parseLiteral. ; Sets Z on success, unset on error. +; To parse successfully, all characters following HL must be digits and those +; digits must form a number that fits in 16 bits. To end the number, both \0 +; and whitespaces (0x20 and 0x09) are accepted. There must be at least one +; digit in the string. parseDecimal: push hl - ld a, (hl) + ld a, (hl) add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff sub 0xff-9 ; maps to 0-9 and carries if not a digit + jr c, .error ; not a digit on first char? error exx ; preserve bc, hl, de ld h, 0 ld l, a ; load first digit in without multiplying ld b, 3 ; Carries can only occur for decimals >=5 in length - jr c, .end .loop: exx inc hl ld a, (hl) exx - + ; inline parseDecimalDigit add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff sub 0xff-9 ; maps to 0-9 and carries if not a digit - jr c, .end - + add hl, hl ; x2 ld d, h ld e, l ; de is x2 @@ -74,4 +74,9 @@ parseDecimal: push hl \ pop ix exx ; restore original de and bc pop hl - ret + ret z + ; A is not 0? Ok, but if it's a space, we're happy too. + jp isSep +.error: + pop hl + jp unsetZ diff --git a/apps/lib/util.asm b/apps/lib/util.asm index 61e1a32..bc739d6 100644 --- a/apps/lib/util.asm +++ b/apps/lib/util.asm @@ -1,3 +1,10 @@ +; Sets Z is A is ' ' or '\t' +isSep: + cp ' ' + ret z + cp 0x09 + ret + ; Copy string from (HL) in (DE), that is, copy bytes until a null char is ; encountered. The null char is also copied. ; HL and DE point to the char right after the null char. diff --git a/apps/zasm/parse.asm b/apps/zasm/parse.asm index b133886..524e304 100644 --- a/apps/zasm/parse.asm +++ b/apps/zasm/parse.asm @@ -195,6 +195,9 @@ parseNumberOrSymbol: ; matter that we didn't find our symbol. Return success anyhow. ; Otherwise return error. Z is already unset, so in fact, this is the ; same as jumping to zasmIsFirstPass + ; however, before we do, load IX with zero. Returning dummy non-zero + ; values can have weird consequence (such as false overflow errors). + ld ix, 0 jp zasmIsFirstPass .returnPC: diff --git a/apps/zasm/tok.asm b/apps/zasm/tok.asm index a7b8750..86e4638 100644 --- a/apps/zasm/tok.asm +++ b/apps/zasm/tok.asm @@ -29,13 +29,6 @@ isLineEnd: cp '\' ret -; Sets Z is A is ' ' '\t' -isSep: - cp ' ' - ret z - cp 0x09 - ret - ; Sets Z is A is ' ', ',', ';', CR, LF, or null. isSepOrLineEnd: call isSep diff --git a/tools/tests/unit/test_parse_z.asm b/tools/tests/unit/test_parse_z.asm index ede3572..69f69c2 100644 --- a/tools/tests/unit/test_parse_z.asm +++ b/tools/tests/unit/test_parse_z.asm @@ -100,29 +100,11 @@ testLiteral: call nexttest ret -; 2b int, 6b str, null-padded -tblDecimalValid: - .dw 99 - .db "99", 0, 0, 0, 0 - .dw 65535 - .db "65535", 0 - -; 7b strings, null-padded -tblDecimalInvalid: - ; TODO: make a null string parse as an invalid decimal - ; null string is invalid - ;.db 0, 0, 0, 0, 0, 0, 0 - ; too big, 5 chars - .db "65536", 0, 0 - .db "99999", 0, 0 - ; too big, 6 chars with rightmost chars being within bound - .db "111111", 0 - testDecimal: ; test valid cases. We loop through tblDecimalValid for our cases - ld b, 2 - ld hl, tblDecimalValid + ld b, 5 + ld hl, .valid .loop1: push hl ; --> lvl 1 @@ -147,8 +129,8 @@ testDecimal: call nexttest ; test invalid cases. We loop through tblDecimalInvalid for our cases - ld b, 3 - ld hl, tblDecimalInvalid + ld b, 4 + ld hl, .invalid .loop2: call parseDecimal @@ -159,6 +141,34 @@ testDecimal: call nexttest ret +; 2b int, 6b str, null-padded +.valid: + .dw 99 + .db "99", 0, 0, 0, 0 + .dw 65535 + .db "65535", 0 + ; Space is also accepted as a number "ender" + .dw 42 + .db "42 x", 0, 0 + ; Tab too + .dw 42 + .db "42", 0x09, 'x', 0, 0 + ; A simple "0" works too! + .dw 0 + .db '0', 0, 0, 0, 0, 0 + + +; 7b strings, null-padded +.invalid: + ; null string is invalid + .db 0, 0, 0, 0, 0, 0, 0 + ; too big, 5 chars + .db "65536", 0, 0 + .db "99999", 0, 0 + ; too big, 6 chars with rightmost chars being within bound + .db "111111", 0 + + nexttest: ld a, (testNum) inc a