summaryrefslogtreecommitdiff
path: root/load.lisp
blob: 65b85d97f1028ff65af0974d073b063a99cc8d9a (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
101
102
103
104
(in-package #:json)

(defparameter 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)))))

(defparameter 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 v)))

(defparameter true-symbol
  (json-symbol "true"))

(defparameter false-symbol
  (json-symbol "false"))

(defparameter null-symbol
  (json-symbol "null"))

(defun json-array (value)
  (comp ((_ (unit #\[))
         (vn (optional (separated-list value (unit #\,))))
         (_ (unit #\])))
        (apply #'vector vn)))

(defun json-object (value)
  (let ((json-pair
          (comp ((_ whitespace)
                 (k string-literal)
                 (_ whitespace)
                 (_ (unit #\:))
                 (v value))
                (cons k v))))
    (comp ((_ (unit #\{))
           (vn (optional (separated-list 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))))

(defparameter json-value
  (comp ((_ whitespace)
         (value (one-of number-literal
                        string-literal
                        (json-object json-value)
                        (json-array json-value)
                        true-symbol
                        false-symbol
                        null-symbol))
         (_ whitespace))
        value))

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

(defun from-file (path)
  (let (buf)
    (with-open-file (f path)
      (let ((size (file-length f)))
        (setf buf (make-string size))
        (read-sequence buf f)))
    (parse json-value buf)))