Browse code
Optimize number parsing, break out fset synthesizers
Ed Langley authored on 06/07/2018 01:13:23
Showing 8 changed files
Showing 8 changed files
- cl-edn.asd
- edn.lisp
- fset-lossy-synthesize.lisp
- fset-synthesize.lisp
- generate-edn.lisp
- package.lisp
- synthesize.lisp
- test.lisp
... | ... |
@@ -2,17 +2,18 @@ |
2 | 2 |
(in-package :asdf-user) |
3 | 3 |
|
4 | 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 |
- )) |
|
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 "package") |
|
15 |
+ (:file "edn") |
|
16 |
+ (:file "synthesize"))) |
|
16 | 17 |
|
17 | 18 |
(defsystem :cl-edn/fset |
18 | 19 |
:depends-on (#:cl-edn |
... | ... |
@@ -20,9 +21,17 @@ |
20 | 21 |
#:fwoar.lisputils) |
21 | 22 |
:components ((:file "fset-synthesize"))) |
22 | 23 |
|
24 |
+(defsystem :cl-edn/fset-lossy |
|
25 |
+ :depends-on (#:cl-edn |
|
26 |
+ #:cl-edn/fset |
|
27 |
+ #:fset |
|
28 |
+ #:fwoar.lisputils) |
|
29 |
+ :components ((:file "fset-lossy-synthesize"))) |
|
30 |
+ |
|
23 | 31 |
(defsystem :cl-edn/test |
24 | 32 |
:depends-on (#:should-test) |
25 | 33 |
:perform (test-op (o s) |
26 | 34 |
(uiop:symbol-call :st '#:test |
27 | 35 |
:package :edn-test)) |
28 |
- :components ((:file "test"))) |
|
36 |
+ :components ((:file "package") |
|
37 |
+ (:file "test"))) |
... | ... |
@@ -1,14 +1,3 @@ |
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 | 1 |
(in-package :edn) |
13 | 2 |
|
14 | 3 |
(defun .satisfies (predicate &rest args) |
... | ... |
@@ -63,6 +52,27 @@ |
63 | 52 |
(defun .alt (&rest r) |
64 | 53 |
(reduce '.plus r)) |
65 | 54 |
|
55 |
+(defun .compound-element-start () |
|
56 |
+ (.or (.string= "#{") |
|
57 |
+ (.char= #\{) |
|
58 |
+ (.char= #\[) |
|
59 |
+ (.char= #\() |
|
60 |
+ (.char= #\{))) |
|
61 |
+ |
|
62 |
+(defun .compound-element-finish (closing) |
|
63 |
+ (lambda () |
|
64 |
+ (.prog1 (.first (.elements)) |
|
65 |
+ (.s) |
|
66 |
+ (.char= closing)))) |
|
67 |
+ |
|
68 |
+(defun .map-element () |
|
69 |
+ (.prog2 (.char= #\{) |
|
70 |
+ (.progn (.s) |
|
71 |
+ (.zero-or-more (.let* ((first (.prog1 (.element) (.s))) |
|
72 |
+ (second (.prog1 (.element) (.s)))) |
|
73 |
+ (.identity (list :pair first second))))) |
|
74 |
+ (.char= #\}))) |
|
75 |
+ |
|
66 | 76 |
(defun .element () |
67 | 77 |
(.or (.or (.nil) |
68 | 78 |
(.boolean)) |
... | ... |
@@ -73,28 +83,23 @@ |
73 | 83 |
(.string) |
74 | 84 |
|
75 | 85 |
(.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= #\})))) |
|
86 |
+ (.map-element))) |
|
82 | 87 |
(.identity (cons :map pairs))) |
83 | 88 |
(.let* ((pairs |
84 | 89 |
(.prog2 (.string= "#{") |
85 |
- (.elements) |
|
90 |
+ (.first (.elements)) |
|
86 | 91 |
(.s) |
87 | 92 |
(.char= #\})))) |
88 | 93 |
(.identity (cons :set pairs))) |
89 | 94 |
(.let* ((pairs |
90 | 95 |
(.prog2 (.char= #\[) |
91 |
- (.elements) |
|
96 |
+ (.first (.elements)) |
|
92 | 97 |
(.s) |
93 | 98 |
(.char= #\])))) |
94 | 99 |
(.identity (cons :vector pairs))) |
95 | 100 |
(.let* ((pairs |
96 | 101 |
(.prog2 (.char= #\() |
97 |
- (.elements) |
|
102 |
+ (.first (.elements)) |
|
98 | 103 |
(.s) |
99 | 104 |
(.char= #\))))) |
100 | 105 |
(.identity (cons :list pairs))) |
... | ... |
@@ -164,10 +169,6 @@ |
164 | 169 |
(.satisfies (lambda (x) |
165 | 170 |
(or (digit-char-p x) |
166 | 171 |
(member x '(#\# #\:))))))) |
167 |
-(defun .number () |
|
168 |
- (.or (.float) |
|
169 |
- (.integer))) |
|
170 |
- |
|
171 | 172 |
(defun apply-sign (sign num) |
172 | 173 |
(if sign |
173 | 174 |
(ecase sign |
... | ... |
@@ -175,27 +176,6 @@ |
175 | 176 |
(#\- (* -1 num))) |
176 | 177 |
num)) |
177 | 178 |
|
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 | 179 |
(defun .frac-exp () |
200 | 180 |
(.alt (.let* ((frac (.frac)) |
201 | 181 |
(exp (.optional (.exp))) |
... | ... |
@@ -225,9 +205,50 @@ |
225 | 205 |
(floor |
226 | 206 |
(1+ (log num |
227 | 207 |
10)))))) |
228 |
- 'float)) |
|
208 |
+ 'double-float)) |
|
229 | 209 |
0)))) |
230 | 210 |
|
211 |
+(defun .one-of (items &optional (test 'eql)) |
|
212 |
+ (.satisfies |
|
213 |
+ (serapeum:op |
|
214 |
+ (member _ items :test test)))) |
|
215 |
+ |
|
216 |
+(defun interpret-number (parts) |
|
217 |
+ (destructuring-bind (sign radix float-info flag) parts |
|
218 |
+ (let* ((base-value (if float-info |
|
219 |
+ (destructuring-bind (mantissa exp) float-info |
|
220 |
+ (coerce (* (+ radix |
|
221 |
+ (or mantissa 0)) |
|
222 |
+ (if exp |
|
223 |
+ (expt 10 exp) |
|
224 |
+ 1)) |
|
225 |
+ 'double-float)) |
|
226 |
+ radix)) |
|
227 |
+ (signed (case sign |
|
228 |
+ ((#\+ nil) base-value) |
|
229 |
+ (#\- (- base-value))))) |
|
230 |
+ (typecase signed |
|
231 |
+ (integer (if (member flag '(nil #\N)) |
|
232 |
+ (.identity signed) |
|
233 |
+ (.fail))) |
|
234 |
+ (float (if (member flag '(nil #\M)) |
|
235 |
+ (.identity signed) |
|
236 |
+ (.fail))))))) |
|
237 |
+ |
|
238 |
+(defun .number () |
|
239 |
+ (flet ((.sign () (.one-of '(#\+ #\-)))) |
|
240 |
+ (.let* ((sign (.optional (.sign))) |
|
241 |
+ (num (.cardinal)) |
|
242 |
+ (frac (.optional (.frac))) |
|
243 |
+ (exp (.optional (.exp))) |
|
244 |
+ (flag (.optional (.one-of '(#\N #\M))))) |
|
245 |
+ (interpret-number |
|
246 |
+ (list sign |
|
247 |
+ num |
|
248 |
+ (when (or frac exp) |
|
249 |
+ (list frac exp)) |
|
250 |
+ flag))))) |
|
251 |
+ |
|
231 | 252 |
(defun .exp () |
232 | 253 |
(.progn (.char-equal #\e) |
233 | 254 |
(.let* ((sign (.optional |
... | ... |
@@ -237,12 +258,13 @@ |
237 | 258 |
(.identity (apply-sign sign num))))) |
238 | 259 |
|
239 | 260 |
(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))))))) |
|
261 |
+ (.let* ((nums (.or (.first |
|
262 |
+ (.let* ((first (.non-zero-digit)) |
|
263 |
+ (rest (.zero-or-more |
|
264 |
+ (.digit)))) |
|
265 |
+ (.identity (list* first rest)))) |
|
266 |
+ (.let* ((c (.digit))) |
|
267 |
+ (.identity (list c)))))) |
|
246 | 268 |
(.identity (parse-integer (coerce nums 'string))))) |
247 | 269 |
|
248 | 270 |
(defun .digit () |
... | ... |
@@ -255,7 +277,7 @@ |
255 | 277 |
|
256 | 278 |
(defun .printable-character () |
257 | 279 |
(.or (.satisfies (lambda (x) (char>= #\~ x #\!))) |
258 |
- (.satisfies (lambda (x) (>= (char-code x) #xA1))))) |
|
280 |
+ (.satisfies (lambda (x) (char<= #\space x))))) |
|
259 | 281 |
|
260 | 282 |
(defun .character-name () |
261 | 283 |
(.or (.string= "newline") |
... | ... |
@@ -298,5 +320,13 @@ |
298 | 320 |
(.char= #\")))) |
299 | 321 |
(.identity (list :string (combine string))))) |
300 | 322 |
|
301 |
-(defgeneric synthesize-compound (implementation discriminator args)) |
|
302 |
-(defgeneric synthesize (implementation args)) |
|
323 |
+(defun read-edn (s) |
|
324 |
+ (car |
|
325 |
+ (smug:parse (.prog1 (.elements) |
|
326 |
+ (.s) |
|
327 |
+ (.not (.item))) |
|
328 |
+ s))) |
|
329 |
+ |
|
330 |
+(defun parse (input &optional (realizer 'fset)) |
|
331 |
+ (synthesize realizer |
|
332 |
+ (read-edn input))) |
303 | 333 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,39 @@ |
1 |
+;; capitalizes symbols ---- possible information loss |
|
2 |
+(in-package :edn) |
|
3 |
+ |
|
4 |
+(defclass fset-lossy (fset) |
|
5 |
+ () |
|
6 |
+ (:documentation "An eden synthesizer that applies semantic |
|
7 |
+ transformations that could lose information: in particular, when |
|
8 |
+ it's translating a keyword or symbol, it uppercases the namespace |
|
9 |
+ and name to match CL's symbol behavior")) |
|
10 |
+ |
|
11 |
+ |
|
12 |
+(defmethod synthesize ((implementation (eql 'fset-lossy)) thing) |
|
13 |
+ (typecase thing |
|
14 |
+ (list (synthesize-compound implementation (car thing) (cdr thing))) |
|
15 |
+ (t thing))) |
|
16 |
+ |
|
17 |
+ |
|
18 |
+(defmethod synthesize-compound ((implementation fset-lossy) (discriminator (eql :list)) args) |
|
19 |
+ (mapcar (lambda (a) |
|
20 |
+ (synthesize implementation a)) |
|
21 |
+ args)) |
|
22 |
+ |
|
23 |
+(defmethod synthesize-compound :around ((implementation fset-lossy) (discriminator (eql :keyword)) args) |
|
24 |
+ (destructuring-bind (ns name) args |
|
25 |
+ (call-next-method implementation |
|
26 |
+ discriminator |
|
27 |
+ (list (when ns (string-upcase ns)) |
|
28 |
+ (string-upcase name))))) |
|
29 |
+ |
|
30 |
+(defmethod synthesize-compound (implementation (discriminator (eql :string)) args) |
|
31 |
+ (car args)) |
|
32 |
+ |
|
33 |
+(defmethod synthesize-compound :around ((implementation fset-lossy) (discriminator (eql :symbol)) args) |
|
34 |
+ (destructuring-bind (ns name) args |
|
35 |
+ (call-next-method implementation |
|
36 |
+ discriminator |
|
37 |
+ (list (when ns (string-upcase ns)) |
|
38 |
+ (string-upcase name))))) |
|
39 |
+ |
... | ... |
@@ -1,12 +1,16 @@ |
1 | 1 |
(in-package :edn) |
2 |
+(defclass fset () |
|
3 |
+ () |
|
4 |
+ (:documentation "An EDN synthesizer that produces fset datastructures")) |
|
2 | 5 |
|
3 |
-(defmethod synthesize ((implementation (eql 'fset)) thing) |
|
6 |
+ |
|
7 |
+(defmethod synthesize ((implementation fset) thing) |
|
4 | 8 |
(typecase thing |
5 | 9 |
(list (synthesize-compound implementation (car thing) (cdr thing))) |
6 | 10 |
(t thing))) |
7 | 11 |
|
8 | 12 |
|
9 |
-(defmethod synthesize-compound ((implementation (eql 'fset)) (discriminator (eql :map)) args) |
|
13 |
+(defmethod synthesize-compound ((implementation fset) (discriminator (eql :map)) args) |
|
10 | 14 |
(fset:convert 'fset:map |
11 | 15 |
(mapcar (fw.lu:destructuring-lambda ((p k v)) |
12 | 16 |
(declare (ignore p)) |
... | ... |
@@ -14,39 +18,20 @@ |
14 | 18 |
(synthesize implementation v))) |
15 | 19 |
args))) |
16 | 20 |
|
17 |
-(defmethod synthesize-compound ((implementation (eql 'fset)) (discriminator (eql :set)) args) |
|
21 |
+(defmethod synthesize-compound ((implementation fset) (discriminator (eql :set)) args) |
|
18 | 22 |
(fset:convert 'fset:set |
19 | 23 |
(mapcar (lambda (a) |
20 | 24 |
(synthesize implementation a)) |
21 | 25 |
args))) |
22 | 26 |
|
23 |
-(defmethod synthesize-compound ((implementation (eql 'fset)) (discriminator (eql :vector)) args) |
|
27 |
+(defmethod synthesize-compound ((implementation fset) (discriminator (eql :vector)) args) |
|
24 | 28 |
(fset:convert 'fset:seq |
25 | 29 |
(mapcar (lambda (a) |
26 | 30 |
(synthesize implementation a)) |
27 | 31 |
args))) |
28 | 32 |
|
29 |
-(defmethod synthesize-compound ((implementation (eql 'fset)) (discriminator (eql :list)) args) |
|
33 |
+(defmethod synthesize-compound ((implementation fset) (discriminator (eql :list)) args) |
|
30 | 34 |
(mapcar (lambda (a) |
31 | 35 |
(synthesize implementation a)) |
32 | 36 |
args)) |
33 | 37 |
|
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))))) |
|
48 |
- |
|
49 |
-(defmethod synthesize-compound (implementation (discriminator (eql :tagged)) args) |
|
50 |
- (destructuring-bind (sym obj) args |
|
51 |
- (list :tagged (synthesize-compound implementation (car sym) (cdr sym)) |
|
52 |
- (synthesize implementation obj)))) |
53 | 38 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,167 @@ |
1 |
+(in-package :edn.generate) |
|
2 |
+ |
|
3 |
+(defun generate-edn () |
|
4 |
+ (case (random 3) |
|
5 |
+ (0 (generate-map)) |
|
6 |
+ (1 (generate-set)) |
|
7 |
+ (2 (generate-vect)))) |
|
8 |
+ |
|
9 |
+(defun generate-nil () |
|
10 |
+ "nil") |
|
11 |
+ |
|
12 |
+(defun prim-generate-char () |
|
13 |
+ (code-char (+ 32 (random #.(- 128 32))))) |
|
14 |
+ |
|
15 |
+(defun generate-string () |
|
16 |
+ (loop with limit = (random 25) |
|
17 |
+ repeat limit |
|
18 |
+ collect (prim-generate-char) into chars |
|
19 |
+ finally (return (format nil "\"~a\"" |
|
20 |
+ (serapeum:string-replace-all "\"" |
|
21 |
+ (serapeum:string-replace-all |
|
22 |
+ "\\" |
|
23 |
+ (coerce chars 'string) |
|
24 |
+ "\\\\") |
|
25 |
+ "\\\""))))) |
|
26 |
+ |
|
27 |
+(defun generate-int () |
|
28 |
+ (princ-to-string (- (random 20000) |
|
29 |
+ 10000))) |
|
30 |
+ |
|
31 |
+(defun flip-coin () |
|
32 |
+ (= 1 (random 2))) |
|
33 |
+ |
|
34 |
+(defun generate-float () |
|
35 |
+ (format nil "~[~;-~;+~]~a.~:[~;~:*~a~]~:[~;e~:*~a~]" |
|
36 |
+ (random 3) |
|
37 |
+ (if (flip-coin) |
|
38 |
+ (random 10000) |
|
39 |
+ 0) |
|
40 |
+ (when (flip-coin) |
|
41 |
+ (random 10000)) |
|
42 |
+ (when (flip-coin) |
|
43 |
+ (- (random 100) |
|
44 |
+ 50)))) |
|
45 |
+ |
|
46 |
+(defun generate-character () |
|
47 |
+ (format nil "\\~c" (prim-generate-char))) |
|
48 |
+ |
|
49 |
+(defun generate-bool () |
|
50 |
+ (if (flip-coin) |
|
51 |
+ "true" |
|
52 |
+ "false")) |
|
53 |
+ |
|
54 |
+(defmacro comment (&body b) |
|
55 |
+ (declare (ignore b)) |
|
56 |
+ (values)) |
|
57 |
+ |
|
58 |
+(comment |
|
59 |
+ (or (alpha-char-p x) |
|
60 |
+ (member x '(#\! #\* #\? #\_ #\$ #\% #\& #\=)))) |
|
61 |
+ |
|
62 |
+(defun generate-capital () |
|
63 |
+ (code-char |
|
64 |
+ (+ #.(char-code #\A) |
|
65 |
+ (random 26)))) |
|
66 |
+ |
|
67 |
+(defun generate-lower () |
|
68 |
+ (code-char |
|
69 |
+ (+ #.(char-code #\a) |
|
70 |
+ (random 26)))) |
|
71 |
+ |
|
72 |
+(defun generate-initial-char () |
|
73 |
+ (case (random 2) |
|
74 |
+ (0 (generate-capital)) |
|
75 |
+ (1 (generate-lower)))) |
|
76 |
+ |
|
77 |
+(defun generate-middle-char () |
|
78 |
+ (case (random 5) |
|
79 |
+ (0 (generate-capital)) |
|
80 |
+ (1 (generate-lower)) |
|
81 |
+ (2 (generate-capital)) |
|
82 |
+ (3 (generate-lower)) |
|
83 |
+ (4 (elt #(#\- #\_) (random 2))))) |
|
84 |
+ |
|
85 |
+(defun generate-name (&optional (length 20)) |
|
86 |
+ (loop repeat (+ 2 (random length)) |
|
87 |
+ for char = (generate-initial-char) then (generate-middle-char) |
|
88 |
+ collect char into chars |
|
89 |
+ finally (return (coerce chars 'string)))) |
|
90 |
+ |
|
91 |
+(defun generate-symbol () |
|
92 |
+ (let ((ns (generate-name 5)) |
|
93 |
+ (name (generate-name 20))) |
|
94 |
+ (if (flip-coin) |
|
95 |
+ name |
|
96 |
+ (format nil "~a/~a" ns name)))) |
|
97 |
+ |
|
98 |
+(defun generate-keyword () |
|
99 |
+ (format nil ":~a" (generate-symbol))) |
|
100 |
+ |
|
101 |
+(defun generate-primitive () |
|
102 |
+ (case (random 8) |
|
103 |
+ (0 (generate-string)) |
|
104 |
+ (1 (generate-int)) |
|
105 |
+ (2 (generate-bool)) |
|
106 |
+ (3 (generate-float)) |
|
107 |
+ (4 (generate-nil)) |
|
108 |
+ (5 (generate-character)) |
|
109 |
+ (6 (generate-keyword)) |
|
110 |
+ (7 (generate-symbol)))) |
|
111 |
+ |
|
112 |
+(defun compound-or-primitive (&optional (primitive-func 'generate-primitive)) |
|
113 |
+ (case (random 10) |
|
114 |
+ (0 (generate-edn)) |
|
115 |
+ (1 (funcall primitive-func)) |
|
116 |
+ (2 (funcall primitive-func)) |
|
117 |
+ (3 (funcall primitive-func)) |
|
118 |
+ (4 (funcall primitive-func)) |
|
119 |
+ (5 (funcall primitive-func)) |
|
120 |
+ (6 (funcall primitive-func)) |
|
121 |
+ (7 (funcall primitive-func)) |
|
122 |
+ (8 (funcall primitive-func)) |
|
123 |
+ (9 (funcall primitive-func)))) |
|
124 |
+ |
|
125 |
+(defun not-float () |
|
126 |
+ (compound-or-primitive |
|
127 |
+ (lambda () |
|
128 |
+ (case (random 5) |
|
129 |
+ (0 (generate-string)) |
|
130 |
+ (1 (generate-int)) |
|
131 |
+ (2 (generate-bool)) |
|
132 |
+ (3 (generate-nil)) |
|
133 |
+ (4 (generate-character)))))) |
|
134 |
+ |
|
135 |
+(defun generate-map (&optional (key-func 'not-float) (value-func 'compound-or-primitive)) |
|
136 |
+ (loop |
|
137 |
+ with keys = (fset:set) |
|
138 |
+ repeat (random 10) |
|
139 |
+ for key = (loop for next = (funcall key-func) |
|
140 |
+ until (not (fset:contains? keys next)) |
|
141 |
+ finally |
|
142 |
+ (fset:includef keys next) |
|
143 |
+ (return next)) |
|
144 |
+ for value = (funcall value-func) |
|
145 |
+ collect (format nil "~a ~a" key value) into res |
|
146 |
+ finally (return (format nil "{~{~{~a~^~[ ~;, ~;,~; ,~]~}~}}" |
|
147 |
+ (mapcar (serapeum:op (list _1 (random 3))) |
|
148 |
+ (remove-duplicates res :test 'equal)))))) |
|
149 |
+ |
|
150 |
+ |
|
151 |
+(defun generate-set (&optional (value-func 'not-float)) |
|
152 |
+ (loop |
|
153 |
+ repeat (random 19) |
|
154 |
+ for value = (funcall value-func) |
|
155 |
+ collect value into res |
|
156 |
+ finally (return (format nil "#{~{~{~a~^~[ ~;, ~;,~; ,~]~}~}}" |
|
157 |
+ (mapcar (serapeum:op (list _1 (random 3))) |
|
158 |
+ (remove-duplicates res :test 'equal)))))) |
|
159 |
+ |
|
160 |
+(defun generate-vect (&optional (value-func 'compound-or-primitive)) |
|
161 |
+ (loop |
|
162 |
+ repeat (random 19) |
|
163 |
+ for value = (funcall value-func) |
|
164 |
+ collect value into res |
|
165 |
+ finally (return (format nil "[~{~{~a~^~[ ~;, ~;,~; ,~]~}~}]" |
|
166 |
+ (mapcar (serapeum:op (list _1 (random 3))) |
|
167 |
+ res))))) |
0 | 168 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,20 @@ |
1 |
+(defpackage :edn |
|
2 |
+ (:use :cl :smug) |
|
3 |
+ (:shadow :parse) |
|
4 |
+ (:export :read-edn |
|
5 |
+ :synthesize |
|
6 |
+ :fset |
|
7 |
+ :fset-lossy |
|
8 |
+ :convert-primitive)) |
|
9 |
+ |
|
10 |
+(defpackage :edn.generate |
|
11 |
+ (:use :cl) |
|
12 |
+ (:export :generate-edn)) |
|
13 |
+ |
|
14 |
+(defpackage :edn-primitives |
|
15 |
+ (:use) |
|
16 |
+ (:export :nil :true :false)) |
|
17 |
+ |
|
18 |
+(defconstant edn-primitives:nil 'edn-primitives:nil) |
|
19 |
+(defconstant edn-primitives:true 'edn-primitives:true) |
|
20 |
+(defconstant edn-primitives:false 'edn-primitives:false) |
0 | 21 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,39 @@ |
1 |
+(in-package :edn) |
|
2 |
+ |
|
3 |
+ |
|
4 |
+(defgeneric convert-primitive (implementation primitive)) |
|
5 |
+ |
|
6 |
+(defgeneric synthesize (implementation args)) |
|
7 |
+(defgeneric synthesize-compound (implementation discriminator args)) |
|
8 |
+ |
|
9 |
+(defmethod synthesize ((implementation symbol) discriminator) |
|
10 |
+ (synthesize (make-instance 'implementation) discriminator)) |
|
11 |
+ |
|
12 |
+(defmethod synthesize-compound (implementation (discriminator (eql :keyword)) args) |
|
13 |
+ (destructuring-bind (ns name) args |
|
14 |
+ (alexandria:make-keyword (if ns |
|
15 |
+ (format nil "~a/~a" ns name) |
|
16 |
+ (format nil "~a" name))))) |
|
17 |
+ |
|
18 |
+(defmethod synthesize-compound (implementation (discriminator (eql :string)) args) |
|
19 |
+ (car args)) |
|
20 |
+ |
|
21 |
+(defmethod synthesize-compound (implementation (discriminator (eql :symbol)) args) |
|
22 |
+ (destructuring-bind (ns name) args |
|
23 |
+ (make-symbol (if ns |
|
24 |
+ (format nil "~a/~a" ns name) |
|
25 |
+ (format nil "~a" name))))) |
|
26 |
+ |
|
27 |
+(defmethod synthesize-compound (implementation (discriminator (eql :tagged)) args) |
|
28 |
+ (destructuring-bind (sym obj) args |
|
29 |
+ (list :tagged |
|
30 |
+ (synthesize-compound implementation (car sym) (cdr sym)) |
|
31 |
+ (synthesize implementation obj)))) |
|
32 |
+ |
|
33 |
+(defmethod synthesize-compound (implementation (discriminator (eql :character)) args) |
|
34 |
+ (car args)) |
|
35 |
+ |
|
36 |
+(defmethod synthesize-compound (implementation (discriminator (eql :tagged)) args) |
|
37 |
+ (destructuring-bind (sym obj) args |
|
38 |
+ (list :tagged (synthesize-compound implementation (car sym) (cdr sym)) |
|
39 |
+ (synthesize implementation obj)))) |
... | ... |
@@ -3,25 +3,59 @@ |
3 | 3 |
(:export )) |
4 | 4 |
(in-package :edn-test) |
5 | 5 |
|
6 |
+(defun float-equal (a b) |
|
7 |
+ (> 0.00001 |
|
8 |
+ (abs (- a b)))) |
|
9 |
+ |
|
10 |
+(deftest floating () |
|
11 |
+ (should be float-equal |
|
12 |
+ 0.1 |
|
13 |
+ (edn:read-edn "0.1")) |
|
14 |
+ (should be float-equal |
|
15 |
+ 0.1 |
|
16 |
+ (edn:read-edn "+0.1")) |
|
17 |
+ (should be float-equal |
|
18 |
+ -0.1 |
|
19 |
+ (edn:read-edn "-0.1")) |
|
20 |
+ (should be float-equal |
|
21 |
+ 1 |
|
22 |
+ (edn:read-edn "0.1e1")) |
|
23 |
+ (should be float-equal |
|
24 |
+ 1 |
|
25 |
+ (edn:read-edn "0.1e+1")) |
|
26 |
+ (should be float-equal |
|
27 |
+ 0.01 |
|
28 |
+ (edn:read-edn "0.1e-1")) |
|
29 |
+ (should be float-equal |
|
30 |
+ -0.01 |
|
31 |
+ (edn:read-edn "-0.1e-1")) |
|
32 |
+ (should be float-equal |
|
33 |
+ -0.01 |
|
34 |
+ (edn:read-edn "-0.1e-1M")) |
|
35 |
+ (should be float-equal |
|
36 |
+ -0.0 |
|
37 |
+ (edn:read-edn "-0.e-1M"))) |
|
38 |
+ |
|
6 | 39 |
(deftest edn-parser () |
7 | 40 |
(should be equal |
8 |
- '((:map (:pair 1 1))) |
|
9 |
- (smug:parse (edn::.elements) "{ 1 1 }")) |
|
41 |
+ '(:list) |
|
42 |
+ (edn:read-edn (format nil "()~%"))) |
|
43 |
+ (should be equal |
|
44 |
+ '(:map (:pair 1 1)) |
|
45 |
+ (edn:read-edn "{ 1 1 }")) |
|
10 | 46 |
(should be equal |
11 |
- '((:vector 1 1)) |
|
12 |
- (smug:parse (edn::.elements) "[ 1 1 ]")) |
|
47 |
+ '(:vector 1 1) |
|
48 |
+ (edn:read-edn "[ 1 1 ]")) |
|
13 | 49 |
(should be equal |
14 |
- '((:set 1 1)) |
|
15 |
- (smug:parse (edn::.elements) "#{ 1 1 }")) |
|
50 |
+ '(:set 1 1) |
|
51 |
+ (edn:read-edn "#{ 1 1 }")) |
|
16 | 52 |
(should be equal |
17 |
- '((:tagged (:symbol nil "foobar") (:vector 1 1))) |
|
18 |
- (smug:parse (edn::.elements) "#foobar [ 1 1 ]")) |
|
53 |
+ '(:tagged (:symbol nil "foobar") (:vector 1 1)) |
|
54 |
+ (edn:read-edn "#foobar [ 1 1 ]")) |
|
19 | 55 |
(should be equal |
20 | 56 |
'(:list |
21 | 57 |
(:set |
22 | 58 |
(:vector |
23 | 59 |
(:map (:pair edn-primitives:nil edn-primitives:true) |
24 | 60 |
(: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 |
- ) |
|
61 |
+ (edn:read-edn "(#{[{nil true,false nil}]})"))) |