6#include <fe/worklist.h>
14using namespace std::literals;
28Def* isa_decl(
const Def* def) {
29 if (
auto mut = def->isa_mut()) {
30 if (mut->is_external() || mut->isa<
Lam>() || (mut->sym() && mut->sym() !=
'_'))
return mut;
36std::string name(
const Def* def) {
37 if (
auto sym = def->sym(); sym && sym !=
'_' &&
PlainNames::claim(def->world().driver(), sym, def->gid()))
42std::string
id(
const Def* def) {
43 if (def->is_external() || (!def->is_set() && def->isa<
Lam>()))
return def->sym().str();
47std::string_view external(
const Def* def) {
48 if (def->is_external())
return "extern "sv;
57 if (def->isa<
Extract>())
return Prec::Extract;
58 if (def->isa<
Insert>())
return Prec::Ins;
59 if (def->isa<
Join>())
return Prec::Union;
60 if (def->isa<
Inj>())
return Prec::Inj;
61 if (def->isa<
Reform>())
return Prec::App;
62 if (
auto pi = def->isa<
Pi>(); pi && !
Pi::isa_cn(pi))
return Prec::Arrow;
63 if (
auto app = def->isa<
App>()) {
72 case 0_n:
return Prec::Lit;
87 Op(
const Def* def,
Prec prec = Prec::Bot,
bool is_left =
false)
90 , is_left_(is_left) {}
91 static Op
l(
const Def* def,
Prec prec = Prec::Bot) {
return {def, prec,
true}; }
92 static Op
r(
const Def* def,
Prec prec = Prec::Bot) {
return {def, prec,
false}; }
94 static auto map(
const auto& range,
const char* sep =
", ",
Prec prec = Prec::Bot) {
95 return fe::Join(range | std::views::transform([prec](
auto op) {
return Op(op, prec); }), sep);
100 Prec prec()
const {
return prec_; }
101 bool is_left()
const {
return is_left_; }
102 const Def* def()
const {
return def_; }
103 const Def* operator->()
const {
return def_; }
104 const Def* operator*()
const {
return def_; }
105 explicit operator bool()
const {
return def_ !=
nullptr; }
115 friend std::ostream&
operator<<(std::ostream&, Op);
119class Dump :
public Op {
121 Dump(
const Def* def,
Prec prec = Prec::Bot,
bool is_left =
false)
122 : Op(def, prec, is_left) {}
124 : Dump(
op.def(),
op.prec(),
op.is_left()) {}
126 explicit operator bool()
const {
return is_inline(); }
128 bool is_inline()
const {
129 if (
auto mut = def()->isa_mut()) {
130 if (isa_decl(mut))
return false;
136 if (
auto app = def()->isa<App>()) {
137 if (app->type()->isa<Pi>())
return true;
138 if (app->type()->isa<Type>())
return true;
139 if (app->callee()->isa<Axm>())
return app->callee_type()->num_doms() <= 1;
146 bool needs_parens()
const {
147 if (!is_inline())
return false;
149 auto child_prec = def2prec(def());
150 if (child_prec < prec())
return true;
151 if (child_prec > prec())
return false;
154 case Assoc::R:
return is_left();
155 case Assoc::L:
return !is_left();
156 case Assoc::N:
return false;
161 friend std::ostream&
operator<<(std::ostream&, Dump);
168template<>
struct std::formatter<
mim::Op > : fe::ostream_formatter {};
169template<>
struct std::formatter<
mim::Dump> : fe::ostream_formatter {};
175std::ostream& ptrn(std::ostream& os,
const Def* def,
const Def* type) {
176 if (!def)
return os << std::format(
"_: {}", Op(type));
178 auto projs = def->tprojs();
179 if (projs.size() == 1 || std::ranges::all_of(projs, [](
auto d) { return !d; }))
180 return os << std::format(
"{}: {}", name(def), Op(type));
184 for (
auto sep =
"";
auto proj : projs) {
186 ptrn(os, proj,
type->proj(i++));
189 return os << std::format(
") as {}", name(def));
192std::ostream& bndr(std::ostream& os,
const Def* def,
const Def* type) {
193 if (def)
return ptrn(os, def, type);
194 return os << std::format(
"_: {}", Op(type));
197std::ostream& curry(std::ostream& os,
const Def* def,
const Def* type,
bool implicit,
size_t limit,
bool alias) {
198 auto l = implicit ?
'{' :
'(';
199 auto r = implicit ?
'}' :
')';
201 if (limit == 0)
return os <<
l <<
r;
204 bndr(os, def ? def->tproj(0) :
nullptr,
type->tproj(0));
209 for (
auto sep =
"";
auto i : std::views::iota(
size_t(0), limit)) {
211 bndr(os, def ? def->tproj(i) :
nullptr,
type->tproj(i));
215 if (alias && def) os << std::format(
" as {}", name(def));
219std::ostream&
operator<<(std::ostream& os, Op op) {
220 if (*op ==
nullptr)
return os <<
"<nullptr>";
221 if (
auto d = Dump(op))
return os <<
d;
222 return os <<
id(*op);
225std::ostream&
operator<<(std::ostream& os, Dump d) {
226 if (
auto mut =
d->isa_mut(); mut && !mut->is_set())
return os <<
"unset";
227 if (
d.needs_parens())
return os << std::format(
"({})", Dump(*d));
229 bool ascii =
d->world().flags().ascii;
230 auto arw = ascii ?
"->" :
"→";
231 auto al = ascii ?
"<<" :
"«";
232 auto ar = ascii ?
">>" :
"»";
233 auto pl = ascii ?
"(<" :
"‹";
234 auto pr = ascii ?
">)" :
"›";
235 auto bot = ascii ?
"bot" :
"⊥";
236 auto top = ascii ?
"top" :
"⊤";
238 if (
auto type =
d->isa<
Type>()) {
239 if (
auto level =
Lit::isa(
type->level()); level && !ascii) {
240 if (level == 0)
return os <<
"*";
241 if (level == 1)
return os <<
"□";
243 return os << std::format(
"Type {}", Op::r(
type->level(), Prec::App));
244 }
else if (
auto reform =
d->isa<
Reform>()) {
245 return os << std::format(
"Rule {}", Op::r(reform->dom(), Prec::App));
246 }
else if (
d->isa<
Univ>()) {
248 }
else if (
d->isa<
Nat>()) {
250 }
else if (
d->isa<
Idx>()) {
252 }
else if (
auto ext =
d->isa<
Ext>()) {
253 return os << std::format(
"{}:{}", ext->isa<
Bot>() ? bot : top, Op::r(ext->type(), Prec::Lit));
254 }
else if (
auto axm =
d->isa<
Axm>()) {
255 return os << axm->sym();
256 }
else if (
auto lit =
d->isa<
Lit>()) {
257 if (
lit->type()->isa<
Nat>()) {
259 switch (
lit->get()) {
260 case 0x0'0000'0100_n:
return os <<
"i8";
261 case 0x0'0001'0000_n:
return os <<
"i16";
262 case 0x1'0000'0000_n:
return os <<
"i32";
263 default:
return os << std::format(
"{}",
lit->get());
270 case 0x0'0000'0002_n:
return os << (
lit->get<
bool>() ?
"tt" :
"ff");
271 case 0x0'0000'0100_n:
return os <<
lit->get() <<
"I8";
272 case 0x0'0001'0000_n:
return os <<
lit->get() <<
"I16";
273 case 0x1'0000'0000_n:
return os <<
lit->get() <<
"I32";
274 case 0_n:
return os <<
lit->get() <<
"I64";
277 std::vector<uint8_t> digits;
278 for (
auto z = *s; z; z /= 10) digits.emplace_back(z % 10);
282 for (
auto d : digits | std::views::reverse)
285 for (
auto d : digits | std::views::reverse)
286 os << uint8_t(0xE2) << uint8_t(0x82) << (uint8_t(0x80 + d));
294 return os << std::format(
"{}:{}",
lit->get(), Op::r(
lit->type(), Prec::Lit));
295 }
else if (
auto ex =
d->isa<
Extract>()) {
296 if (ex->tuple()->isa<
Var>() && ex->index()->isa<
Lit>())
return os << name(ex);
297 return os << std::format(
"{}#{}", Op::l(ex->tuple(), Prec::Extract), Op::r(ex->index(), Prec::Extract));
298 }
else if (
auto ins =
d->isa<
Insert>()) {
299 auto tup = Op::l(ins->tuple(), Prec::Extract);
301 if (
auto ex = ins->tuple()->isa<
Extract>(); ex && !(ex->tuple()->isa<
Var>() && ex->index()->isa<
Lit>()))
302 os << std::format(
"({})", tup);
304 os << std::format(
"{}", tup);
305 return os << std::format(
"#{} ← {}", Op::r(ins->index(), Prec::Extract), Op::r(ins->value(), Prec::Ins));
306 }
else if (
auto var =
d->isa<
Var>()) {
307 return os << name(var);
308 }
else if (
auto [pi, var] =
d->isa_binder<
Pi>(); pi) {
309 auto l = pi->is_implicit() ?
'{' :
'[';
310 auto r = pi->is_implicit() ?
'}' :
']';
311 return os << std::format(
"{}{}: {}{} {} {}", l, Op(var), Op(pi->dom()), r, arw,
312 Op::r(pi->codom(), Prec::Arrow));
313 }
else if (
auto pi =
d->isa<
Pi>()) {
314 if (
Pi::isa_cn(pi))
return os << std::format(
"Cn {}", Op(pi->dom()));
315 return os << std::format(
"{} {} {}", Op::l(pi->dom(), Prec::Arrow), arw, Op::r(pi->codom(), Prec::Arrow));
316 }
else if (
auto lam =
d->isa<
Lam>()) {
318 return os << std::format(
"{}, {}", Op(lam->filter()), Op(lam->body()));
319 }
else if (
auto app =
d->isa<
App>()) {
324 case 0x0'0000'0002_n:
return os <<
"Bool";
325 case 0x0'0000'0100_n:
return os <<
"I8";
326 case 0x0'0001'0000_n:
return os <<
"I16";
327 case 0x1'0000'0000_n:
return os <<
"I32";
328 case 0_n:
return os <<
"I64";
335 return os << std::format(
"{} {}", Op::l(app->callee(), Prec::App), Op::r(app->arg(), Prec::App));
336 }
else if (
auto [sigma, var] =
d->isa_binder<
Sigma>(); sigma) {
338 auto elem = fe::StreamFn{[&](std::ostream& os) -> std::ostream& {
340 for (
auto op : sigma->ops()) {
342 if (
auto v = sigma->var(i++))
343 os << std::format(
"{}: {}", v, Op(op));
351 return os << std::format(
"[{}]", elem);
352 }
else if (
auto sigma =
d->isa<
Sigma>()) {
353 return os << std::format(
"[{}]", Op::map(sigma->ops()));
354 }
else if (
auto tuple =
d->isa<
Tuple>()) {
355 return os << std::format(
"({})", Op::map(tuple->ops()));
356 }
else if (
auto [arr, var] =
d->isa_binder<
Arr>(); arr) {
357 return os << std::format(
"{}{}: {}; {}{}", al, var, Op(arr->arity()), Op(arr->body()), ar);
358 }
else if (
auto arr =
d->isa<
Arr>()) {
359 return os << std::format(
"{}{}; {}{}", al, Op(arr->arity()), Op(arr->body()), ar);
360 }
else if (
auto [pack, var] =
d->isa_binder<
Pack>(); pack) {
361 return os << std::format(
"{}{}: {}; {}{}", pl, pack->var(), Op(pack->arity()), Op(pack->body()), pr);
362 }
else if (
auto pack =
d->isa<
Pack>()) {
363 return os << std::format(
"{}{}; {}{}", pl, Op(pack->arity()), Op(pack->body()), pr);
364 }
else if (
auto proxy =
d->isa<
Proxy>()) {
365 return os << std::format(
"(proxy#{} {})", proxy->tag(), Op::map(proxy->ops()));
366 }
else if (
auto bound =
d->isa<
Bound>()) {
367 auto op = bound->isa<
Join>() ?
"∪" :
"∩";
368 if (
auto mut =
d->isa_mut()) std::print(os,
"{}{}: {}", op, name(mut), Op(mut->type()));
369 if (!bound->isa<
Join>())
return os << std::format(
"{}({})", op, Op::map(bound->ops()));
370 return os << Op::map(bound->ops(),
" ∪ ", Prec::Union);
371 }
else if (
auto inj =
d->isa<
Inj>()) {
372 return os << std::format(
"{} inj {}", Op::l(inj->value(), Prec::Inj), Op::r(inj->type(), Prec::Inj));
373 }
else if (
auto uniq =
d->isa<
Uniq>()) {
374 return os << std::format(
"⦃{}⦄", Op(uniq->op()));
378 auto tag =
d->flags() == 0 ? std::string(
d->node_name()) : std::format(
"{}#{}",
d->node_name(),
d->flags());
379 if (
d->ops().empty())
return os << std::format(
"({})", tag);
380 return os << std::format(
"({} {})", tag, Op::map(
d->ops(),
" "));
393 Dumper(std::ostream& os,
const Nest* nest =
nullptr)
399 void dump_let(
const Def*);
400 void recurse(
const Nest::Node*);
401 void recurse(
const Def*,
bool first =
false);
405 fe::Tab tab = fe::Tab::spaces();
406 fe::BFSWorklist<MutSet> muts;
410void Dumper::dump(
Def* mut) {
411 if (
auto lam = mut->isa<Lam>()) {
416 auto mut_prefix = [&](
const Def* def) {
417 if (def->isa<Sigma>())
return "Sigma";
418 if (def->isa<Arr>())
return "Arr";
419 if (def->isa<Pack>())
return "pack";
420 if (def->isa<Pi>())
return "Pi";
421 if (def->isa<Hole>())
return "Hole";
422 if (def->isa<Rule>())
return "Rule";
426 auto mut_op0 = [&](
const Def* def) -> std::ostream& {
427 if (
auto sig = def->isa<Sigma>())
return os << std::format(
", {}", sig->num_ops());
428 if (
auto arr = def->isa<Arr>())
return os << std::format(
", {}", arr->arity());
429 if (
auto pack = def->isa<Pack>())
return os << std::format(
", {}", pack->arity());
430 if (
auto pi = def->isa<Pi>())
return os << std::format(
", {}", pi->dom());
431 if (
auto hole = def->isa_mut<Hole>())
432 return hole->is_set() ? (os << std::format(
", {}", hole->op())) : (os <<
", ??");
433 if (
auto rule = def->isa<Rule>())
return os << std::format(
"{} => {}", rule->lhs(), rule->rhs());
437 if (!mut->is_set()) {
438 std::print(os,
"{}{}: {} = {{ <unset> }};", tab,
id(mut), mut->type());
442 std::print(os,
"{}{} {}{}: {}", tab, mut_prefix(mut), external(mut),
id(mut), mut->type());
445 if (
auto e = mut->num_vars(); e != 1) {
446 for (
auto sep =
"";
auto def : mut->vars()) {
449 os << def->unique_name();
455 std::print(os,
", @{}", mut->var()->unique_name());
458 std::println(os,
"{} = {{", tab);
460 if (nest) recurse((*nest)[mut]);
462 std::println(os,
"{}{}", tab, fe::Join(mut->ops()));
464 std::println(os,
"{}}};", tab);
467void Dumper::dump_lam(Lam* lam) {
468 std::vector<Lam*> currys;
469 for (Lam* curr = lam;;) {
470 currys.emplace_back(curr);
471 if (
auto body = curr->body())
472 if (
auto next = body->isa_mut<Lam>()) {
479 auto last = currys.back();
480 auto is_fun = Lam::isa_returning(last);
481 auto is_con = Lam::isa_cn(last) && !is_fun;
483 std::print(os,
"{}{}{} {}", tab, external(lam), is_fun ?
"fun" : is_con ?
"con" :
"lam",
id(lam));
484 for (
auto* c : currys) {
486 auto num_doms =
c->var() ?
c->var()->num_tprojs() :
c->type()->dom()->num_tprojs();
487 auto limit = is_fun &&
c ==
last ? num_doms - 1 : num_doms;
488 curry(os,
c->var(),
c->type()->dom(),
c->type()->is_implicit(), limit, !is_fun || c != last);
489 if (is_con && c == last) std::print(os,
"@({})",
c->filter());
493 std::print(os,
": {} =",
last->ret_dom());
495 std::print(os,
": {} =",
last->type()->codom());
497 std::print(os,
" =");
501 if (
last->is_set()) {
502 if (nest && currys.size() == 1) recurse((*nest)[lam]);
503 for (
auto* curry : currys)
504 recurse(curry->filter());
505 recurse(
last->body(),
true);
506 if (
last->body()->isa_mut())
507 std::println(os,
"{}{};", tab,
last->body());
509 std::println(os,
"{}{};", tab, Dump(
last->body()));
511 std::println(os,
"{}<unset>;", tab);
514 std::println(os,
"{}", tab);
517void Dumper::dump_let(
const Def* def) {
518 std::println(os,
"{}let {}: {} = {};", tab, def->unique_name(), Op(def->type()), Dump(def));
521void Dumper::recurse(
const Nest::Node* node) {
522 for (
auto child : node->children().muts())
523 if (
auto mut = isa_decl(child)) dump(mut);
526void Dumper::recurse(
const Def* def,
bool first ) {
527 if (
auto mut = isa_decl(def)) {
528 if (!nest) muts.push(mut);
532 if (!defs.emplace(def).second)
return;
534 for (
auto op : def->deps())
537 if (!first && !Dump(def)) dump_let(def);
549 if (def ==
nullptr)
return os <<
"<nullptr>";
550 if (
auto d = Dump(def)) {
554 return os << id(def);
558 auto _ =
world().freeze();
559 auto dumper = Dumper(os);
562 os <<
this << std::endl;
563 }
else if (
auto mut = isa_decl(
this)) {
564 dumper.muts.push(mut);
566 dumper.recurse(
this);
567 std::println(os,
"{}{}", dumper.tab, Dump(
this));
571 for (; !dumper.muts.empty() && max > 0; --max)
572 dumper.dump(dumper.muts.pop());
577void Def::dump()
const { std::cout <<
this << std::endl; }
581 auto ofs = std::ofstream(file);
586 auto file = id(
this) +
".mim"s;
587 write(max, file.c_str());
598 if (
flags().dump_recursive) {
599 auto dumper = Dumper(os);
601 dumper.muts.push(mut);
602 while (!dumper.muts.empty())
603 dumper.dump(dumper.muts.pop());
605 auto nest =
Nest(*
this);
606 auto dumper = Dumper(os, &nest);
608 for (
const auto&
import :
driver().imports()) {
609 auto kw =
import.tag == ast::Tok::Tag::K_plugin ?
"plugin" :
"import";
613 std::print(os,
"{} \"{}\";\n", kw,
ast::Lexer::escape(
import.src->path().generic_string()));
615 std::print(os,
"{} {};\n", kw,
import.
sym);
617 dumper.recurse(nest.root());
620 assertf(old_gid ==
curr_gid(),
"new nodes created during dump. old_gid: {}; curr_gid: {}", old_gid,
curr_gid());
626 if (
log().level() >= fe::Log::Level::Debug)
dump(
log().ostream());
630 auto ofs = std::ofstream(file);
635 auto file = (
name() ?
name() :
sym(
"_default")).str() +
".mim"s;
A (possibly paramterized) Array.
World & world() const noexcept
friend std::ostream & operator<<(std::ostream &, const Def *)
This will stream def as an operand.
void write(int max) const
std::ostream & stream(std::ostream &, int max) const
std::string unique_name() const
name + "_" + Def::gid
Common base for TExtremum.
A built-in constant of type Nat -> *.
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
Creates a new Tuple / Pack by inserting Insert::value at position Insert::index into Insert::tuple.
static std::optional< T > isa(const Def *def)
A (possibly paramterized) Tuple.
A dependent function type.
static const Pi * isa_cn(const Def *d)
static bool claim(const Driver &, Sym sym, uint32_t gid)
Registers that gid renders as sym and reports whether the plain sym may be used.
Used as intermediate value during optimizatinos such as Analysis.
Data constructor for a Sigma.
A singleton wraps a type into a higher order type.
A variable introduced by a binder (mutable).
u32 curr_gid() const
Manage global identifier - a unique number for each Def.
const fe::Log & log() const
Log via log().e("...", args) etc.; owned by the Driver.
void dump()
Dump to std::cout.
void write()
Same above but file name defaults to World::name.
Flags & flags()
Retrieve compile Flags.
void debug_dump()
Dump in Debug build if World::log::level is fe::Log::Level::Debug.
Sym sym(std::string_view)
void write(const char *file)
Write to a file named file.
const Externals & externals() const
auto freeze() const
Freezes the World until the end of the scope and restores the previous frozen state afterwards:
void dump(std::ostream &os)
Dump to os.
static std::string escape(std::string_view str)
Inverse of Lexer::lex_char: renders str as the body of a Mim string literal.
Assoc
Associativity of an infix expression.
constexpr Assoc prec_assoc(Prec p)
Associativity of precedence level p.
Prec
Expression precedences used by the parser and the dumper; ordered low to high.
@ bot
Alias for Mode::fast.
TBound< true > Join
AKA union.
GIDSet< const Def * > DefSet
std::ostream & operator<<(std::ostream &os, const Def *def)
This will stream def as an operand.
constexpr Assoc prec_assoc(Prec p)
Associativity of precedence level p.
Prec
Expression precedences used by the parser and the dumper; ordered low to high.