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