src/discard.lisp @ 37cd2173940e

Refactor discarding into its own file
author Steve Losh <steve@stevelosh.com>
date Fri, 14 Aug 2020 22:56:53 -0400
parents (none)
children (none)
(in-package :jarl)

;; Optimized reader for cases where you don't actually care about the value and
;; just need to parse over it without allocating anything.

(defun discard-literal (string input)
  (loop :for next :across string
        :for char = (r input)
        :unless (eql next char)
        :do (e nil input "expected ~S when parsing ~S but got ~S" next string char)))

(defun discard-array (input)
  (r input) ; [
  (incf-depth input)
  (skip-whitespace input)
  (if (eql (p input) #\])
    (progn (decf-depth input)
           (r input))
    (loop (discard-any input)
          (skip-whitespace input)
          (let ((ch (r input)))
            (case ch
              (#\] (decf-depth input) (return))
              (#\, (skip-whitespace input))
              (t (e nil input "expected ~S or ~S but got ~S." #\] #\, ch)))))))

(defun discard-object (input)
  (r input) ; {
  (incf-depth input)
  (skip-whitespace input)
  (if (eql (p input) #\})
    (progn (decf-depth input)
           (r input))
    (loop (discard-string input)
          (parse-kv-separator nil input)
          (discard-any input)
          (skip-whitespace input)
          (let ((ch (r input)))
            (case ch
              (#\} (decf-depth input) (return))
              (#\, (skip-whitespace input))
              (t (e nil input "expected ~S or ~S but got ~S" #\} #\, ch)))))))

(defun discard-string (input)
  (let ((ch (r input)))
    (unless (eql ch #\")
      (e nil input "expected opening ~S but got ~S" #\" ch)))
  (loop :for ch = (r input)
        :do (cond
              ((eql ch :eof) (e nil input "got ~S" :eof))
              ((eql ch #\\) (parse-escaped-character input)) ; TODO: Optimize this too.
              ((eql ch #\") (return))
              ((requires-escape-p ch) (e nil input "bad unescaped character ~S" ch))
              (t nil))))

(defun discard-number (input)
  ;; TODO: Optimize this too.  Not a huge priority since fixnums don't cons.
  (parse-number input))

(defun discard-any (input)
  (case (p input)
    (:eof (r input) (e nil input "got ~S" :eof))
    (#\n (discard-literal "null" input))
    (#\t (discard-literal "true" input))
    (#\f (discard-literal "false" input))
    (#\" (discard-string input))
    (#\{ (discard-object input))
    (#\[ (discard-array input))
    ((#\- #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (discard-number input))
    (t (e nil input "unexpected character ~S" (r input)))))


(defmethod read% ((class (eql 'nil)) contained-class input)
  (skip-whitespace input)
  (discard-any input)
  (values))