git.fiddlerwoaroof.com
Browse code

Preparing for release, documenting

Ed Langley authored on 09/07/2018 05:53:40
Showing 8 changed files
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
... ...
@@ -336,3 +336,4 @@
336 336
 (defun parse (input &optional (realizer 'fset))
337 337
   (synthesize realizer
338 338
               (read-edn input)))
339
+
... ...
@@ -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,7 +5,9 @@
5 5
            :synthesize
6 6
            :fset
7 7
            :fset-lossy
8
-           :convert-primitive))
8
+           :convert-primitive
9
+           :synthesize-compound
10
+           :synthesize-tag))
9 11
 
10 12
 (defpackage :edn.generate
11 13
   (:use :cl)
... ...
@@ -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 ()