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