MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
tok.h
Go to the documentation of this file.
1#pragma once
2
3#include <optional>
4#include <string_view>
5#include <utility>
6
7#include <fe/assert.h>
8#include <fe/format.h>
9
10#include "mim/util/dbg.h"
11
12namespace mim {
13
14class Def;
15
16namespace ast {
17
18/// @name Precedence Table
19/// X-macro listing all expression precedences from lowest to highest as `m(name, assoc)`.
20/// @p assoc is `L`eft-, `R`ight-, or `N`on-associative; `a op b op c` is an error for an `N` level.
21/// Only a level named by MIM_INFIX or by an entry below is an actual operator:
22/// `Err`, `Bot`, `Pi`, and `Lit` merely serve as a `curr_prec` bound while parsing.
23/// Application binds tighter than every operator - only `Extract` and `Lit` bind tighter still.
24///@{
25// clang-format off
26#define MIM_PREC(m) \
27 m(Err, N) \
28 m(Bot, N) \
29 m(Where, L) \
30 m(Ins, R) \
31 m(Inj, R) \
32 m(Union, L) \
33 m(Arrow, R) \
34 m(Pi, N) \
35 m(Eq, N) \
36 m(Rel, N) \
37 m(Add, L) \
38 m(Mul, L) \
39 m(Shift, L) \
40 m(App, L) \
41 m(Extract, L) \
42 m(Lit, N)
43// clang-format on
44///@}
45
46/// Associativity of an infix expression.
47enum class Assoc { N, L, R };
48
49/// Expression precedences used by the parser and the dumper; ordered low to high.
50enum class Prec {
51#define CODE(name, ...) name,
53#undef CODE
54};
55
56/// Associativity of precedence level @p p.
57constexpr Assoc prec_assoc(Prec p) {
58 switch (p) {
59#define CODE(name, assoc) \
60 case Prec::name: return Assoc::assoc;
62#undef CODE
63 }
64 fe::unreachable();
65}
66
67constexpr bool is_rassoc(Prec p) { return prec_assoc(p) == Assoc::R; }
68constexpr bool is_lassoc(Prec p) { return prec_assoc(p) == Assoc::L; }
69
70/// Should a Pratt parser reduce when the current binding power is @p curr
71/// and the infix operator has precedence @p op?
72constexpr bool should_reduce(Prec curr, Prec op) { return is_rassoc(op) ? curr > op : curr >= op; }
73
74// clang-format off
75#define MIM_KEY(m) \
76 m(K_Bool, "Bool" ) \
77 m(K_Cn, "Cn" ) \
78 m(K_Fn, "Fn" ) \
79 m(K_I1, "I1" ) \
80 m(K_I16, "I16" ) \
81 m(K_I32, "I32" ) \
82 m(K_I64, "I64" ) \
83 m(K_I8, "I8" ) \
84 m(K_Idx, "Idx" ) \
85 m(K_Nat, "Nat" ) \
86 m(K_Type, "Type" ) \
87 m(K_Univ, "Univ" ) \
88 m(K_and, "and" ) \
89 m(K_anx, "anx" ) \
90 m(K_as, "as" ) \
91 m(K_axm, "axm" ) \
92 m(K_cn, "cn" ) \
93 m(K_con, "con" ) \
94 m(K_end, "end" ) \
95 m(K_extern, "extern") \
96 m(K_ff, "ff" ) \
97 m(K_fn, "fn" ) \
98 m(K_fun, "fun" ) \
99 m(K_i1, "i1" ) \
100 m(K_i16, "i16" ) \
101 m(K_i32, "i32" ) \
102 m(K_i64, "i64" ) \
103 m(K_i8, "i8" ) \
104 m(K_import, "import") \
105 m(K_inj, "inj" ) \
106 m(K_lam, "lam" ) \
107 m(K_let, "let" ) \
108 m(K_match, "match" ) \
109 m(K_mod, "mod" ) \
110 m(K_norm, "norm" ) \
111 m(K_plugin, "plugin") \
112 m(K_priv, "priv" ) \
113 m(K_pub, "pub" ) \
114 m(K_rec, "rec" ) \
115 m(K_ret, "ret" ) \
116 m(K_rule, "rule" ) \
117 m(K_Rule, "Rule" ) \
118 m(K_tt, "tt" ) \
119 m(K_use, "use" ) \
120 m(K_when, "when" ) \
121 m(K_where, "where" ) \
122 m(K_with, "with" ) \
123
124#define CODE(t, str) + size_t(1)
125constexpr auto Num_Keys = size_t(0) MIM_KEY(CODE);
126#undef CODE
127
128#define MIM_TOK(m) \
129 m(EoF, "<end of file>" ) \
130 /* literals */ \
131 m(L_s, "<signed integer literal>") \
132 m(L_u, "<integer literal>" ) \
133 m(L_i, "<index literal>" ) \
134 m(L_f, "<floating-point literal>") \
135 m(L_c, "<char literal>" ) \
136 m(L_str, "<string literal>" ) \
137 /* misc */ \
138 m(M_id, "<identifier>" ) \
139 /* delimiters */ \
140 m(D_angle_l, "‹") \
141 m(D_angle_r, "›") \
142 m(D_brace_l, "{") \
143 m(D_brace_r, "}") \
144 m(D_brckt_l, "[") \
145 m(D_brckt_r, "]") \
146 m(D_curly_l, "⦃") \
147 m(D_curly_r, "⦄") \
148 m(D_paren_l, "(") \
149 m(D_paren_r, ")") \
150 m(D_quote_l, "«") \
151 m(D_quote_r, "»") \
152 /* further tokens */ \
153 m(T_add, "+") \
154 m(T_arrow_r, "→") \
155 m(T_fat_arrow, "=>") \
156 m(T_assign, "=") \
157 m(T_at, "@") \
158 m(T_bot, "⊥") \
159 m(T_top, "⊤") \
160 m(T_box, "□") \
161 m(T_colon, ":") \
162 m(T_comma, ",") \
163 m(T_div, "/") \
164 m(T_dollar, "$") \
165 m(T_dot, ".") \
166 m(T_eq, "==") \
167 m(T_extract, "#") \
168 m(T_ge, ">=") \
169 m(T_gt, ">") \
170 m(T_arrow_l, "←") \
171 m(T_le, "<=") \
172 m(T_lm, "λ") \
173 m(T_lt, "<") \
174 m(T_ne, "!=") \
175 m(T_rem, "%") \
176 m(T_semicolon, ";") \
177 m(T_shl, "<<") \
178 m(T_shr, ">>") \
179 m(T_star, "*") \
180 m(T_sub, "-") \
181 m(T_union, "∪") \
182 m(T_pipe, "|") \
183
184/// @name Infix Operator Table
185/// X-macros listing all infix operators as `m(tag, str, prec)`.
186///@{
187
188/// `a str b` is sugar for `` `str (a, b) ``; what `` `str `` means is up to whatever the user binds it to.
189#define MIM_INFIX_SUGAR(m) \
190 m(T_eq, "==", Eq ) \
191 m(T_ne, "!=", Eq ) \
192 m(T_lt, "<", Rel ) \
193 m(T_le, "<=", Rel ) \
194 m(T_gt, ">", Rel ) \
195 m(T_ge, ">=", Rel ) \
196 m(T_shl, "<<", Shift) \
197 m(T_shr, ">>", Shift) \
198 m(T_add, "+", Add ) \
199 m(T_sub, "-", Add ) \
200 m(T_star, "*", Mul ) \
201 m(T_div, "/", Mul ) \
202 m(T_rem, "%", Mul )
203
204/// These have a meaning of their own; InfixExpr::emit_ dispatches on the tag.
205#define MIM_INFIX_CORE(m) \
206 m(T_extract, "#", Extract) \
207 m(T_union, "∪", Union ) \
208 m(K_inj, "inj", Inj ) \
209 m(T_arrow_r, "→", Arrow ) \
210 m(T_arrow_l, "←", Ins ) \
211 m(T_at, "@", App )
212
213#define MIM_INFIX(m) MIM_INFIX_SUGAR(m) MIM_INFIX_CORE(m)
214///@}
215
216#define MIM_SUBST(m) \
217 m("lm", T_lm ) \
218 m("bot", T_bot ) \
219 m("top", T_top ) \
220
221#define CODE(str, t) + size_t(1)
222constexpr auto Num_Subst = size_t(0) MIM_SUBST(CODE);
223#undef CODE
224
225class Tok {
226public:
227 /// @name Tag
228 ///@{
229 enum class Tag {
230 Nil,
231#define CODE(t, str) t,
233#undef CODE
234 };
235
236 static const char* tag2str(Tok::Tag);
237 /// Is @p tag something the keyword table yields? MIM_KEY occupies `[1, Num_Keys]`; MIM_SUBST adds a few more.
238 static constexpr bool is_key(Tag tag) {
239 if (tag == Tag::Nil) return false;
240 if (size_t(tag) <= Num_Keys) return true;
241 switch (tag) {
242#define CODE(str, t) case Tag::t:
244#undef CODE
245 return true;
246 default: return false;
247 }
248 }
249 /// Precedence of the infix operator @p tag; `std::nullopt` if @p tag isn't one.
250 static constexpr std::optional<Prec> infix_prec(Tag tag) {
251 switch (tag) {
252#define CODE(t, str, prec) \
253 case Tag::t: return Prec::prec;
255#undef CODE
256 default: return {};
257 }
258 }
259 /// Name the infix operator @p tag desugars to - including the leading `` ` ``; empty for MIM_INFIX_CORE.
260 static constexpr std::string_view infix_sym(Tag tag) {
261 switch (tag) {
262#define CODE(t, str, prec) \
263 case Tag::t: return "`" str;
265#undef CODE
266 default: return {};
267 }
268 }
269 static constexpr Tok::Tag delim_l2r(Tag tag) { return Tok::Tag(int(tag) + 1); }
270 static constexpr bool is_delim_r(Tag tag) {
271 return Tag::D_angle_l <= tag && tag <= Tag::D_quote_r && (int(tag) - int(Tag::D_angle_l)) % 2 == 1;
272 }
273 ///@}
274
275 // clang-format on
276
277 Tok() {}
279 : loc_(loc)
280 , tag_(tag) {}
281 Tok(Loc loc, char8_t c)
282 : loc_(loc)
283 , tag_(Tag::L_c)
284 , c_(c) {}
285 Tok(Loc loc, uint64_t u)
286 : loc_(loc)
287 , tag_(Tag::L_u)
288 , u_(u) {}
289 Tok(Loc loc, int64_t s)
290 : loc_(loc)
291 , tag_(Tag::L_s)
292 , u_(std::bit_cast<uint64_t>(s)) {}
293 Tok(Loc loc, double d)
294 : loc_(loc)
295 , tag_(Tag::L_f)
296 , u_(std::bit_cast<uint64_t>(d)) {}
297 /// @p size and @p val of an Idx literal; World::lit_idx makes a Lit of it at emit time.
298 Tok(Loc loc, uint64_t size, uint64_t val)
299 : loc_(loc)
300 , tag_(Tag::L_i)
301 , idx_{size, val} {}
302 Tok(Loc loc, Tag tag, Sym sym)
303 : loc_(loc)
304 , tag_(tag)
305 , sym_(sym) {
306 assert(has_sym() || is_key(tag));
307 }
308
309 bool isa(Tag tag) const { return tag == tag_; }
310 Tag tag() const { return tag_; }
311 bool has_sym() const { return isa(Tag::M_id) || isa(Tag::L_str); }
312 /// @note A failed Parser::expect yields a Nil Tok; its Dbg is anonymous instead of asserting in Tok::sym.
313 Dbg dbg() const { return {loc(), has_sym() ? sym_ : Sym()}; }
314 Loc loc() const { return loc_; }
315 explicit operator bool() const { return tag_ != Tag::Nil; }
316 // clang-format off
317 std::pair<uint64_t, uint64_t> lit_i() const { assert(isa(Tag::L_i)); return {idx_.size, idx_.val}; }
318 char8_t lit_c() const { assert(isa(Tag::L_c)); return c_; }
319 uint64_t lit_u() const { assert(isa(Tag::L_u ) || isa(Tag::L_s ) || isa(Tag::L_f )); return u_; }
320 Sym sym() const { assert(has_sym()); return sym_; }
321 // clang-format on
322 std::string str() const;
323
324 friend std::ostream& operator<<(std::ostream&, Tok);
325 friend std::ostream& operator<<(std::ostream& os, Tok::Tag tag) { return os << tag2str(tag); }
326
327private:
328 Loc loc_;
329 Tag tag_ = Tag::Nil;
330 union {
331 Sym sym_ = {};
332 uint64_t u_;
333 char8_t c_;
334 struct {
335 uint64_t size, val;
336 } idx_;
337 };
338};
339
340} // namespace ast
341} // namespace mim
342
343#ifndef DOXYGEN // clang-format off
344template<> struct std::formatter<mim::ast::Tok > : fe::ostream_formatter {};
345template<> struct std::formatter<mim::ast::Tok::Tag> : fe::ostream_formatter {};
346#endif // clang-format on
Base class for all Defs.
Definition def.h:273
Tok(Loc loc, uint64_t u)
Definition tok.h:285
static constexpr bool is_delim_r(Tag tag)
Definition tok.h:270
bool has_sym() const
Definition tok.h:311
Sym sym() const
Definition tok.h:320
Tok(Loc loc, Tag tag)
Definition tok.h:278
Tok(Loc loc, Tag tag, Sym sym)
Definition tok.h:302
Tok(Loc loc, uint64_t size, uint64_t val)
size and val of an Idx literal; World::lit_idx makes a Lit of it at emit time.
Definition tok.h:298
static constexpr std::string_view infix_sym(Tag tag)
Name the infix operator tag desugars to - including the leading ` ; empty for MIM_INFIX_CORE.
Definition tok.h:260
std::pair< uint64_t, uint64_t > lit_i() const
Definition tok.h:317
Dbg dbg() const
Definition tok.h:313
Loc loc() const
Definition tok.h:314
uint64_t lit_u() const
Definition tok.h:319
static constexpr std::optional< Prec > infix_prec(Tag tag)
Precedence of the infix operator tag; std::nullopt if tag isn't one.
Definition tok.h:250
bool isa(Tag tag) const
Definition tok.h:309
static constexpr Tok::Tag delim_l2r(Tag tag)
Definition tok.h:269
Tok(Loc loc, int64_t s)
Definition tok.h:289
Tok(Loc loc, char8_t c)
Definition tok.h:281
friend std::ostream & operator<<(std::ostream &os, Tok::Tag tag)
Definition tok.h:325
static const char * tag2str(Tok::Tag)
Definition tok.cpp:10
static constexpr bool is_key(Tag tag)
Is tag something the keyword table yields? MIM_KEY occupies [1, Num_Keys]; MIM_SUBST adds a few more.
Definition tok.h:238
std::string str() const
Definition tok.cpp:22
Tok(Loc loc, double d)
Definition tok.h:293
Tag tag() const
Definition tok.h:310
char8_t lit_c() const
Definition tok.h:318
Definition ast.h:16
constexpr bool is_lassoc(Prec p)
Definition tok.h:68
Assoc
Associativity of an infix expression.
Definition tok.h:47
constexpr bool is_rassoc(Prec p)
Definition tok.h:67
constexpr auto Num_Subst
Definition tok.h:222
constexpr bool should_reduce(Prec curr, Prec op)
Should a Pratt parser reduce when the current binding power is curr and the infix operator has preced...
Definition tok.h:72
constexpr auto Num_Keys
Definition tok.h:125
Tok::Tag Tag
Definition bind.cpp:9
std::ostream & operator<<(std::ostream &os, Tok tok)
Definition tok.cpp:30
constexpr Assoc prec_assoc(Prec p)
Associativity of precedence level p.
Definition tok.h:57
Prec
Expression precedences used by the parser and the dumper; ordered low to high.
Definition tok.h:50
Definition ast.h:16
#define MIM_PREC(m)
Definition tok.h:26
#define CODE(name,...)
Definition tok.h:51
#define MIM_SUBST(m)
Definition tok.h:216
#define MIM_TOK(m)
Definition tok.h:128
#define MIM_INFIX(m)
Definition tok.h:213
#define MIM_KEY(m)
Definition tok.h:75
#define MIM_INFIX_SUGAR(m)
a str b is sugar for `str (a, b) ; what `str means is up to whatever the user binds it to.
Definition tok.h:189