20int main(
int argc,
char** argv) {
21 enum Backends { AST, D, Dot, H, LL, Md, Mim, Num_Backends };
24 static const auto version =
"mim command-line utility version " MIM_VER "\n";
27 bool show_help =
false;
28 bool show_version =
false;
29 bool list_search_paths =
false;
30 bool dot_follow_types =
false;
31 bool dot_all_annexes =
false;
32 std::string input, prefix;
34 std::vector<std::string> plugins, search_paths;
35#ifdef MIM_ENABLE_CHECKS
36 std::vector<size_t> breakpoints;
38 std::array<std::string, Num_Backends> output;
41 auto inc_verbose = [&](bool) { ++verbose; };
42 auto& flags = driver.
flags();
45 auto cli = lyra::cli()
46 | lyra::help(show_help)
47 | lyra::opt(show_version )[
"-v"][
"--version" ](
"Display version info and exit.")
48 | lyra::opt(list_search_paths )[
"-l"][
"--list-search-paths" ](
"List search paths in order and exit.")
49 | lyra::opt(clang,
"clang" )[
"-c"][
"--clang" ](
"Path to clang executable (default: '" MIM_WHICH " clang').")
50 | lyra::opt(plugins,
"plugin" )[
"-p"][
"--plugin" ](
"Dynamically load plugin.")
51 | lyra::opt(search_paths,
"path" )[
"-P"][
"--plugin-path" ](
"Path to search for plugins.")
52 | lyra::opt(inc_verbose )[
"-V"][
"--verbose" ](
"Verbose mode. Multiple -V options increase the verbosity. The maximum is 4.").cardinality(0, 4)
53 | lyra::opt(opt,
"level" )[
"-O"][
"--optimize" ](
"Optimization level (default: 2).")
54 | lyra::opt(output[AST],
"file" ) [
"--output-ast" ](
"Directly emits AST represntation of input.")
55 | lyra::opt(output[D ],
"file" ) [
"--output-d" ](
"Emits dependency file containing a rule suitable for 'make' describing the dependencies of the source file (requires --output-h).")
56 | lyra::opt(output[Dot],
"file" ) [
"--output-dot" ](
"Emits the Mim program as a MimIR graph using Graphviz' DOT language.")
57 | lyra::opt(output[H ],
"file" ) [
"--output-h" ](
"Emits a header file to be used to interface with a plugin in C++.")
58 | lyra::opt(output[LL ],
"file" ) [
"--output-ll" ](
"Compiles the Mim program to LLVM.")
59 | lyra::opt(output[Md ],
"file" ) [
"--output-md" ](
"Emits the input formatted as Markdown.")
60 | lyra::opt(output[Mim],
"file" )[
"-o"][
"--output-mim" ](
"Emits the Mim program again.")
61 | lyra::opt(flags.ascii )[
"-a"][
"--ascii" ](
"Use ASCII alternatives in output instead of UTF-8.")
62 | 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.")
63 | lyra::opt(dot_follow_types ) [
"--dot-follow-types" ](
"Follow type dependencies in DOT output.")
64 | lyra::opt(dot_all_annexes ) [
"--dot-all-annexes" ](
"Output all annexes - even if unused - in DOT output.")
65 | lyra::opt(flags.dump_gid,
"level" ) [
"--dump-gid" ](
"Dumps gid of inline expressions as a comment in output if <level> > 0. Use a <level> of 2 to also emit the gid of trivial defs.")
66 | 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.")
67 | lyra::opt(flags.aggressive_lam_spec ) [
"--aggr-lam-spec" ](
"Overrides LamSpec behavior to follow recursive calls.")
68 | 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.")
70 | lyra::opt(breakpoints,
"gid" )[
"-b"][
"--break" ](
"*Triggers breakpoint upon construction of node with global id <gid>. Useful when running in a debugger.")
71 | lyra::opt(flags.reeval_breakpoints ) [
"--reeval-breakpoints" ](
"*Triggers breakpoint even upon unfying a node that has already been built.")
72 | lyra::opt(flags.break_on_alpha_unequal ) [
"--break-on-alpha-unequal"](
"*Triggers breakpoint as soon as two expressions turn out to be not alpha-equivalent.")
73 | lyra::opt(flags.break_on_error ) [
"--break-on-error" ](
"*Triggers breakpoint on ELOG.")
74 | lyra::opt(flags.break_on_warn ) [
"--break-on-warn" ](
"*Triggers breakpoint on WLOG.")
75 | lyra::opt(flags.trace_gids ) [
"--trace-gids" ](
"*Output gids during World::unify/insert.")
77 | lyra::arg(input,
"file" ) (
"Input file.")
81 if (
auto result = cli.parse({argc, argv}); !result)
throw std::invalid_argument(result.message());
84 std::cout << cli << std::endl;
85#ifdef MIM_ENABLE_CHECKS
86 std::cout <<
"*These are developer options only enabled, if 'MIM_ENABLE_CHECKS' is ON." << std::endl;
88 std::cout <<
"Use \"-\" as <file> to output to stdout." << std::endl;
94 std::exit(EXIT_SUCCESS);
99 if (list_search_paths) {
100 for (
auto&& path : driver.
search_paths() | std::views::drop(1))
101 std::cout << path << std::endl;
102 std::exit(EXIT_SUCCESS);
106#ifdef MIM_ENABLE_CHECKS
107 for (
auto b : breakpoints) world.
breakpoint(b);
112 std::array<std::ofstream, Num_Backends> ofs;
113 std::array<std::ostream*, Num_Backends> os;
115 for (
size_t be = 0; be != Num_Backends; ++be) {
116 if (output[be].empty())
continue;
117 if (output[be] ==
"-") {
120 ofs[be].open(output[be]);
125 if (input.empty())
throw std::invalid_argument(
"error: no input given");
126 if (input[0] ==
'-' || input.substr(0, 2) ==
"--")
127 throw std::invalid_argument(
"error: unknown option " + input);
130 if (!flags.bootstrap) {
131 plugins.insert(plugins.begin(),
"compile"s);
132 if (opt >= 2) plugins.emplace_back(
"opt"s);
136 auto path = fs::path(input);
137 world.
set(path.filename().replace_extension().string());
143 for (
const auto& plugin : plugins) {
144 auto mod = parser.plugin(plugin);
146 = ast.ptr<
ast::Import>(Loc(), ast::Tok::Tag::K_plugin,
Dbg(driver.sym(plugin)), std::move(mod));
147 imports.emplace_back(std::move(
import));
149 auto mod = parser.import(driver.sym(input), os[Md]);
150 mod->add_implicit_imports(std::move(imports));
153 if (
auto s = os[AST]) {
155 mod->stream(tab, *s);
158 if (
auto dep = os[D]) {
159 if (
auto autogen_h = output[H]; !autogen_h.empty()) {
160 *dep << autogen_h <<
": ";
161 assert(!driver.
imports().empty());
162 for (
auto sep =
"";
const auto& [path, _] : driver.
imports() | std::views::drop(1)) {
167 throw std::invalid_argument(
"error: --output-d requires --output-h");
172 if (flags.bootstrap) {
173 if (
auto h = os[H]) {
174 auto plugin = world.
sym(fs::path{path}.filename().replace_extension().
string());
175 ast.bootstrap(plugin, *h);
177 opt = std::min(opt, 1);
184 default:
error(
"illegal optimization level '{}'", opt);
187 if (
auto s = os[Dot]) world.
dot(*s, dot_all_annexes, dot_follow_types);
188 if (
auto s = os[Mim]) world.
dump(*s);
190 if (
auto s = os[LL]) {
191 if (
auto backend = driver.
backend(
"ll"))
194 error(
"'ll' emitter not loaded; try loading 'mem' plugin");
196 }
catch (
const Error& e) {
200 }
catch (
const std::exception& e) {
201 errln(
"{}", e.what());
204 errln(
"error: unknown exception");
int main(int argc, char **argv)