(in-package #:json) (defparameter number-literal (let ((signed-digits (comp ((sign (zero-or-one (unit (lambda (x) (or (char= x #\-) (char= x #\+)))))) (natural (one-or-more (unit #'digit-char-p)))) (cons sign natural)))) (comp ((base (either signed-digits (fail "Malformed number."))) (dot (zero-or-one (unit (lambda (x) (char= x #\.))))) (fraction (if dot (either (one-or-more (unit #'digit-char-p)) (fail "Malformed fractional part.")) nothing)) (e (zero-or-one (unit (lambda (x) (or (char= x #\e) (char= x #\E)))))) (exponent (if e (either signed-digits (fail "Malformed exponent part.")) nothing))) (read-from-string (str:from-list (remove nil (append base (cons dot fraction) (cons e exponent)))))))) (defparameter string-literal (comp ((_ (unit (lambda (x) (char= x #\")))) (chars (zero-or-more (either (comp ((slash (unit (lambda (x) (char= x #\\)))) (escaped (unit)) (codepoints (if (and escaped (char= escaped #\u)) (comp ((cp0 (unit #'digit-char-p)) (cp1 (unit #'digit-char-p)) (cp2 (unit #'digit-char-p)) (cp3 (unit #'digit-char-p))) (let ((str (make-string 7))) (setf (char str 0) #\#) (setf (char str 1) #\\) (setf (char str 2) #\u) (setf (char str 3) cp0) (setf (char str 4) cp1) (setf (char str 5) cp2) (setf (char str 6) cp3) str)) nothing))) (case escaped (#\n #\Newline) (#\t #\Tab) (#\u (read-from-string codepoints)) (t escaped))) (unit (lambda (x) (and (char/= x #\") (char/= x #\\))))))) (_ (unit (lambda (x) (char= x #\"))))) (str:from-list chars))) (defparameter whitespace (comp ((_ (zero-or-more (unit (lambda (x) (or (char= x #\Space) (char= x #\Newline) (char= x #\Tab))))))) nil)) (defparameter true-symbol (comp ((_ (unit (lambda (x) (char= x #\t)))) (_ (unit (lambda (x) (char= x #\r)))) (_ (unit (lambda (x) (char= x #\u)))) (_ (unit (lambda (x) (char= x #\e))))) 'true)) (defparameter false-symbol (comp ((_ (unit (lambda (x) (char= x #\f)))) (_ (unit (lambda (x) (char= x #\a)))) (_ (unit (lambda (x) (char= x #\l)))) (_ (unit (lambda (x) (char= x #\s)))) (_ (unit (lambda (x) (char= x #\e))))) 'false)) (defparameter null-symbol (comp ((_ (unit (lambda (x) (char= x #\n)))) (_ (unit (lambda (x) (char= x #\u)))) (_ (unit (lambda (x) (char= x #\l)))) (_ (unit (lambda (x) (char= x #\l))))) 'null)) (defvar json-value) (defparameter json-array (comp ((_ (unit (lambda (x) (char= x #\[)))) (v0 (either json-value whitespace)) (vn (if v0 (zero-or-more (comp ((_ (unit (lambda (x) (char= #\,)))) (vi json-value)) vi)) nothing)) (_ (unit (lambda (x) (char= x #\]))))) (if v0 (if vn (cons v0 vn) (list v0)) nil))) (defparameter json-object (let ((json-pair (comp ((_ whitespace) (k string-literal) (_ whitespace) (_ (unit (lambda (x) (char= x #\:)))) (v json-value)) (cons k v)))) (comp ((_ (unit (lambda (x) (char= x #\{)))) (v0 (either json-pair whitespace)) (vn (if v0 (zero-or-more (comp ((_ (unit (lambda (x) (char= #\,)))) (vi json-pair)) vi)) nothing)) (_ (unit (lambda (x) (char= x #\}))))) (if v0 (if vn (let* ((vlist (cons v0 vn)) (obj (make-hash-table :test #'equal :size (length vlist)))) (dolist (v vlist) (setf (gethash (car v) obj) (cdr v))) obj) (let ((obj (make-hash-table :test #'equal))) (setf (gethash (car v0) obj) (cdr v0)) obj)) (make-hash-table :test #'equal))))) (setf json-value (comp ((_ whitespace) (v (either number-literal string-literal json-array json-object true-symbol false-symbol null-symbol)) (_ whitespace)) v)) (defun parse-string (str) (run json-value (input:from-string str))) (defun parse-file (file) (run json-value (input:from-file file)))