blob: c8087747a9757e3ceb029a28049386717e0035cf (
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
|
(in-package #:json)
(defparameter number-literal
(let ((signed-digits
(comp ((sign (optional (one-of (unit #\-)
(unit #\+))))
(natural (if sign
(one-of (many (unit-if #'digit-char-p))
(fail "Malformed number."))
(many (unit-if #'digit-char-p)))))
(cons sign natural))))
(comp ((base signed-digits)
(dot (optional (unit #\.)))
(fraction (if dot
(one-of (many (unit-if #'digit-char-p))
(fail "Malformed fractional part."))
nothing))
(e (optional (one-of (unit #\e) (unit #\E))))
(exponent (if e
(one-of 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 #\"))
(chars (optional (many (one-of (comp ((slash (unit #\\))
(escaped (unit-if))
(codepoints (if (char= escaped #\u)
(comp ((cp0 (unit-if #'digit-char-p))
(cp1 (unit-if #'digit-char-p))
(cp2 (unit-if #'digit-char-p))
(cp3 (unit-if #'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-if (lambda (x) (and (char/= x #\")
(char/= x #\\))))))))
(_ (one-of (unit #\")
(fail "String is not properly closed."))))
(str:from-list chars)))
(defparameter true-symbol
(comp ((_ (unit #\t))
(_ (one-of (literal "rue")
(fail "Expected 'true'."))))
'true))
(defparameter false-symbol
(comp ((_ (unit #\f))
(_ (one-of (literal "alse")
(fail "Expected 'false'."))))
'false))
(defparameter null-symbol
(comp ((_ (unit #\n))
(_ (one-of (literal "ull")
(fail "Expected 'null'."))))
'null))
(defvar json-value)
(defparameter json-array
(comp ((_ (unit #\[))
(vn (optional (separated-list json-value (unit #\,))))
(_ (unit #\])))
(apply #'vector vn)))
(defparameter json-object
(let ((json-pair
(comp ((_ whitespace)
(k (one-of string-literal
(fail "Expected a string.")))
(_ whitespace)
(_ (one-of (unit #\:)
(fail "Expected a \":\"")))
(v json-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)
(v (one-of number-literal
string-literal
json-object
json-array
true-symbol
false-symbol
null-symbol))
(_ whitespace))
v))
(defun from-string (str)
(run json-value (input:from-string str)))
(defun from-file (file)
(run json-value (input:from-file file)))
|