Skip to content
← Back

src/syntax/parsers/base.ghul

1
namespace Syntax.Parsers is
2
use System.Exception;
3
4
trait Parser[T: class] is
5
// Null means the parser could not produce a result — parsers
6
// return null on unparseable input as part of error recovery,
7
// so every caller sees the possibility in the type.
8
parse(context: CONTEXT) -> T?;
9
si
10
11
class Base[T: class]: Parser[T] abstract is
12
_expected_tokens: Collections.MutableList[Lexical.TOKEN];
13
parsers: Collections.MAP[Lexical.TOKEN, (CONTEXT) -> T];
14
15
expected_tokens: Collections.Iterable[Lexical.TOKEN] is
16
if _expected_tokens == null then
17
populate_expected_tokens();
18
fi
19
return _expected_tokens;
20
si
21
22
description: string? => null;
23
24
syntax_error_message: string =>
25
let d = description in
26
if d? then
27
"in {d}"
28
else
29
"syntax error"
30
fi;
31
32
init() is
33
parsers = Collections.MAP[Lexical.TOKEN, (CONTEXT) -> T]();
34
si
35
36
add_parser(p: (CONTEXT) -> T, token: Lexical.TOKEN) is
37
if parsers.contains_key(token) then
38
throw Exception("{token} already has a parser ({p})");
39
fi
40
41
parsers[token] = p;
42
si
43
44
add_parser(p: (CONTEXT) -> T, tokens: Collections.Iterable[Lexical.TOKEN]) is
45
for t in tokens do
46
add_parser(p, t);
47
od
48
si
49
50
parse(context: CONTEXT) -> T? is
51
let token = context.current.token;
52
53
return
54
if parsers.contains_key(token) then
55
let p = parsers[token];
56
57
p(context);
58
else
59
other_token(context);
60
fi
61
si
62
63
other_token(context: CONTEXT) -> T? is
64
if context.current.token != Lexical.TOKEN.CANCEL_STRING then
65
context.error(context.location,
66
"{syntax_error_message}: expected {Lexical.TOKEN_NAMES[expected_tokens]} but found {context.current_token_name}"
67
);
68
fi
69
return _;
70
si
71
72
populate_expected_tokens() private is
73
let et = Collections.LIST[Lexical.TOKEN]();
74
75
for k in parsers.keys do
76
et.add(k);
77
od
78
79
assert et.count > 0 else "should have at least one expected token";
80
81
_expected_tokens = et;
82
si
83
si
84
si