src/syntax/lex.bend
bolt@1.27.0.0 · 394 lines · 31 definitions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31import Base
32import bolt@1.27.0.0/src/rchars.bend as Rchars
33import bolt@1.27.0.0/src/lazy/lazy.bend as Lazy
34
35
36
37type TokKind is Data:
38 TName{}
39 TUpper{}
40 TDotted{}
41 TWild{}
42 TKey{}
43 TNum{}
44 TStr{}
45 TChar{}
46 TComment{}
47 TSpace{}
48 TNewline{}
49 TOp{}
50 TColon{}
51 TEq{}
52 TBind{}
53 TArrow{}
54 TLam{}
55 TAll{}
56 TAmp{}
57 TOpen{}
58 TClose{}
59 TComma{}
60
61
62type Tok is Data:
63 Tok{kind: TokKind, text: String, line: U32, col: U32}
64
65
66type Class is Data:
67 CAlpha{}
68 CDigit{}
69 CDot{}
70 CQuote{}
71 CTick{}
72 CBack{}
73 CHash{}
74 CSpace{}
75 CNewline{}
76 COp{}
77 CColon{}
78 COpen{}
79 CClose{}
80 CComma{}
81
82
83type Mode is Data:
84 Fresh{}
85 InName{}
86 InNum{}
87 InStr{}
88 InStrEsc{}
89 InChar{}
90 InCharEsc{}
91 InComment{}
92 InSpace{}
93 InOp{}
94
95
96type St is Data:
97 St{mode: Mode, kind: TokKind, line: U32, col: U32, sl: U32, sc: U32, buf: List<&2, Char>, toks: List<&2, Tok>}
98
99
100
101
102
103
104def classify.go(+xx: U32, other: Class) -> Class:
105 Bool.pick(Class, U32.is_eq(xx, 46), CDot{},
106 Bool.pick(Class, U32.is_eq(xx, 34), CQuote{},
107 Bool.pick(Class, U32.is_eq(xx, 39), CTick{},
108 Bool.pick(Class, U32.is_eq(xx, 92), CBack{},
109 Bool.pick(Class, U32.is_eq(xx, 35), CHash{},
110 Bool.pick(Class, U32.is_eq(xx, 32), CSpace{},
111 Bool.pick(Class, U32.is_eq(xx, 9), CSpace{},
112 Bool.pick(Class, U32.is_eq(xx, 13), CSpace{},
113 Bool.pick(Class, U32.is_eq(xx, 10), CNewline{},
114 Bool.pick(Class, U32.is_eq(xx, 58), CColon{},
115 Bool.pick(Class, U32.is_eq(xx, 44), CComma{},
116 Bool.pick(Class, U32.is_eq(xx, 40), COpen{},
117 Bool.pick(Class, U32.is_eq(xx, 91), COpen{},
118 Bool.pick(Class, U32.is_eq(xx, 123), COpen{},
119 Bool.pick(Class, U32.is_eq(xx, 41), CClose{},
120 Bool.pick(Class, U32.is_eq(xx, 93), CClose{},
121 Bool.pick(Class, U32.is_eq(xx, 125), CClose{},
122 Bool.pick(Class, U32.is_eq(xx, 95), CAlpha{},
123 other))))))))))))))))))
124
125
126def classify(+cc: Char) -> Class:
127 +other = Bool.pick(Class, Char.is_alpha(cc), CAlpha{},
128 Bool.pick(Class, Char.is_digit(cc), CDigit{}, COp{}))
129 classify.go(Char.to_u32(cc), other)
130
131
132def continues(mm: Mode, cls: Class) -> Bool:
133 match mm cls:
134 case Fresh{} c:
135 False{}
136 case InName{} CAlpha{}:
137 True{}
138 case InName{} CDigit{}:
139 True{}
140 case InName{} CDot{}:
141 True{}
142 case InNum{} CDigit{}:
143 True{}
144 case InNum{} CDot{}:
145 True{}
146 case InNum{} CAlpha{}:
147 True{}
148 case InStr{} c:
149 True{}
150 case InStrEsc{} c:
151 True{}
152 case InChar{} CNewline{}:
153 False{}
154 case InChar{} c:
155 True{}
156 case InCharEsc{} CNewline{}:
157 False{}
158 case InCharEsc{} c:
159 True{}
160 case InComment{} CNewline{}:
161 False{}
162 case InComment{} c:
163 True{}
164 case InSpace{} CSpace{}:
165 True{}
166 case InOp{} COp{}:
167 True{}
168 case InOp{} CDot{}:
169 True{}
170 case m c:
171 False{}
172
173
174def after(mm: Mode, cls: Class) -> Mode:
175 match mm cls:
176 case InStr{} CQuote{}:
177 Fresh{}
178 case InStr{} CBack{}:
179 InStrEsc{}
180 case InStrEsc{} c:
181 InStr{}
182 case InChar{} CTick{}:
183 Fresh{}
184 case InChar{} CBack{}:
185 InCharEsc{}
186 case InCharEsc{} c:
187 InChar{}
188 case m c:
189 m
190
191
192def begin.mode(cls: Class) -> Mode:
193 match cls:
194 case CAlpha{}:
195 InName{}
196 case CDigit{}:
197 InNum{}
198 case CQuote{}:
199 InStr{}
200 case CTick{}:
201 InChar{}
202 case CHash{}:
203 InComment{}
204 case CSpace{}:
205 InSpace{}
206 case COp{}:
207 InOp{}
208 case CDot{}:
209 InOp{}
210 case CBack{}:
211 InOp{}
212 case other:
213 Fresh{}
214
215def begin.kind(cls: Class) -> TokKind:
216 match cls:
217 case CAlpha{}:
218 TName{}
219 case CDigit{}:
220 TNum{}
221 case CQuote{}:
222 TStr{}
223 case CTick{}:
224 TChar{}
225 case CHash{}:
226 TComment{}
227 case CSpace{}:
228 TSpace{}
229 case CNewline{}:
230 TNewline{}
231 case COpen{}:
232 TOpen{}
233 case CClose{}:
234 TClose{}
235 case CComma{}:
236 TComma{}
237 case other:
238 TOp{}
239
240
241def word(buf: List<&2, Char>) -> String:
242 Rchars.text(buf)
243
244
245def is_keyword(+tt: String) -> Bool:
246 List.contains(~String, ~String.eq,
247 ["def", "type", "law", "match", "case", "do", "return", "for", "exs", "where", "is", "import",
248 "Type", "Data", "Kind", "Quant"], tt)
249
250
251def upper_first(cs: List<&2, Char>) -> Bool:
252 match cs:
253 case Nil{}:
254 False{}
255 case Con{c, t}:
256 Char.is_upper(c)
257
258
259def refine_name(+tt: String) -> TokKind:
260 Bool.pick(TokKind, is_keyword(tt), TKey{},
261 Bool.pick(TokKind, String.contains(tt, "."), TDotted{},
262 Bool.pick(TokKind, String.eq(tt, "_"), TWild{},
263 Bool.pick(TokKind, upper_first(String.to_list(tt)), TUpper{}, TName{}))))
264
265
266def refine_op(+tt: String) -> TokKind:
267 Bool.pick(TokKind, String.eq(tt, ":"), TColon{},
268 Bool.pick(TokKind, String.eq(tt, "="), TEq{},
269 Bool.pick(TokKind, String.eq(tt, "<-"), TBind{},
270 Bool.pick(TokKind, String.eq(tt, "->"), TArrow{},
271 Bool.pick(TokKind, String.eq(tt, "=>"), TLam{},
272 Bool.pick(TokKind, Bool.or(String.eq(tt, "@"), Bool.or(String.eq(tt, "@+"), String.eq(tt, "@-"))), TAll{},
273 Bool.pick(TokKind, String.eq(tt, "&"), TAmp{}, TOp{})))))))
274
275
276def refine(kind: TokKind, tt: String) -> TokKind:
277 match kind:
278 case TName{}:
279 refine_name(tt)
280 case TOp{}:
281 refine_op(tt)
282 case other:
283 other
284
285
286def flush(kind: TokKind, sl: U32, sc: U32, +buf: List<&2, Char>, +toks: List<&2, Tok>) -> List<&2, Tok>:
287 +t = word(buf)
288 Bool.pick(List<&2, Tok>, List.is_empty(&2, Char, buf), toks, Tok{refine(kind, t), t, sl, sc} <> toks)
289
290
291def is_newline(cc: Char) -> Bool:
292 U32.is_eq(Char.to_u32(cc), 10)
293
294
295
296
297def step(+cc: Char, st: St) -> St:
298 St{+mode, +kind, +line, +col, +sl, +sc, +buf, +toks} = st
299 +cls = classify(cc)
300 +nl = is_newline(cc)
301 +line2 = Bool.pick(U32, nl, (line + 1 : U32), line)
302 +col2 = Bool.pick(U32, nl, 0, (col + 1 : U32))
303 Lazy.either(St, continues(mode, cls),
304 _u => St{after(mode, cls), kind, line2, col2, sl, sc, cc <> buf, toks},
305 _v => St{begin.mode(cls), begin.kind(cls), line2, col2, line, col, [cc], flush(kind, sl, sc, buf, toks)})
306
307
308def run(cs: List<&2, Char>, st: St) -> St:
309 match cs:
310 case Nil{}:
311 st
312 case Con{c, t}:
313 run(t, step(c, st))
314
315
316def finish(st: St) -> List<&2, Tok>:
317 St{mode, kind, line, col, sl, sc, buf, toks} = st
318 List.reverse(&2, Tok, flush(kind, sl, sc, buf, toks))
319
320
321def tokens(source: String) -> List<&2, Tok>:
322 finish(run(String.to_list(source), St{Fresh{}, TSpace{}, 0, 0, 0, 0, [], []}))
323
324
325def text.go(toks: List<&2, Tok>, acc: List<&2, String>) -> List<&2, String>:
326 match toks:
327 case Nil{}:
328 acc
329 case Con{Tok{k, t, l, c}, rest}:
330 text.go(rest, t <> acc)
331
332
333def text(toks: List<&2, Tok>) -> String:
334 String.concat(List.reverse(&2, String, text.go(toks, [])))
335
336
337def is_open(kk: TokKind) -> Bool:
338 match kk:
339 case TOpen{}:
340 True{}
341 case other:
342 False{}
343
344
345def is_comma(kk: TokKind) -> Bool:
346 match kk:
347 case TComma{}:
348 True{}
349 case other:
350 False{}
351
352
353def is_name(kk: TokKind) -> Bool:
354 match kk:
355 case TName{}:
356 True{}
357 case TUpper{}:
358 True{}
359 case TDotted{}:
360 True{}
361 case TWild{}:
362 True{}
363 case TKey{}:
364 True{}
365 case other:
366 False{}
367
368
369def is_nl(kk: TokKind) -> Bool:
370 match kk:
371 case TNewline{}:
372 True{}
373 case other:
374 False{}
375
376
377def significant(kk: TokKind) -> Bool:
378 match kk:
379 case TSpace{}:
380 False{}
381 case TNewline{}:
382 False{}
383 case TComment{}:
384 False{}
385 case other:
386 True{}
387
388
389def is_str(kk: TokKind) -> Bool:
390 match kk:
391 case TStr{}:
392 True{}
393 case other:
394 False{}