s-expressionists / Eclector

A portable Common Lisp reader that is highly customizable, can recover from errors and can return concrete syntax trees
https://s-expressionists.github.io/Eclector/
BSD 2-Clause "Simplified" License
109 stars 9 forks source link

read-from string error with float and *read-base* #66

Closed kpoeck closed 4 years ago

kpoeck commented 4 years ago

http://www.lispworks.com/documentation/lw51/CLHS/Body/v_rd_bas.htm

The value of read-base, called the current input base, is the radix in which integers and ratios are to be read by the Lisp reader. The parsing of other numeric types (e.g., floats) is not affected by this option.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
* (ql:quickload :eclector)
To load "eclector":
  Load 1 ASDF system:
    eclector
; Loading "eclector"
[package closer-mop]..............................
[package closer-common-lisp]......................
[package closer-common-lisp-user].................
[package acclimation].............................
[package eclector.base]...........................
[package eclector.readtable]......................
[package eclector.readtable.simple]...............
[package eclector.reader].........................
[package eclector.parse-result].
(:ECLECTOR)
* (let ((*read-base* 3))
           (eclector.reader:read-from-string "2140.969"))

debugger invoked on a TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {1000510083}>:
  The value
    NIL
  is not of type
    NUMBER
  when binding SB-KERNEL::X

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Exit debugger, returning to top level.

(SB-KERNEL:TWO-ARG-* NIL 3) [external]
0] 0

Should return 2140.969.

The error is in make-integer-accumulator once value is nil, and the next call gives a valid digit, it will (* value base) with value = nil.

e.g.:

(defparameter *acc* (make-integer-accumulator 4))
* (funcall *acc* #\3 t)
3
* (funcall *acc* #\4 t)
NIL
* (funcall *acc* #\2 t)

debugger invoked on a TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {1000510083}>:
  The value
    NIL
  is not of type
    NUMBER
  when binding SB-KERNEL::X
kpoeck commented 4 years ago

That seems to work:

(defun make-integer-accumulator (base)
  (let ((value 0))
    (lambda (&optional char invalidatep)
      (if char
          (let ((digit (digit-char-p char base)))
            (cond ((not (null digit))
                   (when (numberp value)
                     (setf value (+ (* value base) digit))))
                  (invalidatep
                   (setf value nil))
                  (t
                   nil)))
          value))))