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