6#include <lyra/lyra.hpp>
21using namespace std::literals;
25int main(
int argc,
char** argv) {
27 static const auto version =
"thorin command-line utility version " THORIN_VER "\n";
30 bool show_help =
false;
31 bool show_version =
false;
32 bool list_search_paths =
false;
33 std::string input, prefix;
35 std::vector<std::string> plugins, search_paths;
36#ifdef THORIN_ENABLE_CHECKS
37 std::vector<size_t> breakpoints;
39 std::array<std::string, Num_Backends> output;
42 auto inc_verbose = [&](bool) { ++verbose; };
43 auto& flags = driver.
flags();
46 auto cli = lyra::cli()
47 | lyra::help(show_help)
48 | lyra::opt(show_version )[
"-v"][
"--version" ](
"Display version info and exit.")
49 | lyra::opt(list_search_paths )[
"-l"][
"--list-search-paths" ](
"List search paths in order and exit.")
50 | lyra::opt(clang,
"clang" )[
"-c"][
"--clang" ](
"Path to clang executable (default: '" THORIN_WHICH " clang').")
51 | lyra::opt(plugins,
"plugin" )[
"-p"][
"--plugin" ](
"Dynamically load plugin.")
52 | lyra::opt(search_paths,
"path" )[
"-P"][
"--plugin-path" ](
"Path to search for plugins.")
53 | lyra::opt(inc_verbose )[
"-V"][
"--verbose" ](
"Verbose mode. Multiple -V options increase the verbosity. The maximum is 4.").cardinality(0, 4)
54 | lyra::opt(opt,
"level" )[
"-O"][
"--optimize" ](
"Optimization level (default: 2).")
55 | lyra::opt(output[
Dot ],
"file" ) [
"--output-dot" ](
"Emits the Thorin program as a graph using Graphviz' DOT language.")
56 | lyra::opt(output[
H ],
"file" ) [
"--output-h" ](
"Emits a header file to be used to interface with a plugin in C++.")
57 | lyra::opt(output[
LL ],
"file" ) [
"--output-ll" ](
"Compiles the Thorin program to LLVM.")
58 | lyra::opt(output[
Md ],
"file" ) [
"--output-md" ](
"Emits the input formatted as Markdown.")
59 | lyra::opt(output[
Thorin],
"file" )[
"-o"][
"--output-thorin" ](
"Emits the Thorin program again.")
60 | 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.")
61 | 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.")
62 | 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.")
63 | lyra::opt(flags.aggressive_lam_spec ) [
"--aggr-lam-spec" ](
"Overrides LamSpec behavior to follow recursive calls.")
64 | 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.")
66 | lyra::opt(breakpoints,
"gid" )[
"-b"][
"--break" ](
"*Triggers breakpoint upon construction of node with global id <gid>. Useful when running in a debugger.")
67 | lyra::opt(flags.reeval_breakpoints ) [
"--reeval-breakpoints" ](
"*Triggers breakpoint even upon unfying a node that has already been built.")
68 | 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.")
69 | lyra::opt(flags.break_on_error ) [
"--break-on-error" ](
"*Triggers breakpoint on ELOG.")
70 | lyra::opt(flags.break_on_warn ) [
"--break-on-warn" ](
"*Triggers breakpoint on WLOG.")
71 | lyra::opt(flags.trace_gids ) [
"--trace-gids" ](
"*Output gids during World::unify/insert.")
73 | lyra::arg(input,
"file" ) (
"Input file.")
77 if (
auto result = cli.parse({argc, argv}); !result)
throw std::invalid_argument(result.message());
80 std::cout << cli << std::endl;
81#ifdef THORIN_ENABLE_CHECKS
82 std::cout <<
"*These are developer options only enabled, if 'THORIN_ENABLE_CHECKS' is ON." << std::endl;
84 std::cout <<
"Use \"-\" as <file> to output to stdout." << std::endl;
90 std::exit(EXIT_SUCCESS);
93 for (
auto&& path : search_paths) driver.add_search_path(path);
95 if (list_search_paths) {
96 for (
auto&& path : driver.search_paths() | std::views::drop(1))
97 std::cout << path << std::endl;
98 std::exit(EXIT_SUCCESS);
101 World& world = driver.world();
102#ifdef THORIN_ENABLE_CHECKS
103 for (
auto b : breakpoints) world.
breakpoint(b);
105 driver.log().set(&std::cerr).set((
Log::Level)verbose);
108 std::array<std::ofstream, Num_Backends> ofs;
109 std::array<std::ostream*, Num_Backends> os;
112 if (output[be].empty())
continue;
113 if (output[be] ==
"-") {
116 ofs[be].open(output[be]);
122 if (!flags.bootstrap) plugins.insert(plugins.end(), {
"core",
"mem",
"compile",
"opt"});
124 if (!plugins.empty())
125 for (
const auto& plugin : plugins) driver.load(plugin);
127 if (input.empty())
throw std::invalid_argument(
"error: no input given");
128 if (input[0] ==
'-' || input.substr(0, 2) ==
"--")
129 throw std::invalid_argument(
"error: unknown option " + input);
131 auto path = fs::path(input);
132 world.
set(path.filename().replace_extension().string());
134 parser.import(input, os[
Md]);
136 if (flags.bootstrap) {
138 bootstrap(driver, world.
sym(fs::path{path}.filename().replace_extension().string()), *h);
139 opt = std::min(opt, 1);
144 case 1: Phase::run<Cleanup>(world);
break;
146 parser.import(
"opt");
149 default:
error(
"illegal optimization level '{}'", opt);
156 if (
auto backend = driver.backend(
"ll"))
157 backend(world, *os[
LL]);
159 error(
"'ll' emitter not loaded; try loading 'mem' plugin");
161 }
catch (
const std::exception& e) {
165 errln(
"error: unknown exception");
Some "global" variables needed all over the place.
The World represents the whole program and manages creation of Thorin nodes (Defs).
Sym sym(std::string_view)
void dump(std::ostream &os)
Dump to os.
void breakpoint(u32 gid)
Trigger breakpoint in your debugger when creating Def with Def::gid gid.
Parses Thorin code into the provided World.
#define THORIN_ENABLE_CHECKS
int main(int argc, char **argv)
void emit(World &w, std::ostream &s, std::function< void(std::ostream &, const Def *)> stream_def)
std::string find_cmd(std::string cmd)
std::ostream & errln(const char *fmt, Args &&... args)
void error(const Def *def, const char *fmt, Args &&... args)
void bootstrap(Driver &driver, Sym plugin, std::ostream &h)
void optimize(World &world)