(defvar *digits* '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\. #\/))

(defun read-rational (stream char)
  "read all decimals as rationals"
  (let ((this-char nil)
        (result (position char *digits*))
        (n 0)
        (position-of-dot nil)
        (position-of-slash nil))
    (loop while (member (peek-char nil stream) *digits*) do
         (setf this-char (read-char stream))
         (cond ((eql this-char #\.)
                (setf position-of-dot n))
               ((eql this-char #\/)
                (setf position-of-slash n))
               (t
                (setf result (+ (* 10 result) (position this-char *digits*)))))
         (incf n))
    (cond ((and position-of-dot position-of-slash)
           (error "Cannot read number with both . and / characters in it"))
          (position-of-dot
           (/ result (expt 10 (- n position-of-dot 1))))
          (position-of-slash
           (multiple-value-bind (x y) (truncate result (expt 10 (- n position-of-slash 1)))
             (/ x y)))
          (t
           result))))

(mapcar (lambda (c)
          (set-macro-character c #'read-rational))
        (remove #\/ (remove #\. *digits*))) ;; \#. and #\/ are used for other things!