summaryrefslogtreecommitdiff
path: root/json.lisp
blob: cc3e0c12a51738675208951272e18a38b9e0703f (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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
(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)))
      (list 'number base fraction 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 4)))
                                                               (setf (char str 0) cp0)
                                                               (setf (char str 1) cp1)
                                                               (setf (char str 2) cp2)
                                                               (setf (char str 3) cp3)
                                                               str))
                                                           nothing)))
                                        (case escaped
                                          (#\n
                                           #\Newline)
                                          (#\t
                                           #\Tab)
                                          (#\u
                                           codepoints)
                                          (t escaped)))
                                      (unit (lambda (x) (char/= x #\"))))))
         (_ (unit (lambda (x) (char= x #\")))))
    (list 'string 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 vn
      (list 'array (cons v0 vn))
      (list 'array v0))))

(defparameter json-object
  (let ((json-pair
          (comp ((_ whitespace)
                 (k string-literal)
                 (_ whitespace)
                 (_ (unit (lambda (x) (char= x #\:))))
                 (v json-value))
            (list 'pair 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 vn
        (list 'object (cons v0 vn))
        (list 'object v0)))))

(setf json-value
  (comp ((_ whitespace)
         (v (either number-literal
                    string-literal
                    json-array
                    json-object
                    true-symbol
                    false-symbol
                    null-symbol))
         (_ whitespace))
    v))