JsonParser (Polyglot)¶

In [ ]:
#!import ../../lib/fsharp/Notebooks.dib
#!import ../../lib/fsharp/Testing.dib
In [ ]:
#!import ../../lib/fsharp/Common.fs
#!import Parser.fs
In [ ]:
open Common
open Parser

JsonParser¶

In [ ]:
(*
// --------------------------------
JSON spec from http://www.json.org/
// --------------------------------

The JSON spec is available at [json.org](http://www.json.org/). I'll paraphase it here:

* A `value` can be a `string` or a `number` or a `bool` or `null` or an `object` or an `array`.
  * These structures can be nested.
* A `string` is a sequence of zero or more Unicode characters, wrapped in double quotes, using backslash escapes.
* A `number` is very much like a C or Java number, except that the octal and hexadecimal formats are not used.
* A `boolean` is the literal `true` or `false`
* A `null` is the literal `null`
* An `object` is an unordered set of name/value pairs.
  * An object begins with { (left brace) and ends with } (right brace).
  * Each name is followed by : (colon) and the name/value pairs are separated by , (comma).
* An `array` is an ordered collection of values.
  * An array begins with [ (left bracket) and ends with ] (right bracket).
  * Values are separated by , (comma).
* Whitespace can be inserted between any pair of tokens.
*)
In [ ]:
//// test

let inline parserEqual (expected : ParseResult<'a>) (actual : ParseResult<'a * Input>) =
    match actual, expected with
    | Success (_actual, _), Success _expected ->
        printResult actual
        _actual |> _assertEqual _expected
    | Failure (l1, e1, p1), Failure (l2, e2, p2) when l1 = l2 && e1 = e2 && p1 = p2 ->
        printResult actual
    | _ ->
        printfn $"Actual: {actual}"
        printfn $"Expected: {expected}"
        failwith "Parse failed"
    actual

JValue¶

In [ ]:
type JValue =
    | JString of string
    | JNumber of float
    | JBool   of bool
    | JNull
    | JObject of Map<string, JValue>
    | JArray  of JValue list
In [ ]:
let jValue, jValueRef = createParserForwardedToRef<JValue> ()

jNull¶

In [ ]:
let jNull =
    pstring "null"
    >>% JNull
    <?> "null"
In [ ]:
//// test

jValueRef <|
    choice
        [
            jNull
        ]
In [ ]:
//// test

run jValue "null"
|> parserEqual (Success JNull)
Success (JNull, { lines = [|"null"|]\n position = { line = 0\n column = 4 } })
Item
(JNull, { lines = [|"null"|]\n position = { line = 0\n column = 4 } })
Item1
JNull
Item2
{ lines = [|"null"|]\n position = { line = 0\n column = 4 } }
lines
[ null ]
position
{ line = 0\n column = 4 }
line
0
column
4
JNull
JNull

In [ ]:
//// test

run jNull "nulp"
|> parserEqual (
    Failure (
        "null",
        "Unexpected 'p'",
        { currentLine = "nulp"; line = 0; column = 3 }
    )
)
Failure ("null", "Unexpected 'p'", { currentLine = "nulp"\n line = 0\n column = 3 })
Item1
"null"
Item2
"Unexpected 'p'"
Item3
{ currentLine = "nulp"\n line = 0\n column = 3 }
currentLine
"nulp"
line
0
column
3
Line:0 Col:3 Error parsing null
nulp
   ^Unexpected 'p'

jBool¶

In [ ]:
let jBool =
    let jtrue =
        pstring "true"
        >>% JBool true
    let jfalse =
        pstring "false"
        >>% JBool false

    jtrue <|> jfalse
    <?> "bool"
In [ ]:
//// test

jValueRef <|
    choice
        [
            jNull
            jBool
        ]
In [ ]:
//// test

run jBool "true"
|> parserEqual (Success (JBool true))
Success (JBool true, { lines = [|"true"|]\n position = { line = 0\n column = 4 } })
Item
(JBool true, { lines = [|"true"|]\n position = { line = 0\n column = 4 } })
Item1
JBool true
Item
true
Item2
{ lines = [|"true"|]\n position = { line = 0\n column = 4 } }
lines
[ true ]
position
{ line = 0\n column = 4 }
line
0
column
4
JBool true
JBool true

In [ ]:
//// test

run jBool "false"
|> parserEqual (Success (JBool false))
Success (JBool false, { lines = [|"false"|]\n position = { line = 0\n column = 5 } })
Item
(JBool false, { lines = [|"false"|]\n position = { line = 0\n column = 5 } })
Item1
JBool false
Item
false
Item2
{ lines = [|"false"|]\n position = { line = 0\n column = 5 } }
lines
[ false ]
position
{ line = 0\n column = 5 }
line
0
column
5
JBool false
JBool false

In [ ]:
//// test

run jBool "truX"
|> parserEqual (
    Failure (
        "bool",
        "Unexpected 't'",
        { currentLine = "truX"; line = 0; column = 0 }
    )
)
Failure ("bool", "Unexpected 't'", { currentLine = "truX"\n line = 0\n column = 0 })
Item1
"bool"
Item2
"Unexpected 't'"
Item3
{ currentLine = "truX"\n line = 0\n column = 0 }
currentLine
"truX"
line
0
column
0
Line:0 Col:0 Error parsing bool
truX
^Unexpected 't'

jUnescapedChar¶

In [ ]:
let jUnescapedChar =
    satisfy (fun ch -> ch <> '\\' && ch <> '\"') "char"
In [ ]:
//// test

run jUnescapedChar "a"
|> parserEqual (Success 'a')
Success ('a', { lines = [|"a"|]\n position = { line = 0\n column = 1 } })
Item
(a, { lines = [|"a"|]\n position = { line = 0\n column = 1 } })
Item1
'a'
Item2
{ lines = [|"a"|]\n position = { line = 0\n column = 1 } }
lines
[ a ]
position
{ line = 0\n column = 1 }
line
0
column
1
'a'
'a'

In [ ]:
//// test

run jUnescapedChar "\\"
|> parserEqual (
    Failure (
        "char",
        "Unexpected '\\'",
        { currentLine = "\\"; line = 0; column = 0 }
    )
)
Failure ("char", "Unexpected '\'", { currentLine = "\"\n line = 0\n column = 0 })
Item1
"char"
Item2
"Unexpected '\'"
Item3
{ currentLine = "\"\n line = 0\n column = 0 }
currentLine
"\"
line
0
column
0
Line:0 Col:0 Error parsing char
\
^Unexpected '\'

jEscapedChar¶

In [ ]:
let jEscapedChar =
    [
        ("\\\"",'\"')
        ("\\\\",'\\')
        ("\\/",'/')
        ("\\b",'\b')
        ("\\f",'\f')
        ("\\n",'\n')
        ("\\r",'\r')
        ("\\t",'\t')
    ]
    |> List.map (fun (toMatch, result) ->
        pstring toMatch >>% result
    )
    |> choice
    <?> "escaped char"
In [ ]:
//// test

run jEscapedChar "\\\\"
|> parserEqual (Success '\\')
Success ('\\', { lines = [|"\\"|]\n position = { line = 0\n column = 2 } })
Item
(\, { lines = [|"\\"|]\n position = { line = 0\n column = 2 } })
Item1
'\\'
Item2
{ lines = [|"\\"|]\n position = { line = 0\n column = 2 } }
lines
[ \\ ]
position
{ line = 0\n column = 2 }
line
0
column
2
'\\'
'\\'

In [ ]:
//// test

run jEscapedChar "\\t"
|> parserEqual (Success '\t')
Success ('\009', { lines = [|"\t"|]\n position = { line = 0\n column = 2 } })
Item
( , { lines = [|"\t"|]\n position = { line = 0\n column = 2 } })
Item1
'\009'
Item2
{ lines = [|"\t"|]\n position = { line = 0\n column = 2 } }
lines
[ \t ]
position
{ line = 0\n column = 2 }
line
0
column
2
'\009'
'\009'

In [ ]:
//// test

run jEscapedChar @"\\"
|> parserEqual (Success '\\')
Success ('\\', { lines = [|"\\"|]\n position = { line = 0\n column = 2 } })
Item
(\, { lines = [|"\\"|]\n position = { line = 0\n column = 2 } })
Item1
'\\'
Item2
{ lines = [|"\\"|]\n position = { line = 0\n column = 2 } }
lines
[ \\ ]
position
{ line = 0\n column = 2 }
line
0
column
2
'\\'
'\\'

In [ ]:
//// test

run jEscapedChar @"\n"
|> parserEqual (Success '\n')
Success ('\010', { lines = [|"\n"|]\n position = { line = 0\n column = 2 } })
Item
(\n, { lines = [|"\n"|]\n position = { line = 0\n column = 2 } })
Item1
'\010'
Item2
{ lines = [|"\n"|]\n position = { line = 0\n column = 2 } }
lines
[ \n ]
position
{ line = 0\n column = 2 }
line
0
column
2
'\010'
'\010'

In [ ]:
//// test

run jEscapedChar "a"
|> parserEqual (
    Failure (
        "escaped char",
        "Unexpected 'a'",
        { currentLine = "a"; line = 0; column = 0 }
    )
)
Failure ("escaped char", "Unexpected 'a'", { currentLine = "a"\n line = 0\n column = 0 })
Item1
"escaped char"
Item2
"Unexpected 'a'"
Item3
{ currentLine = "a"\n line = 0\n column = 0 }
currentLine
"a"
line
0
column
0
Line:0 Col:0 Error parsing escaped char
a
^Unexpected 'a'

jUnicodeChar¶

In [ ]:
let jUnicodeChar =
    let backslash = pchar '\\'
    let uChar = pchar 'u'
    let hexdigit = anyOf ([ '0' .. '9' ] @ [ 'A' .. 'F' ] @ [ 'a' .. 'f' ])
    let fourHexDigits = hexdigit .>>. hexdigit .>>. hexdigit .>>. hexdigit

    let inline convertToChar (((h1, h2), h3), h4) =
        let str = $"%c{h1}%c{h2}%c{h3}%c{h4}"
        Int32.Parse (str, Globalization.NumberStyles.HexNumber) |> char

    backslash >>. uChar >>. fourHexDigits
    |>> convertToChar
In [ ]:
//// test

run jUnicodeChar "\\u263A"
|> parserEqual (Success '☺')
Success ('☺', { lines = [|"\u263A"|]\n position = { line = 0\n column = 6 } })
Item
(☺, { lines = [|"\u263A"|]\n position = { line = 0\n column = 6 } })
Item1
'☺'
Item2
{ lines = [|"\u263A"|]\n position = { line = 0\n column = 6 } }
lines
[ \u263A ]
position
{ line = 0\n column = 6 }
line
0
column
6
'☺'
'☺'

jString¶

In [ ]:
let quotedString =
    let quote = pchar '\"' <?> "quote"
    let jchar = jUnescapedChar <|> jEscapedChar <|> jUnicodeChar

    quote >>. manyChars jchar .>> quote
In [ ]:
let jString =
    quotedString
    |>> JString
    <?> "quoted string"
In [ ]:
//// test

jValueRef <|
    choice
        [
            jNull
            jBool
            jString
        ]
In [ ]:
//// test

run jString "\"\""
|> parserEqual (Success (JString ""))
Success (JString "", { lines = [|""""|]\n position = { line = 0\n column = 2 } })
Item
(JString "", { lines = [|""""|]\n position = { line = 0\n column = 2 } })
Item1
JString ""
Item
""
Item2
{ lines = [|""""|]\n position = { line = 0\n column = 2 } }
lines
[ "" ]
position
{ line = 0\n column = 2 }
line
0
column
2
JString ""
JString ""

In [ ]:
//// test

run jString "\"a\""
|> parserEqual (Success (JString "a"))
Success (JString "a", { lines = [|""a""|]\n position = { line = 0\n column = 3 } })
Item
(JString "a", { lines = [|""a""|]\n position = { line = 0\n column = 3 } })
Item1
JString "a"
Item
"a"
Item2
{ lines = [|""a""|]\n position = { line = 0\n column = 3 } }
lines
[ "a" ]
position
{ line = 0\n column = 3 }
line
0
column
3
JString "a"
JString "a"

In [ ]:
//// test

run jString "\"ab\""
|> parserEqual (Success (JString "ab"))
Success (JString "ab", { lines = [|""ab""|]\n position = { line = 0\n column = 4 } })
Item
(JString "ab", { lines = [|""ab""|]\n position = { line = 0\n column = 4 } })
Item1
JString "ab"
Item
"ab"
Item2
{ lines = [|""ab""|]\n position = { line = 0\n column = 4 } }
lines
[ "ab" ]
position
{ line = 0\n column = 4 }
line
0
column
4
JString "ab"
JString "ab"

In [ ]:
//// test

run jString "\"ab\\tde\""
|> parserEqual (Success (JString "ab\tde"))
Success (JString "ab de", { lines = [|""ab\tde""|]\n position = { line = 0\n column = 8 } })
Item
(JString "ab de", { lines = [|""ab\tde""|]\n position = { line = 0\n column = 8 } })
Item1
JString "ab de"
Item
"ab	de"
Item2
{ lines = [|""ab\tde""|]\n position = { line = 0\n column = 8 } }
lines
[ "ab\tde" ]
position
{ line = 0\n column = 8 }
line
0
column
8
JString "ab	de"
JString "ab	de"

In [ ]:
//// test

run jString "\"ab\\u263Ade\""
|> parserEqual (Success (JString "ab☺de"))
Success (JString "ab☺de", { lines = [|""ab\u263Ade""|]\n position = { line = 0\n column = 12 } })
Item
(JString "ab☺de", { lines = [|""ab\u263Ade""|]\n position = { line = 0\n column = 12 } })
Item1
JString "ab☺de"
Item
"ab☺de"
Item2
{ lines = [|""ab\u263Ade""|]\n position = { line = 0\n column = 12 } }
lines
[ "ab\u263Ade" ]
position
{ line = 0\n column = 12 }
line
0
column
12
JString "ab☺de"
JString "ab☺de"

jNumber¶

In [ ]:
let jNumber =
    let optSign = opt (pchar '-')

    let zero = pstring "0"

    let digitOneNine =
        satisfy (fun ch -> Char.IsDigit ch && ch <> '0') "1-9"

    let digit =
        satisfy Char.IsDigit "digit"

    let point = pchar '.'

    let e = pchar 'e' <|> pchar 'E'

    let optPlusMinus = opt (pchar '-' <|> pchar '+')

    let nonZeroInt =
        digitOneNine .>>. manyChars digit
        |>> fun (first, rest) -> string first + rest

    let intPart = zero <|> nonZeroInt

    let fractionPart = point >>. manyChars1 digit

    let exponentPart = e >>. optPlusMinus .>>. manyChars1 digit

    let inline (|>?) opt f =
        match opt with
        | None -> ""
        | Some x -> f x

    let inline convertToJNumber (((optSign, intPart), fractionPart), expPart) =
        let signStr =
            optSign
            |>? string

        let fractionPartStr =
            fractionPart
            |>? (fun digits -> "." + digits)

        let expPartStr =
            expPart
            |>? fun (optSign, digits) ->
                let sign = optSign |>? string
                "e" + sign + digits

        (signStr + intPart + fractionPartStr + expPartStr)
        |> float
        |> JNumber

    optSign .>>. intPart .>>. opt fractionPart .>>. opt exponentPart
    |>> convertToJNumber
    <?> "number"
In [ ]:
//// test

jValueRef <|
    choice
        [
            jNull
            jBool
            jString
            jNumber
        ]
In [ ]:
//// test

run jNumber "123"
|> parserEqual (Success (JNumber 123.0))
Success (JNumber 123.0, { lines = [|"123"|]\n position = { line = 0\n column = 3 } })
Item
(JNumber 123.0, { lines = [|"123"|]\n position = { line = 0\n column = 3 } })
Item1
JNumber 123.0
Item
123.0
Item2
{ lines = [|"123"|]\n position = { line = 0\n column = 3 } }
lines
[ 123 ]
position
{ line = 0\n column = 3 }
line
0
column
3
JNumber 123.0
JNumber 123.0

In [ ]:
//// test

run jNumber "-123"
|> parserEqual (Success (JNumber -123.0))
Success (JNumber -123.0, { lines = [|"-123"|]\n position = { line = 0\n column = 4 } })
Item
(JNumber -123.0, { lines = [|"-123"|]\n position = { line = 0\n column = 4 } })
Item1
JNumber -123.0
Item
-123.0
Item2
{ lines = [|"-123"|]\n position = { line = 0\n column = 4 } }
lines
[ -123 ]
position
{ line = 0\n column = 4 }
line
0
column
4
JNumber -123.0
JNumber -123.0

In [ ]:
//// test

run jNumber "123.4"
|> parserEqual (Success (JNumber 123.4))
Success (JNumber 123.4, { lines = [|"123.4"|]\n position = { line = 0\n column = 5 } })
Item
(JNumber 123.4, { lines = [|"123.4"|]\n position = { line = 0\n column = 5 } })
Item1
JNumber 123.4
Item
123.4
Item2
{ lines = [|"123.4"|]\n position = { line = 0\n column = 5 } }
lines
[ 123.4 ]
position
{ line = 0\n column = 5 }
line
0
column
5
JNumber 123.4
JNumber 123.4

In [ ]:
//// test

run jNumber "-123."
|> parserEqual (Success (JNumber -123.0))
Success (JNumber -123.0, { lines = [|"-123."|]\n position = { line = 0\n column = 4 } })
Item
(JNumber -123.0, { lines = [|"-123."|]\n position = { line = 0\n column = 4 } })
Item1
JNumber -123.0
Item
-123.0
Item2
{ lines = [|"-123."|]\n position = { line = 0\n column = 4 } }
lines
[ -123. ]
position
{ line = 0\n column = 4 }
line
0
column
4
JNumber -123.0
JNumber -123.0

In [ ]:
//// test

run jNumber "00.1"
|> parserEqual (Success (JNumber 0.0))
Success (JNumber 0.0, { lines = [|"00.1"|]\n position = { line = 0\n column = 1 } })
Item
(JNumber 0.0, { lines = [|"00.1"|]\n position = { line = 0\n column = 1 } })
Item1
JNumber 0.0
Item
0.0
Item2
{ lines = [|"00.1"|]\n position = { line = 0\n column = 1 } }
lines
[ 00.1 ]
position
{ line = 0\n column = 1 }
line
0
column
1
JNumber 0.0
JNumber 0.0

In [ ]:
//// test

let jNumber_ = jNumber .>> spaces1
In [ ]:
//// test

run jNumber_ "123"
|> parserEqual (Success (JNumber 123.0))
Success (JNumber 123.0, { lines = [|"123"|]\n position = { line = 1\n column = 0 } })
Item
(JNumber 123.0, { lines = [|"123"|]\n position = { line = 1\n column = 0 } })
Item1
JNumber 123.0
Item
123.0
Item2
{ lines = [|"123"|]\n position = { line = 1\n column = 0 } }
lines
[ 123 ]
position
{ line = 1\n column = 0 }
line
1
column
0
JNumber 123.0
JNumber 123.0

In [ ]:
//// test

run jNumber_ "-123"
|> parserEqual (Success (JNumber -123.0))
Success (JNumber -123.0, { lines = [|"-123"|]\n position = { line = 1\n column = 0 } })
Item
(JNumber -123.0, { lines = [|"-123"|]\n position = { line = 1\n column = 0 } })
Item1
JNumber -123.0
Item
-123.0
Item2
{ lines = [|"-123"|]\n position = { line = 1\n column = 0 } }
lines
[ -123 ]
position
{ line = 1\n column = 0 }
line
1
column
0
JNumber -123.0
JNumber -123.0

In [ ]:
//// test

run jNumber_ "-123."
|> parserEqual (
    Failure (
        "number andThen many1 whitespace",
        "Unexpected '.'",
        { currentLine = "-123."; line = 0; column = 4 }
    )
)
Failure\n ("number andThen many1 whitespace", "Unexpected '.'", { currentLine = "-123."\n line = 0\n column = 4 })
Item1
"number andThen many1 whitespace"
Item2
"Unexpected '.'"
Item3
{ currentLine = "-123."\n line = 0\n column = 4 }
currentLine
"-123."
line
0
column
4
Line:0 Col:4 Error parsing number andThen many1 whitespace
-123.
    ^Unexpected '.'
In [ ]:
//// test

run jNumber_ "123.4"
|> parserEqual (Success (JNumber 123.4))
Success (JNumber 123.4, { lines = [|"123.4"|]\n position = { line = 1\n column = 0 } })
Item
(JNumber 123.4, { lines = [|"123.4"|]\n position = { line = 1\n column = 0 } })
Item1
JNumber 123.4
Item
123.4
Item2
{ lines = [|"123.4"|]\n position = { line = 1\n column = 0 } }
lines
[ 123.4 ]
position
{ line = 1\n column = 0 }
line
1
column
0
JNumber 123.4
JNumber 123.4

In [ ]:
//// test

run jNumber_ "00.4"
|> parserEqual (
    Failure (
        "number andThen many1 whitespace",
        "Unexpected '0'",
        { currentLine = "00.4"; line = 0; column = 1 }
    )
)
Failure\n ("number andThen many1 whitespace", "Unexpected '0'", { currentLine = "00.4"\n line = 0\n column = 1 })
Item1
"number andThen many1 whitespace"
Item2
"Unexpected '0'"
Item3
{ currentLine = "00.4"\n line = 0\n column = 1 }
currentLine
"00.4"
line
0
column
1
Line:0 Col:1 Error parsing number andThen many1 whitespace
00.4
 ^Unexpected '0'
In [ ]:
//// test

run jNumber_ "123e4"
|> parserEqual (Success (JNumber 1230000.0))
Success (JNumber 1230000.0, { lines = [|"123e4"|]\n position = { line = 1\n column = 0 } })
Item
(JNumber 1230000.0, { lines = [|"123e4"|]\n position = { line = 1\n column = 0 } })
Item1
JNumber 1230000.0
Item
1230000.0
Item2
{ lines = [|"123e4"|]\n position = { line = 1\n column = 0 } }
lines
[ 123e4 ]
position
{ line = 1\n column = 0 }
line
1
column
0
JNumber 1230000.0
JNumber 1230000.0

In [ ]:
//// test

run jNumber_ "123.4e5"
|> parserEqual (Success (JNumber 12340000.0))
Success (JNumber 12340000.0, { lines = [|"123.4e5"|]\n position = { line = 1\n column = 0 } })
Item
(JNumber 12340000.0, { lines = [|"123.4e5"|]\n position = { line = 1\n column = 0 } })
Item1
JNumber 12340000.0
Item
12340000.0
Item2
{ lines = [|"123.4e5"|]\n position = { line = 1\n column = 0 } }
lines
[ 123.4e5 ]
position
{ line = 1\n column = 0 }
line
1
column
0
JNumber 12340000.0
JNumber 12340000.0

In [ ]:
//// test

run jNumber_ "123.4e-5"
|> parserEqual (Success (JNumber 0.001234))
Success (JNumber 0.001234, { lines = [|"123.4e-5"|]\n position = { line = 1\n column = 0 } })
Item
(JNumber 0.001234, { lines = [|"123.4e-5"|]\n position = { line = 1\n column = 0 } })
Item1
JNumber 0.001234
Item
0.001234
Item2
{ lines = [|"123.4e-5"|]\n position = { line = 1\n column = 0 } }
lines
[ 123.4e-5 ]
position
{ line = 1\n column = 0 }
line
1
column
0
JNumber 0.001234
JNumber 0.001234

jArray¶

In [ ]:
let jArray =
    let left = pchar '[' .>> spaces
    let right = pchar ']' .>> spaces
    let comma = pchar ',' .>> spaces
    let value = jValue .>> spaces

    let values = sepBy value comma

    between left values right
    |>> JArray
    <?> "array"
In [ ]:
//// test

jValueRef <|
    choice
        [
            jNull
            jBool
            jString
            jNumber
            jArray
        ]
In [ ]:
//// test

run jArray "[ 1, 2 ]"
|> parserEqual (Success (JArray [ JNumber 1.0; JNumber 2.0 ]))
Success (JArray [JNumber 1.0; JNumber 2.0], { lines = [|"[ 1, 2 ]"|]\n position = { line = 1\n column = 0 } })
Item
(JArray [JNumber 1.0; JNumber 2.0], { lines = [|"[ 1, 2 ]"|]\n position = { line = 1\n column = 0 } })
Item1
JArray [JNumber 1.0; JNumber 2.0]
Item
indexvalue
0
JNumber 1.0
Item
1.0
1
JNumber 2.0
Item
2.0
Item2
{ lines = [|"[ 1, 2 ]"|]\n position = { line = 1\n column = 0 } }
lines
[ [ 1, 2 ] ]
position
{ line = 1\n column = 0 }
line
1
column
0
JArray [JNumber 1.0; JNumber 2.0]
JArray [JNumber 1.0; JNumber 2.0]

In [ ]:
//// test

run jArray "[ 1, 2, ]"
|> parserEqual (
    Failure (
        "array",
        "Unexpected ','",
        { currentLine = "[ 1, 2, ]"; line = 0; column = 6 }
    )
)
Failure ("array", "Unexpected ','", { currentLine = "[ 1, 2, ]"\n line = 0\n column = 6 })
Item1
"array"
Item2
"Unexpected ','"
Item3
{ currentLine = "[ 1, 2, ]"\n line = 0\n column = 6 }
currentLine
"[ 1, 2, ]"
line
0
column
6
Line:0 Col:6 Error parsing array
[ 1, 2, ]
      ^Unexpected ','

jObject¶

In [ ]:
let jObject =
    let left = spaces >>. pchar '{' .>> spaces
    let right = pchar '}' .>> spaces
    let colon = pchar ':' .>> spaces
    let comma = pchar ',' .>> spaces
    let key = quotedString .>> spaces
    let value = jValue .>> spaces

    let keyValue = (key .>> colon) .>>. value
    let keyValues = sepBy keyValue comma

    between left keyValues right
    |>> Map.ofList
    |>> JObject
    <?> "object"
In [ ]:
jValueRef <|
    choice
        [
            jNull
            jBool
            jString
            jNumber
            jArray
            jObject
        ]
In [ ]:
//// test

run jObject """{ "a":1, "b"  :  2 }"""
|> parserEqual (
    Success (
        JObject (
            Map.ofList [
                "a", JNumber 1.0
                "b", JNumber 2.0
            ]
        )
    )
)
Success\n (JObject (map [("a", JNumber 1.0); ("b", JNumber 2.0)]),\n { lines = [|"{ "a":1, "b" : 2 }"|]\n position = { line = 1\n column = 0 } })
Item
(JObject (map [("a", JNumber 1.0); ("b", JNumber 2.0)]), { lines = [|"{ "a":1, "b" : 2 }"|]\n position = { line = 1\n column = 0 } })
Item1
JObject (map [("a", JNumber 1.0); ("b", JNumber 2.0)])
Item
keyvalue
"a"
JNumber 1.0
Item
1.0
"b"
JNumber 2.0
Item
2.0
Item2
{ lines = [|"{ "a":1, "b" : 2 }"|]\n position = { line = 1\n column = 0 } }
lines
[ { "a":1, "b"  :  2 } ]
position
{ line = 1\n column = 0 }
line
1
column
0
JObject (map [("a", JNumber 1.0); ("b", JNumber 2.0)])
JObject (map [("a", JNumber 1.0); ("b", JNumber 2.0)])

In [ ]:
//// test

run jObject """{ "a":1, "b"  :  2, }"""
|> parserEqual (
    Failure (
        "object",
        "Unexpected ','",
        { currentLine = """{ "a":1, "b"  :  2, }"""; line = 0; column = 18 }
    )
)
Failure ("object", "Unexpected ','", { currentLine = "{ "a":1, "b" : 2, }"\n line = 0\n column = 18 })
Item1
"object"
Item2
"Unexpected ','"
Item3
{ currentLine = "{ "a":1, "b" : 2, }"\n line = 0\n column = 18 }
currentLine
"{ "a":1, "b"  :  2, }"
line
0
column
18
Line:0 Col:18 Error parsing object
{ "a":1, "b"  :  2, }
                  ^Unexpected ','

jValue¶

In [ ]:
//// test

let example1 = """{
    "name" : "Scott",
    "isMale" : true,
    "bday" : {"year":2001, "month":12, "day":25 },
    "favouriteColors" : ["blue", "green"],
    "emptyArray" : [],
    "emptyObject" : {}
}"""
run jValue example1
|> parserEqual (
    Success (
        JObject (
            Map.ofList [
                "name", JString "Scott"
                "isMale", JBool true
                "bday", JObject (
                    Map.ofList [
                        "year", JNumber 2001.0
                        "month", JNumber 12.0
                        "day", JNumber 25.0
                    ]
                )
                "favouriteColors", JArray [ JString "blue"; JString "green" ]
                "emptyArray", JArray []
                "emptyObject", JObject Map.empty
            ]
        )
    )
)
Success\n (JObject\n (map\n [("bday",\n JObject\n (map\n [("day", JNumber 25.0); ("month", JNumber 12.0);\n ("year", JNumber 2001.0)])); ("emptyArray", JArray []);\n ("emptyObject", JObject (map []));\n ("favouriteColors", ...
Item
(JObject\n (map\n [("bday",\n JObject\n (map\n [("day", JNumber 25.0); ("month", JNumber 12.0);\n ("year", JNumber 2001.0)])); ("emptyArray", JArray []);\n ("emptyObject", JObject (map []));\n ("favouriteColors", JArray [JString "blue"; JString "gr...
Item1
JObject\n (map\n [("bday",\n JObject\n (map\n [("day", JNumber 25.0); ("month", JNumber 12.0);\n ("year", JNumber 2001.0)])); ("emptyArray", JArray []);\n ("emptyObject", JObject (map []));\n ("favouriteColors", JArray [JString "blue"; JString "gre...
Item
keytypevalue
"bday"
FSI_0031+JValue+JObject
JObject\n (map\n [("day", JNumber 25.0); ("month", JNumber 12.0); ("year", JNumber 2001.0)])
Item
keyvalue
"day"
JNumber 25.0
"month"
JNumber 12.0
"year"
JNumber 2001.0
"emptyArray"
FSI_0031+JValue+JArray
JArray []
Item(empty)
"emptyObject"
FSI_0031+JValue+JObject
JObject (map [])
Item(empty)
"favouriteColors"
FSI_0031+JValue+JArray
JArray [JString "blue"; JString "green"]
Item
indexvalue
0JString "blue"
1JString "green"
"isMale"
FSI_0031+JValue+JBool
JBool true
Item
true
"name"
FSI_0031+JValue+JString
JString "Scott"
Item
"Scott"
Item2
{ lines =\n [|"{"; " "name" : "Scott","; " "isMale" : true,";\n " "bday" : {"year":2001, "month":12, "day":25 },";\n " "favouriteColors" : ["blue", "green"],"; " "emptyArray" : [],";\n " "emptyObject" : {}"; "}"|]\n position = { line = 8\n column = 0 } ...
lines
[ {,     "name" : "Scott",,     "isMale" : true,,     "bday" : {"year":2001, "month":12, "day":25 },,     "favouriteColors" : ["blue", "green"],,     "emptyArray" : [],,     "emptyObject" : {}, } ]
position
{ line = 8\n column = 0 }
line
8
column
0
JObject
  (map
     [("bday",
       JObject
         (map
            [("day", JNumber 25.0); ("month", JNumber 12.0);
             ("year", JNumber 2001.0)])); ("emptyArray", JArray []);
      ("emptyObject", JObject (map []));
      ("favouriteColors", JArray [JString "blue"; JString "green"]);
      ("isMale", JBool true); ("name", JString "Scott")])
JObject
  (map
     [("bday", JObject (map [("day", JNumber 25.0); ("month", JNumber 12.0); ("year", JNumber 2001.0)]));
      ("emptyArray", JArray []); ("emptyObject", JObject (map []));
      ("favouriteColors", JArray [JString "blue"; JString "green"]); ("isMale", JBool true); ("name", JString "Scott")])

In [ ]:
//// test

let example2 = """{"widget": {
    "debug": "on",
    "window": {
        "title": "Sample Konfabulator Widget",
        "name": "main_window",
        "width": 500,
        "height": 500
    },
    "image": {
        "src": "Images/Sun.png",
        "name": "sun1",
        "hOffset": 250,
        "vOffset": 250,
        "alignment": "center"
    },
    "text": {
        "data": "Click Here",
        "size": 36,
        "style": "bold",
        "name": "text1",
        "hOffset": 250,
        "vOffset": 100,
        "alignment": "center",
        "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
    }
}}"""

run jValue example2
|> parserEqual (
    Success (
        JObject (
            Map.ofList [
                "widget", JObject (
                    Map.ofList [
                        "debug", JString "on"
                        "window", JObject (
                            Map.ofList [
                                "title", JString "Sample Konfabulator Widget"
                                "name", JString "main_window"
                                "width", JNumber 500.0
                                "height", JNumber 500.0
                            ]
                        )
                        "image", JObject (
                            Map.ofList [
                                "src", JString "Images/Sun.png"
                                "name", JString "sun1"
                                "hOffset", JNumber 250.0
                                "vOffset", JNumber 250.0
                                "alignment", JString "center"
                            ]
                        )
                        "text", JObject (
                            Map.ofList [
                                "data", JString "Click Here"
                                "size", JNumber 36.0
                                "style", JString "bold"
                                "name", JString "text1"
                                "hOffset", JNumber 250.0
                                "vOffset", JNumber 100.0
                                "alignment", JString "center"
                                "onMouseUp", JString "sun1.opacity = (sun1.opacity / 100) * 90;"
                            ]
                        )
                    ]
                )
            ]
        )
    )
)
Success\n (JObject\n (map\n [("widget",\n JObject\n (map\n [("debug", JString "on");\n ("image",\n JObject\n (map\n [("alignment", JString "center");\n ("hOffset"...
Item
(JObject\n (map\n [("widget",\n JObject\n (map\n [("debug", JString "on");\n ("image",\n JObject\n (map\n [("alignment", JString "center"); ("hOffset", JNumber 250.0);\n ("name", JString "sun1"...
Item1
JObject\n (map\n [("widget",\n JObject\n (map\n [("debug", JString "on");\n ("image",\n JObject\n (map\n [("alignment", JString "center"); ("hOffset", JNumber 250.0);\n ("name", JString "sun1")...
Item
keyvalue
"widget"
JObject\n (map\n [("debug", JString "on");\n ("image",\n JObject\n (map\n [("alignment", JString "center"); ("hOffset", JNumber 250.0);\n ("name", JString "sun1"); ("src", JString "Images/Sun.png");\n ("vOffset", JNumber 250.0)]));\n ("...
Item
keytypevalue
"debug"
FSI_0031+JValue+JStringJString "on"
"image"
FSI_0031+JValue+JObjectJObject (map [("alignment", JString "center"); ("hOffset", JNumber 250.0); ("name", JString "sun1"); ("src", JString "Images/Sun.png"); ("vOffset", JNumber 250.0)])
"text"
FSI_0031+JValue+JObjectJObject (map [("alignment", JString "center"); ("data", JString "Click Here"); ("hOffset", JNumber 250.0); ("name", JString "text1"); ("onMouseUp", JString "sun1.opacity = (sun1.opacity / 100) * 90;"); ("size", JNumber 36.0); ("style", JString "bold"); ("vOffset", JNumber 100.0)])
"window"
FSI_0031+JValue+JObjectJObject (map [("height", JNumber 500.0); ("name", JString "main_window"); ("title", JString "Sample Konfabulator Widget"); ("width", JNumber 500.0)])
Item2
{ lines =\n [|"{"widget": {"; " "debug": "on","; " "window": {";\n " "title": "Sample Konfabulator Widget",";\n " "name": "main_window","; " "width": 500,";\n " "height": 500"; " },"; " "image": {";\n " "src": "Images/Sun.png","; " ...
lines
[ {"widget": {,     "debug": "on",,     "window": {,         "title": "Sample Konfabulator Widget",,         "name": "main_window",,         "width": 500,,         "height": 500,     },,     "image": {,         "src": "Images/Sun.png",,         "name": "sun1",,         "hOffset": 250,,         "vOffset": 250,,         "alignment": "center",     },,     "text": {,         "data": "Click Here",,         "size": 36,,         "style": "bold",,         "name": "text1",,         "hOffset": 250,,         "vOffset": 100,,         "alignment": "center",,         "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;",     }, }} ]
position
{ line = 26\n column = 0 }
line
26
column
0
JObject
  (map
     [("widget",
       JObject
         (map
            [("debug", JString "on");
             ("image",
              JObject
                (map
                   [("alignment", JString "center"); ("hOffset", JNumber 250.0);
                    ("name", JString "sun1"); ("src", JString "Images/Sun.png");
                    ("vOffset", JNumber 250.0)]));
             ("text",
              JObject
                (map
                   [("alignment", JString "center");
                    ("data", JString "Click Here"); ("hOffset", JNumber 250.0);
                    ("name", JString "text1");
                    ("onMouseUp",
                     JString "sun1.opacity = (sun1.opacity / 100) * 90;");
                    ("size", JNumber 36.0); ("style", JString "bold");
                    ("vOffset", JNumber 100.0)]));
             ("window",
              JObject
                (map
                   [("height", JNumber 500.0); ("name", JString "main_window");
                    ("title", JString "Sample Konfabulator Widget");
                    ("width", JNumber 500.0)]))]))])
JObject
  (map
     [("widget",
       JObject
         (map
            [("debug", JString "on");
             ("image",
              JObject
                (map
                   [("alignment", JString "center"); ("hOffset", JNumber 250.0); ("name", JString "sun1");
                    ("src", JString "Images/Sun.png"); ("vOffset", JNumber 250.0)]));
             ("text",
              JObject
                (map
                   [("alignment", JString "center"); ("data", JString "Click Here"); ("hOffset", JNumber 250.0);
                    ("name", JString "text1"); ("onMouseUp", JString "sun1.opacity = (sun1.opacity / 100) * 90;");
                    ("size", JNumber 36.0); ("style", JString "bold"); ("vOffset", JNumber 100.0)]));
             ("window",
              JObject
                (map
                   [("height", JNumber 500.0); ("name", JString "main_window");
                    ("title", JString "Sample Konfabulator Widget"); ("width", JNumber 500.0)]))]))])

In [ ]:
//// test

let example3 = """{
  "string": "Hello, \"World\"!",
  "escapedString": "This string contains \\/\\\\\\b\\f\\n\\r\\t\\\"\\'",
  "number": 42,
  "scientificNumber": 3.14e-10,
  "boolean": true,
  "nullValue": null,
  "array": [1, 2, 3, 4, 5],
  "unicodeString1": "프리마",
  "unicodeString2": "\u0048\u0065\u006C\u006C\u006F, \u0022\u0057\u006F\u0072\u006C\u0064\u0022!",
  "specialCharacters": "!@#$%^&*()",
  "emptyArray": [],
  "emptyObject": {},
  "nestedArrays": [[1, 2, 3], [4, 5, 6]],
  "object": {
    "nestedString": "Nested Value",
    "nestedNumber": 3.14,
    "nestedBoolean": false,
    "nestedNull": null,
    "nestedArray": ["a", "b", "c"],
    "nestedObject": {
      "nestedProperty": "Nested Object Value"
    }
  },
  "nestedObjects": [
    {"name": "Alice", "age": 25},
    {"name": "Bob", "age": 30}
  ]
}"""
run jValue example3
|> parserEqual (
    Success (
        JObject (
            Map.ofList [
                "string", JString @"Hello, ""World""!"
                "escapedString", JString @"This string contains \/\\\b\f\n\r\t\""\'"
                "number", JNumber 42.0
                "scientificNumber", JNumber 3.14e-10
                "boolean", JBool true
                "nullValue", JNull
                "array", JArray [
                    JNumber 1.0; JNumber 2.0; JNumber 3.0; JNumber 4.0; JNumber 5.0
                ]
                "unicodeString1", JString "프리마"
                "unicodeString2", JString @"Hello, ""World""!"
                "specialCharacters", JString "!@#$%^&*()"
                "emptyArray", JArray []
                "emptyObject", JObject Map.empty
                "nestedArrays", JArray [
                    JArray [ JNumber 1.0; JNumber 2.0; JNumber 3.0 ]
                    JArray [ JNumber 4.0; JNumber 5.0; JNumber 6.0 ]
                ]
                "object", JObject (
                    Map.ofList [
                        "nestedString", JString "Nested Value"
                        "nestedNumber", JNumber 3.14
                        "nestedBoolean", JBool false
                        "nestedNull", JNull
                        "nestedArray", JArray [JString "a"; JString "b"; JString "c"]
                        "nestedObject", JObject (
                            Map.ofList [
                                "nestedProperty", JString "Nested Object Value"
                            ]
                        )
                    ]
                )
                "nestedObjects", JArray [
                  JObject (Map.ofList [ "name", JString "Alice"; "age", JNumber 25.0 ])
                  JObject (Map.ofList [ "name", JString "Bob"; "age", JNumber 30.0 ])
                ]
            ]
        )
    )
)
Success\n (JObject\n (map\n [("array",\n JArray\n [JNumber 1.0; JNumber 2.0; JNumber 3.0; JNumber 4.0; JNumber 5.0]);\n ("boolean", JBool true); ("emptyArray", JArray []);\n ("emptyObject", JObject (map []));\n ("escapedString", JString "This s...
Item
(JObject\n (map\n [("array",\n JArray [JNumber 1.0; JNumber 2.0; JNumber 3.0; JNumber 4.0; JNumber 5.0]);\n ("boolean", JBool true); ("emptyArray", JArray []);\n ("emptyObject", JObject (map []));\n ("escapedString", JString "This string contains \/\\\b\f\n\r\t\"\'");\n ...
Item1
JObject\n (map\n [("array",\n JArray [JNumber 1.0; JNumber 2.0; JNumber 3.0; JNumber 4.0; JNumber 5.0]);\n ("boolean", JBool true); ("emptyArray", JArray []);\n ("emptyObject", JObject (map []));\n ("escapedString", JString "This string contains \/\\\b\f\n\r\t\"\'");\n ...
Item
keytypevalue
"array"
FSI_0031+JValue+JArray
JArray [JNumber 1.0; JNumber 2.0; JNumber 3.0; JNumber 4.0; JNumber 5.0]
Item
indexvalue
0JNumber 1.0
1JNumber 2.0
2JNumber 3.0
3JNumber 4.0
4JNumber 5.0
"boolean"
FSI_0031+JValue+JBool
JBool true
Item
true
"emptyArray"
FSI_0031+JValue+JArray
JArray []
Item(empty)
"emptyObject"
FSI_0031+JValue+JObject
JObject (map [])
Item(empty)
"escapedString"
FSI_0031+JValue+JString
JString "This string contains \/\\\b\f\n\r\t\"\'"
Item
"This string contains \/\\\b\f\n\r\t\"\'"
"nestedArrays"
FSI_0031+JValue+JArray
JArray\n [JArray [JNumber 1.0; JNumber 2.0; JNumber 3.0];\n JArray [JNumber 4.0; JNumber 5.0; JNumber 6.0]]
Item
indexvalue
0JArray [JNumber 1.0; JNumber 2.0; JNumber 3.0]
1JArray [JNumber 4.0; JNumber 5.0; JNumber 6.0]
"nestedObjects"
FSI_0031+JValue+JArray
JArray\n [JObject (map [("age", JNumber 25.0); ("name", JString "Alice")]);\n JObject (map [("age", JNumber 30.0); ("name", JString "Bob")])]
Item
indexvalue
0JObject (map [("age", JNumber 25.0); ("name", JString "Alice")])
1JObject (map [("age", JNumber 30.0); ("name", JString "Bob")])
"nullValue"
FSI_0031+JValue
JNull
"number"
FSI_0031+JValue+JNumber
JNumber 42.0
Item
42.0
"object"
FSI_0031+JValue+JObject
JObject\n (map\n [("nestedArray", JArray [JString "a"; JString "b"; JString "c"]);\n ("nestedBoolean", JBool false); ("nestedNull", JNull);\n ("nestedNumber", JNumber 3.14);\n ("nestedObject",\n JObject (map [("nestedProperty", JString "Nested Object Value")]));\n ("ne...
Item
keytypevalue
"nestedArray"
FSI_0031+JValue+JArrayJArray [JString "a"; JString "b"; JString "c"]
"nestedBoolean"
FSI_0031+JValue+JBoolJBool false
"nestedNull"
FSI_0031+JValueJNull
"nestedNumber"
FSI_0031+JValue+JNumberJNumber 3.14
"nestedObject"
FSI_0031+JValue+JObjectJObject (map [("nestedProperty", JString "Nested Object Value")])
"nestedString"
FSI_0031+JValue+JStringJString "Nested Value"
"scientificNumber"
FSI_0031+JValue+JNumber
JNumber 3.14e-10
Item
3.14e-10
"specialCharacters"
FSI_0031+JValue+JString
JString "!@#$%^&*()"
Item
"!@#$%^&*()"
"string"
FSI_0031+JValue+JString
JString "Hello, "World"!"
Item
"Hello, "World"!"
"unicodeString1"
FSI_0031+JValue+JString
JString "프리마"
Item
"프리마"
"unicodeString2"
FSI_0031+JValue+JString
JString "Hello, "World"!"
Item
"Hello, "World"!"
Item2
{ lines =\n [|"{"; " "string": "Hello, \"World\"!",";\n " "escapedString": "This string contains \\/\\\\\\b\\f\\n\\r\\t\\\"\\'",";\n " "number": 42,"; " "scientificNumber": 3.14e-10,"; " "boolean": true,";\n " "nullValue": null,"; " "array": [1, 2, 3, 4, 5],";\n " "unicodeS...
lines
[ {,   "string": "Hello, \"World\"!",,   "escapedString": "This string contains \\/\\\\\\b\\f\\n\\r\\t\\\"\\'",,   "number": 42,,   "scientificNumber": 3.14e-10,,   "boolean": true,,   "nullValue": null,,   "array": [1, 2, 3, 4, 5],,   "unicodeString1": "프리마",,   "unicodeString2": "\u0048\u0065\u006C\u006C\u006F, \u0022\u0057\u006F\u0072\u006C\u0064\u0022!",,   "specialCharacters": "!@#$%^&*()",,   "emptyArray": [],,   "emptyObject": {},,   "nestedArrays": [[1, 2, 3], [4, 5, 6]],,   "object": {,     "nestedString": "Nested Value",,     "nestedNumber": 3.14,,     "nestedBoolean": false,,     "nestedNull": null,,     "nestedArray": ["a", "b", "c"],,     "nestedObject": {,       "nestedProperty": "Nested Object Value",     },   },,   "nestedObjects": [,     {"name": "Alice", "age": 25},,     {"name": "Bob", "age": 30},   ], } ]
position
{ line = 29\n column = 0 }
line
29
column
0
JObject
  (map
     [("array",
       JArray [JNumber 1.0; JNumber 2.0; JNumber 3.0; JNumber 4.0; JNumber 5.0]);
      ("boolean", JBool true); ("emptyArray", JArray []);
      ("emptyObject", JObject (map []));
      ("escapedString", JString "This string contains \/\\\b\f\n\r\t\"\'");
      ("nestedArrays",
       JArray
         [JArray [JNumber 1.0; JNumber 2.0; JNumber 3.0];
          JArray [JNumber 4.0; JNumber 5.0; JNumber 6.0]]);
      ("nestedObjects",
       JArray
         [JObject (map [("age", JNumber 25.0); ("name", JString "Alice")]);
          JObject (map [("age", JNumber 30.0); ("name", JString "Bob")])]);
      ("nullValue", JNull); ("number", JNumber 42.0); ...])
JObject
  (map
     [("array", JArray [JNumber 1.0; JNumber 2.0; JNumber 3.0; JNumber 4.0; JNumber 5.0]); ("boolean", JBool true);
      ("emptyArray", JArray []); ("emptyObject", JObject (map []));
      ("escapedString", JString "This string contains \/\\\b\f\n\r\t\"\'");
      ("nestedArrays",
       JArray [JArray [JNumber 1.0; JNumber 2.0; JNumber 3.0]; JArray [JNumber 4.0; JNumber 5.0; JNumber 6.0]]);
      ("nestedObjects",
       JArray
         [JObject (map [("age", JNumber 25.0); ("name", JString "Alice")]);
          JObject (map [("age", JNumber 30.0); ("name", JString "Bob")])]); ("nullValue", JNull);
      ("number", JNumber 42.0); ...])