Browse code
Preparing for release, documenting
Ed Langley authored on 09/07/2018 05:53:40
Showing 8 changed files
Showing 8 changed files
- README.md
- cl-edn.asd
- edn.lisp
- fset-lossy-synthesize.lisp
- fset-synthesize.lisp
- package.lisp
- synthesize.lisp
- test.lisp
1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,53 @@ |
1 |
+Installation |
|
2 |
+============ |
|
3 |
+ |
|
4 |
+- Clone this repository somewhere ASDF can find the system: e.g. ```git clone https://github.com/fiddlerwoaroof/cl-edn.git ~/quicklisp/local-projects/cl-edn/``` |
|
5 |
+- At a REPL, ```(ql:quickload :cl-edn)``` |
|
6 |
+ |
|
7 |
+Usage |
|
8 |
+===== |
|
9 |
+ |
|
10 |
+This library divides the task of parsing EDN into two stages. In the |
|
11 |
+first stage, implemented by `(EDN:READ-EDN string)` an EDN file is is |
|
12 |
+parsed into an AST where primitives are converted into lisp values and |
|
13 |
+compound forms are converted into lists of the form |
|
14 |
+`(type-specifier . data)`. This AST can be passed to |
|
15 |
+`(EDN:SYNTHESIZE implementation ast)` to produce datastructures of a |
|
16 |
+specific kind. The system `cl-edn/fset` provides a synthesizer that |
|
17 |
+produces appropriate fset datastructures. The system |
|
18 |
+`cl-edn/fset-lossy` produces fset datastructures, but forces symbols |
|
19 |
+and keywords to have uppercase names, for easier interoperation with |
|
20 |
+CL's default readtable. As a convenience, there is also |
|
21 |
+`(EDN:PARSE string &optional (implementation 'fset))` that combines |
|
22 |
+the two steps into a single call. These implementations can passed to |
|
23 |
+`PARSE` and `SYNTHESIZE` either as the symbols `EDN:FSET` and |
|
24 |
+`EDN:FSET-LOSSY` or by instantiating the classes named by those symbols. |
|
25 |
+ |
|
26 |
+EXTENSION |
|
27 |
+========= |
|
28 |
+ |
|
29 |
+`EDN:SYNTHESIZE` is a generic function that takes an implementation as |
|
30 |
+the first argument. The main system, `CL-EDN`, provides two |
|
31 |
+implementations of this function: one that specializes the first |
|
32 |
+argument on `SYMBOL`, that just makes an instance of the class named |
|
33 |
+by `IMPLEMENTATION` and calls `EDN:SYNTHESIZE` with the instance as |
|
34 |
+its first argument; the other implementation inspects the second |
|
35 |
+argument and, if it is a list, it delegates to |
|
36 |
+`(EDN:SYNTHESIZE-COMPOUND IMPLEMENTATION DISCRIMINATOR ARGS)`, the |
|
37 |
+head of the list as `DISCRIMINATOR` and the tail as `ARGS`. The |
|
38 |
+default implementation of this generic also provides methods for |
|
39 |
+strings, symbols and keywords that produce the relevant lisp types, as |
|
40 |
+well as an implementation for tagged literals that implements `#inst` |
|
41 |
+and `#uuid` and produces a form `(:TAGGED TAG-SYMBOL DATA)` where |
|
42 |
+`DATA` is synthesized according to the rules governing |
|
43 |
+`IMPLEMENTATION` (e.g. the `FSET` implementation makes `DATA` to be an |
|
44 |
+instance of the datastructures provided by the `FSET` library). Tags |
|
45 |
+can also be added to the default implementation by providing an |
|
46 |
+EQL-specialized method of `(EDN:SYNTHESIZE-TAG IMPLEMENTATION TAG ARG)` |
|
47 |
+for the symbol you want to define a behavior for. When this method is |
|
48 |
+called, the tag-symbol will be uppercased and converted into a common |
|
49 |
+lisp keyword and the `ARG` will have been synthesized according to the |
|
50 |
+rules provided by `IMPLEMENTATION`. To override this behavior, an |
|
51 |
+implementation can override `EDN:SYNTHESIZE-COMPOUND`, but such |
|
52 |
+implementations should either call `CALL-NEXT-METHOD` or implement |
|
53 |
+`#inst` and `#uuid` processing themselves. |
... | ... |
@@ -8,12 +8,14 @@ |
8 | 8 |
:depends-on (#:alexandria |
9 | 9 |
#:uiop |
10 | 10 |
#:serapeum |
11 |
- #:smug) |
|
12 |
- :serial t |
|
11 |
+ #:smug |
|
12 |
+ :local-time |
|
13 |
+ :uuid) |
|
13 | 14 |
:in-order-to ((test-op (test-op :cl-edn/test))) |
14 | 15 |
:components ((:file "package") |
15 |
- (:file "edn") |
|
16 |
- (:file "synthesize"))) |
|
16 |
+ (:file "edn" :depends-on ("package" "synthesize")) |
|
17 |
+ (:file "synthesize" |
|
18 |
+ :depends-on ("package")))) |
|
17 | 19 |
|
18 | 20 |
(defsystem :cl-edn/fset |
19 | 21 |
:depends-on (#:cl-edn |
... | ... |
@@ -9,12 +9,6 @@ |
9 | 9 |
and name to match CL's symbol behavior")) |
10 | 10 |
|
11 | 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 | 12 |
(defmethod synthesize-compound ((implementation fset-lossy) (discriminator (eql :list)) args) |
19 | 13 |
(mapcar (lambda (a) |
20 | 14 |
(synthesize implementation a)) |
... | ... |
@@ -3,13 +3,6 @@ |
3 | 3 |
() |
4 | 4 |
(:documentation "An EDN synthesizer that produces fset datastructures")) |
5 | 5 |
|
6 |
- |
|
7 |
-(defmethod synthesize ((implementation fset) thing) |
|
8 |
- (typecase thing |
|
9 |
- (list (synthesize-compound implementation (car thing) (cdr thing))) |
|
10 |
- (t thing))) |
|
11 |
- |
|
12 |
- |
|
13 | 6 |
(defmethod synthesize-compound ((implementation fset) (discriminator (eql :map)) args) |
14 | 7 |
(fset:convert 'fset:map |
15 | 8 |
(mapcar (fw.lu:destructuring-lambda ((p k v)) |
... | ... |
@@ -5,10 +5,16 @@ |
5 | 5 |
|
6 | 6 |
(defgeneric synthesize (implementation args)) |
7 | 7 |
(defgeneric synthesize-compound (implementation discriminator args)) |
8 |
+(defgeneric synthesize-tag (implementation tag args)) |
|
8 | 9 |
|
9 | 10 |
(defmethod synthesize ((implementation symbol) discriminator) |
10 | 11 |
(synthesize (make-instance 'implementation) discriminator)) |
11 | 12 |
|
13 |
+(defmethod synthesize (implementation thing) |
|
14 |
+ (typecase thing |
|
15 |
+ (list (synthesize-compound implementation (car thing) (cdr thing))) |
|
16 |
+ (t thing))) |
|
17 |
+ |
|
12 | 18 |
(defmethod synthesize-compound (implementation (discriminator (eql :keyword)) args) |
13 | 19 |
(destructuring-bind (ns name) args |
14 | 20 |
(alexandria:make-keyword (if ns |
... | ... |
@@ -39,5 +45,14 @@ |
39 | 45 |
(alexandria:switch ((symbol-name tag) :test 'string-equal) |
40 | 46 |
("inst" (local-time:parse-rfc3339-timestring (synthesize implementation obj))) |
41 | 47 |
("uuid" (uuid:make-uuid-from-string (synthesize implementation obj))) |
42 |
- (t (list :tagged tag |
|
43 |
- (synthesize implementation obj))))))) |
|
48 |
+ (t (let ((synthesized-object (synthesize implementation obj))) |
|
49 |
+ (fw.lu:if-let* ((tag-keyword (alexandria:make-keyword |
|
50 |
+ (string-upcase |
|
51 |
+ (symbol-name tag)))) |
|
52 |
+ (methods (compute-applicable-methods |
|
53 |
+ #'synthesize-tag |
|
54 |
+ (list implementation tag-keyword synthesized-object)))) |
|
55 |
+ |
|
56 |
+ (synthesize-tag implementation tag-keyword synthesized-object) |
|
57 |
+ (list :tagged tag |
|
58 |
+ synthesized-object)))))))) |
... | ... |
@@ -4,36 +4,39 @@ |
4 | 4 |
(in-package :edn-test) |
5 | 5 |
|
6 | 6 |
(defun float-equal (a b) |
7 |
- (> 0.00001 |
|
8 |
- (abs (- a b)))) |
|
7 |
+ (and (typep a (type-of b)) |
|
8 |
+ (typep b (type-of a)) |
|
9 |
+ (> 0.00001 |
|
10 |
+ (abs (- a b))))) |
|
9 | 11 |
|
10 | 12 |
(deftest floating () |
11 | 13 |
(should be float-equal |
12 |
- 0.1 |
|
13 |
- (edn:read-edn "0.1")) |
|
14 |
+ 0.1d0 |
|
15 |
+ (edn:read-edn "0.1") |
|
16 |
+ ) |
|
14 | 17 |
(should be float-equal |
15 |
- 0.1 |
|
18 |
+ 0.1d0 |
|
16 | 19 |
(edn:read-edn "+0.1")) |
17 | 20 |
(should be float-equal |
18 |
- -0.1 |
|
21 |
+ -0.1d0 |
|
19 | 22 |
(edn:read-edn "-0.1")) |
20 | 23 |
(should be float-equal |
21 |
- 1 |
|
24 |
+ 1d0 |
|
22 | 25 |
(edn:read-edn "0.1e1")) |
23 | 26 |
(should be float-equal |
24 |
- 1 |
|
27 |
+ 1d0 |
|
25 | 28 |
(edn:read-edn "0.1e+1")) |
26 | 29 |
(should be float-equal |
27 |
- 0.01 |
|
30 |
+ 0.01d0 |
|
28 | 31 |
(edn:read-edn "0.1e-1")) |
29 | 32 |
(should be float-equal |
30 |
- -0.01 |
|
33 |
+ -0.01d0 |
|
31 | 34 |
(edn:read-edn "-0.1e-1")) |
32 | 35 |
(should be float-equal |
33 |
- -0.01 |
|
36 |
+ -0.01d0 |
|
34 | 37 |
(edn:read-edn "-0.1e-1M")) |
35 | 38 |
(should be float-equal |
36 |
- -0.0 |
|
39 |
+ -0.0d0 |
|
37 | 40 |
(edn:read-edn "-0.e-1M"))) |
38 | 41 |
|
39 | 42 |
(deftest edn-parser () |