src/rep.bend

bend-toon-codec@0.1.0.0 · 113 lines · 14 definitions

1import Base
2
3# What the encoder builds for one value, before it knows where the value sits.
4#
5# A line is a depth relative to the value and its text. A parent turns the
6# reps of its children into its own lines by shifting them, so the encoder
7# needs no running depth and recurses on Json alone.
8
9type Ln is Data:
10  Ln{d: U32, t: String}
11
12type Rep is Data:
13  # A primitive: its text.
14  RPrim{t: String}
15  # An object (field lines) or a chain of list items: lines at relative depth 0.
16  RLines{ls: List<&2, Ln>}
17  # A non-empty array: the text after the key (`[2]: a,b`, `[2]{a,b}:`,
18  # `[2]:`) and the lines beneath the header, already at depth 1 and deeper.
19  RArr{h: String, ks: List<&2, Ln>}
20  # An empty array.
21  REmpty{}
22
23def shift(+ls: List<&2, Ln>) -> List<&2, Ln>:
24  match ls:
25    case Nil{}:
26      Nil{}
27    case Con{Ln{d, t}, rest}:
28      Con{Ln{(d + 1 : U32), t}, shift(rest)}
29
30def lines.of(+r: Rep) -> List<&2, Ln>:
31  match r:
32    case RLines{ls}:
33      ls
34    case _:
35      Nil{}
36
37def cat(+a: List<&2, Ln>, +b: List<&2, Ln>) -> List<&2, Ln>:
38  match a:
39    case Nil{}:
40      b
41    case Con{h, t}:
42      Con{h, cat(t, b)}
43
44def one(+d: U32, +t: String) -> List<&2, Ln>:
45  Con{Ln{d, t}, Nil{}}
46
47# Lines of `key: value` for any value rep.
48def field(+k: String, +r: Rep) -> List<&2, Ln>:
49  match r:
50    case RPrim{t}:
51      one(0, k ++ ": " ++ t)
52    case RLines{ls}:
53      Con{Ln{0, k ++ ":"}, shift(ls)}
54    case RArr{h, ks}:
55      Con{Ln{0, k ++ h}, ks}
56    case REmpty{}:
57      one(0, k ++ ": []")
58
59# The first line of an object item rides on the hyphen line; the rest sit one
60# level under it.
61def item.obj(+ls: List<&2, Ln>) -> List<&2, Ln>:
62  match ls:
63    case Nil{}:
64      one(0, "-")
65    case Con{Ln{d, t}, rest}:
66      Con{Ln{0, "- " ++ t}, shift(rest)}
67
68# Lines of `- value` for any value rep.
69def item(+r: Rep) -> List<&2, Ln>:
70  match r:
71    case RPrim{t}:
72      one(0, "- " ++ t)
73    case RLines{ls}:
74      item.obj(ls)
75    case RArr{h, ks}:
76      Con{Ln{0, "- " ++ h}, ks}
77    case REmpty{}:
78      one(0, "- [0]:")
79
80# Lines of a whole document.
81def root(+r: Rep) -> List<&2, Ln>:
82  match r:
83    case RPrim{t}:
84      one(0, t)
85    case RLines{ls}:
86      ls
87    case RArr{h, ks}:
88      Con{Ln{0, h}, ks}
89    case REmpty{}:
90      one(0, "[]")
91
92def indent.of(+size: U32, +d: U32) -> String:
93  String.repeat(" ", U32.to_nat((size * d : U32)))
94
95def render.line(+size: U32, +l: Ln) -> String:
96  match l:
97    case Ln{d, t}:
98      indent.of(size, d) ++ t
99
100def render.tail(+size: U32, +ls: List<&2, Ln>) -> String:
101  match ls:
102    case Nil{}:
103      ""
104    case Con{l, rest}:
105      "\n" ++ render.line(size, l) ++ render.tail(size, rest)
106
107# Join lines with LF; no trailing newline.
108def render(+size: U32, +ls: List<&2, Ln>) -> String:
109  match ls:
110    case Nil{}:
111      ""
112    case Con{l, rest}:
113      render.line(size, l) ++ render.tail(size, rest)