24int main(
int argc,
char** argv) {
25 enum Backends { D, Dot, H, LL, Md, Thorin, Num_Backends };
28 static const auto version =
"thorin command-line utility version " THORIN_VER "\n";
31 bool show_help =
false;
32 bool show_version =
false;
33 bool list_search_paths =
false;
34 bool dot_follow_types =
false;
35 bool dot_all_annexes =
false;
36 std::string input, prefix;
38 std::vector<std::string> plugins, search_paths;
39#ifdef THORIN_ENABLE_CHECKS
40 std::vector<size_t> breakpoints;
42 std::array<std::string, Num_Backends> output;
45 auto inc_verbose = [&](bool) { ++verbose; };
46 auto& flags = driver.
flags();
49 auto cli = lyra::cli()
50 | lyra::help(show_help)
51 | lyra::opt(show_version )[
"-v"][
"--version" ](
"Display version info and exit.")
52 | lyra::opt(list_search_paths )[
"-l"][
"--list-search-paths" ](
"List search paths in order and exit.")
53 | lyra::opt(clang,
"clang" )[
"-c"][
"--clang" ](
"Path to clang executable (default: '" THORIN_WHICH " clang').")
54 | lyra::opt(plugins,
"plugin" )[
"-p"][
"--plugin" ](
"Dynamically load plugin.")
55 | lyra::opt(search_paths,
"path" )[
"-P"][
"--plugin-path" ](
"Path to search for plugins.")
56 | lyra::opt(inc_verbose )[
"-V"][
"--verbose" ](
"Verbose mode. Multiple -V options increase the verbosity. The maximum is 4.").cardinality(0, 4)
57 | lyra::opt(opt,
"level" )[
"-O"][
"--optimize" ](
"Optimization level (default: 2).")
58 | 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).")
59 | lyra::opt(output[Dot ],
"file" ) [
"--output-dot" ](
"Emits the Thorin program as a graph using Graphviz' DOT language.")
60 | lyra::opt(output[H ],
"file" ) [
"--output-h" ](
"Emits a header file to be used to interface with a plugin in C++.")
61 | lyra::opt(output[LL ],
"file" ) [
"--output-ll" ](
"Compiles the Thorin program to LLVM.")
62 | lyra::opt(output[Md ],
"file" ) [
"--output-md" ](
"Emits the input formatted as Markdown.")
63 | lyra::opt(output[Thorin],
"file" )[
"-o"][
"--output-thorin" ](
"Emits the Thorin program again.")
64 | lyra::opt(flags.ascii )[
"-a"][
"--ascii" ](
"Use ASCII alternatives in output instead of UTF-8.")
65 | lyra::opt(flags.bootstrap ) [
"--bootstrap" ](
"Puts thorin 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.")
66 | lyra::opt(dot_follow_types ) [
"--dot-follow-types" ](
"Follow type dependencies in DOT output.")
67 | lyra::opt(dot_all_annexes ) [
"--dot-all-annexes" ](
"Output all annexes - even if unused - in DOT output.")
68 | 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.")
69 | lyra::opt(flags.dump_recursive ) [
"--dump-recursive" ](
"Dumps Thorin program with a simple recursive algorithm that is not readable again from Thorin but is less fragile and also works for broken Thorin programs.")
70 | lyra::opt(flags.aggressive_lam_spec ) [
"--aggr-lam-spec" ](
"Overrides LamSpec behavior to follow recursive calls.")
71 | lyra::opt(flags.scalerize_threshold,
"threshold") [
"--scalerize-threshold" ](
"Thorin will not scalerize tuples/packs/sigmas/arrays with a number of elements greater than or equal this threshold.")
73 | lyra::opt(breakpoints,
"gid" )[
"-b"][
"--break" ](
"*Triggers breakpoint upon construction of node with global id <gid>. Useful when running in a debugger.")
74 | lyra::opt(flags.reeval_breakpoints ) [
"--reeval-breakpoints" ](
"*Triggers breakpoint even upon unfying a node that has already been built.")
75 | 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.")
76 | lyra::opt(flags.break_on_error ) [
"--break-on-error" ](
"*Triggers breakpoint on ELOG.")
77 | lyra::opt(flags.break_on_warn ) [
"--break-on-warn" ](
"*Triggers breakpoint on WLOG.")
78 | lyra::opt(flags.trace_gids ) [
"--trace-gids" ](
"*Output gids during World::unify/insert.")
80 | lyra::arg(input,
"file" ) (
"Input file.")
84 if (
auto result = cli.parse({argc, argv}); !result)
throw std::invalid_argument(result.message());
87 std::cout << cli << std::endl;
88#ifdef THORIN_ENABLE_CHECKS
89 std::cout <<
"*These are developer options only enabled, if 'THORIN_ENABLE_CHECKS' is ON." << std::endl;
91 std::cout <<
"Use \"-\" as <file> to output to stdout." << std::endl;
97 std::exit(EXIT_SUCCESS);
100 for (
auto&& path : search_paths) driver.add_search_path(path);
102 if (list_search_paths) {
103 for (
auto&& path : driver.search_paths() | std::views::drop(1))
104 std::cout << path << std::endl;
105 std::exit(EXIT_SUCCESS);
108 World& world = driver.world();
109#ifdef THORIN_ENABLE_CHECKS
110 for (
auto b : breakpoints) world.
breakpoint(b);
112 driver.log().set(&std::cerr).set((
Log::Level)verbose);
115 std::array<std::ofstream, Num_Backends> ofs;
116 std::array<std::ostream*, Num_Backends> os;
118 for (
size_t be = 0; be != Num_Backends; ++be) {
119 if (output[be].empty())
continue;
120 if (output[be] ==
"-") {
123 ofs[be].open(output[be]);
129 if (!flags.bootstrap) plugins.insert(plugins.end(), {
"compile",
"opt"});
131 if (!plugins.empty())
132 for (
const auto& plugin : plugins) driver.load(plugin);
134 if (input.empty())
throw std::invalid_argument(
"error: no input given");
135 if (input[0] ==
'-' || input.substr(0, 2) ==
"--")
136 throw std::invalid_argument(
"error: unknown option " + input);
138 auto path = fs::path(input);
139 world.
set(path.filename().replace_extension().string());
140 auto parser =
Parser(world);
141 parser.import(driver.sym(input), os[Md]);
143 if (
auto dep = os[D]) {
144 if (
auto autogen_h = output[H]; !autogen_h.empty()) {
145 *dep << autogen_h <<
": ";
146 assert(!driver.imports().empty());
147 for (
auto sep =
"";
const auto& [path, _] : driver.imports() | std::views::drop(1)) {
152 throw std::invalid_argument(
"error: --output-d requires --output-h");
157 if (flags.bootstrap) {
159 bootstrap(driver, world.
sym(fs::path{path}.filename().replace_extension().string()), *h);
160 opt = std::min(opt, 1);
165 case 1: Phase::run<Cleanup>(world);
break;
167 parser.import(
"opt");
170 default:
error(
"illegal optimization level '{}'", opt);
173 if (os[Thorin]) world.
dump(*os[Thorin]);
174 if (os[Dot]) world.
dot(*os[Dot], dot_all_annexes, dot_follow_types);
177 if (
auto backend = driver.backend(
"ll"))
178 backend(world, *os[LL]);
180 error(
"'ll' emitter not loaded; try loading 'mem' plugin");
182 }
catch (
const std::exception& e) {
186 errln(
"error: unknown exception");
int main(int argc, char **argv)