MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1#include <cstdlib>
2#include <cstring>
3
4#include <fstream>
5#include <string>
6
7#include <lyra/lyra.hpp>
8
9#include "mim/config.h"
10#include "mim/driver.h"
11#include "mim/phase.h"
12#include "mim/sexpr.h"
13
14#include "mim/ast/parser.h"
15#include "mim/pass/optimize.h"
16#include "mim/util/sys.h"
17
18using namespace mim;
19using namespace std::literals;
20
21int main(int argc, char** argv) {
22 enum Backends { AST, Dot, H, PY, Md, Mim, Nest, SExpr, SlottedSExpr, Num_Backends };
23
24 try {
25 Driver driver;
26 bool show_help = false;
27 bool show_version = false;
28 bool list_search_paths = false;
29 bool dot_follow_types = false;
30 bool dot_all_annexes = false;
31 bool sexpr_include_types = false;
32 std::string input, prefix;
33 std::string clang = sys::find_cmd("clang");
34 std::vector<std::string> plugins, search_paths;
35#ifdef MIM_ENABLE_CHECKS
36 std::vector<uint32_t> breakpoints;
37 std::vector<uint32_t> watchpoints;
38#endif
39 std::array<std::string, Num_Backends> output;
40 int verbose = 0;
41 auto inc_verbose = [&](bool) { ++verbose; };
42 auto& flags = driver.flags();
43
44 // clang-format off
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, 5)
53 | lyra::opt(output[AST], "file" ) ["--output-ast" ]("Directly emits AST representation of input.")
54 | lyra::opt(output[Dot], "file" ) ["--output-dot" ]("Emits the Mim program as a MimIR graph using Graphviz' DOT language.")
55 | lyra::opt(output[H ], "file" ) ["--output-h" ]("Emits a header file to be used to interface with a plugin in C++.")
56 | lyra::opt(output[PY ], "file" ) ["--output-py" ]("Emits a Python enum to be used to interface with a plugin in Python.")
57 | lyra::opt(output[Md ], "file" ) ["--output-md" ]("Emits the input formatted as Markdown.")
58 | lyra::opt(output[Mim], "file" )["-o"]["--output-mim" ]("Emits the Mim program again.")
59 | lyra::opt(output[Nest], "file" ) ["--output-nest" ]("Emits program nesting tree as Dot.")
60 | lyra::opt(output[SExpr],"file" ) ["--output-sexpr" ]("Emits the program as symbolic expression.")
61 | lyra::opt(output[SlottedSExpr],"file" ) ["--output-sexpr-slotted" ]("Emits the program as symbolic expression that follows the format required by slotted-egraphs.")
62 | lyra::opt(flags.force_load ) ["--force-load" ]("Load plugins even on version mismatch.")
63 | lyra::opt(flags.ascii )["-a"]["--ascii" ]("Use ASCII alternatives in output instead of UTF-8.")
64 | 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.")
65 | lyra::opt(sexpr_include_types ) ["--sexpr-include-types" ]("Wraps symbolic expression terms in a type annotation. Types will not be wrapped in type annotations.")
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_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.")
69 | lyra::opt(flags.aggressive_lam_spec ) ["--aggr-lam-spec" ]("Overrides LamSpec behavior to follow recursive calls.")
70 | 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.")
72 | lyra::opt(breakpoints, "gid" )["-b"]["--break" ]("*Triggers breakpoint when creating a node whose global id is <gid>.")
73 | lyra::opt(watchpoints, "gid" )["-w"]["--watch" ]("*Triggers breakpoint when setting a node whose global id is <gid>.")
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 ) ["--break-on-alpha" ]("*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.")
79#endif
80 | lyra::arg(input, "file" ) ("Input file.")
81 ;
82 // clang-format on
83
84 if (auto result = cli.parse({argc, argv}); !result) throw std::invalid_argument(result.message());
85
86 if (show_help) {
87 std::cout << cli << std::endl;
88#ifdef MIM_ENABLE_CHECKS
89 std::cout << "*These are developer options only enabled, if 'MIM_ENABLE_CHECKS' is ON." << std::endl;
90#endif
91 std::cout << "Use \"-\" as <file> to output to stdout." << std::endl;
92 return EXIT_SUCCESS;
93 }
94
95 if (show_version) {
96 std::cout << "mim " << driver.version() << std::endl;
97 std::exit(EXIT_SUCCESS);
98 }
99
100 for (auto&& path : search_paths)
101 driver.add_search_path(path);
102
103 if (list_search_paths) {
104 for (auto&& path : driver.search_paths() | std::views::drop(1)) // skip first empty path
105 std::cout << path << std::endl;
106 std::exit(EXIT_SUCCESS);
107 }
108
109 World& world = driver.world();
110#ifdef MIM_ENABLE_CHECKS
111 for (auto b : breakpoints)
112 world.breakpoint(b);
113 for (auto w : watchpoints)
114 world.watchpoint(w);
115#endif
116 driver.log().set(&std::cerr).set((Log::Level)verbose);
117
118 // prepare output files and streams
119 std::array<std::ofstream, Num_Backends> ofs;
120 std::array<std::ostream*, Num_Backends> os;
121 os.fill(nullptr);
122 for (size_t be = 0; be != Num_Backends; ++be) {
123 if (output[be].empty()) continue;
124 if (output[be] == "-") {
125 os[be] = &std::cout;
126 } else {
127 ofs[be].open(output[be]);
128 os[be] = &ofs[be];
129 }
130 }
131
132 if (input.empty()) throw std::invalid_argument("error: no input given");
133 if (input[0] == '-' || input.substr(0, 2) == "--")
134 throw std::invalid_argument("error: unknown option " + input);
135
136 try {
137 auto path = fs::path(input);
138 world.set(path.filename().replace_extension().string());
139
140 auto ast = ast::AST(world);
141 auto parser = ast::Parser(ast);
142
143 if (auto mod = parser.import_main(input, plugins, os[Md])) {
144 if (auto s = os[AST]) {
145 auto tab = fe::Tab::spaces();
146 mod->stream(tab, *s);
147 }
148
149 auto h = os[H];
150 auto py = os[PY];
151 if (h || py) {
152 mod->bind(ast);
153 ast.error().ack();
154 auto plugin = world.sym(fs::path{path}.filename().replace_extension().string());
155 if (h) ast.bootstrap(plugin, *h);
156 if (py) ast.bootstrap_py(plugin, *py);
157 return EXIT_SUCCESS;
158 }
159
160 mod->compile(ast);
161 optimize(world);
162
163 if (auto s = os[Dot]) world.dot(*s, dot_all_annexes, dot_follow_types);
164 if (auto s = os[Mim]) world.dump(*s);
165 if (auto s = os[Nest]) mim::Nest(world).dot(*s);
166
167 if (auto s = os[SExpr]) {
168 if (sexpr_include_types)
169 sexpr::emit_typed(world, *s);
170 else
171 sexpr::emit(world, *s);
172 }
173 if (auto s = os[SlottedSExpr]) {
174 if (sexpr_include_types)
175 sexpr::emit_slotted_typed(world, *s);
176 else
177 sexpr::emit_slotted(world, *s);
178 }
179 } else {
180 error("couldn't read file '{}'", input);
181 }
182 } catch (const Error& e) { // e.loc.path doesn't exist anymore in outer scope so catch Error here
183 std::cerr << e;
184 return EXIT_FAILURE;
185 }
186 } catch (const std::exception& e) {
187 std::println(std::cerr, "{}", e.what());
188 return EXIT_FAILURE;
189 } catch (...) {
190 std::println(std::cerr, "error: unknown exception");
191 return EXIT_FAILURE;
192 }
193
194 return EXIT_SUCCESS;
195}
Some "global" variables needed all over the place.
Definition driver.h:19
void add_search_path(fs::path path)
Definition driver.h:50
const Version & version() const
MimIR Version.
Definition driver.h:38
World & world()
Definition driver.h:37
Log & log() const
Definition driver.h:36
Flags & flags()
Definition driver.h:34
const auto & search_paths() const
Definition driver.h:49
Log & set(std::ostream *ostream)
Definition log.h:38
Level
Definition log.h:23
Builds a nesting tree for all mutables/binders.
Definition nest.h:18
void dot(std::ostream &os) const
Definition dot.cpp:204
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:36
void watchpoint(u32 gid)
Trigger breakpoint in your debugger when Def::setting a Def with this gid.
Definition world.cpp:736
void set(Sym name)
Definition world.h:98
Sym sym(std::string_view)
Definition world.cpp:105
void breakpoint(u32 gid)
Trigger breakpoint in your debugger when creating a Def with this gid.
Definition world.cpp:735
void dump(std::ostream &os)
Dump to os.
Definition dump.cpp:566
void dot(std::ostream &os, bool annexes=false, bool types=false) const
Dumps DOT to os.
Definition dot.cpp:180
Parses Mim code as AST.
Definition parser.h:30
#define MIM_ENABLE_CHECKS
Definition config.h:3
int main(int argc, char **argv)
Definition main.cpp:21
Definition Mim.cmake:1
Definition ast.h:14
void emit_slotted(World &, std::ostream &)
Definition sexpr.cpp:997
void emit_typed(World &, std::ostream &)
Definition sexpr.cpp:992
void emit(World &, std::ostream &)
Definition sexpr.cpp:987
void emit_slotted_typed(World &, std::ostream &)
Definition sexpr.cpp:1002
std::string find_cmd(std::string)
Definition sys.cpp:95
Definition ast.h:14
void optimize(World &)
Runs _compile or _default_compile, if available (in this order).
Definition optimize.cpp:8
void error(std::format_string< Args... > fmt, Args &&... args)
Wraps std::format to throw T with a formatted message.
Definition dbg.h:17
#define MIM_WHICH
Definition sys.h:10