Browse code
(init)
Ed Langley authored on 17/06/2018 23:47:56
Showing 5 changed files
Showing 5 changed files
0 | 2 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,27 @@ |
1 |
+;;; -*- Mode:Lisp; Syntax:ANSI-Common-Lisp; Package: ASDF-USER -*- |
|
2 |
+(in-package :asdf-user) |
|
3 |
+ |
|
4 |
+(defsystem :cl-edn |
|
5 |
+ :description "" |
|
6 |
+ :author "Ed L <edward@elangley.org>" |
|
7 |
+ :license "MIT" |
|
8 |
+ :depends-on (#:alexandria |
|
9 |
+ #:uiop |
|
10 |
+ #:serapeum |
|
11 |
+ #:smug) |
|
12 |
+ :serial t |
|
13 |
+ :in-order-to ((test-op (test-op :cl-edn/test))) |
|
14 |
+ :components ((:file "edn") |
|
15 |
+ )) |
|
16 |
+ |
|
17 |
+(defsystem :cl-edn/fset |
|
18 |
+ :depends-on (#:cl-edn |
|
19 |
+ #:fset) |
|
20 |
+ :components ((:file "fset-synthesize"))) |
|
21 |
+ |
|
22 |
+(defsystem :cl-edn/test |
|
23 |
+ :depends-on (#:should-test) |
|
24 |
+ :perform (test-op (o s) |
|
25 |
+ (uiop:symbol-call :st '#:test |
|
26 |
+ :package :edn-test)) |
|
27 |
+ :components ((:file "test"))) |
0 | 28 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,302 @@ |
1 |
+(defpackage :edn |
|
2 |
+ (:use :cl :smug) |
|
3 |
+ (:export )) |
|
4 |
+(defpackage :edn-primitives |
|
5 |
+ (:use) |
|
6 |
+ (:export :nil :true :false)) |
|
7 |
+ |
|
8 |
+(defconstant edn-primitives:nil 'edn-primitives:nil) |
|
9 |
+(defconstant edn-primitives:true 'edn-primitives:true) |
|
10 |
+(defconstant edn-primitives:false 'edn-primitives:false) |
|
11 |
+ |
|
12 |
+(in-package :edn) |
|
13 |
+ |
|
14 |
+(defun .satisfies (predicate &rest args) |
|
15 |
+ (.bind (.item) |
|
16 |
+ (lambda (x) |
|
17 |
+ (if (apply predicate x args) |
|
18 |
+ (.identity x) |
|
19 |
+ (.fail))))) |
|
20 |
+ |
|
21 |
+(defun .zero-or-more (parser) |
|
22 |
+ (.plus (.let* ((x parser) |
|
23 |
+ (xs (.zero-or-more parser))) |
|
24 |
+ (.identity (cons x xs))) |
|
25 |
+ (.identity ()))) |
|
26 |
+ |
|
27 |
+(defun .one-or-more (parser) |
|
28 |
+ (.let* ((x parser) |
|
29 |
+ (y (.zero-or-more parser))) |
|
30 |
+ (.identity (cons x y)))) |
|
31 |
+ |
|
32 |
+(defun .elements () |
|
33 |
+ (.zero-or-more (.progn (.s) |
|
34 |
+ (.element)))) |
|
35 |
+ |
|
36 |
+(defun .s () |
|
37 |
+ (.zero-or-more |
|
38 |
+ (.or (.whitespace) |
|
39 |
+ (.comment) |
|
40 |
+ (.discarded-element)))) |
|
41 |
+ |
|
42 |
+(defun .whitespace () |
|
43 |
+ (.or (.char= #\space) |
|
44 |
+ (.char= #\tab) |
|
45 |
+ (.char= #\return) |
|
46 |
+ (.char= #\newline) |
|
47 |
+ (.char= #\,))) |
|
48 |
+ |
|
49 |
+(defun .comment () |
|
50 |
+ (.let* ((result (.prog2 (.char= #\;) |
|
51 |
+ (.zero-or-more (.and (.not (.or (.char= #\newline) |
|
52 |
+ (.char= #\nul))) |
|
53 |
+ (.item))) |
|
54 |
+ (.or (.char= #\newline) |
|
55 |
+ (.not (.item)))))) |
|
56 |
+ (.identity (list :comment (coerce result 'string))))) |
|
57 |
+ |
|
58 |
+(defun .discarded-element () |
|
59 |
+ (.progn (.string= "#_") |
|
60 |
+ (.s) |
|
61 |
+ (.element))) |
|
62 |
+ |
|
63 |
+(defun .alt (&rest r) |
|
64 |
+ (reduce '.plus r)) |
|
65 |
+ |
|
66 |
+(defun .element () |
|
67 |
+ (.or (.or (.nil) |
|
68 |
+ (.boolean)) |
|
69 |
+ (.alt (.symbol) |
|
70 |
+ (.keyword) |
|
71 |
+ (.number) |
|
72 |
+ (.character) |
|
73 |
+ (.string) |
|
74 |
+ |
|
75 |
+ (.let* ((pairs |
|
76 |
+ (.prog2 (.char= #\{) |
|
77 |
+ (.progn (.s) |
|
78 |
+ (.zero-or-more (.let* ((first (.prog1 (.element) (.s))) |
|
79 |
+ (second (.prog1 (.element) (.s)))) |
|
80 |
+ (.identity (list :pair first second))))) |
|
81 |
+ (.char= #\})))) |
|
82 |
+ (.identity (cons :map pairs))) |
|
83 |
+ (.let* ((pairs |
|
84 |
+ (.prog2 (.string= "#{") |
|
85 |
+ (.elements) |
|
86 |
+ (.s) |
|
87 |
+ (.char= #\})))) |
|
88 |
+ (.identity (cons :set pairs))) |
|
89 |
+ (.let* ((pairs |
|
90 |
+ (.prog2 (.char= #\[) |
|
91 |
+ (.elements) |
|
92 |
+ (.s) |
|
93 |
+ (.char= #\])))) |
|
94 |
+ (.identity (cons :vector pairs))) |
|
95 |
+ (.let* ((pairs |
|
96 |
+ (.prog2 (.char= #\() |
|
97 |
+ (.elements) |
|
98 |
+ (.s) |
|
99 |
+ (.char= #\))))) |
|
100 |
+ (.identity (cons :list pairs))) |
|
101 |
+ (.let* ((tag (.progn (.char= #\#) (.tag-symbol))) |
|
102 |
+ (element (.progn (.s) (.element)))) |
|
103 |
+ (.identity (list :tagged tag element))) |
|
104 |
+ ))) |
|
105 |
+ |
|
106 |
+(defun .nil () |
|
107 |
+ (.and (.string= "nil") |
|
108 |
+ (.identity edn-primitives:nil))) |
|
109 |
+ |
|
110 |
+(defun .boolean () |
|
111 |
+ (.let* ((r (.or (.string= "true") |
|
112 |
+ (.string= "false")))) |
|
113 |
+ (string-case:string-case (r) |
|
114 |
+ ("true" (.identity edn-primitives:true)) |
|
115 |
+ ("false" (.identity edn-primitives:false))))) |
|
116 |
+ |
|
117 |
+(defun .symbol () |
|
118 |
+ (.plus (.char= #\/) |
|
119 |
+ (.let* ((ns (.optional (.prog1 (.name) (.char= #\/)))) |
|
120 |
+ (name (.name))) |
|
121 |
+ (.identity (list :symbol ns name))))) |
|
122 |
+ |
|
123 |
+(defun .tag-symbol () |
|
124 |
+ (.let* ((first (.satisfies #'alpha-char-p)) |
|
125 |
+ (rest (.let* ((ns (.or (.char= #\/) |
|
126 |
+ (.optional (.prog1 (.name) (.char= #\/))))) |
|
127 |
+ (name (.name))) |
|
128 |
+ (.identity (list ns name))))) |
|
129 |
+ (destructuring-bind (ns name) rest |
|
130 |
+ (if ns |
|
131 |
+ (if (eql ns #\/) |
|
132 |
+ (.identity (list :symbol (format nil "~c" first) name)) |
|
133 |
+ (.identity (list :symbol (format nil "~c~a" first ns) name))) |
|
134 |
+ (.identity (list :symbol nil (format nil "~c~a" first name))))))) |
|
135 |
+ |
|
136 |
+(defun .keyword () |
|
137 |
+ (.progn (.char= #\:) |
|
138 |
+ (.let* ((ns (.optional (.prog1 (.name) (.char= #\/)))) |
|
139 |
+ (name (.name))) |
|
140 |
+ (.identity (list :keyword ns name))))) |
|
141 |
+ |
|
142 |
+(defun .name () |
|
143 |
+ (.first (.plus (.let* ((first (.name-start-1)) |
|
144 |
+ (rest (.zero-or-more (.name-constituent)))) |
|
145 |
+ (.identity (format nil "~c~{~c~}" first rest))) |
|
146 |
+ (.let* ((first (.name-start-2)) |
|
147 |
+ (second (.satisfies #'alpha-char-p)) |
|
148 |
+ (rest (.zero-or-more (.name-constituent)))) |
|
149 |
+ (.identity (format nil "~c~c~{~c~}" first second rest)))))) |
|
150 |
+ |
|
151 |
+(defun .name-start-1 () |
|
152 |
+ (.satisfies (lambda (x) |
|
153 |
+ (or (alpha-char-p x) |
|
154 |
+ (member x '(#\! #\* #\? #\_ #\$ #\% #\& #\=)))))) |
|
155 |
+ |
|
156 |
+(defun .name-start-2 () |
|
157 |
+ (.satisfies (lambda (x) |
|
158 |
+ (or (alpha-char-p x) |
|
159 |
+ (member x '(#\. #\- #\+)))))) |
|
160 |
+ |
|
161 |
+(defun .name-constituent () |
|
162 |
+ (.or (.name-start-1) |
|
163 |
+ (.name-start-2) |
|
164 |
+ (.satisfies (lambda (x) |
|
165 |
+ (or (digit-char-p x) |
|
166 |
+ (member x '(#\# #\:))))))) |
|
167 |
+(defun .number () |
|
168 |
+ (.or (.float) |
|
169 |
+ (.integer))) |
|
170 |
+ |
|
171 |
+(defun apply-sign (sign num) |
|
172 |
+ (if sign |
|
173 |
+ (ecase sign |
|
174 |
+ (#\+ num) |
|
175 |
+ (#\- (* -1 num))) |
|
176 |
+ num)) |
|
177 |
+ |
|
178 |
+(defun .integer () |
|
179 |
+ (.let* ((sign (.optional |
|
180 |
+ (.or (.char= #\+) |
|
181 |
+ (.char= #\-)))) |
|
182 |
+ (num (.cardinal)) |
|
183 |
+ (flag (.optional (.char= #\N)))) |
|
184 |
+ flag |
|
185 |
+ (.identity (apply-sign sign num)))) |
|
186 |
+ |
|
187 |
+(defun .float () |
|
188 |
+ (.let* ((sign (.optional |
|
189 |
+ (.or (.char= #\+) |
|
190 |
+ (.char= #\-)))) |
|
191 |
+ (num (.cardinal)) |
|
192 |
+ (frac (.frac-exp))) |
|
193 |
+ (destructuring-bind (mant exp) frac |
|
194 |
+ (.identity (apply-sign sign (* (+ num mant) |
|
195 |
+ (if exp |
|
196 |
+ (expt 10 exp) |
|
197 |
+ 1))))))) |
|
198 |
+ |
|
199 |
+(defun .frac-exp () |
|
200 |
+ (.alt (.let* ((frac (.frac)) |
|
201 |
+ (exp (.optional (.exp))) |
|
202 |
+ (flag (.optional (.char= #\M)))) |
|
203 |
+ flag |
|
204 |
+ (.identity (list frac exp))) |
|
205 |
+ (.let* ((exp (.exp)) |
|
206 |
+ (flag (.optional (.char= #\M)))) |
|
207 |
+ flag |
|
208 |
+ (.identity (list 0 exp))) |
|
209 |
+ (.let* ((flag (.optional (.char= #\M)))) |
|
210 |
+ flag |
|
211 |
+ (.identity (list 0 0))))) |
|
212 |
+ |
|
213 |
+(defun .frac () |
|
214 |
+ (declare (optimize debug)) |
|
215 |
+ (.let* ((nums (.first |
|
216 |
+ (.progn (.char= #\.) |
|
217 |
+ (.zero-or-more (.digit)))))) |
|
218 |
+ (.identity |
|
219 |
+ (if nums |
|
220 |
+ (let ((num (parse-integer (coerce nums 'string)))) |
|
221 |
+ (coerce (if (= num 0) |
|
222 |
+ 0 |
|
223 |
+ (/ num |
|
224 |
+ (expt 10 |
|
225 |
+ (floor |
|
226 |
+ (1+ (log num |
|
227 |
+ 10)))))) |
|
228 |
+ 'float)) |
|
229 |
+ 0)))) |
|
230 |
+ |
|
231 |
+(defun .exp () |
|
232 |
+ (.progn (.char-equal #\e) |
|
233 |
+ (.let* ((sign (.optional |
|
234 |
+ (.or (.char= #\+) |
|
235 |
+ (.char= #\-)))) |
|
236 |
+ (num (.cardinal))) |
|
237 |
+ (.identity (apply-sign sign num))))) |
|
238 |
+ |
|
239 |
+(defun .cardinal () |
|
240 |
+ (.let* ((nums (.first |
|
241 |
+ (.or (.let* ((first (.non-zero-digit)) |
|
242 |
+ (rest (.zero-or-more (.digit)))) |
|
243 |
+ (.identity (list* first rest))) |
|
244 |
+ (.let* ((c (.digit))) |
|
245 |
+ (.identity (list c))))))) |
|
246 |
+ (.identity (parse-integer (coerce nums 'string))))) |
|
247 |
+ |
|
248 |
+(defun .digit () |
|
249 |
+ (.satisfies #'digit-char-p)) |
|
250 |
+ |
|
251 |
+(defun .non-zero-digit () |
|
252 |
+ (.satisfies (lambda (x) |
|
253 |
+ (and (digit-char-p x) |
|
254 |
+ (not (eql #\0 x)))))) |
|
255 |
+ |
|
256 |
+(defun .printable-character () |
|
257 |
+ (.or (.satisfies (lambda (x) (char>= #\~ x #\!))) |
|
258 |
+ (.satisfies (lambda (x) (>= (char-code x) #xA1))))) |
|
259 |
+ |
|
260 |
+(defun .character-name () |
|
261 |
+ (.or (.string= "newline") |
|
262 |
+ (.string= "space") |
|
263 |
+ (.string= "tab") |
|
264 |
+ (.string= "return") |
|
265 |
+ (.string= "backspace") |
|
266 |
+ (.string= "formfeed"))) |
|
267 |
+ |
|
268 |
+(defun .character () |
|
269 |
+ (.let* ((char (.progn (.char= #\\) |
|
270 |
+ (.or (.character-name) |
|
271 |
+ (.printable-character))))) |
|
272 |
+ (.identity (list :character char)))) |
|
273 |
+ |
|
274 |
+(defun .string-char () |
|
275 |
+ (.and (.not (.char= #\nul)) |
|
276 |
+ (.not (.char= #\")) |
|
277 |
+ (.not (.char= #\\)) |
|
278 |
+ (.item))) |
|
279 |
+ |
|
280 |
+(defun .string-escape () |
|
281 |
+ (.let* ((esc (.or (.char= #\") |
|
282 |
+ (.char= #\b) |
|
283 |
+ (.char= #\t) |
|
284 |
+ (.char= #\n) |
|
285 |
+ (.char= #\f) |
|
286 |
+ (.char= #\r) |
|
287 |
+ (.char= #\\)))) |
|
288 |
+ (.identity (format nil "\\~c" esc)))) |
|
289 |
+ |
|
290 |
+(defun combine (list) |
|
291 |
+ (format nil "~{~a~}" list)) |
|
292 |
+ |
|
293 |
+(defun .string () |
|
294 |
+ (.let* ((string (.prog2 (.char= #\") |
|
295 |
+ (.zero-or-more (.or (.string-char) |
|
296 |
+ (.progn (.char= #\\) |
|
297 |
+ (.string-escape)))) |
|
298 |
+ (.char= #\")))) |
|
299 |
+ (.identity (list :string (combine string))))) |
|
300 |
+ |
|
301 |
+(defgeneric synthesize-compound (implementation discriminator args)) |
|
302 |
+(defgeneric synthesize (implementation args)) |
0 | 303 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,47 @@ |
1 |
+(in-package :edn) |
|
2 |
+ |
|
3 |
+(defmethod synthesize ((implementation (eql 'fset)) thing) |
|
4 |
+ (typecase thing |
|
5 |
+ (list (synthesize-compound implementation (car thing) (cdr thing))) |
|
6 |
+ (t thing))) |
|
7 |
+ |
|
8 |
+ |
|
9 |
+(defmethod synthesize-compound ((implementation (eql 'fset)) (discriminator (eql :map)) args) |
|
10 |
+ (fset:convert 'fset:map |
|
11 |
+ (mapcar (fw.lu:destructuring-lambda ((p k v)) |
|
12 |
+ (declare (ignore p)) |
|
13 |
+ (cons (synthesize implementation k) |
|
14 |
+ (synthesize implementation v))) |
|
15 |
+ args))) |
|
16 |
+ |
|
17 |
+(defmethod synthesize-compound ((implementation (eql 'fset)) (discriminator (eql :set)) args) |
|
18 |
+ (fset:convert 'fset:set |
|
19 |
+ (mapcar (lambda (a) |
|
20 |
+ (synthesize implementation a)) |
|
21 |
+ args))) |
|
22 |
+ |
|
23 |
+(defmethod synthesize-compound ((implementation (eql 'fset)) (discriminator (eql :vector)) args) |
|
24 |
+ (fset:convert 'fset:seq |
|
25 |
+ (mapcar (lambda (a) |
|
26 |
+ (synthesize implementation a)) |
|
27 |
+ args))) |
|
28 |
+ |
|
29 |
+(defmethod synthesize-compound ((implementation (eql 'fset)) (discriminator (eql :list)) args) |
|
30 |
+ (mapcar (lambda (a) |
|
31 |
+ (synthesize implementation a)) |
|
32 |
+ args)) |
|
33 |
+ |
|
34 |
+(defmethod synthesize-compound (implementation (discriminator (eql :keyword)) args) |
|
35 |
+ (destructuring-bind (ns name) args |
|
36 |
+ (alexandria:make-keyword (if ns |
|
37 |
+ (format nil "~a/~a" ns name) |
|
38 |
+ (format nil "~a" name))))) |
|
39 |
+ |
|
40 |
+(defmethod synthesize-compound (implementation (discriminator (eql :string)) args) |
|
41 |
+ (car args)) |
|
42 |
+ |
|
43 |
+(defmethod synthesize-compound (implementation (discriminator (eql :symbol)) args) |
|
44 |
+ (destructuring-bind (ns name) args |
|
45 |
+ (make-symbol (if ns |
|
46 |
+ (format nil "~a/~a" ns name) |
|
47 |
+ (format nil "~a" name))))) |
0 | 48 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,27 @@ |
1 |
+(defpackage :edn-test |
|
2 |
+ (:use :cl :st) |
|
3 |
+ (:export )) |
|
4 |
+(in-package :edn-test) |
|
5 |
+ |
|
6 |
+(deftest edn-parser () |
|
7 |
+ (should be equal |
|
8 |
+ '((:map (:pair 1 1))) |
|
9 |
+ (smug:parse (edn::.elements) "{ 1 1 }")) |
|
10 |
+ (should be equal |
|
11 |
+ '((:vector 1 1)) |
|
12 |
+ (smug:parse (edn::.elements) "[ 1 1 ]")) |
|
13 |
+ (should be equal |
|
14 |
+ '((:set 1 1)) |
|
15 |
+ (smug:parse (edn::.elements) "#{ 1 1 }")) |
|
16 |
+ (should be equal |
|
17 |
+ '((:tagged (:symbol nil "foobar") (:vector 1 1))) |
|
18 |
+ (smug:parse (edn::.elements) "#foobar [ 1 1 ]")) |
|
19 |
+ (should be equal |
|
20 |
+ '(:list |
|
21 |
+ (:set |
|
22 |
+ (:vector |
|
23 |
+ (:map (:pair edn-primitives:nil edn-primitives:true) |
|
24 |
+ (:pair edn-primitives:false edn-primitives:nil))))) |
|
25 |
+ (car (smug:parse (smug:.prog1 (edn::.elements) (smug:.not (smug:.item))) |
|
26 |
+ "(#{[{nil true,false nil}]})"))) |
|
27 |
+ ) |