summaryrefslogtreecommitdiff
path: root/load.lisp
blob: e75ad18f8547e37091daa3be145bc9d2897d7e85 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
(in-package #:json)

(defparser whitespace ()
  (optional (many (unit whitespace?))))

(defparser number-literal ()
  (let ((signed-digits
          (comp ((sign (optional (one-of (unit #\-)
                                         (unit #\+))))
                 (natural (many (unit digit-char-p))))
            (cons sign natural))))
    (comp ((base signed-digits)
           (dot (optional (unit #\.)))
           (fraction (if dot
                       (many (unit digit-char-p))
                       nothing))
           (e (optional (one-of (unit #\e) (unit #\E))))
           (exponent (if e
                       signed-digits
                       nothing)))
      (read-from-string
        (coerce
          (remove nil (append base
                              (cons dot fraction)
                              (cons (when e #\d) exponent)))
          'string)))))

(defparser string-literal ()
  (comp ((_ (unit #\"))
         (chars
           (optional
             (many
               (one-of
                 (comp ((slash (unit #\\))
                        (escaped (unit))
                        (codepoints (if (char= escaped #\u)
                                      (repeat (unit digit-char-p) 4)
                                      nothing)))
                   (case escaped
                     (#\n #\Newline)
                     (#\t #\Tab)
                     (#\u (read-from-string
                            (coerce (append '(#\# #\\ #\u)
                                            codepoints)
                                    'string)))
                     (t escaped)))
                 (unit (and (char/= it #\") (char/= it #\\)))))))
         (_ (unit #\")))
    (coerce chars 'string)))

(defmacro json-symbol (name)
  `(comp ((v (literal ,name)))
     (intern (coerce v 'string))))

(defparser true-symbol ()
  (json-symbol "true"))

(defparser false-symbol ()
  (json-symbol "false"))

(defparser null-symbol ()
  (json-symbol "null"))

(defparser json-array ()
  (comp ((_ (unit #\[))
         (vn (optional (interlinked (lazy json-value) (unit #\,))))
         (_ (unit #\])))
    (apply #'vector vn)))

(defparser json-object ()
  (let ((json-pair
          (comp ((_ whitespace)
                 (k string-literal)
                 (_ whitespace)
                 (_ (unit #\:))
                 (v (lazy json-value)))
            (cons k v))))
    (comp ((_ (unit #\{))
           (vn (optional (interlinked json-pair (unit #\,))))
           (_ (unit #\})))
      (let* ((obj (make-hash-table :test #'equal :size (length vn))))
        (dolist (v vn)
          (setf (gethash (car v) obj) (cdr v)))
        obj))))

(defparser json-value ()
  (within whitespace
          (one-of number-literal
                  string-literal
                  json-object
                  json-array
                  true-symbol
                  false-symbol
                  null-symbol)))

(defun from-string (str)
  (parse json-value str))

(defun from-file (path)
  (parse json-value (str:read-file path)))