src/split.bend

bend-toon-codec@0.1.0.0 · 65 lines · 10 definitions

1import Base
2
3# Quote-aware scanning of a line (spec appendix B.3): where the first unquoted
4# colon or delimiter is, and how to cut a row on its delimiter.
5
6# Scanner state: 0 outside quotes, 1 inside, 2 inside after a backslash.
7def sel.u(+b: Bool, +x: U32, +y: U32) -> U32:
8  match b:
9    case True{}:
10      x
11    case False{}:
12      y
13
14def qnext.pick(+st: U32, +quote: Bool, +bslash: Bool) -> U32:
15  sel.u(U32.is_eq(st, 0), sel.u(quote, 1, 0),
16  sel.u(U32.is_eq(st, 1), sel.u(bslash, 2, sel.u(quote, 0, 1)), 1))
17
18def qnext(+st: U32, +c: U32) -> U32:
19  qnext.pick(st, U32.is_eq(c, 34), U32.is_eq(c, 92))
20
21# Sentinel index for "not found".
22def none() -> U32:
23  4294967295
24
25def found(+hit: Bool, +i: U32, +later: U32) -> U32:
26  sel.u(hit, i, later)
27
28# Index of the first `target` outside quotes, or `none()`.
29def find_unq(+s: String, +target: U32, +st: U32, +i: U32) -> U32:
30  match s:
31    case SNil{}:
32      none()
33    case SCon{Chr{c}, t}:
34      found(U32.is_eq(st, 0) && U32.is_eq(c, target), i, find_unq(t, target, qnext(st, c), (i + 1 : U32)))
35
36# Put a character in front of the token being built, or start a new token.
37def split.put(+c: U32, +sep: Bool, +r: List<&2, String>) -> List<&2, String>:
38  match sep:
39    case True{}:
40      Con{SNil{}, r}
41    case False{}:
42      match r:
43        case Con{h, rest}:
44          Con{SCon{Chr{c}, h}, rest}
45        case Nil{}:
46          Nil{}
47
48def split.go(+s: String, +d: U32, +st: U32) -> List<&2, String>:
49  match s:
50    case SNil{}:
51      Con{SNil{}, Nil{}}
52    case SCon{Chr{c}, t}:
53      split.put(c, U32.is_eq(st, 0) && U32.is_eq(c, d), split.go(t, d, qnext(st, c)))
54
55# Cut on the delimiter `d` outside quotes. Always at least one token.
56def split(+s: String, +d: U32) -> List<&2, String>:
57  split.go(s, d, 0)
58
59# Index of the first `target`, ignoring quotes, or `none()`.
60def find(+s: String, +target: U32, +i: U32) -> U32:
61  match s:
62    case SNil{}:
63      none()
64    case SCon{Chr{c}, t}:
65      found(U32.is_eq(c, target), i, find(t, target, (i + 1 : U32)))