shared/strings.bend

bend-lint-api@0.2.0.0 · 87 lines · 13 definitions

1import Base
2
3def take.go(n: Nat, s: String, acc: String) -> String:
4  match n s:
5    case 1n+k SCon{c, rest}:
6      take.go(k, rest, SCon{c, acc})
7    case _ _:
8      String.reverse(acc)
9
10# The first `n` characters of `s`. Base's String.take is not tail-recursive, so long text would overflow the stack.
11def take(s: String, n: Nat) -> String:
12  take.go(n, s, "")
13
14# The text of `text` from `beg` to `end`, in code points.
15def slice(text: String, +beg: U32, end: U32) -> String:
16  take(String.drop(text, U32.to_nat(beg)), U32.to_nat((end - beg: U32)))
17
18def line_start.go(s: String, +i: U32, +at: U32, start: U32, going: Bool) -> U32:
19  match s going:
20    case _ False{}:
21      start
22    case SNil{} True{}:
23      start
24    case SCon{c, rest} True{}:
25      line_start.go(
26        rest,
27        (i + 1: U32),
28        at,
29        Bool.pick(U32, Char.is_eq(c, '\n'), (i + 1: U32), start),
30        U32.is_lt((i + 1: U32), at)
31      )
32
33# Where the line of `text` that holds the offset `at` starts: after the last newline before it.
34def line_start(text: String, +at: U32) -> U32:
35  line_start.go(text, 0, at, 0, U32.is_lt(0, at))
36
37def line_end.go(s: String, i: U32) -> U32:
38  match s:
39    case SNil{}:
40      i
41    case SCon{'\n', rest}:
42      (i + 1: U32)
43    case SCon{c, rest}:
44      line_end.go(rest, (i + 1: U32))
45
46# Where the line of `text` that starts at `beg` ends: after its newline, or at the end of the text.
47def line_end(text: String, +beg: U32) -> U32:
48  line_end.go(String.drop(text, U32.to_nat(beg)), beg)
49
50def starts_with(s: String, c: Char) -> Bool:
51  match s:
52    case SCon{h, t}:
53      Char.is_eq(h, c)
54    case SNil{}:
55      False{}
56
57def before.go(s: String, +sep: Char, acc: String, at: Bool) -> String:
58  match s at:
59    case SNil{} _:
60      ""
61    case _ True{}:
62      String.reverse(acc)
63    case SCon{c, +rest} False{}:
64      before.go(rest, sep, SCon{c, acc}, starts_with(rest, sep))
65
66# What comes before the first `sep` of `s`, or "" if there is none.
67def before(+s: String, +sep: Char) -> String:
68  before.go(s, sep, "", starts_with(s, sep))
69
70def after_last.go(s: String, +sep: Char, seg: String) -> String:
71  match s:
72    case SNil{}:
73      String.reverse(seg)
74    case SCon{+c, rest}:
75      after_last.go(rest, sep, Bool.pick(String, Char.is_eq(c, sep), "", SCon{c, seg}))
76
77# What comes after the last `sep` of `s`, or all of `s` if there is none.
78def after_last(s: String, sep: Char) -> String:
79  after_last.go(s, sep, "")
80
81# The chunks of `chunks` (last first) as one text, before `out`. Each chunk is copied once, so it is linear.
82def onto(chunks: List<&2, String>, out: String) -> String:
83  match chunks:
84    case Nil{}:
85      out
86    case Con{c, rest}:
87      onto(rest, c ++ out)