23int main(
int argc,
char** argv) {
24 enum Backends { AST, Dot, H, PY, Md,
Mim,
Nest, SExpr, SlottedSExpr, ProfileTrace, Num_Backends };
27 fe::term::resolve_mode();
31 bool show_help =
false;
32 bool show_version =
false;
33 bool list_search_paths =
false;
34 bool sexpr_include_types =
false;
36 std::string input, prefix;
38 std::vector<std::string> plugins, search_paths, plugin_args;
39#ifdef MIM_ENABLE_CHECKS
40 std::vector<uint32_t> breakpoints;
41 std::vector<uint32_t> watchpoints;
43 std::array<std::string, Num_Backends> output;
45 auto inc_verbose = [&](bool) { ++verbose; };
46 auto& flags = driver.
flags();
48 auto profile = [&](
const std::string& t) {
51 else if (t ==
"trace")
55 if (output[ProfileTrace].empty()) output[ProfileTrace] =
"-";
57 auto profile_path = [&](
const std::string& t) {
62 output[ProfileTrace] = t;
66 auto cli = lyra::cli()
67 | lyra::help(show_help)
68 | lyra::opt(show_version )[
"-v"][
"--version" ](
"Display version info and exit.")
69 | lyra::opt(list_search_paths )[
"-l"][
"--list-search-paths" ](
"List search paths in order and exit.")
70 | lyra::opt(clang,
"clang" )[
"-c"][
"--clang" ](
"Path to clang executable (default: '" MIM_WHICH " clang').")
71 | lyra::opt(plugins,
"plugin" )[
"-p"][
"--plugin" ](
"Dynamically load plugin.")
72 | lyra::opt(search_paths,
"path" )[
"-P"][
"--plugin-path" ](
"Path to search for plugins.")
73 | lyra::opt(plugin_args,
"plugin:arg" )[
"-X"][
"--plugin-arg" ](
"Pass <arg> to plugin/phase <plugin>, e.g. -X ll:--target=sm_80. Repeatable.")
74 | lyra::opt(inc_verbose )[
"-V"][
"--verbose" ](
"Verbose mode. Multiple -V options increase the verbosity. The maximum is 4.").cardinality(0, 5)
75 | lyra::opt(output[AST],
"file" ) [
"--output-ast" ](
"Directly emits AST representation of input.")
76 | lyra::opt(output[Dot],
"file" ) [
"--output-dot" ](
"Emits the Mim program as a MimIR graph using Graphviz' DOT language.")
77 | lyra::opt(output[H ],
"file" ) [
"--output-h" ](
"Emits a header file to be used to interface with a plugin in C++.")
78 | lyra::opt(output[PY ],
"file" ) [
"--output-py" ](
"Emits a Python enum to be used to interface with a plugin in Python.")
79 | lyra::opt(output[Md ],
"file" ) [
"--output-md" ](
"Emits the input formatted as Markdown.")
80 | lyra::opt(output[
Mim],
"file" )[
"-o"][
"--output-mim" ](
"Emits the Mim program again.")
81 | lyra::opt(output[
Nest],
"file" ) [
"--output-nest" ](
"Emits program nesting tree as Dot.")
82 | lyra::opt(output[SExpr],
"file" ) [
"--output-sexpr" ](
"Emits the program as symbolic expression.")
83 | lyra::opt(output[SlottedSExpr],
"file" ) [
"--output-sexpr-slotted" ](
"Emits the program as symbolic expression that follows the format required by slotted-egraphs.")
84 | lyra::opt(flags.force_load ) [
"--force-load" ](
"Load plugins even on version mismatch.")
85 | lyra::opt(profile,
"|summary|tree|trace" ) [
"--profile" ](
"Measure how long each phase takes and write a summary, tree or chrome://tracing compatible output to the output-profile provided destination.")
86 | lyra::opt(profile_path,
"file" ) [
"--output-profile" ](
"The output path (or '-' for stdout) for the profiling information.")
87 | lyra::opt(flags.ascii )[
"-a"][
"--ascii" ](
"Use ASCII alternatives in output instead of UTF-8.")
88 | lyra::opt(flags.bootstrap ) [
"--bootstrap" ](
"Puts mim into \"bootstrap mode\". This means a 'plugin' directive has the same effect as an 'import' and will not load a library. In addition, no standard plugins will be loaded.")
89 | lyra::opt(sexpr_include_types ) [
"--sexpr-include-types" ](
"Wraps symbolic expression terms in a type annotation. Types will not be wrapped in type annotations.")
90 | lyra::opt(dot.
follow_types ) [
"--dot-follow-types" ](
"Follow type dependencies in DOT output.")
91 | lyra::opt(dot.
all_annexes ) [
"--dot-all-annexes" ](
"Output all annexes - even if unused - in DOT output.")
92 | lyra::opt(dot.
inline_consts ) [
"--dot-inline-consts" ](
"Wire up literals, axioms, etc. with normal edges in DOT output instead of detaching them into a separate row; useful for small graphs.")
93 | lyra::opt(dot.
default_filter ) [
"--dot-default-filter" ](
"Always show a lambda's filter in DOT output - even if it is the default one (ff for continuations, tt for direct-style functions).")
94 | lyra::opt(dot.
show_hidden ) [
"--dot-show-hidden" ](
"Render otherwise-transparent detached edges in DOT output (Var->binder back-edges, shared literals/axioms, and type edges) in a subtle gray instead of fully transparent.")
95 | lyra::opt(flags.dump_recursive ) [
"--dump-recursive" ](
"Dumps Mim program with a simple recursive algorithm that is not readable again from Mim but is less fragile and also works for broken Mim programs.")
96 | lyra::opt(flags.aggressive_lam_spec ) [
"--aggr-lam-spec" ](
"Overrides LamSpec behavior to follow recursive calls.")
97 | lyra::opt(flags.scalarize_threshold,
"threshold") [
"--scalarize-threshold" ](
"MimIR will not scalarize tuples/packs/sigmas/arrays with a number of elements greater than or equal this threshold.")
98 | lyra::opt(flags.max_fp_iters,
"num" ) [
"--max-fp-iters" ](
"Maximum number of fixed-point iterations before a phase errors out; guards against non-monotone analyses.")
100 | lyra::opt(breakpoints,
"gid" )[
"-b"][
"--break" ](
"*Triggers breakpoint when creating a node whose global id is <gid>.")
101 | lyra::opt(watchpoints,
"gid" )[
"-w"][
"--watch" ](
"*Triggers breakpoint when setting a node whose global id is <gid>.")
102 | lyra::opt(flags.reeval_breakpoints ) [
"--reeval-breakpoints" ](
"*Triggers breakpoint even upon unfying a node that has already been built.")
103 | lyra::opt(flags.break_on_alpha ) [
"--break-on-alpha" ](
"*Triggers breakpoint as soon as two expressions turn out to be not alpha-equivalent.")
104 | lyra::opt(flags.break_on_error ) [
"--break-on-error" ](
"*Triggers breakpoint on ELOG.")
105 | lyra::opt(flags.break_on_warn ) [
"--break-on-warn" ](
"*Triggers breakpoint on WLOG.")
106 | lyra::opt(flags.trace_gids ) [
"--trace-gids" ](
"*Output gids during World::unify/insert.")
108 | lyra::arg(input,
"file" ) (
"Input file.")
112 if (
auto result = cli.parse({argc, argv}); !result)
throw std::invalid_argument(result.message());
115 std::cout << cli << std::endl;
116#ifdef MIM_ENABLE_CHECKS
117 std::cout <<
"*These are developer options only enabled, if 'MIM_ENABLE_CHECKS' is ON." << std::endl;
119 std::cout <<
"Use \"-\" as <file> to output to stdout." << std::endl;
124 std::cout <<
"mim " << driver.
version() << std::endl;
125 std::exit(EXIT_SUCCESS);
128 for (
auto&& path : search_paths)
131 for (
auto&& pa : plugin_args) {
132 auto pos = pa.find(
':');
133 if (pos == std::string::npos)
134 throw std::invalid_argument(
"error: --plugin-arg expects <plugin>:<arg>, got '" + pa +
"'");
135 driver.
add_arg(driver.sym(pa.substr(0, pos)), pa.substr(pos + 1));
138 if (list_search_paths) {
139 for (
auto&& path : driver.
search_paths() | std::views::drop(1))
140 std::cout << path << std::endl;
141 std::exit(EXIT_SUCCESS);
145#ifdef MIM_ENABLE_CHECKS
146 for (
auto b : breakpoints)
148 for (
auto w : watchpoints)
154 std::array<std::ofstream, Num_Backends> ofs;
155 std::array<std::ostream*, Num_Backends> os;
157 for (
size_t be = 0; be != Num_Backends; ++be) {
158 if (output[be].empty())
continue;
159 if (output[be] ==
"-") {
162 ofs[be].open(output[be]);
167 if (input.empty())
throw std::invalid_argument(
"error: no input given");
168 if (input[0] ==
'-' || input.substr(0, 2) ==
"--")
169 throw std::invalid_argument(
"error: unknown option " + input);
172 auto path = fs::path(input);
173 world.
set(path.filename().replace_extension().string());
178 if (
auto mod = parser.import_main(input, plugins, os[Md])) {
179 if (
auto s = os[AST]) {
180 auto tab = fe::Tab::spaces();
181 mod->stream(tab, *s);
189 auto plugin = world.
sym(fs::path{path}.filename().replace_extension().
string());
190 if (h)
ast.bootstrap(plugin, *h);
191 if (py)
ast.bootstrap_py(plugin, *py);
198 if (
auto s = os[Dot]) world.
dot(*s, dot);
199 if (
auto s = os[
Mim]) world.
dump(*s);
202 if (
auto s = os[SExpr]) {
203 if (sexpr_include_types)
208 if (
auto s = os[SlottedSExpr]) {
209 if (sexpr_include_types)
214 if (
auto s = os[ProfileTrace]) {
215 switch (flags.profile) {
223 fe::throwf(
"couldn't read file '{}'", input);
225 }
catch (
const Error& e) {
229 }
catch (
const std::exception& e) {
230 std::println(std::cerr,
"{}", e.what());
233 std::println(std::cerr,
"error: unknown exception");
int main(int argc, char **argv)