1import gleam/option.{None, Some}
 2import gleam/string
 3import nibble.{type Parser, do, return}
 4import node.{type Node, EndOfLine}
 5
 6pub fn exact_string(
 7  expected: String,
 8  node: Node,
 9) -> Parser(Node, String, String) {
10  nibble.in(
11    {
12      use _ <- do(string.to_graphemes(expected) |> match_chars(expected))
13      return(node)
14    },
15    "exact_string('" <> expected <> "')",
16  )
17}
18
19pub fn consume_exact_string(expected: String) -> Parser(Nil, String, String) {
20  nibble.in(
21    {
22      use _ <- nibble.do(exact_string(expected, node.Let))
23      // NOTE: doesn't matter which constructor
24
25      return(Nil)
26    },
27    "consume_exact_string('" <> expected <> "')",
28  )
29}
30
31fn match_chars(
32  chars: List(String),
33  context: String,
34) -> Parser(Nil, String, String) {
35  nibble.in(
36    {
37      case chars {
38        [] -> return(Nil)
39
40        [first, ..rest] -> {
41          use _ <- do(
42            nibble.take_map(
43              "expected '" <> first <> "' in keyword '" <> context <> "'",
44              fn(tok) {
45                case tok == first {
46                  True -> Some(Nil)
47                  False -> None
48                }
49              },
50            ),
51          )
52          match_chars(rest, context)
53        }
54      }
55    },
56    "match_chars(" <> context <> ")",
57  )
58}
59
60pub fn let_keyword() -> Parser(Node, String, String) {
61  nibble.in(exact_string("let", node.Let), "let_keyword")
62}
63
64pub fn end_of_line() -> Parser(Node, String, String) {
65  nibble.in(
66    {
67      use _ <- nibble.do(
68        nibble.one_of([
69          nibble.in(nibble.token("\n"), "unix_line_ending"),
70          consume_exact_string("\r\n"),
71        ]),
72      )
73
74      return(EndOfLine)
75    },
76    "end_of_line",
77  )
78}