MimIR 0.1
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
dump.cpp
Go to the documentation of this file.
1#include <fstream>
2
3#include <fe/assert.h>
4
5#include "mim/driver.h"
6#include "mim/nest.h"
7
8#include "mim/ast/tok.h"
9#include "mim/util/util.h"
10
11using namespace std::literals;
12
13// During dumping, we classify Defs according to the following logic:
14// * Inline: These Defs are *always* displayed with all of its operands "inline".
15// E.g.: (1, 2, 3).
16// * All other Defs are referenced by its name/unique_name (see id) when they appear as an operand.
17// * Mutables are either classifed as "decl" (see isa_decl).
18// In this case, recursing through the Defs' operands stops and this particular Decl is dumped as its own thing.
19// * Or - if they are not a "decl" - they are basicallally handled like immutables.
20
21namespace mim {
22
23namespace {
24
25Def* isa_decl(const Def* def) {
26 if (auto mut = def->isa_mut()) {
27 if (mut->is_external() || mut->isa<Lam>() || (mut->sym() && mut->sym() != '_')) return mut;
28 }
29 return nullptr;
30}
31
32std::string id(const Def* def) {
33 if (def->is_external() || (!def->is_set() && def->isa<Lam>())) return def->sym().str();
34 return def->unique_name();
35}
36
37std::string_view external(const Def* def) {
38 if (def->is_external()) return "extern "sv;
39 return ""sv;
40}
41
42/*
43 * Inline + LRPrec
44 */
45
46/// This is a wrapper to dump a Def "inline" and print it with all of its operands.
47struct Inline {
48 Inline(const Def* def, int dump_gid)
49 : def_(def)
50 , dump_gid_(dump_gid) {}
51 Inline(const Def* def)
52 : Inline(def, def->world().flags().dump_gid) {}
53
54 const Def* operator->() const { return def_; }
55 const Def* operator*() const { return def_; }
56 explicit operator bool() const {
57 if (auto mut = def_->isa_mut()) {
58 if (isa_decl(mut)) return false;
59 return true;
60 }
61
62 if (def_->is_closed()) return true;
63
64 if (auto app = def_->isa<App>()) {
65 if (app->type()->isa<Pi>()) return true; // curried apps are printed inline
66 if (app->type()->isa<Type>()) return true;
67 if (app->callee()->isa<Axm>()) return app->callee_type()->num_doms() <= 1;
68 return false;
69 }
70
71 return true;
72 }
73
74private:
75 const Def* def_;
76 const int dump_gid_;
77
78 friend std::ostream& operator<<(std::ostream&, Inline);
79};
80
81using ast::Assoc;
82using ast::Prec;
83using ast::prec_assoc;
84
85bool is_atomic_app(const Def* def) {
86 if (auto app = def->isa<App>()) {
87 if (auto size = Idx::isa(app)) {
88 if (auto l = Lit::isa(size)) {
89 switch (*l) {
90 // clang-format off
91 case 0x0'0000'0002_n:
92 case 0x0'0000'0100_n:
93 case 0x0'0001'0000_n:
94 case 0x1'0000'0000_n:
95 case 0_n: return true;
96 // clang-format on
97 default: break;
98 }
99 }
100 }
101 }
102 return false;
103}
104
105Prec def2prec(const Def* def) {
106 if (def->isa<Extract>()) return Prec::Extract;
107 if (auto pi = def->isa<Pi>(); pi && !Pi::isa_cn(pi)) return Prec::Arrow;
108 if (def->isa<App>() && !is_atomic_app(def)) return Prec::App;
109 return Prec::Lit;
110}
111
112bool needs_parens(Prec parent, const Def* child, bool is_left) {
113 if (!Inline(child)) return false;
114
115 auto child_prec = def2prec(child);
116 if (child_prec < parent) return true;
117 if (child_prec > parent) return false;
118
119 switch (prec_assoc(parent)) {
120 case Assoc::R: return is_left;
121 case Assoc::L: return !is_left;
122 case Assoc::N: return false;
123 }
124 fe::unreachable();
125}
126
127std::ostream& dump_child(std::ostream& os, Prec parent, const Def* child, bool is_left) {
128 if (needs_parens(parent, child, is_left)) return print(os, "({})", child);
129 return print(os, "{}", child);
130}
131
132std::ostream& dump_ascribed(std::ostream& os, const auto& value, const Def* type) {
133 os << value << ':';
134 if (def2prec(type) == Prec::Lit) return print(os, "{}", type);
135 return print(os, "({})", type);
136}
137
138std::ostream& dump_pi_dom(std::ostream& os, const Pi* pi) {
139 auto mut = pi->isa_mut<Pi>();
140 auto l = pi->is_implicit() ? '{' : '[';
141 auto r = pi->is_implicit() ? '}' : ']';
142
143 if (mut && mut->var()) {
144 os << l << mut->var() << ": ";
145 dump_child(os, Prec::Arrow, pi->dom(), true);
146 return os << r;
147 }
148
149 return dump_child(os, Prec::Arrow, pi->dom(), true);
150}
151
152std::ostream& operator<<(std::ostream& os, Inline u) {
153 if (u.dump_gid_ == 2 || (u.dump_gid_ == 1 && !u->isa<Var>() && u->num_ops() != 0)) print(os, "/*{}*/", u->gid());
154 if (auto mut = u->isa_mut(); mut && !mut->is_set()) return os << "unset";
155
156 bool ascii = u->world().flags().ascii;
157 auto arw = ascii ? "->" : "→";
158 auto al = ascii ? "<<" : "«";
159 auto ar = ascii ? ">>" : "»";
160 auto pl = ascii ? "(<" : "‹";
161 auto pr = ascii ? ">)" : "›";
162
163 if (auto type = u->isa<Type>()) {
164 if (auto level = Lit::isa(type->level()); level && !ascii) {
165 if (level == 0) return print(os, "*");
166 if (level == 1) return print(os, "□");
167 }
168 os << "Type ";
169 return dump_child(os, Prec::App, type->level(), false);
170 } else if (u->isa<Nat>()) {
171 return print(os, "Nat");
172 } else if (u->isa<Idx>()) {
173 return print(os, "Idx");
174 } else if (auto ext = u->isa<Ext>()) {
175 auto x = ext->isa<Bot>() ? (ascii ? "bot" : "⊥") : (ascii ? "top" : "⊤");
176 return dump_ascribed(os, x, ext->type());
177 } else if (auto top = u->isa<Top>()) {
178 return dump_ascribed(os, ascii ? "top" : "⊤", top->type());
179 } else if (auto axm = u->isa<Axm>()) {
180 const auto name = axm->sym();
181 return print(os, "{}{}", name[0] == '%' ? "" : "%", name);
182 } else if (auto lit = u->isa<Lit>()) {
183 if (lit->type()->isa<Nat>()) {
184 switch (lit->get()) {
185 // clang-format off
186 case 0x0'0000'0100_n: return os << "i8";
187 case 0x0'0001'0000_n: return os << "i16";
188 case 0x1'0000'0000_n: return os << "i32";
189 // clang-format on
190 default: return print(os, "{}", lit->get());
191 }
192 } else if (auto size = Idx::isa(lit->type())) {
193 if (auto s = Lit::isa(size)) {
194 switch (*s) {
195 // clang-format off
196 case 0x0'0000'0002_n: return os << (lit->get<bool>() ? "tt" : "ff");
197 case 0x0'0000'0100_n: return os << lit->get() << "I8";
198 case 0x0'0001'0000_n: return os << lit->get() << "I16";
199 case 0x1'0000'0000_n: return os << lit->get() << "I32";
200 case 0_n: return os << lit->get() << "I64";
201 default: {
202 os << lit->get();
203 std::vector<uint8_t> digits;
204 for (auto z = *s; z; z /= 10) digits.emplace_back(z % 10);
205
206 if (ascii) {
207 os << '_';
208 for (auto d : digits | std::ranges::views::reverse)
209 os << char('0' + d);
210 } else {
211 for (auto d : digits | std::ranges::views::reverse)
212 os << uint8_t(0xE2) << uint8_t(0x82) << (uint8_t(0x80 + d));
213 }
214 return os;
215 }
216 // clang-format on
217 }
218 }
219 }
220 return dump_ascribed(os, lit->get(), lit->type());
221 } else if (auto ex = u->isa<Extract>()) {
222 if (ex->tuple()->isa<Var>() && ex->index()->isa<Lit>()) return print(os, "{}", ex->unique_name());
223 dump_child(os, Prec::Extract, ex->tuple(), true);
224 os << '#';
225 return dump_child(os, Prec::Extract, ex->index(), false);
226 } else if (auto var = u->isa<Var>()) {
227 return print(os, "{}", var->unique_name());
228 } else if (auto pi = u->isa<Pi>()) {
229 if (Pi::isa_cn(pi)) {
230 os << "Cn ";
231 return dump_pi_dom(os, pi);
232 }
233 dump_pi_dom(os, pi);
234 os << ' ' << arw << ' ';
235 return dump_child(os, Prec::Arrow, pi->codom(), false);
236 } else if (auto lam = u->isa<Lam>()) {
237 return print(os, "{}, {}", lam->filter(), lam->body());
238 } else if (auto app = u->isa<App>()) {
239 if (auto size = Idx::isa(app)) {
240 if (auto l = Lit::isa(size)) {
241 // clang-format off
242 switch (*l) {
243 case 0x0'0000'0002_n: return os << "Bool";
244 case 0x0'0000'0100_n: return os << "I8";
245 case 0x0'0001'0000_n: return os << "I16";
246 case 0x1'0000'0000_n: return os << "I32";
247 case 0_n: return os << "I64";
248 default: break;
249 }
250 // clang-format on
251 }
252 }
253 dump_child(os, Prec::App, app->callee(), true);
254 os << ' ';
255 return dump_child(os, Prec::App, app->arg(), false);
256 } else if (auto sigma = u->isa<Sigma>()) {
257 if (auto mut = sigma->isa_mut<Sigma>(); mut && mut->var()) {
258 size_t i = 0;
259 return print(os, "[{, }]", Elem(sigma->ops(), [&](auto op) {
260 if (auto v = mut->var(i++))
261 print(os, "{}: {}", v, op);
262 else
263 os << op;
264 }));
265 }
266 return print(os, "[{, }]", sigma->ops());
267 } else if (auto tuple = u->isa<Tuple>()) {
268 print(os, "({, })", tuple->ops());
269 return tuple->type()->isa_mut() ? print(os, ":{}", tuple->type()) : os;
270 } else if (auto arr = u->isa<Arr>()) {
271 if (auto mut = arr->isa_mut<Arr>(); mut && mut->var())
272 return print(os, "{}{}: {}; {}{}", al, mut->var(), mut->arity(), mut->body(), ar);
273 return print(os, "{}{}; {}{}", al, arr->arity(), arr->body(), ar);
274 } else if (auto pack = u->isa<Pack>()) {
275 if (auto mut = pack->isa_mut<Pack>(); mut && mut->var())
276 return print(os, "{}{}: {}; {}{}", pl, mut->var(), mut->arity(), mut->body(), pr);
277 return print(os, "{}{}; {}{}", pl, pack->arity(), pack->body(), pr);
278 } else if (auto proxy = u->isa<Proxy>()) {
279 return print(os, ".proxy#{}#{} {, }", proxy->pass(), proxy->tag(), proxy->ops());
280 } else if (auto bound = u->isa<Bound>()) {
281 auto op = bound->isa<Join>() ? "∪" : "∩"; // TODO ascii
282 if (auto mut = u->isa_mut()) print(os, "{}{}: {}", op, mut->unique_name(), mut->type());
283 return print(os, "{}({, })", op, bound->ops());
284 }
285
286 // other
287 if (u->flags() == 0) return print(os, "(.{} {, })", u->node_name(), u->ops());
288 return print(os, "(.{}#{} {, })", u->node_name(), u->flags(), u->ops());
289}
290
291/*
292 * Dumper
293 */
294
295/// This thing operates in two modes:
296/// 1. The output of decls is driven by the Nest.
297/// 2. Alternatively, decls are output as soon as they appear somewhere during recurse%ing.
298/// Then, they are pushed to Dumper::muts.
299class Dumper {
300public:
301 Dumper(std::ostream& os, const Nest* nest = nullptr)
302 : os(os)
303 , nest(nest) {}
304
305 void dump(Def*);
306 void dump(Lam*);
307 void dump_let(const Def*);
308 void dump_ptrn(const Def*, const Def*);
309 void dump_group(const Def*, const Def*, bool implicit, bool paren_style, size_t limit, bool alias = true);
310 void recurse(const Nest::Node*);
311 void recurse(const Def*, bool first = false);
312
313 std::ostream& os;
314 const Nest* nest;
315 Tab tab;
316 unique_queue<MutSet> muts;
317 DefSet defs;
318};
319
320void Dumper::dump(Def* mut) {
321 if (auto lam = mut->isa<Lam>()) {
322 dump(lam);
323 return;
324 }
325
326 auto mut_prefix = [&](const Def* def) {
327 if (def->isa<Sigma>()) return "Sigma";
328 if (def->isa<Arr>()) return "Arr";
329 if (def->isa<Pack>()) return "pack";
330 if (def->isa<Pi>()) return "Pi";
331 if (def->isa<Hole>()) return "Hole";
332 if (def->isa<Rule>()) return "Rule";
333 fe::unreachable();
334 };
335
336 auto mut_op0 = [&](const Def* def) -> std::ostream& {
337 if (auto sig = def->isa<Sigma>()) return print(os, ", {}", sig->num_ops());
338 if (auto arr = def->isa<Arr>()) return print(os, ", {}", arr->arity());
339 if (auto pack = def->isa<Pack>()) return print(os, ", {}", pack->arity());
340 if (auto pi = def->isa<Pi>()) return print(os, ", {}", pi->dom());
341 if (auto hole = def->isa_mut<Hole>()) return hole->is_set() ? print(os, ", {}", hole->op()) : print(os, ", ??");
342 if (auto rule = def->isa<Rule>()) return print(os, "{} => {}", rule->lhs(), rule->rhs());
343 fe::unreachable();
344 };
345
346 if (!mut->is_set()) {
347 tab.print(os, "{}: {} = {{ <unset> }};", id(mut), mut->type());
348 return;
349 }
350
351 tab.print(os, "{} {}{}: {}", mut_prefix(mut), external(mut), id(mut), mut->type());
352 mut_op0(mut);
353 if (mut->var()) { // TODO rewrite with dedicated methods
354 if (auto e = mut->num_vars(); e != 1) {
355 print(os, "{, }", Elem(mut->vars(), [&](auto def) {
356 if (def)
357 os << def->unique_name();
358 else
359 os << "<TODO>";
360 }));
361 } else {
362 print(os, ", @{}", mut->var()->unique_name());
363 }
364 }
365 tab.println(os, " = {{");
366 ++tab;
367 if (nest) recurse((*nest)[mut]);
368 recurse(mut);
369 tab.print(os, "{, }\n", mut->ops());
370 --tab;
371 tab.print(os, "}};\n");
372}
373
374void Dumper::dump(Lam* lam) {
375 std::vector<Lam*> groups;
376 for (Lam* curr = lam;;) {
377 groups.emplace_back(curr);
378 auto body = curr->body();
379 if (!body) break;
380 auto next = body->isa<Lam>();
381 if (!next) break;
382 curr = const_cast<Lam*>(next);
383 }
384
385 auto last = groups.back();
386 auto is_fun = Lam::isa_returning(last);
387 auto is_con = Lam::isa_cn(last) && !is_fun;
388
389 tab.print(os, "{} {}{}", is_fun ? "fun" : is_con ? "con" : "lam", external(lam), id(lam));
390 for (auto* group : groups) {
391 os << ' ';
392 auto num_doms = group->var() ? group->var()->num_tprojs() : group->type()->dom()->num_tprojs();
393 auto limit = is_fun && group == last ? num_doms - 1 : num_doms;
394 dump_group(group->var(),
395 group->type()->dom(),
396 group->type()->is_implicit(),
397 !is_con,
398 limit,
399 !is_fun || group != last);
400 if (is_con && group == last) print(os, "@({})", group->filter());
401 }
402
403 if (is_fun)
404 print(os, ": {} =", last->ret_dom());
405 else if (!is_con)
406 print(os, ": {} =", last->type()->codom());
407 else
408 print(os, " =");
409 os << '\n';
410
411 ++tab;
412 if (last->is_set()) {
413 if (nest && groups.size() == 1) recurse((*nest)[lam]);
414 for (auto* group : groups)
415 recurse(group->filter());
416 recurse(last->body(), true);
417 if (last->body()->isa_mut())
418 tab.print(os, "{};\n", last->body());
419 else
420 tab.print(os, "{};\n", Inline(last->body()));
421 } else {
422 tab.print(os, "<unset>;\n");
423 }
424 --tab;
425 tab.print(os, "\n");
426}
427
428void Dumper::dump_let(const Def* def) {
429 tab.print(os, "let {}: {} = {};\n", def->unique_name(), def->type(), Inline(def, 0));
430}
431
432void Dumper::dump_ptrn(const Def* def, const Def* type) {
433 if (!def) {
434 os << type;
435 } else {
436 auto projs = def->tprojs();
437 if (projs.size() == 1 || std::ranges::all_of(projs, [](auto def) { return !def; })) {
438 print(os, "{}: {}", def->unique_name(), type);
439 } else {
440 size_t i = 0;
441 print(os,
442 "({, }) as {}",
443 Elem(projs, [&](auto proj) { dump_ptrn(proj, type->proj(i++)); }),
444 def->unique_name());
445 }
446 }
447}
448
449void Dumper::dump_group(const Def* def, const Def* type, bool implicit, bool paren_style, size_t limit, bool alias) {
450 auto l = implicit ? '{' : paren_style ? '(' : '[';
451 auto r = implicit ? '}' : paren_style ? ')' : ']';
452 auto dump_binder = [&](const Def* binder, const Def* binder_type) {
453 if (binder)
454 dump_ptrn(binder, binder_type);
455 else
456 print(os, "_: {}", binder_type);
457 };
458
459 if (limit == 0) {
460 os << l << r;
461 return;
462 }
463
464 if (limit == 1) {
465 os << l;
466 dump_binder(def ? def->tproj(0) : nullptr, type->tproj(0));
467 os << r;
468 return;
469 }
470
471 os << l;
472 print(os,
473 "{, }",
474 Elem(std::views::iota(size_t(0), limit), [&](auto i) { dump_binder(def ? def->tproj(i) : nullptr, type->tproj(i)); }));
475 os << r;
476 if (alias && def) print(os, " as {}", def->unique_name());
477}
478
479void Dumper::recurse(const Nest::Node* node) {
480 for (auto child : node->children().muts())
481 if (auto mut = isa_decl(child)) dump(mut);
482}
483
484void Dumper::recurse(const Def* def, bool first /*= false*/) {
485 if (auto mut = isa_decl(def)) {
486 if (!nest) muts.push(mut);
487 return;
488 }
489
490 if (!defs.emplace(def).second) return;
491
492 for (auto op : def->deps())
493 recurse(op);
494
495 if (!first && !Inline(def)) dump_let(def);
496}
497
498} // namespace
499
500/*
501 * Def
502 */
503
504/// This will stream @p def as an operand.
505/// This is usually `id(def)` unless it can be displayed Inline.
506std::ostream& operator<<(std::ostream& os, const Def* def) {
507 if (def == nullptr) return os << "<nullptr>";
508 if (Inline(def)) {
509 auto freezer = World::Freezer(def->world());
510 return os << Inline(def);
511 }
512 return os << id(def);
513}
514
515std::ostream& Def::stream(std::ostream& os, int max) const {
516 auto freezer = World::Freezer(world());
517 auto dumper = Dumper(os);
518
519 if (max == 0) {
520 os << this << std::endl;
521 } else if (auto mut = isa_decl(this)) {
522 dumper.muts.push(mut);
523 } else {
524 dumper.recurse(this);
525 dumper.tab.print(os, "{}\n", Inline(this));
526 --max;
527 }
528
529 for (; !dumper.muts.empty() && max > 0; --max)
530 dumper.dump(dumper.muts.pop());
531
532 return os;
533}
534
535void Def::dump() const { std::cout << this << std::endl; }
536void Def::dump(int max) const { stream(std::cout, max) << std::endl; }
537
538void Def::write(int max, const char* file) const {
539 auto ofs = std::ofstream(file);
540 stream(ofs, max);
541}
542
543void Def::write(int max) const {
544 auto file = id(this) + ".mim"s;
545 write(max, file.c_str());
546}
547
548/*
549 * World
550 */
551
552void World::dump(std::ostream& os) {
553 auto freezer = World::Freezer(*this);
554 auto old_gid = curr_gid();
555
556 if (flags().dump_recursive) {
557 auto dumper = Dumper(os);
558 for (auto mut : externals().muts())
559 dumper.muts.push(mut);
560 while (!dumper.muts.empty())
561 dumper.dump(dumper.muts.pop());
562 } else {
563 auto nest = Nest(*this);
564 auto dumper = Dumper(os, &nest);
565
566 for (const auto& import : driver().imports())
567 print(os,
568 "{} {};\n",
569 import.tag == ast::Tok::Tag::K_plugin ? "plugin" : "import",
570 import.sym);
571 dumper.recurse(nest.root());
572 }
573
574 assertf(old_gid == curr_gid(), "new nodes created during dump. old_gid: {}; curr_gid: {}", old_gid, curr_gid());
575}
576
577void World::dump() { dump(std::cout); }
578
580 if (log().level() >= Log::Level::Debug) dump(log().ostream());
581}
582
583void World::write(const char* file) {
584 auto ofs = std::ofstream(file);
585 dump(ofs);
586}
587
589 auto file = name().str() + ".mim"s;
590 write(file.c_str());
591}
592
593} // namespace mim
A (possibly paramterized) Array.
Definition tuple.h:117
Definition axm.h:9
Common base for TBound.
Definition lattice.h:13
Base class for all Defs.
Definition def.h:251
void dump() const
Definition dump.cpp:535
World & world() const noexcept
Definition def.cpp:439
const Def * var(nat_t a, nat_t i) noexcept
Definition def.h:429
void write(int max) const
Definition dump.cpp:543
std::ostream & stream(std::ostream &, int max) const
Definition dump.cpp:515
Common base for TExtremum.
Definition lattice.h:144
Extracts from a Sigma or Array-typed Extract::tuple the element at position Extract::index.
Definition tuple.h:206
A built-in constant of type Nat -> *.
Definition def.h:861
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
Definition def.cpp:611
A function.
Definition lam.h:110
static std::optional< T > isa(const Def *def)
Definition def.h:826
Builds a nesting tree of all mutables‍/binders.
Definition nest.h:12
A (possibly paramterized) Tuple.
Definition tuple.h:166
A dependent function type.
Definition lam.h:14
static const Pi * isa_cn(const Def *d)
Is this a continuation - i.e. is the Pi::codom mim::Bottom?
Definition lam.h:47
A dependent tuple type.
Definition tuple.h:20
std::ostream & println(std::ostream &os, const char *s, Args &&... args)
Same as Tab::print but appends a std::endl to os.
Definition print.h:235
std::ostream & print(std::ostream &os, const char *s, Args &&... args)
Definition print.h:223
Data constructor for a Sigma.
Definition tuple.h:68
A variable introduced by a binder (mutable).
Definition def.h:702
auto & muts()
Definition world.h:584
const Driver & driver() const
Definition world.h:86
u32 curr_gid() const
Manage global identifier - a unique number for each Def.
Definition world.h:95
Sym name() const
Definition world.h:90
void dump()
Dump to std::cout.
Definition dump.cpp:577
void write()
Same above but file name defaults to World::name.
Definition dump.cpp:588
Flags & flags()
Retrieve compile Flags.
Definition world.cpp:87
void debug_dump()
Dump in Debug build if World::log::level is Log::Level::Debug.
Definition dump.cpp:579
Sym sym(std::string_view)
Definition world.cpp:90
void write(const char *file)
Write to a file named file.
Definition dump.cpp:583
const Externals & externals() const
Definition world.h:240
void dump(std::ostream &os)
Dump to os.
Definition dump.cpp:552
Log & log() const
Definition world.cpp:86
Assoc
Associativity of an infix expression.
Definition tok.h:34
constexpr Assoc prec_assoc(Prec p)
Associativity of precedence level p.
Definition tok.h:44
Prec
Expression precedences used by the parser and the dumper; ordered low to high.
Definition tok.h:37
const Def * op(trait o, const Def *type)
Definition core.h:33
Definition ast.h:14
std::ostream & print(std::ostream &os, const char *s)
Base case.
Definition print.cpp:5
TBound< true > Join
AKA union.
Definition lattice.h:174
TExt< true > Top
Definition lattice.h:172
GIDSet< const Def * > DefSet
Definition def.h:74
std::ostream & operator<<(std::ostream &os, const Def *def)
This will stream def as an operand.
Definition dump.cpp:506
TExt< false > Bot
Definition lattice.h:171
constexpr Assoc prec_assoc(Prec p)
Associativity of precedence level p.
Definition tok.h:44
@ Pi
Definition def.h:114
@ Lam
Definition def.h:114
@ Axm
Definition def.h:114
@ Type
Definition def.h:114
@ App
Definition def.h:114
Prec
Expression precedences used by the parser and the dumper; ordered low to high.
Definition tok.h:37
#define assertf(condition,...)
Definition print.h:183
Use with print to output complicated std::ranges::ranges.
Definition print.h:85
Use to World::freeze and automatically unfreeze at the end of scope.
Definition world.h:150