json/quote.bend

cswap-bend-cli@0.1.0.0 · 61 lines · 12 definitions

1import Base
2import cswap-bend-cli@0.1.0.0/json/lex.bend as Lex
3
4def hexd(+n: U32) -> Char:
5  Chr{Lex.sel(U32.is_lt(n, 10), (n + 48 : U32), (n + 87 : U32))}
6
7# \uXXXX, lowercase hex as Python writes it.
8def uesc(+n: U32) -> String:
9  "\\u" ++ SCon{hexd(((n >> 12n) .&. 15 : U32)), SCon{hexd(((n >> 8n) .&. 15 : U32)), SCon{hexd(((n >> 4n) .&. 15 : U32)), SCon{hexd((n .&. 15 : U32)), SNil{}}}}}
10
11def uesc.pair(+c: U32) -> String:
12  uesc((55296 + ((c - 65536) >> 10n) : U32)) ++ uesc((56320 + ((c - 65536) .&. 1023) : U32))
13
14def esc.high(ascii: Bool, +c: U32) -> String:
15  match ascii:
16    case False{}:
17      SCon{Chr{c}, SNil{}}
18    case True{}:
19      Lex.sel.s(U32.is_lt(c, 65536), uesc(c), uesc.pair(c))
20
21def esc.rest(ascii: Bool, +c: U32) -> String:
22  Lex.sel.s(U32.is_lt(c, 32), uesc(c),
23  Lex.sel.s(U32.is_lt(c, 127), SCon{Chr{c}, SNil{}}, esc.high(ascii, c)))
24
25# One char as Python's encoder writes it inside quotes. ensure_ascii escapes
26# everything above `~` (U+007F included); otherwise only controls.
27def esc.char(+ascii: Bool, +c: U32) -> String:
28  Lex.sel.s(U32.is_eq(c, 34), "\\\"",
29  Lex.sel.s(U32.is_eq(c, 92), "\\\\",
30  Lex.sel.s(U32.is_eq(c, 10), "\\n",
31  Lex.sel.s(U32.is_eq(c, 13), "\\r",
32  Lex.sel.s(U32.is_eq(c, 9), "\\t",
33  Lex.sel.s(U32.is_eq(c, 8), "\\b",
34  Lex.sel.s(U32.is_eq(c, 12), "\\f",
35  Lex.sel.s(U32.is_eq(c, 127), Lex.sel.s(ascii, uesc(127), SCon{Chr{127}, SNil{}}), esc.rest(ascii, c)))))))))
36
37def quote.body(s: String, +ascii: Bool) -> String:
38  match s:
39    case SNil{}:
40      SNil{}
41    case SCon{Chr{c}, t}:
42      esc.char(ascii, c) ++ quote.body(t, ascii)
43
44# `"text"` with escapes.
45def quote(s: String, ascii: Bool) -> String:
46  "\"" ++ quote.body(s, ascii) ++ "\""
47
48# style 0: compact `,` `:`; 1: Python's default `, ` `: `; 2: indent=2.
49def indent(d: Nat) -> String:
50  String.repeat("  ", d)
51
52def nl(style: U32, d: Nat) -> String:
53  Lex.sel.s(U32.is_eq(style, 2), "\n" ++ indent(d), "")
54
55def sep(+style: U32, d: Nat) -> String:
56  Lex.sel.s(U32.is_eq(style, 0), ",", Lex.sel.s(U32.is_eq(style, 1), ", ", ",\n" ++ indent(d)))
57
58def colon(+style: U32) -> String:
59  Lex.sel.s(U32.is_eq(style, 0), ":", ": ")
60
61# Separator between a chain cell and the next: nothing at the end.