MimIR 0.1
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
sexpr.cpp
Go to the documentation of this file.
1#include <iostream>
2#include <regex>
3#include <sstream>
4
6// TODO: Uncomment after 'eqsat' plugin is merged.
7// #include <mim/plug/eqsat/eqsat.h>
9
10#include "mim/def.h"
11
12#include "mim/be/emitter.h"
13#include "mim/util/print.h"
14
15namespace mim::sexpr {
16
17struct BB {
18 BB() = default;
19 BB(const BB&) = delete;
20 BB(BB&& other) noexcept = default;
21 BB& operator=(BB other) noexcept { return swap(*this, other), *this; }
22
23 std::deque<std::ostringstream>& head() { return parts[0]; }
24 std::deque<std::ostringstream>& body() { return parts[1]; }
25 std::deque<std::ostringstream>& tail() { return parts[2]; }
26
27 template<class... Args>
28 void body(const char* s, Args&&... args) {
29 print(body().emplace_back(), s, std::forward<Args>(args)...);
30 }
31
32 template<class... Args>
33 void tail(const char* s, Args&&... args) {
34 print(tail().emplace_back(), s, std::forward<Args>(args)...);
35 }
36
37 template<class... Args>
38 std::string assign(Tab tab, std::string name, const char* s, Args&&... args) {
39 if (!assigned.contains(name)) {
40 assigned.insert(name);
41 auto& os = body().emplace_back();
42 print(tab.lnprint(os, "(let {} ", name), s, std::forward<Args>(args)...);
43 }
44 return name;
45 }
46
47 template<class Fn>
48 std::string assign(Tab tab, std::string name, Fn&& print_term) {
49 if (!assigned.contains(name)) {
50 assigned.insert(name);
51 auto& os = body().emplace_back();
52 tab.lnprint(os, "(let {} ", name);
53 print_term(os);
54 }
55 return name;
56 }
57
58 friend void swap(BB& a, BB& b) noexcept {
59 using std::swap;
60 swap(a.parts, b.parts);
61 }
62
63 std::array<std::deque<std::ostringstream>, 3> parts;
64 std::set<std::string> assigned;
65};
66
67class Emitter : public mim::Emitter<std::string, std::string, BB, Emitter> {
68public:
70
71 Emitter(World& world, std::ostream& ostream, bool slotted = false)
72 : Super(world, "sexpr_emitter", ostream) {
73 slotted_ = slotted;
74 }
75
76 bool is_valid(std::string_view s) { return !s.empty(); }
77 void start() override;
78 void emit_imported(Lam*);
79 std::string prepare();
80 void emit_epilogue(Lam*);
81 void finalize();
82 void emit_lam(Lam* lam, MutSet& done);
83 std::string emit_var(BB& bb, const Def* var, const Def* type);
84 std::string emit_head(BB& bb, Lam* lam, bool as_binding = false);
85 std::string emit_type(BB& bb, const Def* type);
86 std::string emit_cons(std::vector<std::string> op_vals);
87 std::string emit_node(BB& bb, const Def* def, std::string node_name, bool variadic = false, bool with_type = false);
88 std::string emit_bb(BB& bb, const Def* def);
89
90private:
91 std::string id(const Def*, bool is_var_use = false) const;
92 std::string indent(size_t tabs, std::string term);
93 std::string flatten(std::string term);
94
95 // Determines whether the symbolic expression should
96 // be emitted in a style that is compatible with slotted-egg.
97 bool slotted() const { return slotted_; }
98 bool slotted_;
99
100 std::ostringstream decls_;
101 std::ostringstream func_decls_;
102 std::ostringstream func_impls_;
103};
104
105std::string Emitter::id(const Def* def, bool is_var_use) const {
106 std::string prefix = slotted() ? "$" : "";
107 std::string id;
108
109 // In slotted-egg variable-uses need to be explicitly wrapped in a var node i.e. in λx.x (lam $x (var $x))
110 auto var_wrap = [&](std::string id) { return slotted() && is_var_use ? "(var " + id + ")" : id; };
111
112 // Axioms, rules, unset lambdas(imports) and externals need to be emitted without a uid
113 if (def->isa<Axm>())
114 id = def->sym().str();
115 else if (def->isa<Rule>())
116 id = def->sym().str();
117 else if (def->isa<Lam>() && !def->is_set())
118 id = def->sym().str();
119 else if (def->is_external())
120 id = def->sym().str();
121 else
122 id = def->unique_name();
123
124 return var_wrap(prefix + id);
125}
126
127// Adjusts the base indentation of a term-string like
128//
129// " (app
130// foo
131// bar
132// )"
133//
134// to the number of tabs specified with 'tabs' (i.e. for tabs=1)
135//
136// " (app
137// foo
138// bar
139// )"
140//
141std::string Emitter::indent(size_t tabs, std::string term) {
142 std::string indent(tabs * 4, ' ');
143 std::string result;
144 std::string line;
145
146 while (!term.empty() && (term.front() == '\n' || term.front() == '\r'))
147 term.erase(0, 1);
148
149 std::stringstream term_stream(term);
150 size_t min_indent = term.find_first_not_of(' ');
151 while (std::getline(term_stream, line)) {
152 // Skips empty lines
153 if (line.find_first_not_of(" \t\r\n") == std::string::npos) continue;
154 result += "\n" + indent + line.substr(min_indent);
155 }
156
157 return result;
158}
159
160// Removes all indentation so a term-string like
161//
162// " (app
163// foo
164// bar
165// )"
166//
167// becomes flattened like below
168//
169// "(app foo bar)"
170//
171std::string Emitter::flatten(std::string term) {
172 term = std::regex_replace(term, std::regex("( {4})"), "");
173
174 while (!term.empty() && (term.front() == '\n' || term.front() == '\r'))
175 term.erase(0, 1);
176
177 term = std::regex_replace(term, std::regex("(\\r|\\n)"), " ");
178 return term;
179}
180
182 Super::start();
183
184 ostream() << decls_.str();
185 ostream() << func_decls_.str();
186 ostream() << func_impls_.str();
187
188 // TODO: Use pretty(sexpr, line_len) from the egg FFI
189 // to pretty-print the sexpr either based on a switch from the cli
190 // or as default after the sexpr backend has been completed
191}
192
194 auto bb = BB();
195
196 const std::string ext = lam->is_external() ? "extern" : "intern";
197 // We assume that the lambda will be a continuation since imports
198 // only exist via cfun and ccon which are both internally modelled as con
199 print(func_decls_, "(con {} {}", ext, id(lam));
200 print(func_decls_, "{})\n\n", emit_var(bb, lam->var(), lam->type()->dom()));
201}
202
203std::string Emitter::prepare() { return root()->unique_name(); }
204
206 auto& bb = lam2bb_[lam];
207 bb.tail("{}", emit(lam->body()));
208}
209
211 if (root()->sym().str() == "_fallback_compile") return;
212 // We don't want to emit config lams that define which rules should be emitted.
213 // The rules in the body of such a lambda will be emitted into decls_
214 // via emit_bb() but we don't want to emit the lambda itself.
215 // TODO: Uncomment after 'eqsat' plugin is merged.
216 // else if (Axm::isa<mim::plug::eqsat::Rules>(root()->ret_dom()))
217 // return;
218
219 MutSet done;
220 auto root_lam = nest().root()->mut()->as_mut<Lam>();
221 emit_lam(root_lam, done);
222}
223
224void Emitter::emit_lam(Lam* lam, MutSet& done) {
225 done.emplace(lam);
226 assert(lam2bb_.contains(lam));
227 auto& bb = lam2bb_[lam];
228
229 if (lam->is_closed())
230 print(func_impls_, "{}", emit_head(bb, lam));
231 else
232 print(func_impls_, "{}", emit_head(bb, lam, true));
233
234 ++tab;
235 // Keeps count of parentheses opened by let-bindings that need to be closed later on
236 auto unclosed_bindings = 0;
237 for (auto op : lam->deps()) {
238 for (auto mut : op->local_muts())
239 if (auto next = nest()[mut]) {
240 if (next->mut()->isa<Lam>() && !done.contains(next->mut())) {
241 auto next_lam = next->mut()->as_mut<Lam>();
242 emit_lam(next_lam, done);
243 unclosed_bindings++;
244 }
245 }
246 }
247
248 for (auto& term : bb.body()) {
249 auto opened = std::ranges::count(term.str(), '(');
250 auto closed = std::ranges::count(term.str(), ')');
251 unclosed_bindings += opened - closed;
252 print(func_impls_, "{}", indent(tab.indent(), term.str()));
253 }
254
255 for (auto& term : bb.tail())
256 print(func_impls_, "{}", indent(tab.indent(), term.str()));
257
258 std::string closing_parens(unclosed_bindings, ')');
259 print(func_impls_, "{}", closing_parens);
260 --tab;
261
262 if (lam->is_closed())
263 print(func_impls_, ")\n\n");
264 else {
265 --tab;
266 print(func_impls_, ")");
267 }
268}
269
270std::string Emitter::emit_var(BB& bb, const Def* var, const Def* type) {
271 std::ostringstream os;
272
273 if (slotted()) {
274 ++tab;
275 tab.lnprint(os, "{}", id(var));
276 tab.lnprint(os, "{}", emit_type(bb, type));
277 --tab;
278 return os.str();
279 }
280
281 ++tab;
282 auto projs = var->projs();
283 if (projs.size() == 1 || std::ranges::all_of(projs, [](auto proj) { return proj->sym().empty(); }))
284 tab.lnprint(os, "(var {} {})", id(var), emit_type(bb, type));
285 else {
286 tab.lnprint(os, "(var {}", id(var));
287 size_t i = 0;
288 for (auto proj : projs)
289 tab.print(os, " {}", emit_var(bb, proj, type->proj(i++)));
290 ++tab;
291 tab.lnprint(os, "{})", emit_type(bb, type));
292 --tab;
293 }
294 --tab;
295 return os.str();
296}
297
298std::string Emitter::emit_head(BB& bb, Lam* lam, bool as_binding) {
299 std::ostringstream os;
300
301 // TODO: Is there ever a case where we would be emitting a lambda instead of a continuation
302 // since the assertions in the Emitter visit() method seem to make this impossible?
303 const std::string lam_kind = lam->isa_cn(lam) ? "con" : "lam";
304 const std::string ext = lam->is_external() ? "extern" : "intern";
305
306 if (as_binding) {
307 tab.lnprint(os, "(let {}", id(lam));
308 ++tab;
309 tab.lnprint(os, "({} {} {}", lam_kind, ext, id(lam));
310 } else
311 tab.print(os, "({} {} {}", lam_kind, ext, id(lam));
312
313 tab.print(os, "{}", emit_var(bb, lam->var(), lam->type()->dom()));
314
315 // Continuations have codomain .bot but lambdas can have arbitrary codomains
316 if (!lam->isa_cn(lam)) {
317 ++tab;
318 tab.lnprint(os, "(sigma");
319 ++tab;
320 for (auto codom : lam->codoms())
321 tab.lnprint(os, "{}", emit_type(bb, codom));
322 print(os, ")");
323 --tab;
324 --tab;
325 }
326
327 tab.print(os, "{}", emit_bb(bb, lam->filter()));
328
329 return os.str();
330}
331
332std::string Emitter::emit_type(BB& bb, const Def* type) {
333 if (auto i = types_.find(type); i != types_.end()) return i->second;
334
335 std::ostringstream os;
336 if (type->isa<Nat>()) {
337 print(os, "Nat");
338 } else if (auto size = Idx::isa(type)) {
339 if (auto lit_size = Idx::size2bitwidth(size)) {
340 switch (*lit_size) {
341 case 1: return types_[type] = "Bool";
342 case 8: return types_[type] = "I8";
343 case 16: return types_[type] = "I16";
344 case 32: return types_[type] = "I32";
345 case 64: return types_[type] = "I64";
346 default: break;
347 }
348 }
349 print(os, "(idx (lit {}))", size);
350 } else if (auto lit = type->isa<Lit>()) {
351 if (lit->type()->isa<Nat>())
352 print(os, "(lit {})", lit->get());
353 else
354 print(os, "(lit {} {})", lit->get(), emit_type(bb, lit->type()));
355 } else if (auto arr = type->isa<Arr>()) {
356 if (auto arity = Lit::isa(arr->arity())) {
357 u64 size = *arity;
358 print(os, "(arr (lit {}) {})", size, emit_type(bb, arr->body()));
359 } else if (auto top = arr->arity()->isa<Top>()) {
360 print(os, "(arr (top {}) {})", emit_type(bb, top->type()), emit_type(bb, arr->body()));
361 } else {
362 print(os, "(arr {} {})", flatten(emit_bb(bb, arr->arity())), emit_type(bb, arr->body()));
363 }
364 } else if (auto pi = type->isa<Pi>()) {
365 if (Pi::isa_cn(pi))
366 print(os, "(cn {})", emit_type(bb, pi->dom()));
367 else
368 print(os, "(pi {} {})", emit_type(bb, pi->dom()), emit_type(bb, pi->codom()));
369 } else if (auto sigma = type->isa<Sigma>()) {
370 print(os, "(sigma { })", Elem(sigma->ops(), [&](auto op) { print(os, "{}", emit_type(bb, op)); }));
371 } else if (auto tuple = type->isa<Tuple>()) {
372 print(os, "(tuple { })", Elem(tuple->ops(), [&](auto op) { print(os, "{}", emit_type(bb, op)); }));
373 } else if (auto app = type->isa<App>()) {
374 print(os, "(app {} {})", emit_type(bb, app->callee()), emit_type(bb, app->arg()));
375 } else if (auto axm = type->isa<Axm>()) {
376 print(os, "{}", id(axm));
377 if (!world().flags2annex().contains(axm->flags()))
378 print(decls_, "(axm {} {})\n\n", id(axm), emit_type(bb, axm->type()));
379 } else if (auto var = type->isa<Var>()) {
380 print(os, "{}", id(var));
381 } else if (auto hole = type->isa<Hole>()) {
382 print(os, "(hole {})", id(hole));
383 } else if (auto extract = type->isa<Extract>()) {
384 auto tuple = extract->tuple();
385 auto index = extract->index();
386 // See explanation for the same thing in emit_bb
387 auto is_nested_proj = false;
388 if (auto lit = Lit::isa(index); lit && tuple->isa<Extract>()) {
389 auto curr_tuple = tuple;
390 auto curr_index = index;
391 while (curr_tuple != nullptr && curr_index != nullptr)
392 if (auto lit = Lit::isa(curr_index); lit && curr_tuple->isa<Extract>()) {
393 curr_tuple = tuple->as<Extract>()->tuple();
394 curr_index = tuple->as<Extract>()->index();
395 continue;
396 } else if (auto lit = Lit::isa(curr_index); lit && curr_tuple->isa<Var>()) {
397 is_nested_proj = true;
398 break;
399 } else {
400 break;
401 }
402 }
403 if (!slotted() && ((Lit::isa(index) && tuple->isa<Var>()) || is_nested_proj))
404 print(os, "{}", id(extract));
405 else
406 print(os, "(extract {} {})", emit_type(bb, tuple), emit_type(bb, index));
407 } else if (auto mType = type->isa<Type>()) {
408 print(os, "(type {})", emit_type(bb, mType->level()));
409 } else if (type->isa<Univ>()) {
410 print(os, "Univ");
411 } else if (auto reform = type->isa<Reform>()) {
412 print(os, "(reform {})", emit_type(bb, reform->meta_type()));
413 } else if (auto join = type->isa<Join>()) {
414 print(os, "(join { })", Elem(join->ops(), [&](auto op) { print(os, "{}", emit_type(bb, op)); }));
415 } else if (auto meet = type->isa<Meet>()) {
416 print(os, "(meet { })", Elem(meet->ops(), [&](auto op) { print(os, "{}", emit_type(bb, op)); }));
417 } else {
418 error("unsupported type '{}'", type);
419 fe::unreachable();
420 }
421
422 return types_[type] = os.str();
423}
424
425// This is primarily needed because slotted-egraphs don't support
426// variadic enodes (yet?) so we have to represent those as nested cons lists
427// i.e. for Tuple: (tuple (cons a (cons b nil)))
428std::string Emitter::emit_cons(std::vector<std::string> op_vals) {
429 std::ostringstream os;
430
431 size_t op_idx = 0;
432 for (auto op_val : op_vals) {
433 ++tab;
434 tab.lnprint(os, "(cons");
435 ++tab;
436 tab.print(os, "{}", indent(tab.indent(), op_val));
437 --tab;
438 if (op_idx == op_vals.size() - 1) tab.lnprint(os, "nil");
439 --tab;
440
441 op_idx++;
442 }
443
444 std::string closing_brackets(op_vals.size(), ')');
445 print(os, "{}", closing_brackets);
446
447 return os.str();
448}
449
450std::string Emitter::emit_node(BB& bb, const Def* def, std::string node_name, bool variadic, bool with_type) {
451 std::ostringstream os;
452
453 std::vector<std::string> op_vals;
454
455 if (with_type) {
456 if (auto type_val = emit_bb(bb, def->type()); !type_val.empty()) op_vals.push_back(type_val);
457 }
458
459 // This is a bit of an edge case? because the ops of a pack don't contain its arity
460 if (auto pack = def->isa<Pack>())
461 if (auto arity_val = emit_bb(bb, pack->arity()); !arity_val.empty()) op_vals.push_back(arity_val);
462
463 for (auto op : def->ops())
464 if (auto op_val = emit_bb(bb, op); !op_val.empty()) op_vals.push_back(op_val);
465
466 if (def->sym().empty()) {
467 tab.lnprint(os, "({}", node_name);
468
469 if (slotted() && variadic)
470 tab.print(os, "{}", emit_cons(op_vals));
471 else
472 for (auto op_val : op_vals)
473 tab.print(os, "{}", op_val);
474
475 print(os, ")");
476
477 } else {
478 bb.assign(tab, id(def), [&](auto& os) {
479 ++tab;
480 tab.lnprint(os, "({}", node_name);
481
482 if (slotted() && variadic)
483 tab.print(os, "{}", emit_cons(op_vals));
484 else {
485 ++tab;
486 for (auto op_val : op_vals)
487 tab.print(os, "{}", indent(tab.indent(), op_val));
488 --tab;
489 }
490
491 print(os, ")");
492 --tab;
493 });
494 tab.lnprint(os, "{}", id(def, true));
495 }
496
497 return os.str();
498}
499
500std::string Emitter::emit_bb(BB& bb, const Def* def) {
501 std::ostringstream os;
502
503 ++tab;
504 if (def->type()->isa<Type>() || def->type()->isa<Univ>()) {
505 tab.lnprint(os, "{}", emit_type(bb, def));
506
507 } else if (auto lam = def->isa<Lam>()) {
508 tab.lnprint(os, "{}", id(lam, true));
509
510 // TODO: Lit bindings
511 } else if (auto lit = def->isa<Lit>()) {
512 if (lit->type()->isa<Nat>()) {
513 std::string alias;
514 auto nat_val = lit->get<u64>();
515 switch (nat_val) {
516 case 0x100: alias = "i8"; break;
517 case 0x10000: alias = "i16"; break;
518 case 0x100000000: alias = "i32"; break;
519 default: break;
520 }
521 if (!alias.empty())
522 tab.lnprint(os, "(lit {})", alias);
523 else
524 tab.lnprint(os, "(lit {})", nat_val);
525 } else if (auto size = Idx::isa(lit->type()))
526 if (auto lit_size = Idx::size2bitwidth(size); lit_size && *lit_size == 1)
527 tab.lnprint(os, "(lit {})", lit);
528 else
529 tab.lnprint(os, "(lit {} {})", lit->get(), emit_type(bb, lit->type()));
530 else
531 tab.lnprint(os, "(lit {})", lit);
532
533 } else if (auto tuple = def->isa<Tuple>()) {
534 tab.print(os, "{}", emit_node(bb, tuple, "tuple", true));
535
536 } else if (auto pack = def->isa<Pack>()) {
537 tab.print(os, "{}", emit_node(bb, pack, "pack"));
538
539 } else if (auto extract = def->isa<Extract>()) {
540 auto tuple = extract->tuple();
541 auto index = extract->index();
542 // 'tuple' is another extract if we have for example two nested, sigma-typed variables
543 // and we are trying to print a named projection of the inner variable. ('baz' in the example below)
544 // ex.: (var foo (sigma (var bar (sigma (var baz Nat)))))
545 // In this example, we have an extract where the tuple: 'bar' is another extract from 'foo'.
546 auto is_nested_proj = false;
547 if (auto lit = Lit::isa(index); lit && tuple->isa<Extract>()) {
548 auto curr_tuple = tuple;
549 auto curr_index = index;
550 while (curr_tuple != nullptr && curr_index != nullptr)
551 if (auto lit = Lit::isa(curr_index); lit && curr_tuple->isa<Extract>()) {
552 curr_tuple = tuple->as<Extract>()->tuple();
553 curr_index = tuple->as<Extract>()->index();
554 continue;
555 } else if (auto lit = Lit::isa(curr_index); lit && curr_tuple->isa<Var>()) {
556 is_nested_proj = true;
557 break;
558 } else {
559 break;
560 }
561 }
562 if (!slotted() && ((Lit::isa(index) && tuple->isa<Var>()) || is_nested_proj))
563 tab.lnprint(os, "{}", id(extract));
564 else
565 tab.print(os, "{}", emit_node(bb, extract, "extract"));
566
567 } else if (auto insert = def->isa<Insert>()) {
568 tab.print(os, "{}", emit_node(bb, insert, "insert"));
569
570 } else if (auto var = def->isa<Var>()) {
571 tab.lnprint(os, "{}", id(var, true));
572
573 } else if (auto app = def->isa<App>()) {
574 tab.print(os, "{}", emit_node(bb, app, "app"));
575
576 } else if (auto axm = def->isa<Axm>()) {
577 tab.lnprint(os, "{}", id(axm));
578 if (!world().flags2annex().contains(axm->flags()))
579 print(decls_, "(axm {} {})\n\n", id(axm), emit_type(bb, axm->type()));
580
581 } else if (auto bot = def->isa<Bot>()) {
582 if (bot->sym().empty())
583 tab.lnprint(os, "(bot {})", emit_type(bb, bot->type()));
584 else {
585 bb.assign(tab, id(bot), "(bot {})", emit_type(bb, bot->type()));
586 tab.lnprint(os, "{}", id(bot, true));
587 }
588
589 } else if (auto top = def->isa<Top>()) {
590 if (top->sym().empty())
591 tab.lnprint(os, "(top {})", emit_type(bb, top->type()));
592 else {
593 bb.assign(tab, id(top), "(top {})", emit_type(bb, top->type()));
594 tab.lnprint(os, "{}", id(top, true));
595 }
596
597 } else if (auto rule = def->isa<Rule>()) {
598 auto lhs_val = emit_bb(bb, rule->lhs());
599 auto rhs_val = emit_bb(bb, rule->rhs());
600 auto guard_val = emit_bb(bb, rule->guard());
601 tab.lnprint(os, "(rule {} {} {})", lhs_val, rhs_val, guard_val);
602 print(decls_, "(rule {} {} {})\n\n", indent(1, lhs_val), indent(1, rhs_val), indent(1, guard_val));
603
604 } else if (auto inj = def->isa<Inj>()) {
605 tab.print(os, "{}", emit_node(bb, inj, "inj", false, true));
606
607 } else if (auto merge = def->isa<Merge>()) {
608 tab.print(os, "{}", emit_node(bb, merge, "merge", true, true));
609
610 } else if (auto match = def->isa<Match>()) {
611 tab.print(os, "{}", emit_node(bb, match, "match", true));
612
613 } else if (auto proxy = def->isa<Proxy>()) {
614 auto type_val = emit_bb(bb, proxy->type());
615 auto pass_val = proxy->pass();
616 auto tag_val = proxy->tag();
617 std::vector<std::string> op_vals;
618 for (auto op : proxy->ops())
619 if (auto op_val = emit_bb(bb, op); !op_val.empty()) op_vals.push_back(op_val);
620
621 if (proxy->sym().empty()) {
622 tab.lnprint(os, "(proxy");
623 tab.print(os, "{}", type_val);
624 // pass_val and tag_val are not emitted via emit_bb and therefore have no
625 // leading newlines and indentation levels so we add those here
626 ++tab;
627 tab.lnprint(os, "{}", pass_val);
628 tab.lnprint(os, "{}", tag_val);
629 --tab;
630 for (auto op_val : op_vals)
631 tab.print(os, "{}", op_val);
632 print(os, ")");
633 } else {
634 bb.assign(tab, id(proxy), [&](auto& os) {
635 ++tab;
636 tab.lnprint(os, "(proxy");
637 ++tab;
638 tab.print(os, "{}", type_val);
639 ++tab;
640 tab.lnprint(os, "{}", pass_val);
641 tab.lnprint(os, "{}", tag_val);
642 --tab;
643 for (auto op_val : op_vals)
644 tab.print(os, "{}", indent(tab.indent(), op_val));
645 --tab;
646 print(os, ")");
647 --tab;
648 });
649 tab.lnprint(os, "{}", id(proxy, true));
650 }
651
652 } else {
653 error("Unhandled Def in SExpr backend: {} : {}", def, def->type());
654 fe::unreachable();
655 }
656 --tab;
657
658 return os.str();
659}
660
661void emit(World& world, std::ostream& ostream) {
662 Emitter emitter(world, ostream);
663 emitter.run();
664}
665
666void emit_slotted(World& world, std::ostream& ostream) {
667 Emitter emitter(world, ostream, true);
668 emitter.run();
669}
670
671} // namespace mim::sexpr
A (possibly paramterized) Array.
Definition tuple.h:117
Definition axm.h:9
Lam * root() const
Definition phase.h:310
Base class for all Defs.
Definition def.h:251
T * as_mut() const
Asserts that this is a mutable, casts constness away and performs a static_cast to T.
Definition def.h:495
Defs deps() const noexcept
Definition def.cpp:470
constexpr auto ops() const noexcept
Definition def.h:305
const Def * var(nat_t a, nat_t i) noexcept
Definition def.h:429
auto projs(F f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
Definition def.h:390
Muts local_muts() const
Mutables reachable by following immutable deps(); mut->local_muts() is by definition the set { mut }...
Definition def.cpp:332
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.cpp:447
bool is_external() const noexcept
Definition def.h:467
Sym sym() const
Definition def.h:508
std::string unique_name() const
name + "_" + Def::gid
Definition def.cpp:579
bool is_closed() const
Has no free_vars()?
Definition def.cpp:418
Extracts from a Sigma or Array-typed Extract::tuple the element at position Extract::index.
Definition tuple.h:206
This node is a hole in the IR that is inferred by its context later on.
Definition check.h:14
static constexpr nat_t size2bitwidth(nat_t n)
Definition def.h:893
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
Definition def.cpp:611
Constructs a Join value.
Definition lattice.h:70
Creates a new Tuple / Pack by inserting Insert::value at position Insert::index into Insert::tuple.
Definition tuple.h:233
A function.
Definition lam.h:110
const Def * filter() const
Definition lam.h:122
static const Lam * isa_cn(const Def *d)
Definition lam.h:141
const Pi * type() const
Definition lam.h:130
const Def * body() const
Definition lam.h:123
static std::optional< T > isa(const Def *def)
Definition def.h:826
Scrutinize Match::scrutinee() and dispatch to Match::arms.
Definition lattice.h:116
Constructs a Meet value.
Definition lattice.h:53
const Nest & nest() const
Definition phase.h:327
Def * mut() const
The mutable capsulated in this Node or nullptr, if it's a virtual root comprising several Nodes.
Definition nest.h:27
const Node * root() const
Definition nest.h:202
A (possibly paramterized) Tuple.
Definition tuple.h:166
virtual void run()
Entry point and generates some debug output; invokes Phase::start.
Definition phase.cpp:13
virtual void start()=0
Actual entry.
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
const Def * dom() const
Definition lam.h:35
Type formation of a rewrite Rule.
Definition rule.h:9
A rewrite rule.
Definition rule.h:38
A dependent tuple type.
Definition tuple.h:20
World & world()
Definition pass.h:64
Keeps track of indentation level.
Definition print.h:206
std::ostream & lnprint(std::ostream &os, const char *s, Args &&... args)
Same as Tab::print but prepends a std::endl to os.
Definition print.h:230
Data constructor for a Sigma.
Definition tuple.h:68
A variable introduced by a binder (mutable).
Definition def.h:702
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:31
std::string prepare()
Definition sexpr.cpp:203
void start() override
Actual entry.
Definition sexpr.cpp:181
std::string emit_node(BB &bb, const Def *def, std::string node_name, bool variadic=false, bool with_type=false)
Definition sexpr.cpp:450
std::string emit_head(BB &bb, Lam *lam, bool as_binding=false)
Definition sexpr.cpp:298
std::string emit_var(BB &bb, const Def *var, const Def *type)
Definition sexpr.cpp:270
void emit_lam(Lam *lam, MutSet &done)
Definition sexpr.cpp:224
Emitter(World &world, std::ostream &ostream, bool slotted=false)
Definition sexpr.cpp:71
std::string emit_type(BB &bb, const Def *type)
Definition sexpr.cpp:332
void emit_imported(Lam *)
Definition sexpr.cpp:193
std::string emit_cons(std::vector< std::string > op_vals)
Definition sexpr.cpp:428
bool is_valid(std::string_view s)
Definition sexpr.cpp:76
mim::Emitter< std::string, std::string, BB, Emitter > Super
Definition sexpr.cpp:69
std::string emit_bb(BB &bb, const Def *def)
Definition sexpr.cpp:500
void emit_epilogue(Lam *)
Definition sexpr.cpp:205
void emit_slotted(World &, std::ostream &)
Definition sexpr.cpp:666
void emit(World &, std::ostream &)
Definition sexpr.cpp:661
const Def * flatten(const Def *def)
Flattens a sigma/array/pack/tuple.
Definition tuple.cpp:117
std::ostream & print(std::ostream &os, const char *s)
Base case.
Definition print.cpp:5
TBound< true > Join
AKA union.
Definition lattice.h:174
void error(Loc loc, const char *f, Args &&... args)
Definition dbg.h:125
TExt< true > Top
Definition lattice.h:172
GIDSet< Def * > MutSet
Definition def.h:85
TExt< false > Bot
Definition lattice.h:171
uint64_t u64
Definition types.h:35
TBound< false > Meet
AKA intersection.
Definition lattice.h:173
@ Lam
Definition def.h:114
@ Axm
Definition def.h:114
@ Rule
Definition def.h:114
Use with print to output complicated std::ranges::ranges.
Definition print.h:85
std::array< std::deque< std::ostringstream >, 3 > parts
Definition sexpr.cpp:63
BB & operator=(BB other) noexcept
Definition sexpr.cpp:21
void body(const char *s, Args &&... args)
Definition sexpr.cpp:28
std::deque< std::ostringstream > & tail()
Definition sexpr.cpp:25
BB(BB &&other) noexcept=default
BB()=default
std::string assign(Tab tab, std::string name, const char *s, Args &&... args)
Definition sexpr.cpp:38
std::string assign(Tab tab, std::string name, Fn &&print_term)
Definition sexpr.cpp:48
std::deque< std::ostringstream > & head()
Definition sexpr.cpp:23
friend void swap(BB &a, BB &b) noexcept
Definition sexpr.cpp:58
std::deque< std::ostringstream > & body()
Definition sexpr.cpp:24
std::set< std::string > assigned
Definition sexpr.cpp:64
BB(const BB &)=delete
void tail(const char *s, Args &&... args)
Definition sexpr.cpp:33