json
JSON (RFC 8259), written in heff: a value, read from its text
(parse), written back (stringify, as JavaScript’s JSON.stringify
writes it), read into (field, at, str, …) and built (obj,
kv, num, jstr). Import it by a name, import json as JSON, as
its at, int and str are short.
import json as JSON
-- | >>> uri "{\"params\": {\"textDocument\": {\"uri\": \"file:///a.hf\"}}}"-- | Just ("file:///a.hf")uri : String -> Maybe Stringuri body = either (\e -> Nothing) (\j -> JSON.str ["params", "textDocument", "uri"] j) (JSON.parse body)A number without a fraction or an exponent is a JInt, of any size, as
an Int is; one with either, a JFloat, the float nearest it (as C’s
strtod reads it), written so that it is read back the very float —
but an infinity or NaN, which JSON has no number for, written null,
as JavaScript writes them. An object keeps its fields in order, and a
key given twice twice. A string is text: an escape of a surrogate not
in a pair is U+FFFD, as what is not UTF-8 is in a string heff reads.
Json
data Json = JNull | JBool Bool | JInt Int | JFloat Float64 | JString String | JArray (List Json) | JObject (List (String, Json))A JSON value: a number without a fraction or an exponent a JInt,
with either a JFloat; an object its fields in order, a key given
twice twice.
stringify
stringify : Json -> StringThe text of a value, as JavaScript’s JSON.stringify writes it: no
space, an object’s fields in their order.
>>> stringify (JObject [MkPair "a" (JInt 1), MkPair "b" (JArray [JString "x\n", JNull, JBool True])])"{\"a\":1,\"b\":[\"x\\n\",null,true]}"(a float as JavaScript writes it, the fewest digits that read back
as it, but -0.0 with its sign, so that parse reads back the very
float; an infinity or NaN, which JSON has no number for, null, as
JavaScript writes them)
>>> stringify (JArray [JFloat 1.0e300, JFloat (neg 0.0), JFloat (1.0 /. 0.0), JFloat (0.0 /. 0.0)])"[1e+300,-0.0,null,null]">>> stringify (JArray [JFloat 1.5e-300, JFloat 5.0e-324, JFloat 1.0e21, JFloat 1.0e20, JFloat 0.5])"[1.5e-300,5e-324,1e+21,100000000000000000000.0,0.5]"(a string’s text: what is not UTF-8 in it, U+FFFD)
>>> stringify (JString ("é😀" ^ strSlice "😀" 0 1 ^ "\t"))"\"é😀\u{FFFD}\\t\""repr
repr : Json -> StringinstanceDebugJson
instance instanceDebugJson : Debug JsonA value as the source writes it.
>>> debug (JArray [JInt -1, JString "a", JObject [MkPair "b" JNull]] : Json)"JArray [JInt (-1), JString \"a\", JObject [MkPair \"b\" (JNull)]]"instanceShowJson
instance instanceShowJson : Show JsonA value as JSON writes it, stringify.
Miss
data Miss = Expected Int StringParseError
type ParseError = { offset : Int, line : Int, column : Int, expected : String }Where the text is not JSON: the byte at which it is not (offset,
from 0), its line and its column there (from 1, the column counted
in characters, as an editor counts them), and what was expected
there.
instanceDebugParseError
instance instanceDebugParseError : Debug ParseErrorA parse error as it is written.
>>> debug ({ offset = 3, line = 1, column = 4, expected = "a value" } : ParseError)"{ offset = 3, line = 1, column = 4, expected = \"a value\" }"describe
describe : ParseError -> StringWhat a parse error says: line 1, column 4: expected a value.
instanceShowParseError
instance instanceShowParseError : Show ParseError>>> show ({ offset = 3, line = 1, column = 4, expected = "a value" } : ParseError)"line 1, column 4: expected a value"Frame
data Frame = InArray (List Json) | InObject (List (String, Json)) StringStep
data Step = Value Int (List Frame) | Got Json Int (List Frame) | Finished Json | Failed Missparse
parse : String -> Either ParseError JsonThe value a text is, white space around it; or where it is not one. It reads as it goes, a value inside another kept on a list, not on the stack, so that however deep the text it does not overflow.
>>> parse " {\"a\": [1, -2.5e1, \"\\u00e9\\ud83d\\ude00\"], \"b\": null} "Right (JObject [MkPair "a" (JArray [JInt 1, JFloat (-25.0), JString "é😀"]), MkPair "b" (JNull)])>>> parse "[1, 2"Left ({ offset = 5, line = 1, column = 6, expected = "',' or ']'" })>>> either describe (\j -> "parsed") (parse "{\n \"é\": tru\n}")"line 2, column 8: expected a value"field
field : String -> Json -> Maybe JsonA field of an object.
at
at : List String -> Json -> Maybe JsonThe value at a path of fields.
>>> at ["a", "b"] (JObject [MkPair "a" (JObject [MkPair "b" (JInt 1)])])Just (JInt 1)str
str : List String -> Json -> Maybe StringA string at a path; Nothing where it is missing or of another
kind, as each reading at a path is.
>>> either (\e -> Nothing) (\j -> str ["params", "textDocument", "uri"] j) (parse "{\"params\": {\"textDocument\": {\"uri\": \"file:///a.hf\"}}}")Just ("file:///a.hf")int
int : List String -> Json -> Maybe IntAn integer at a path: a float’s, rounded down.
bool
bool : List String -> Json -> Maybe BoolA boolean at a path.
arr
arr : List String -> Json -> Maybe (List Json)An array at a path: its elements.
obj
obj : List (String, Json) -> JsonAn object, of its fields in order.
kv
kv : String -> Json -> (String, Json)A field of an object: its key and its value.
num
num : Int -> JsonAn integer.
jstr
jstr : String -> JsonA string.