From 73120f5cc97224c2a5c961b4aa881738cc78e2af Mon Sep 17 00:00:00 2001 From: Eduardo Julian Date: Tue, 4 Dec 2018 19:10:43 -0400 Subject: Some refactoring. --- stdlib/source/lux/data/format/json.lux | 264 +++++++++++++++++---------------- 1 file changed, 140 insertions(+), 124 deletions(-) (limited to 'stdlib/source/lux/data/format/json.lux') diff --git a/stdlib/source/lux/data/format/json.lux b/stdlib/source/lux/data/format/json.lux index 20f059503..63075804e 100644 --- a/stdlib/source/lux/data/format/json.lux +++ b/stdlib/source/lux/data/format/json.lux @@ -5,11 +5,12 @@ ["." monad (#+ do Monad)] [equivalence (#+ Equivalence)] codec - ["p" parser ("parser/." Monad)]] + ["p" parser ("parser/." Monad)] + ["ex" exception (#+ exception:)]] [data ["." bit] ["." maybe] - ["e" error] + ["." error (#+ Error)] ["." sum] ["." product] ["." number ("frac/." Codec) ("nat/." Codec)] @@ -18,7 +19,7 @@ [collection ["." list ("list/." Fold Monad)] ["." row (#+ Row row) ("row/." Monad)] - ["dict" dictionary (#+ Dictionary)]]] + ["." dictionary (#+ Dictionary)]]] ["." macro (#+ Monad with-gensyms) ["s" syntax (#+ syntax:)] ["." code]]]) @@ -87,7 +88,7 @@ _ (macro.fail "Wrong syntax for JSON object."))) pairs)] - (wrap (list (` (: JSON (#Object (dict.from-list text.Hash (list (~+ pairs'))))))))) + (wrap (list (` (: JSON (#Object (dictionary.from-list text.Hash (list (~+ pairs'))))))))) _ (wrap (list token)) @@ -95,52 +96,52 @@ (def: #export (get-fields json) {#.doc "Get all the fields in a JSON object."} - (-> JSON (e.Error (List String))) + (-> JSON (Error (List String))) (case json (#Object obj) - (#e.Success (dict.keys obj)) + (#error.Success (dictionary.keys obj)) _ - (#e.Error ($_ text/compose "Cannot get the fields of a non-object.")))) + (#error.Error ($_ text/compose "Cannot get the fields of a non-object.")))) (def: #export (get key json) {#.doc "A JSON object field getter."} - (-> String JSON (e.Error JSON)) + (-> String JSON (Error JSON)) (case json (#Object obj) - (case (dict.get key obj) + (case (dictionary.get key obj) (#.Some value) - (#e.Success value) + (#error.Success value) #.None - (#e.Error ($_ text/compose "Missing field '" key "' on object."))) + (#error.Error ($_ text/compose "Missing field '" key "' on object."))) _ - (#e.Error ($_ text/compose "Cannot get field '" key "' of a non-object.")))) + (#error.Error ($_ text/compose "Cannot get field '" key "' of a non-object.")))) (def: #export (set key value json) {#.doc "A JSON object field setter."} - (-> String JSON JSON (e.Error JSON)) + (-> String JSON JSON (Error JSON)) (case json (#Object obj) - (#e.Success (#Object (dict.put key value obj))) + (#error.Success (#Object (dictionary.put key value obj))) _ - (#e.Error ($_ text/compose "Cannot set field '" key "' of a non-object.")))) + (#error.Error ($_ text/compose "Cannot set field '" key "' of a non-object.")))) (do-template [ ] [(def: #export ( key json) {#.doc (code.text ($_ text/compose "A JSON object field getter for " "."))} - (-> Text JSON (e.Error )) + (-> Text JSON (Error )) (case (get key json) - (#e.Success ( value)) - (#e.Success value) + (#error.Success ( value)) + (#error.Success value) - (#e.Success _) - (#e.Error ($_ text/compose "Wrong value type at key: " key)) + (#error.Success _) + (#error.Error ($_ text/compose "Wrong value type at key: " key)) - (#e.Error error) - (#e.Error error)))] + (#error.Error error) + (#error.Error error)))] [get-boolean #Boolean Boolean "booleans"] [get-number #Number Number "numbers"] @@ -175,14 +176,14 @@ (list.indices (row.size xs)))) [(#Object xs) (#Object ys)] - (and (n/= (dict.size xs) (dict.size ys)) + (and (n/= (dictionary.size xs) (dictionary.size ys)) (list/fold (function (_ [xk xv] prev) (and prev - (case (dict.get xk ys) + (case (dictionary.get xk ys) #.None #0 (#.Some yv) (= xv yv)))) #1 - (dict.entries xs))) + (dictionary.entries xs))) _ #0))) @@ -191,26 +192,79 @@ ############################################################ ############################################################ -(def: unconsumed-input-error Text "Unconsumed JSON.") +(def: (encode-boolean value) + (-> Bit Text) + (case value + #0 "false" + #1 "true")) + +(def: (show-null _) (-> Null Text) "null") +(do-template [ ] + [(def: (-> Text) )] + + [show-boolean Boolean encode-boolean] + [show-number Number (:: number.Codec encode)] + [show-string String text.encode]) + +(def: (show-array show-json elems) + (-> (-> JSON Text) (-> Array Text)) + ($_ text/compose "[" + (|> elems (row/map show-json) row.to-list (text.join-with ",")) + "]")) + +(def: (show-object show-json object) + (-> (-> JSON Text) (-> Object Text)) + ($_ text/compose "{" + (|> object + dictionary.entries + (list/map (function (_ [key value]) ($_ text/compose (show-string key) ":" (show-json value)))) + (text.join-with ",")) + "}")) + +(def: (show-json json) + (-> JSON Text) + (case json + (^template [ ] + ( value) + ( value)) + ([#Null show-null] + [#Boolean show-boolean] + [#Number show-number] + [#String show-string] + [#Array (show-array show-json)] + [#Object (show-object show-json)]) + )) + +############################################################ +############################################################ +############################################################ + +(exception: #export (unconsumed-input {input (List JSON)}) + (|> input + (list/map show-json) + (text.join-with text.new-line))) + +(exception: #export (empty-input) + "") (def: #export (run json parser) - (All [a] (-> JSON (Reader a) (e.Error a))) + (All [a] (-> JSON (Reader a) (Error a))) (case (p.run (list json) parser) - (#e.Success [remainder output]) + (#error.Success [remainder output]) (case remainder #.Nil - (#e.Success output) + (#error.Success output) _ - (#e.Error unconsumed-input-error)) + (ex.throw unconsumed-input remainder)) - (#e.Error error) - (#e.Error error))) + (#error.Error error) + (#error.Error error))) (def: #export (fail error) (All [a] (-> Text (Reader a))) (function (_ inputs) - (#e.Error error))) + (#error.Error error))) (def: #export any {#.doc "Just returns the JSON input without applying any logic."} @@ -218,10 +272,10 @@ (<| (function (_ inputs)) (case inputs #.Nil - (#e.Error "Empty JSON stream.") + (ex.throw empty-input []) (#.Cons head tail) - (#e.Success [tail head])))) + (#error.Success [tail head])))) (do-template [ ] [(def: #export @@ -242,12 +296,6 @@ [string Text #String "string"] ) -(def: (encode-boolean value) - (-> Bit Text) - (if value - "true" - "false")) - (do-template [
]
   [(def: #export ( test)
      {#.doc (code.text ($_ text/compose "Asks whether a JSON value is a "  "."))}
@@ -271,7 +319,7 @@
          (let [value (
 value)]
            (if (::  = test value)
              (wrap [])
-             (fail ($_ text/compose "Value mismatch: " ( test) "=/=" ( value)))))
+             (fail ($_ text/compose "Value mismatch: " ( test) " =/= " ( value)))))
 
          _
          (fail ($_ text/compose "JSON value is not a "  ".")))))]
