MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
parser.cpp
Go to the documentation of this file.
1#include "mim/ast/parser.h"
2
3#include <filesystem>
4#include <ranges>
5
6#include "mim/driver.h"
7
8#include "family.h"
9
10using namespace std::literals;
11
12namespace mim::ast {
13
14using Tag = Tok::Tag;
15
16/*
17 * entry points
18 */
19
20Ptr<File> Parser::parse_file() {
21 auto track = tracker();
22 auto decls = parse_decls();
23 bool where = ahead().isa(Tag::K_where);
24 expect(Tag::EoF, "file");
25 auto file = ptr<File>(track, ast().scope(), ast().copy(decls));
26 // curr_ is the stray `;`; file->loc().anew_end() would sit past the end of that line and render no snippet.
27 if (where) error().n(curr_, "did you accidentally end your declaration expression with a `;`?");
28 return file;
29}
30
31const File* Parser::import(Dbg dbg, bool is_path, Tok::Tag tag, std::ostream* md, bool record) {
32 auto name = dbg.sym();
33 if (tag == Tag::K_plugin && !driver().is_loaded(name) && !driver().flags().bootstrap) driver().load(name);
34
35 auto filename = fs::path(name.view());
36 driver().log().v("📥 import `{}`", name);
37
38 if (!filename.has_extension()) filename.replace_extension("mim"); // TODO error cases
39
40 fs::path rel_path;
41 auto is_file = [](const fs::path& p) {
42 std::error_code ignore;
43 bool reg_file = fs::is_regular_file(p, ignore);
44 return reg_file && !ignore;
45 };
46
47 // A path is relative to the importing file first; a bare name is only ever looked up in the search paths.
48 if (is_path && !filename.is_absolute()) {
49 rel_path = curr_dir() / filename;
50 if (!is_file(rel_path)) rel_path.clear();
51 }
52
53 // A plugin's two halves must be the pair that shipped together, so take the `.mim` from the library's directory.
54 if (rel_path.empty() && !is_path && tag == Tag::K_plugin) {
55 if (auto dir = driver().plugin_dir(name.view())) {
56 rel_path = *dir / filename;
57 if (!is_file(rel_path)) rel_path = *dir / name.view() / filename;
58 if (!is_file(rel_path)) rel_path.clear();
59 }
60 }
61
62 if (rel_path.empty()) {
63 for (const auto& path : driver().import_paths()) {
64 rel_path = path / filename;
65 if (is_file(rel_path)) break;
66 if (is_path) continue; // `some/dir/foo.mim` must not also be probed as `some/dir/foo.mim/foo.mim`
67 rel_path = path / name.view() / filename;
68 if (is_file(rel_path)) break;
69 }
70 }
71
72 auto [src, _] = driver().src().add(rel_path);
73 if (!src) {
74 // rel_path is whatever candidate the search loop tried last, so it only names a real file here.
75 if (fs::exists(rel_path))
76 error().e(dbg.loc(), "cannot read file `{}`", rel_path.string());
77 else
78 error().e(dbg.loc(), "cannot find `{}` in the search paths", name);
79 return {};
80 }
81
82 if (record) driver().imports().add(src, name, tag, is_path);
83 return import(*src, md, dbg.loc());
84}
85
86const File* Parser::import(std::istream& is, fs::path path, Loc loc, std::ostream* md) {
87 if (!is) {
88 error().e(loc, "cannot read file `{}`", path.string());
89 return {};
90 }
91 auto [src, _] = driver().src().add(std::move(path), fe::SrcMap::slurp(is));
92 return import(*src, md, loc);
93}
94
95const File* Parser::import(const fe::Src& src, std::ostream* md, Loc loc) {
96 auto [slot, fresh] = ast().file(&src);
97 if (!fresh) {
98 // An empty slot is a file that is still being parsed further up the stack.
99 if (!slot) error().e(loc, "cyclic import of `{}`", src.path().string());
100 return slot.get();
101 }
102
103 driver().log().v("📄 read `{}`", src.path().string());
104
105 auto state = std::tuple(curr_, ahead_, lexer_);
106 auto lexer = Lexer(driver(), src, md);
107 lexer_ = &lexer;
108 init();
109 auto parsed = parse_file();
110 std::tie(curr_, ahead_, lexer_) = state;
111
112 slot = parsed;
113 return slot.get();
114}
115
116Ptrs<UseDecl> Parser::import_plugins(fe::View<std::string> plugins, Tok::Tag tag) {
117 Ptrs<UseDecl> imports;
118 for (const auto& name : plugins) {
119 auto dbg = Dbg(Loc(), driver().sym(name));
120 if (auto file = import(dbg, false, tag))
121 imports.emplace_back(ptr<UseDecl>(Loc(), Mods{}, tag, path(dbg), Sym(), Dbg(), false, file));
122 }
123 return imports;
124}
125
126const File* Parser::import_main(std::string_view input, fe::View<std::string> plugins, std::ostream* md) {
127 auto imports = import_plugins(plugins, Tag::K_plugin);
128 auto file = import({Loc(), driver().sym(input)}, true, Tag::K_import, md, false);
129 if (file) file->add_implicit_imports(ast().copy(imports));
130 return file;
131}
132
133/*
134 * misc
135 */
136
137Ptr<UseDecl> Parser::parse_import_or_plugin(Tracker track, Mods mods) {
138 auto tag = lex().tag();
139 auto entity = fe::Cite(tag == Tag::K_import ? "import" : "plugin");
140 if (mods.is_extern) error().e(curr_, "`extern` is only meaningful on a function declaration, not on `{}`", entity);
141 if (mods.is_anx) error().e(curr_, "`anx` doesn't apply to `{}` - it never represents a single value", entity);
142 auto vis = mods.vis.value_or(Vis::Priv);
143
144 Dbg name;
145 Sym file_path;
146 if (tag == Tag::K_import && ahead().isa(Tag::L_str)) {
147 name = lex().dbg();
148 file_path = name.sym();
149 } else {
150 // Only `plugin` needs a bare name: it is also the key for the shared object and the annex prefix.
151 auto tok = expect(Tag::M_id, "{} name", entity);
152 if (!tok) return {};
153 name = tok.dbg();
154 }
155
156 Dbg alias;
157 bool splice = false;
158 if (accept(Tag::K_as)) {
159 if (accept(Tag::T_star))
160 splice = true;
161 else
162 alias = parse_id("alias of an import");
163 }
164 expect(Tag::T_semicolon, "end of {}", entity);
165
166 auto mod = name;
167 if (file_path) {
168 auto stem = fs::path(file_path.view()).stem().string();
169 bool is_id = Lexer::is_id(stem);
170 if (!is_id && !alias && !splice) {
171 error()
172 .e(name.loc(), "cannot derive a module name from `{}`", file_path)
173 .n("name it explicitly with `as`")
174 .n("or splice its members into this scope with `as *`");
175 return {};
176 }
177 mod.set(is_id ? ast().sym(stem) : Sym());
178 }
179
180 if (auto file = import(name, (bool)file_path, tag))
181 return ptr<UseDecl>(track, Mods{vis}, tag, path(mod), file_path, alias, splice, file);
182 return {};
183}
184
185Dbg Parser::parse_id(fe::Cite ctxt) {
186 if (auto id = accept(Tag::M_id)) return id.dbg();
187 syntax_err("identifier", ctxt);
188 return {missing(), driver().sym("<error>")};
189}
190
191Ptr<Path> Parser::parse_path(fe::Cite ctxt) {
192 auto track = tracker();
193 auto dbgs = Dbgs{parse_id(ctxt)};
194 while (accept(Tag::T_dot))
195 dbgs.emplace_back(parse_id("component of a path"));
196 return ptr<Path>(track, dbgs);
197}
198
199Ptr<Expr> Parser::parse_type_ascr(fe::Cite ctxt) {
200 if (accept(Tag::T_colon)) return parse_expr(ctxt);
201 if (!ctxt) return nullptr;
202 syntax_err("`:`", ctxt);
203 return ptr<ErrorExpr>(missing());
204}
205
206/*
207 * exprs
208 */
209
210Ptr<Expr> Parser::parse_expr(fe::Cite ctxt, Prec curr_prec) {
211 // An empty ctxt makes the expression optional, so there is nothing to recover into.
212 if (ctxt) recover(ctxt);
213 auto track = tracker();
214 auto lhs = parse_primary_expr(ctxt);
215 return parse_infix_expr(track, lhs, curr_prec, ctxt);
216}
217
218Ptr<Expr> Parser::parse_infix_expr(Tracker track, Ptr<Expr> lhs, Prec curr_prec, fe::Cite ctxt) {
219 auto prev = Prec::Err; // precedence this loop built last; a non-associative one must not repeat
220 while (true) {
221 // A closing delimiter nobody is waiting for must not end the expression.
222 recover(ctxt ? ctxt : fe::Cite("expression"));
223
224 if (auto prec = Tok::infix_prec(ahead().tag())) {
225 if (should_reduce(curr_prec, *prec)) return lhs;
226 if (*prec == prev && prec_assoc(*prec) == Assoc::N)
227 error()
228 .e(ahead().loc(), "operator `{}` is not associative", ahead())
229 .n("parenthesize the left- or right-hand side");
230 prev = *prec;
231 auto op = lex();
232 auto rhs = parse_expr(*prec, "right-hand side of the `{}` operator", op);
233 lhs = ptr<InfixExpr>(track, lhs, op, rhs, sugar_callee(op));
234 continue;
235 }
236
237 // Application juxtaposes its operands, so there is no operator token to drive the loop above.
238 if (ISA(ahead().tag(), C_EXPR)) {
239 if (should_reduce(curr_prec, Prec::App)) return lhs;
240 if (ISA(ahead().tag(), C_DECL))
241 error()
242 .w(ahead().loc(), "you are passing a declaration expression as argument")
243 .n(lhs->loc(), "passed to this expression")
244 .n("if this was your intention, consider parenthesizing the declaration expression")
245 .n(lhs->loc().anew_end(), "or insert a `;` here");
246 auto rhs = parse_expr("argument to an application", Prec::App);
247 lhs = ptr<AppExpr>(track, lhs, rhs);
248 continue;
249 }
250
251 // `where` trails a whole declaration block instead of an expression, and closes the expression for good.
252 if (ahead().isa(Tag::K_where)) {
253 if (should_reduce(curr_prec, Prec::Where)) return lhs;
254 lex();
255 auto decls = parse_decls();
256 lhs = ptr<DeclExpr>(track, lhs, true, decls);
257
258 bool where = ahead().tag() == Tag::K_where;
259 expect(Tag::K_end, "end of a where declaration block");
260 if (where) error().n(curr_, "did you accidentally end your declaration expression with a `;`?");
261 }
262
263 return lhs;
264 }
265}
266
267Ptr<Expr> Parser::sugar_callee(Tok op) {
268 auto sym = Tok::infix_sym(op.tag());
269 return sym.empty() ? nullptr : path_expr(Dbg(op.loc(), driver().sym(sym)));
270}
271
272Ptr<Expr> Parser::parse_uniq_expr() {
273 auto track = tracker();
274 expect(Tag::D_curly_l, "opening curly bracket for singleton type");
275 auto _ = this->anchor(Tag::D_curly_r);
276 auto inhabitant = parse_expr("singleton type");
277 recover("singleton type");
278 expect(Tag::D_curly_r, "closing curly bracket for singleton type");
279 return ptr<UniqExpr>(track, inhabitant);
280}
281
282Ptr<Expr> Parser::parse_match_expr() {
283 auto track = tracker();
284 expect(Tag::K_match, "opening match for union destruction");
285 auto scrutinee = parse_expr("destroyed union element");
286 expect(Tag::K_with, "match");
288 accept(Tag::T_pipe);
289 do {
290 auto track = tracker();
291 auto ptrn = parse_ptrn({}, "right-hand side of a match-arm", Prec::Bot);
292 expect(Tag::T_fat_arrow, "arm of a match-expression");
293 auto body = parse_expr("arm of a match-expression");
294 arms.emplace_back(ptr<MatchExpr::Arm>(track, ptrn, body));
295 } while (accept(Tag::T_pipe));
296
297 return ptr<MatchExpr>(track, scrutinee, arms);
298}
299
300Ptr<Expr> Parser::parse_primary_expr(fe::Cite ctxt) {
301 // clang-format off
302 switch (ahead().tag()) {
303 case Tag::C_PRIMARY: return ptr<PrimaryExpr>(lex());
304 case Tag::C_ID: return ptr<PathExpr>(parse_path());
305 case Tag::C_LIT:
306 case Tag::C_SIGN: return parse_lit_expr();
307 case Tag::C_DECL: return parse_decl_expr();
308 case Tag::C_PI: return parse_pi_expr();
309 case Tag::C_LM: return parse_lam_expr();
310 case Tag::C_SEQ: return parse_seq_expr();
311 case Tag::K_ret: return parse_ret_expr();
312 case Tag::D_curly_l: return parse_uniq_expr();
313 case Tag::D_brckt_l: return parse_sigma_expr();
314 case Tag::D_paren_l: return parse_tuple_expr();
315 case Tag::K_Type: return parse_type_expr();
316 case Tag::K_Rule: return parse_rule_expr();
317 case Tag::K_match: return parse_match_expr();
318 default:
319 if (!ctxt) return nullptr;
320 syntax_err("primary expression", ctxt);
321 }
322 // clang-format on
323 return ptr<ErrorExpr>(missing());
324}
325
326Ptr<Expr> Parser::parse_seq_expr() {
327 auto track = tracker();
328 bool is_pack = ahead().isa(Tag::D_angle_l);
329 auto delim_l = is_pack ? Tag::D_angle_l : Tag::D_quote_l;
330 eat(delim_l);
331 auto _ = this->anchor(Tok::delim_l2r(delim_l));
332
333 Ptrs<IdPtrn> arities;
334
335 do {
336 Dbg dbg;
337 if (ahead(0).isa(Tag::M_id) && ahead(1).isa(Tag::T_colon)) {
338 dbg = eat(Tag::M_id).dbg();
339 eat(Tag::T_colon);
340 }
341
342 auto expr = parse_expr(fe::Cite(is_pack ? "shape of pack" : "shape of a array"));
343 arities.emplace_back(IdPtrn::make_id(ast(), dbg, expr));
344 } while (accept(Tag::T_comma));
345
346 expect(Tag::T_semicolon, fe::Cite(is_pack ? "pack" : "array"));
347 auto body = parse_expr(fe::Cite(is_pack ? "body of a pack" : "body of an array"));
348 recover(fe::Cite(is_pack ? "pack" : "array"));
349 expect(Tok::delim_l2r(delim_l),
350 fe::Cite(is_pack ? "closing delimiter of a pack" : "closing delimiter of an array"));
351
352 // `‹a, b; e›` nests one SeqExpr per arity; only the outermost one covers the delimiters.
353 for (auto& ptrn : arities | std::views::reverse) {
354 auto loc = &ptrn == &arities.front() ? Loc(track) : ptrn->loc() + curr_;
355 body = ptr<SeqExpr>(loc, is_pack, ptrn, body);
356 }
357
358 return body;
359}
360
361Ptr<Expr> Parser::parse_decl_expr() {
362 auto track = tracker();
363 auto decls = parse_decls();
364 auto expr = parse_expr("final expression of a declaration expression");
365 return ptr<DeclExpr>(track, expr, false, decls);
366}
367
368/// Applies a leading `-` to a numeric literal Tok.
369static Tok negate(Tok tok) {
370 switch (tok.tag()) {
371 case Tag::L_s:
372 case Tag::L_u: return {tok.loc(), -s64(tok.lit_u())};
373 case Tag::L_f: return {tok.loc(), -std::bit_cast<f64>(tok.lit_u())};
374 case Tag::L_i: {
375 auto [mod, val] = tok.lit_i();
376 return {tok.loc(), mod, mod == 0 ? -val : (mod - val % mod) % mod};
377 }
378 default: fe::unreachable();
379 }
380}
381
382Ptr<Expr> Parser::parse_lit_expr() {
383 auto track = tracker();
384 auto sign = ISA(ahead().tag(), C_SIGN) ? lex() : Tok();
385
386 if (sign && !ISA(ahead().tag(), C_LIT_NUM)) {
387 syntax_err("numeric literal", "signed literal");
388 return ptr<ErrorExpr>(missing());
389 }
390
391 auto tok = sign.isa(Tag::T_sub) ? negate(lex()) : lex();
392 auto type = accept(Tag::T_colon) ? parse_expr("literal", Prec::Lit) : nullptr;
393 return ptr<LitExpr>(track, tok, type);
394}
395
396Ptr<Expr> Parser::parse_sigma_expr() {
397 auto track = tracker();
398 auto ptrn = parse_tuple_ptrn({.brckt = true});
399 switch (ahead().tag()) {
400 case Tag::K_as: {
401 lex();
402 auto alias = ptr<AliasPtrn>(track, ptrn, parse_id("alias pattern"));
403 return parse_pi_expr(alias);
404 }
405 case Tag::C_CURRIED_B:
406 case Tag::T_arrow_r: return parse_pi_expr(ptrn); // TODO precedences for patterns
407 default: return ptr<SigmaExpr>(ptrn);
408 }
409}
410
411Ptr<Expr> Parser::parse_tuple_expr() {
412 auto track = tracker();
413 Ptrs<Expr> elems;
414 parse_list("tuple", Tag::D_paren_l, [&]() { elems.emplace_back(parse_expr("tuple element")); });
415 return ptr<TupleExpr>(track, elems);
416}
417
418Ptr<Expr> Parser::parse_type_expr() {
419 auto track = tracker();
420 eat(Tag::K_Type);
421 auto level = parse_expr("type level", Prec::App);
422 return ptr<TypeExpr>(track, level);
423}
424
425Ptr<Expr> Parser::parse_rule_expr() {
426 auto track = tracker();
427 eat(Tag::K_Rule);
428 return ptr<RuleExpr>(track, parse_expr("domain of rule", Prec::App));
429}
430
431Ptr<Expr> Parser::parse_pi_expr() {
432 auto track = tracker();
433 auto tag = ahead().tag();
434 fe::Cite entity = "dependent function type";
435
436 if (accept(Tag::K_Cn))
437 entity = "continuation type";
438 else if (accept(Tag::K_Fn))
439 entity = "returning continuation type";
440
441 auto domt = tracker();
442 auto prec = ISA(tag, C_CN) ? Prec::Bot : Prec::Pi;
443 auto ptrn = parse_ptrn({.brckt = true, .implicit = true}, prec, "domain of a {}", entity);
444 auto dom = ptr<PiExpr::Dom>(domt, ptrn);
445
446 auto codom = ISA(tag, C_CN) ? nullptr
447 : (expect(Tag::T_arrow_r, entity), parse_expr(Prec::Arrow, "codomain of a {}", entity));
448
449 if (ISA(tag, C_FN)) {
450 dom->add_ret(ast(), codom ? codom : ptr<HoleExpr>(missing()));
451 codom = nullptr; // the `ret` continuation is where it went
452 }
453 return ptr<PiExpr>(track, tag, dom, codom);
454}
455
456Ptr<Expr> Parser::parse_pi_expr(Ptr<Ptrn> ptrn) {
457 auto track = tracker(ptrn->loc());
458 fe::Cite entity = "dependent function type";
459 auto dom = ptr<PiExpr::Dom>(ptrn->loc(), ptrn);
460 expect(Tag::T_arrow_r, entity);
461 auto codom = parse_expr(Prec::Arrow, "codomain of a {}", entity);
462 return ptr<PiExpr>(track, Tag::Nil, dom, codom);
463}
464
465Ptr<Expr> Parser::parse_lam_expr() { return ptr<LamExpr>(parse_lam_decl(tracker(), {})); }
466
467Ptr<Expr> Parser::parse_ret_expr() {
468 auto track = tracker();
469 eat(Tag::K_ret);
470 auto ptrn = parse_ptrn({}, "binding pattern of a ret expression");
471 expect(Tag::T_assign, "ret expression");
472 auto callee = parse_expr("continuation expression of a ret expression");
473 expect(Tag::T_dollar, "separator of a ret expression");
474 auto arg = parse_expr("argument of ret expression");
475 expect(Tag::T_semicolon, "ret expression");
476 auto body = parse_expr("body of a ret expression");
477 return ptr<RetExpr>(track, ptrn, callee, arg, body);
478}
479
480/*
481 * ptrns
482 */
483
484Ptr<Ptrn> Parser::parse_ptrn(PtrnStyle style, fe::Cite ctxt, Prec prec) {
485 auto track = tracker();
486 auto ptrn = parse_ptrn_(style, ctxt, prec);
487 if (accept(Tag::K_as)) return ptr<AliasPtrn>(track, ptrn, parse_id("alias pattern"));
488 return ptrn;
489}
490
491Ptr<Ptrn> Parser::parse_ptrn_(PtrnStyle style, fe::Cite ctxt, Prec prec) {
492 auto track = tracker();
493
494 // p -> (p, ..., p)
495 // p -> {p, ..., p} b -> {b, ..., b}
496 // b -> [b, ..., b]
497 if (!style.brckt && ahead().isa(Tag::D_paren_l)) return parse_tuple_ptrn(style);
498 if (style.implicit && ahead().isa(Tag::D_brace_l)) return parse_tuple_ptrn(style);
499 if (style.brckt && ahead().isa(Tag::D_brckt_l)) return parse_tuple_ptrn(style);
500
501 // p -> s: e b -> s: e
502 if (ahead(0).isa(Tag::M_id) && ahead(1).isa(Tag::T_colon)) {
503 auto dbg = eat(Tag::M_id).dbg();
504 eat(Tag::T_colon);
505 auto type = parse_expr(ctxt, prec);
506 return ptr<IdPtrn>(track, dbg, type);
507 }
508
509 if (!style.brckt) {
510 // p -> s
511 if (auto id = accept(Tag::M_id)) return ptr<IdPtrn>(track, id.dbg(), nullptr);
512 // p -> ↯
513 syntax_err("pattern", ctxt);
514 return ptr<ErrorPtrn>(missing());
515 }
516
517 // b -> e
518 auto type = parse_expr(ctxt, prec);
519 return anon_ptrn(Loc(track), type);
520}
521
522Ptr<TuplePtrn> Parser::parse_tuple_ptrn(PtrnStyle style) {
523 auto track = tracker();
524 auto delim_l = ahead().tag();
525
526 Ptrs<Ptrn> ptrns;
527 parse_list("tuple pattern", delim_l, [&]() {
528 auto track = tracker();
529
530 if (ahead(0).isa(Tag::M_id) && ahead(1).isa(Tag::M_id)) {
531 Dbgs dbgs;
532 while (auto tok = accept(Tag::M_id))
533 dbgs.emplace_back(tok.dbg());
534
535 if (accept(Tag::T_colon)) { // identifier group: x y z: T
536 auto dbg = dbgs.back();
537 auto type = parse_expr("type of an identifier group within a tuple pattern");
538 auto id = ptr<IdPtrn>(dbg.loc() + type->loc().end, dbg, type);
539
540 for (auto dbg : dbgs | std::views::take(dbgs.size() - 1))
541 ptrns.emplace_back(ptr<GrpPtrn>(dbg, id.get()));
542 ptrns.emplace_back(id);
543 return;
544 }
545
546 if (!style.brckt) { // `(x y)` is two binders short a `,`, never an application
547 error().e(dbgs[1].loc(), "expected `,` or `:` between the binders of a tuple pattern");
548 for (auto dbg : dbgs)
549 ptrns.emplace_back(ptr<IdPtrn>(dbg.loc(), dbg, nullptr));
550 return;
551 }
552
553 // "x y z" is a curried app and maybe the prefix of a longer type expression
554 Ptr<Expr> lhs = path_expr(dbgs.front());
555 for (auto dbg : dbgs | std::views::drop(1)) {
556 auto loc = lhs->loc() + dbg.loc();
557 lhs = ptr<AppExpr>(loc, lhs, path_expr(dbg));
558 }
559 auto app = parse_infix_expr(track, lhs, Prec::Bot, "element of a tuple pattern");
560 auto loc = app->loc();
561 ptrns.emplace_back(anon_ptrn(loc, app));
562 return;
563 }
564
565 auto ptrn = parse_ptrn({.brckt = style.brckt}, "element of a tuple pattern");
566
567 // A binder may turn out to be the prefix of an expr: `[[Nat, Nat] -> Nat]`, `[[Nat] Nat]`.
568 if (style.brckt) {
569 if (ahead().isa(Tag::T_arrow_r)) {
570 auto loc = ptrn->loc();
571 ptrn = anon_ptrn(loc, parse_pi_expr(ptrn));
572 } else if (auto expr = Ptrn::to_expr(ast(), ptrn)) {
573 auto addr = expr.get();
574 expr = parse_infix_expr(track, expr, Prec::Bot, "element of a tuple pattern");
575 if (expr.get() != addr) ptrn = anon_ptrn(expr->loc(), expr);
576 }
577 }
578
579 ptrns.emplace_back(ptrn);
580 });
581
582 return ptr<TuplePtrn>(track, delim_l, ptrns);
583}
584
585/*
586 * decls
587 */
588
589Mods Parser::parse_modifiers() {
590 Mods mods;
591 while (true) {
592 // clang-format off
593 switch (ahead().tag()) {
594 case Tag::K_priv:
595 case Tag::K_pub: {
596 auto v = ahead().tag() == Tag::K_priv ? Vis::Priv : Vis::Pub;
597 if (mods.vis) error().e(curr_, "visibility already specified");
598 mods.vis = v;
599 lex();
600 continue;
601 }
602 case Tag::K_extern:
603 if (mods.is_extern) error().e(curr_, "`extern` already specified");
604 mods.is_extern = true;
605 lex();
606 continue;
607 case Tag::K_anx:
608 if (mods.is_anx) error().e(curr_, "`anx` already specified");
609 mods.is_anx = true;
610 lex();
611 continue;
612 default: return mods;
613 }
614 // clang-format on
615 }
616}
617
618void Parser::check_no_extern(const Mods& mods, fe::Cite entity) {
619 if (mods.is_extern) error().e(curr_, "`extern` is only meaningful on a function declaration, not a {}", entity);
620}
621
622Ptrs<ValDecl> Parser::parse_decls() {
623 Ptrs<ValDecl> decls;
624 while (true) {
625 auto track = tracker();
626 auto mods = parse_modifiers();
627 switch (ahead().tag()) {
628 case Tag::T_semicolon: lex(); break; // eat up stray semicolons
629 case Tag::K_axm: parse_axm_decl(track, mods, decls); break;
630 case Tag::K_let: decls.emplace_back(parse_let_decl(track, mods)); break;
631 case Tag::K_mod: decls.emplace_back(parse_mod_decl(track, mods)); break;
632 case Tag::K_use: decls.emplace_back(parse_use_decl(track, mods)); break;
633 case Tag::K_rec: decls.emplace_back(parse_rec_decl(track, true, mods)); break;
634 case Tag::C_LAM: decls.emplace_back(parse_lam_decl(track, mods)); break;
635 case Tag::C_RULE: decls.emplace_back(parse_rule_decl()); break;
636 case Tag::C_IMPORT:
637 if (auto i = parse_import_or_plugin(track, mods)) decls.emplace_back(i);
638 break;
639 case Tag::M_id:
640 if (mods.is_anx) {
641 decls.emplace_back(parse_alias_decl(track, mods));
642 break;
643 }
644 [[fallthrough]];
645 default:
646 if (mods.vis || mods.is_extern || mods.is_anx)
647 error().e(curr_, "expected a declaration after a modifier");
648 return decls;
649 }
650 }
651}
652
653std::tuple<Ptr<Expr>, Dbg, Tok, Tok> Parser::parse_axm_tail() {
654 auto type = parse_type_ascr("type ascription of an axm");
655 Dbg normalizer;
656 Tok curry, trip;
657 if (ahead(0).isa(Tag::T_comma) && ahead(1).isa(Tag::M_id)) {
658 lex();
659 normalizer = lex().dbg();
660 }
661 if (accept(Tag::T_comma)) {
662 if (auto c = expect(Tag::L_u, "curry counter for axm")) curry = c;
663 if (accept(Tag::T_comma)) {
664 if (auto t = expect(Tag::L_u, "trip count for axm")) trip = t;
665 }
666 }
667 return {type, normalizer, curry, trip};
668}
669
670void Parser::parse_axm_decl(Tracker track, Mods mods, Ptrs<ValDecl>& decls) {
671 eat(Tag::K_axm);
672
673 if (mods.is_extern) error().e(curr_, "`axm` is implicitly `anx`; cannot combine with `extern`");
674 auto vis = mods.vis.value_or(Vis::Pub); // axm is always anx, so it always gets the pub nudge
675
676 if (ahead().isa(Tag::D_paren_l)) {
677 for (auto decl : parse_axm_group(vis))
678 decls.emplace_back(decl);
679 return;
680 }
681
682 auto dbg = parse_id("name of an axm");
683 if (accept(Tag::T_dot)) {
684 auto group = parse_axm_group(vis);
685 decls.emplace_back(ptr<ModDecl>(track, Vis::Pub, dbg, ast().scope(), ast().copy(group)));
686 return;
687 }
688
689 auto [type, normalizer, curry, trip] = parse_axm_tail();
690 decls.emplace_back(ptr<AxmDecl>(track, vis, dbg, type, normalizer, curry, trip));
691}
692
693Ptrs<ValDecl> Parser::parse_axm_group(Vis vis) {
694 fe::Vector<Dbgs> members;
695 parse_list("tag list of an axm", Tag::D_paren_l, [&]() {
696 Dbgs names;
697 names.emplace_back(parse_id("tag of an axm"));
698 while (accept(Tag::T_assign))
699 names.emplace_back(parse_id("alias of an axm tag"));
700 members.emplace_back(std::move(names));
701 });
702
703 auto [type, normalizer, curry, trip] = parse_axm_tail();
704
705 Ptrs<ValDecl> decls;
706 const AxmDecl* owner = nullptr;
707 for (auto& names : members) {
708 auto primary = names.front();
709 if (!owner) {
710 auto axm = ptr<AxmDecl>(primary.loc(), vis, primary, type, normalizer, curry, trip);
711 owner = axm.get();
712 decls.emplace_back(axm);
713 } else {
714 decls.emplace_back(ptr<AxmDecl::Sibling>(primary.loc(), vis, primary, owner));
715 }
716 for (auto alias : names | std::views::drop(1))
717 decls.emplace_back(ptr<AliasDecl>(alias.loc(), Vis::Pub, alias, path(primary)));
718 }
719 return decls;
720}
721
722Ptr<ValDecl> Parser::parse_alias_decl(Tracker track, Mods mods) {
723 if (mods.is_extern) error().e(curr_, "`extern` and `anx` cannot be combined on an alias declaration");
724 auto vis = mods.vis.value_or(Vis::Pub); // always anx, so always the pub nudge unless overridden
725 auto dbg = parse_id("name of an alias declaration");
726 expect(Tag::T_assign, "alias declaration");
727 auto path = parse_path("target of an alias declaration");
728 return ptr<AliasDecl>(track, vis, dbg, path);
729}
730
731Ptr<ValDecl> Parser::parse_let_decl(Tracker track, Mods mods) {
732 check_no_extern(mods, "let declaration");
733 eat(Tag::K_let);
734 auto ptrn = parse_ptrn({}, "binding pattern of a let declaration", Prec::Bot);
735 expect(Tag::T_assign, "let");
736 parse_type_ascr();
737 auto value = parse_expr("value of a let declaration");
738 return ptr<LetDecl>(track, mods, ptrn, value);
739}
740
741Ptr<ValDecl> Parser::parse_mod_decl(Tracker track, Mods mods) {
742 // a mod is pure AST grouping, not a single value, so neither `extern` nor `anx` apply to it
743 if (mods.is_extern) error().e(curr_, "`extern` is only meaningful on a function declaration, not a module");
744 if (mods.is_anx) error().e(curr_, "`anx` doesn't apply to a module - it groups declarations, not a single value");
745 auto vis = mods.vis.value_or(Vis::Priv);
746 eat(Tag::K_mod);
747 auto dbg = parse_id("name of a module");
748 expect(Tag::D_brace_l, "opening brace of a module");
749 auto _ = this->anchor(Tag::D_brace_r);
750 auto decls = parse_decls();
751 recover("module");
752 expect(Tag::D_brace_r, "closing brace of a module");
753 return ptr<ModDecl>(track, vis, dbg, ast().scope(), ast().copy(decls));
754}
755
756Ptr<ValDecl> Parser::parse_use_decl(Tracker track, Mods mods) {
757 check_no_extern(mods, "use declaration");
758 if (mods.is_anx) error().e(curr_, "`anx` doesn't apply to a use declaration - it never represents a single value");
759 eat(Tag::K_use);
760 auto path = parse_path("module of a use declaration");
761 Dbg alias;
762 // `use path;` is sugar for `use path as *;`
763 if (accept(Tag::K_as) && !accept(Tag::T_star)) alias = parse_id("alias of a use declaration");
764 expect(Tag::T_semicolon, "end of a use declaration");
765 return ptr<UseDecl>(track, Mods{mods.vis.value_or(Vis::Priv)}, path, alias);
766}
767
768Ptr<RecDecl> Parser::parse_rec_decl(Tracker track, bool first, Mods mods) {
769 check_no_extern(mods, "recursive declaration");
770 eat(first ? Tag::K_rec : Tag::K_and);
771 auto dbg = parse_id("recursive declaration");
772 expect(Tag::T_assign, "recursive declaration");
773 auto body = parse_expr("body of a recursive declaration");
774 auto next = ahead().isa(Tag::K_and) ? parse_and_decl() : nullptr;
775 return ptr<RecDecl>(track, mods, dbg, body, next);
776}
777
778Ptr<ValDecl> Parser::parse_rule_decl() {
779 auto track = tracker();
780 auto is_norm = lex().tag() == Tag::K_norm;
781 auto dbg = parse_id("rewrite rule");
782 auto ptrn = parse_ptrn({}, "meta variables in rewrite rule");
783 expect(Tag::T_colon, "rewrite rule declaration");
784 auto lhs = parse_expr("rewrite pattern");
785 auto guard = ahead().isa(Tag::K_when) ? (eat(Tag::K_when), parse_expr("rewrite guard"))
786 : ptr<PrimaryExpr>(missing(), Tag::K_tt);
787 expect(Tag::T_fat_arrow, "rewrite rule declaration");
788 auto rhs = parse_expr("rewrite result");
789 return ptr<RuleDecl>(track, dbg, ptrn, lhs, rhs, guard, is_norm);
790}
791
792Ptr<LamDecl> Parser::parse_lam_decl(Tracker track, Mods mods) {
793 // unlike let/mod/rec, a function declaration is the one place `extern` currently makes sense
794 if (mods.is_extern && mods.is_anx)
795 error().e(curr_, "`extern` and `anx` cannot be combined on a function declaration");
796 auto tag = lex().tag();
797 auto prec = ISA(tag, C_CN) ? Prec::Bot : Prec::Pi;
798
799 bool decl;
800 fe::Cite entity;
801 // clang-format off
802 switch (tag) {
803 case Tag::T_lm: decl = false; entity = "function expression"; break;
804 case Tag::K_cn: decl = false; entity = "continuation expression"; break;
805 case Tag::K_fn: decl = false; entity = "returning continuation expression"; break;
806 case Tag::K_lam: decl = true ; entity = "function declaration"; break;
807 case Tag::K_con: decl = true ; entity = "continuation declaration"; break;
808 case Tag::K_fun: decl = true ; entity = "returning continuation declaration"; break;
809 default: fe::unreachable();
810 }
811 // clang-format on
812
813 auto dbg = decl ? parse_id(entity) : Dbg();
815 while (true) {
816 auto track = tracker();
817 // A domain spelled with brackets is a telescope; the check after the body rejects that where names must bind.
818 auto style = ahead().isa(Tag::D_brckt_l) ? PtrnStyle{.brckt = true} : PtrnStyle{.implicit = true};
819 auto ptrn = parse_ptrn(style, prec, "domain pattern of a {}", entity);
820 auto filter = accept(Tag::T_at) ? parse_expr("filter") : nullptr;
821 doms.emplace_back(ptr<LamDecl::Dom>(track, ptrn, filter));
822
823 if (!ISA(ahead().tag(), C_CURRIED_P)) break;
824 }
825
826 // The `: codom` slot ends at `=`, so it takes everything short of a `where`.
827 auto codom = accept(Tag::T_colon) ? parse_expr(Prec(int(Prec::Where) + 1), "codomain of a {}", entity) : nullptr;
828 if (ISA(tag, C_FN)) {
829 doms.back()->add_ret(ast(), codom ? codom : ptr<HoleExpr>(missing()));
830 codom = nullptr; // the `ret` continuation is where it went
831 }
832
833 Ptr<Expr> body;
834 if (decl && mods.is_extern && ahead().isa(Tag::T_semicolon)) {
835 // forward declaration - the implementation lives in a native translation unit
836 } else {
837 expect(Tag::T_assign, "body of a {}", entity);
838 body = parse_expr("body of a {}", entity);
839
840 // Only a forward declaration may spell its domain as a telescope; with a body the names must actually bind.
841 for (auto dom : doms)
842 if (auto tuple = dom->ptrn()->isa<TuplePtrn>(); tuple && tuple->is_brckt())
843 error()
844 .e(tuple->loc(), "a {} with a body must spell its domain as a `(...)` pattern", entity)
845 .n("`[...]` describes a type, so its names bind nothing here")
846 .n("write an unnamed component as `_: T`");
847 }
848 auto next = ahead().isa(Tag::K_and) ? parse_and_decl() : nullptr;
849
850 return ptr<LamDecl>(track, mods, tag, dbg, codom, body, next, doms);
851}
852
853Ptr<RecDecl> Parser::parse_and_decl() {
854 if (ISA(ahead(1).tag(), C_LAM)) {
855 lex();
856 auto track = tracker();
857 return parse_lam_decl(track, {});
858 }
859 return parse_rec_decl(tracker(), false, {});
860}
861
862} // namespace mim::ast
void add(const fe::Src *src, Sym, ast::Tok::Tag, bool path)
Remembers the directive that pulled in src; a repeated import of the same file adds nothing.
Definition driver.cpp:43
const Imports & imports() const
Definition driver.h:166
void load(std::string_view name)
Definition driver.cpp:126
fe::Log & log()
Definition driver.h:78
std::pair< Ptr< File > &, bool > file(const fe::Src *src)
Definition ast.cpp:23
The AST of one source file: an anonymous ModDecl that a UseDecl binds under a name of its own.
Definition ast.h:1204
static Ptr< IdPtrn > make_id(AST &ast, Dbg dbg, Ptr< Expr > type)
Definition ast.h:325
static bool is_id(std::string_view str)
Does str match the id production - the same rule Lexer::lex_id applies to the input?
Definition lexer.cpp:221
Lexer(Driver &driver, const fe::Src &src, std::ostream *md=nullptr)
Creates a lexer to read *.mim files (see Lexical Structure).
Definition lexer.h:19
const File * import_main(std::string_view input, fe::View< std::string > plugins, std::ostream *md=nullptr)
Definition parser.cpp:126
const File * import(std::string_view sv, Tok::Tag tag=Tok::Tag::K_import)
Definition parser.h:39
Ptrs< UseDecl > import_plugins(fe::View< std::string > plugins, Tok::Tag)
Imports the plugins the Driver was told about via -p as anonymous, unaliased UseDecls.
Definition parser.cpp:116
AST & ast()
Definition parser.h:37
Driver & driver()
fe::Parser's default diagnostics go to its Driver::error.
Definition parser.h:38
static Ptr< Expr > to_expr(AST &, Ptr< Ptrn >)
Definition ast.cpp:191
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
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
static constexpr Tok::Tag delim_l2r(Tag tag)
Definition tok.h:269
Tag tag() const
Definition tok.h:310
Families of Tok::Tag as reusable case labels; include this in *.cpp files only.
#define C_EXPR
Definition family.h:114
#define C_SIGN
Leading sign of a numeric literal.
Definition family.h:50
#define C_LIT_NUM
Numeric literals that a leading sign may be applied to.
Definition family.h:43
#define C_CURRIED_P
Definition family.h:135
#define ISA(tag, family)
Turns such a family into a predicate - a case label is of no use outside of a switch.
Definition family.h:142
#define C_FN
Binders that receive an implicit ret continuation.
Definition family.h:95
#define C_CN
Binders whose domain binds as tight as a Cn, i.e. no codomain follows.
Definition family.h:89
#define C_LAM
Definition family.h:60
#define C_DECL
Definition family.h:73
Definition ast.h:16
Vis
Visibility tier of a ValDecl.
Definition ast.h:41
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
fe::Vector< Dbg > Dbgs
Definition ast.h:34
fe::Arena::Ref< const T > Ptr
Nodes live in the AST's Arena and are never destroyed, so this merely points at one.
Definition ast.h:26
static Tok negate(Tok tok)
Applies a leading - to a numeric literal Tok.
Definition parser.cpp:369
Tok::Tag Tag
Definition bind.cpp:9
fe::Vector< Ptr< T > > Ptrs
Definition ast.h:33
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
int64_t s64
Definition types.h:27
Raw, unvalidated combination of priv/pub/extern/anx modifiers written before a declaration.
Definition ast.h:55
bool is_anx
Definition ast.h:58
std::optional< Vis > vis
Definition ast.h:56
bool is_extern
Definition ast.h:57