src/prim.bend

bend-toon-codec@0.1.0.0 · 112 lines · 16 definitions

1import Base
2import bend-toon-codec@0.1.0.0/src/json.bend as J
3import bend-toon-codec@0.1.0.0/src/num.bend as N
4import bend-toon-codec@0.1.0.0/src/head.bend as Hd
5import bend-toon-codec@0.1.0.0/src/text.bend as T
6
7# Primitive tokens (spec section 4 and appendix B.4): quoted string, true,
8# false, null, number, or bare string.
9
10def quoted.rest(+ok: Bool, +v: String) -> Result<&2, &2, String, J.Json>:
11  match ok:
12    case True{}:
13      Done{J.JStr{v}}
14    case False{}:
15      Fail{"unexpected characters after the closing quote"}
16
17# The closing quote must end the token.
18def quoted.end(+r: Result<&2, &2, String, T.Cut>) -> Result<&2, &2, String, J.Json>:
19  match r:
20    case Fail{e}:
21      Fail{e}
22    case Done{T.Cut{v, rest}}:
23      quoted.rest(String.is_empty(rest), v)
24
25# A forbidden leading zero: an integer part of two or more digits starting
26# with 0 (`05`, `-007`), which stays a string. `0`, `0.5`, `0e1` are numbers.
27def lead0.ip(+ip: String) -> Bool:
28  Hd.lead(ip, 48) && Hd.at_least(ip, 2n)
29
30def lead0(+m: Maybe<&2, N.Num>) -> Bool:
31  match m:
32    case Some{N.Num{neg, ip, fp, eneg, ex}}:
33      lead0.ip(ip)
34    case None{}:
35      False{}
36
37def num.pick(+ok: Bool, +c: Maybe<&2, String>, +tok: String) -> J.Json:
38  match ok c:
39    case True{} Some{t}:
40      J.JNum{t}
41    case _ _:
42      J.JStr{tok}
43
44# A bare token that is not true, false or null: number or string.
45def bare.num(+tok: String) -> J.Json:
46  num.pick(Bool.not(lead0(N.split(tok))), N.canon(tok), tok)
47
48def bare.pick(+t: Bool, +f: Bool, +n: Bool, +tok: String) -> J.Json:
49  match t f n:
50    case True{} _ _:
51      J.JBool{True{}}
52    case False{} True{} _:
53      J.JBool{False{}}
54    case False{} False{} True{}:
55      J.JNull{}
56    case False{} False{} False{}:
57      bare.num(tok)
58
59def bare(+tok: String) -> J.Json:
60  bare.pick(String.eq(tok, "true"), String.eq(tok, "false"), String.eq(tok, "null"), tok)
61
62def parse.pick(+quoted: Bool, +tok: String) -> Result<&2, &2, String, J.Json>:
63  match quoted:
64    case True{}:
65      quoted.end(T.unq(Hd.drop(tok)))
66    case False{}:
67      Done{bare(tok)}
68
69# A trimmed token as a value.
70def parse(+tok: String) -> Result<&2, &2, String, J.Json>:
71  parse.pick(Hd.lead(tok, 34), tok)
72
73def key.rest(+ok: Bool, +v: String) -> Result<&2, &2, String, String>:
74  match ok:
75    case True{}:
76      Done{v}
77    case False{}:
78      Fail{"unexpected characters after the closing quote"}
79
80def key.end(+r: Result<&2, &2, String, T.Cut>) -> Result<&2, &2, String, String>:
81  match r:
82    case Fail{e}:
83      Fail{e}
84    case Done{T.Cut{v, rest}}:
85      key.rest(String.is_empty(rest), v)
86
87def key.pick(+quoted: Bool, +tok: String) -> Result<&2, &2, String, String>:
88  match quoted:
89    case True{}:
90      key.end(T.unq(Hd.drop(tok)))
91    case False{}:
92      Done{tok}
93
94def key(+tok: String) -> Result<&2, &2, String, String>:
95  key.pick(Hd.lead(tok, 34), tok)
96
97# Values of a delimited list, as an array chain.
98def values.cons(+r: Result<&2, &2, String, J.Json>, +rest: Result<&2, &2, String, J.Json>) -> Result<&2, &2, String, J.Json>:
99  match r rest:
100    case Done{v} Done{vs}:
101      Done{J.JCons{v, vs}}
102    case Fail{e} _:
103      Fail{e}
104    case _ Fail{e}:
105      Fail{e}
106
107def values(+toks: List<&2, String>) -> Result<&2, &2, String, J.Json>:
108  match toks:
109    case Nil{}:
110      Done{J.JNil{}}
111    case Con{t, rest}:
112      values.cons(parse(String.trim(t)), values(rest))