diff src/number.s @ 84:f959c92bc329

New first pass implementation of number parsing, untested Rewrite number parsing using recently constructed infrastructure. The result is untested.
author William Astle <lost@l-w.ca>
date Sun, 08 Oct 2023 00:17:20 -0600
parents bb50ac9fdf37
children 663d8e77b579
line wrap: on
line diff
--- a/src/number.s	Sat Oct 07 15:17:44 2023 -0600
+++ b/src/number.s	Sun Oct 08 00:17:20 2023 -0600
@@ -100,4 +100,197 @@
                 lbeq fps_mod                    ; floating point modulus
                 jmp TMERROR                     ; unsupported type
                 endc
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Parse a number to either an integer or a floating point value
+;
+; First, identify any sign present. Then parse the remainder as an integer until either a decimal point, an exponential
+; indicator, or the value gets larger than 32 bits. If any of those eventualities happens, convert to floating point
+; and then continue parsing the number as floating point. The result will be stored to (Y).
+val_parsenum    lbeq SNERROR                    ; brif no numberr to parse
+                ldd zero                        ; zero out integer value accumulator
+                std fpa0extra
+                std fpa0extra+2
+                std fpa0extra+4
+                std fpa0extra+6
+                sta fpa0extra12                 ; zero out result sign to default positive
+                jsr curchar                     ; get current input character
+                bra val_parsenum1               ; parse flags
+val_parsenum0   jsr nextchar                    ; get next input character
+val_parsenum1   bcs val_parsenum5               ; brif digit
+                beq val_parsenum                ; brif end of input
+                cmpa #'.                        ; decimal?
+                lbeq val_parsefloat             ; switch to parsing floating point
+                cmpa #'-                        ; minus?
+                beq val_parsenum2               ; brif so
+                cmpa #tok_minus                 ; unary minus operator?
+                bne val_parsenum3               ; brif not
+val_parsenum2   com fpa0extra12                 ; invert current sign
+                bra val_parsenum0               ; go handle more stuff at the start of the number
+val_parsenum3   cmpa #'+                        ; unary +?
+                beq val_parsenum0               ; brif so - skip it
+                cmpa #tok_plus                  ; unary + operator?
+                beq val_parsenum0               ; brif so - skip it
+val_parsenum4   lda fpa0extra4                  ; is bit 7 of high byte set?
+                bpl val_parsenum4a              ; brif not - no overflow
+                ldb fpa0extra12                 ; do we want negative?
+                lbpl val_parsefloat             ; brif not - we overflowed so convert to floating point
+                anda #0x7f                      ; lose sign bit then see if any other bits are set
+                ora fpa0extra5
+                ora fpa0extra6
+                ora fpa0extra7
+                lbne val_parsefloat             ; brif nonzero bits - too big for max negative 2's complement
+val_parsenum4a  lda fpa0extra12                 ; do we want negative?
+                bpl val_parsenum4b              ; brif not
+                ldd zero                        ; negate it
+                subd fpa0extra6
+                std fpa0extra6
+                ldd zero
+                sbcb fpa0extra5
+                sbca fpa0extra4
+                std fpa0extra4
+val_parsenum4b  ldd fpa0extra6                  ; copy value to result location
+                std val.int+2,y
+                ldd fpa0extra4
+                std val.int,y
+                lda #valtype_int                ; set value type to integer
+                sta val.type,y
+                rts
+val_parsenum4c  jsr nextchar                    ; fetch next character (after a digit)
+                bcs val_parsenum5               ; it's a digit
+                cmpa #'.                        ; decimal?
+                beq val_parsefloat              ; brif so - handle floating point
+                cmpa #'E                        ; exponent?
+                beq val_parsefloat              ; brif so - handle floating point
+                cmpa #'e                        ; exponent but lower case?
+                beq val_parsefloat              ; brif so - handle floating point
+                bra val_parsenum4               ; unrecognized character - treat as end of number
+val_parsenum5   suba #'0                        ; offset digit to binary
+                pshs a                          ; save it for later addition
+                ldx fpa0extra4                  ; save original value
+                stx fpa0extra8
+                ldx fpa0extra6
+                stx fpa0extra10
+                lsl fpa0extra7                  ; shift partial result left (times 2)
+                rol fpa0extra6
+                rol fpa0extra5
+                rol fpa0extra4
+                rol fpa0extra3
+                lsl fpa0extra7                  ; shift partial result left (times 4)
+                rol fpa0extra6
+                rol fpa0extra5
+                rol fpa0extra4
+                rol fpa0extra3
+                ldd fpa0extra6                  ; add in original value (time 5)
+                addd fpa0extra10
+                std fpa0extra6
+                ldd fpa0extra8
+                adcb fpa0extra5
+                adca fpa0extra4
+                std fpa0extra4
+                ldb fpa0extra3
+                adcb #0
+                stb fpa0extra3
+                lsl fpa0extra7                  ; shift partial result left (times 10)
+                rol fpa0extra6
+                rol fpa0extra5
+                rol fpa0extra4
+                rol fpa0extra3
+                ldd fpa0extra6                  ; add in new digit
+                addb ,s+
+                adca #0
+                std fpa0extra6
+                ldd fpa0extra4                  ; and propagate carry
+                adcb #0
+                adca #0
+                std fpa0extra4
+                ldb fpa0extra3
+                adcb #0
+                stb fpa0extra3
+                beq val_parsenum4c              ; go handle next digit if we didn't overflow past 32 bits
+                jsr nextchar                    ; eat the digit we just handled
+val_parsefloat  pshs y                          ; save destination pointer
+                lda #valtype_float              ; set return type to floating point
+                sta val.type,y
+                ldx #fpa0extra                  ; point to integer accumulator
+                jsr fps_fromuint64              ; convert to floating point
+                clr fpa0extra11                 ; zero out decimal counter
+                clr fpa0extra10                 ; zero out decimal exponent counter
+                clr fpa0extra9                  ; flag for decimal seen
+                jsr curchar                     ; fetch current character
+                bra val_parsefloat1             ; go handle character
+val_parsefloat0 jsr nextchar                    ; fetch next character
+val_parsefloat1 bcc val_parsefloat2             ; brif not digit
+                suba #'0                        ; adjust digit to binary
+                sta fpa0extra3                  ; save it for later (upper 3 bytes of 32 bit value already 0)
+                ldx ,s                          ; get destination value
+                jsr fps_mul10                   ; do a quick multiply by 10
+                ldx #fpa0extra                  ; convert digit to floating point
+                ldy #fpa1
+                jsr fps_fromuint32
+                ldu #fpa1                       ; add digit to accumulated value
+                ldx ,s
+                leay ,x
+                jsr fps_add
+                lda fpa0extra11                 ; update decimal counter
+                suba fpa0extra9
+                sta fpa0extra11
+                bra val_parsefloat0             ; go handle another digit
+val_parsefloat2 cmpa #'.                        ; decimal?
+                bne val_parsefloat7             ; brif not
+                com fpa0extra9                  ; flag for decimal
+                bne val_parsefloat0             ; brif not two decimals - keep parsing
+val_parsefloat3 ldb fpa0extra10                 ; fetch decimal exponent counter
+                subb fpa0extra11                ; subtract out decimal places provided
+                beq val_parsefloat6             ; brif no adjustment needed
+                stb fpa0extra9                  ; save counter
+                bmi val_parsefloat5             ; brif negative exponent - need to do divisions
+val_parsefloat4 ldx ,s                          ; point to destination value
+                jsr fps_mul10                   ; multiply by 10
+                dec fpa0extra9                  ; done all of them?
+                bne val_parsefloat4             ; brif not
+                bra val_parsefloat6
+val_parsefloat5 ldx ,s                          ; point to destination value
+                jsr fps_div10                   ; divide by 10
+                inc fpa0extra9                  ; done all of them?
+                bne val_parsefloat5             ; brif not
+val_parsefloat6 puls y                          ; get back destination pointer
+                lda fpa0extra12                 ; get desired sign
+                sta val.fpssign,y               ; set in result
+                rts
+val_parsefloat7 cmpa #'E                        ; decimal exponent?
+                beq val_parsefloat8             ; brif so
+                cmpa #'e                        ; decimal exponent, lower case edition?
+                bne val_parsefloat3             ; brif not - must be end of number
+val_parsefloat8 clr fpa0extra9                  ; set sign of exponent to positive
+                jsr nextchar                    ; fetch exponent character
+                bcs val_parsefloat11            ; brif digit
+                cmpa #'+                        ; positive exponent?
+                beq val_parsefloat10            ; brif so - skip it
+                cmpa #tok_plus                  ; positive exponent, operator style?
+                beq val_parsefloat10            ; brif so - skip it
+                cmpa #'-                        ; negative exponent?
+                beq val_parsefloat9             ; brif so
+                cmpa #tok_minus                 ; negative exponent, operator style?
+                bne val_parsefloat3             ; brif not - must be end of exponent
+val_parsefloat9 com fpa0extra9                  ; set exponent to negative
+val_parsefloat10
+                jsr nextchar                    ; eat exponent sign
+                bcc val_parsefloat12            ; brif end of exponent - apply sign
+val_parsefloat11
+                suba #'0                        ; binary-ize digit
+                sta fpa0extra8                  ; save digit for later
+                lda #10                         ; mutiply current decimal exponent by 10
+                ldb fpa0extra10                 ; get current exponent
+                mul
+                adca #0                         ; set A if we overflowed *or* bit 7 of B is set
+                lbne OVERROR                    ; brif exponent overflow
+                addb fpa0extra8                 ; add in digit
+                lbvs OVERROR                    ; brif exponent overflow
+                stb fpa0extra10                 ; save new exponent
+                bra val_parsefloat10            ; go handle next exponent digit
+val_parsefloat12
+                ldb fpa0extra9                  ; do we have a negative exponent?
+                beq val_parsefloat3             ; brif not, go adjust value by exponent and return
+                neg fpa0extra10                 ; set base 10 exponent negative
+                bra val_parsefloat3             ; go adjust value by exponent and return
                 *pragmapop list