@@ -287,117 +335,85 @@
         parser))
 
 (def: #export (array parser)
-  {#.doc "Parses a JSON array, assuming that every element can be parsed the same way."}
+  {#.doc "Parses a JSON array."}
   (All [a] (-> (Reader a) (Reader a)))
   (do p.Monad
     [head any]
     (case head
       (#Array values)
       (case (p.run (row.to-list values) parser)
-        (#e.Error error)
+        (#error.Error error)
         (fail error)
 
-        (#e.Success [remainder output])
+        (#error.Success [remainder output])
         (case remainder
           #.Nil
           (wrap output)
 
           _
-          (fail unconsumed-input-error)))
+          (fail (ex.construct unconsumed-input remainder))))
 
       _
-      (fail "JSON value is not an array."))))
+      (fail (text/compose "JSON value is not an array: " (show-json head))))))
 
 (def: #export (object parser)
-  {#.doc "Parses a JSON object, assuming that every element can be parsed the same way."}
-  (All [a] (-> (Reader a) (Reader (Dictionary Text a))))
+  {#.doc "Parses a JSON object. Use this with the 'field' combinator."}
+  (All [a] (-> (Reader a) (Reader a)))
   (do p.Monad
     [head any]
     (case head
-      (#Object object)
-      (case (do e.Monad
-              []
-              (|> (dict.entries object)
-                  (monad.map @ (function (_ [key val])
-                                 (do @
-                                   [val (run val parser)]
-                                   (wrap [key val]))))
-                  (:: @ map (dict.from-list text.Hash))))
-        (#e.Success table)
-        (wrap table)
-
-        (#e.Error error)
-        (fail error))
+      (#Object kvs)
+      (case (p.run (|> kvs
+                       dictionary.entries
+                       (list/map (function (_ [key value])
+                                   (list (#String key) value)))
+                       list.concat)
+                   parser)
+        (#error.Error error)
+        (fail error)
+
+        (#error.Success [remainder output])
+        (case remainder
+          #.Nil
+          (wrap output)
 
+          _
+          (fail (ex.construct unconsumed-input remainder))))
+      
       _
-      (fail "JSON value is not an array."))))
+      (fail (text/compose "JSON value is not an object: " (show-json head))))))
 
 (def: #export (field field-name parser)
-  {#.doc "Parses a field inside a JSON object."}
+  {#.doc "Parses a field inside a JSON object. Use this inside the 'object' combinator."}
   (All [a] (-> Text (Reader a) (Reader a)))
-  (do p.Monad
-    [head any]
-    (case head
-      (#Object object)
-      (case (dict.get field-name object)
-        (#.Some value)
-        (case (run value parser)
-          (#e.Success output)
-          (function (_ tail)
-            (#e.Success [(#.Cons (#Object (dict.remove field-name object))
-                                 tail)
-                         output]))
-
-          (#e.Error error)
-          (fail error))
-
-        _
-        (fail ($_ text/compose "JSON object does not have field '" field-name "'.")))
+  (function (recur inputs)
+    (case inputs
+      (^ (list& (#String key) value inputs'))
+      (if (text/= key field-name)
+        (case (p.run (list value) parser)
+          (#error.Success [#.Nil output])
+          (#error.Success [inputs' output])
+
+          (#error.Success [inputs'' _])
+          (ex.throw unconsumed-input inputs'')
+
+          (#error.Error error)
+          (#error.Error error))
+        (do error.Monad
+          [[inputs'' output] (recur inputs')]
+          (wrap [(list& (#String key) value inputs'')
+                 output])))
+
+      #.Nil
+      (ex.throw empty-input [])
 
       _
-      (fail "JSON value is not an object."))))
+      (ex.throw unconsumed-input inputs))))
 
 ############################################################
 ############################################################
 ############################################################
 
-(def: (show-null _) (-> Null Text) "null")
-(do-template [  ]
-  [(def:  (->  Text) )]
-
-  [show-boolean Boolean encode-boolean]
-  [show-number  Number (:: number.Codec encode)]
-  [show-string  String text.encode])
-
-(def: (show-array show-json elems)
-  (-> (-> JSON Text) (-> Array Text))
-  ($_ text/compose "["
-      (|> elems (row/map show-json) row.to-list (text.join-with ","))
-      "]"))
-
-(def: (show-object show-json object)
-  (-> (-> JSON Text) (-> Object Text))
-  ($_ text/compose "{"
-      (|> object
-          dict.entries
-          (list/map (function (_ [key value]) ($_ text/compose (show-string key) ":" (show-json value))))
-          (text.join-with ","))
-      "}"))
-
-(def: (show-json json)
-  (-> JSON Text)
-  (case json
-    (^template [ ]
-      ( value)
-      ( value))
-    ([#Null    show-null]
-     [#Boolean show-boolean]
-     [#Number  show-number]
-     [#String  show-string]
-     [#Array   (show-array show-json)]
-     [#Object  (show-object show-json)])
-    ))
-
 (def: space~
   (l.Lexer Text)
   (l.some l.space))
@@ -443,10 +459,10 @@
                        offset (l.many l.decimal)]
                       (wrap ($_ text/compose mark (if signed?' "-" "") offset))))]
     (case (frac/decode ($_ text/compose (if signed? "-" "") digits "." decimals exp))
-      (#e.Error message)
+      (#error.Error message)
       (p.fail message)
       
-      (#e.Success value)
+      (#error.Success value)
       (wrap value))))
 
 (def: escaped~
@@ -503,7 +519,7 @@
        (wrap ( elems))))]
 
   [array~  Array  "[" "]" (json~ [])  row.from-list]
-  [object~ Object "{" "}" (kv~ json~) (dict.from-list text.Hash)]
+  [object~ Object "{" "}" (kv~ json~) (dictionary.from-list text.Hash)]
   )
 
 (def: (json~' _)
-- 
cgit v1.2.3