Yume
yumec.cpp
Go to the documentation of this file.
1#include "ast/ast.hpp"
3#include "compiler/vals.hpp"
7#include "token.hpp"
8#include "util.hpp"
9#include <algorithm>
10#include <cstdlib>
11#include <exception>
12#include <filesystem>
13#include <fstream>
14#include <iterator>
15#include <llvm/ADT/StringRef.h>
16#include <llvm/IR/Module.h>
17#include <llvm/Support/ErrorOr.h>
18#include <llvm/Support/MemoryBuffer.h>
19#include <llvm/Support/PrettyStackTrace.h>
20#include <llvm/Support/Signals.h>
21#include <llvm/Support/raw_ostream.h>
22#include <memory>
23#include <span>
24#include <string>
25#include <string_view>
26#include <utility>
27#include <vector>
28
29#if __has_include("yumec-version.hpp")
30#include "yumec-version.hpp"
31#else
32namespace yume {
33static constexpr std::string_view VERSION = "???";
34static constexpr std::string_view GIT_SHORTHASH = "???";
35} // namespace yume
36#endif
37
38// NOLINTNEXTLINE(bugprone-reserved-identifier,readability-identifier-naming): UBSan hook
39extern "C" auto __ubsan_default_options() -> const char* { return "print_stacktrace=1"; }
40
41using namespace std::string_literals;
42
43enum struct CompilerFlags {
44 None = 0,
45 NoLink = 1 << 0,
46 EmitLLVM = 1 << 1,
47 EmitASM = 1 << 2,
48 EmitDot = 1 << 3,
49 EmitUntypedDot = 1 << 4,
50 DumpAST = 1 << 5,
51 NoPrelude = 1 << 6,
52};
53
55 return static_cast<CompilerFlags>(static_cast<int>(a) | static_cast<int>(b));
56}
57
58inline auto operator|=(CompilerFlags& a, CompilerFlags b) -> CompilerFlags { return a = a | b; }
59
60inline auto operator~(CompilerFlags a) -> CompilerFlags { return static_cast<CompilerFlags>(~static_cast<int>(a)); }
61
62inline auto operator&(CompilerFlags a, CompilerFlags b) -> bool {
63 return (static_cast<int>(a) & static_cast<int>(b)) != 0;
64}
65
66auto lib_dir() -> std::string {
67 const char* env_lib_dir = std::getenv("YUME_LIB_DIR");
68 if (env_lib_dir == nullptr)
69 env_lib_dir = YUME_LIB_DIR;
70
71 return env_lib_dir;
72}
73
74auto compile(const std::optional<std::string>& target_triple, std::vector<std::string> src_file_names,
75 CompilerFlags flags) -> int {
76 if (~flags & CompilerFlags::NoPrelude)
77 src_file_names.insert(src_file_names.begin(), lib_dir() + "std.ym");
78
79 std::vector<yume::SourceFile> source_files{};
80 source_files.reserve(src_file_names.size());
81
82 {
83 std::vector<std::unique_ptr<llvm::MemoryBuffer>> inputs{};
84 inputs.reserve(src_file_names.size());
85
86 std::unique_ptr<llvm::raw_ostream> dot_file{};
87 std::unique_ptr<yume::diagnostic::DotVisitor> dot_visitor{};
89 dot_file = yume::open_file("output_untyped.dot");
90 dot_visitor = std::make_unique<yume::diagnostic::DotVisitor>(*dot_file);
91 }
92
93 for (const auto& i : src_file_names) {
94 auto buffer = llvm::MemoryBuffer::getFileOrSTDIN(i);
95 if (!buffer)
96 throw std::runtime_error("While opening file "s + i + ": " + buffer.getError().message());
97
98 inputs.emplace_back(std::move(buffer.get()));
99 }
100
101 for (auto& src_input : inputs) {
102 auto src_name = src_input->getBufferIdentifier().str();
103 auto src_special = src_name.front() == '<' && src_name.back() == '>';
104 auto src_path =
105 src_special ? std::filesystem::path{} : std::filesystem::canonical(std::filesystem::absolute(src_name));
106 auto src_stream = std::stringstream(std::string(src_input->getBufferStart(), src_input->getBufferSize()));
107 auto& source = source_files.emplace_back(src_stream, src_path);
108
110 dot_visitor->visit(*source.program, "");
111
112 if (flags & CompilerFlags::DumpAST) {
113 yume::diagnostic::PrintVisitor(llvm::errs(), true).visit(*source.program, "");
114 llvm::errs() << "\n";
115 }
116
117 auto token_it = source.iterator;
118 if (!token_it.at_end()) {
119 llvm::outs() << "unconsumed tokens:\n";
120 while (!token_it.at_end())
121 llvm::outs() << " " << *token_it++ << "\n";
122 llvm::outs() << "\n";
123 llvm::outs().flush();
124
125 return 2;
126 }
127 }
128 }
129
130 if (flags & CompilerFlags::DumpAST)
131 return 0;
132
133 auto compiler = yume::Compiler{target_triple, std::move(source_files)};
134 compiler.run();
135
136 if (flags & CompilerFlags::EmitDot) {
137 for (const auto& i : compiler.source_files()) {
138 const std::string full_name = "output_"s + i.path.stem().native() + ".dot";
139 auto dot = yume::open_file(full_name.c_str());
141 visitor.visit(*i.program, "");
142 }
143 }
144
145 if (flags & CompilerFlags::EmitLLVM)
146 compiler.module()->print(*yume::open_file("output.ll"), nullptr);
147 if (flags & CompilerFlags::EmitASM)
148 compiler.write_object("output.s", false);
149 compiler.write_object("output.o", true);
150 llvm::outs().flush();
151 if (~flags & CompilerFlags::NoLink)
152 std::system("cc output.o -o yume.out");
153
154 return EXIT_SUCCESS;
155}
156
158 llvm::outs() << "yume version " << yume::VERSION << "-" << yume::GIT_SHORTHASH << "\n";
159 llvm::outs() << "lib: " << lib_dir() << "\n";
160 llvm::outs() << "build-time LIB_DIR: " YUME_LIB_DIR "\n";
161 llvm::outs() << "build-time SRC_DIR: " YUME_SRC_DIR "\n";
162}
163
164auto main(int argc, const char* argv[]) -> int {
165 auto raw_args = std::span(argv, argc);
166 auto args = raw_args.subspan(1); // omit argv 0 (program name)
167
168 llvm::outs().enable_colors(llvm::outs().has_colors());
169 llvm::errs().enable_colors(llvm::errs().has_colors());
170
171 auto fatal_error = [&]() -> auto& {
172 emit_version();
173 llvm::outs() << "\n";
174 llvm::outs().changeColor(llvm::raw_ostream::WHITE, true) << raw_args[0];
175 llvm::outs().resetColor() << ": ";
176 llvm::outs().changeColor(llvm::raw_ostream::RED) << "error";
177 llvm::outs().resetColor() << ": ";
178 return llvm::outs();
179 };
180
181 std::optional<std::string> target_triple = {};
182 std::vector<std::string> source_file_names = {};
183 bool consuming_target = false;
184 bool done_with_flags = false;
185 auto flags = CompilerFlags::None;
186
187 for (const auto& arg : args) {
188 if (consuming_target) {
189 target_triple = arg;
190 consuming_target = false;
191 continue;
192 }
193 if (arg == "--version"s) {
194 emit_version();
195 return EXIT_SUCCESS;
196 }
197 if (arg == "--target"s) {
198 consuming_target = true;
199 } else if (arg == "-c"s) {
200 flags |= CompilerFlags::NoLink;
201 } else if (arg == "--emit-llvm"s) {
203 } else if (arg == "--emit-asm"s) {
204 flags |= CompilerFlags::EmitASM;
205 } else if (arg == "--emit-dot"s) {
206 flags |= CompilerFlags::EmitDot;
207 } else if (arg == "--emit-untyped-dot"s) {
209 } else if (arg == "--dump-ast"s) {
210 flags |= CompilerFlags::DumpAST;
211 } else if (arg == "--no-prelude"s) {
213 } else if (arg == "--"s) {
214 done_with_flags = true;
215 } else if (!done_with_flags && std::string(arg).starts_with('-')) {
216 fatal_error() << "unknown flag " << arg << "\n";
217 return 3;
218 } else {
219 source_file_names.emplace_back(arg);
220 }
221 }
222
223 if (source_file_names.empty()) {
224 fatal_error() << "provide at least one source file\n";
225 return 1;
226 }
227
228 std::set_terminate(yume::print_exception);
229 llvm::EnablePrettyStackTrace();
230 llvm::setBugReportMsg("");
231 llvm::sys::AddSignalHandler(yume::backtrace, args.data());
232
233 return compile(target_triple, source_file_names, flags);
234}
The Compiler the the primary top-level type during compilation. A single instance is created during t...
Definition: compiler.hpp:45
void run()
Begin compilation!
Definition: compiler.cpp:180
auto visit(const ast::AST &expr, string_view label) -> PrintVisitor &override
Definition: ast.cpp:8
void backtrace(void *)
Definition: errors.cpp:284
void print_exception()
Definition: errors.hpp:41
auto open_file(nonnull< const char * > filename) -> unique_ptr< llvm::raw_pwrite_stream >
Opens a writeable stream to a file with the given filename relative to the current working directory.
Definition: util.hpp:91
static constexpr std::string_view GIT_SHORTHASH
Definition: yumec.cpp:34
static constexpr std::string_view VERSION
Definition: yumec.cpp:33
Visitor & visitor
Definition: visit.cpp:17
auto operator|(CompilerFlags a, CompilerFlags b) -> CompilerFlags
Definition: yumec.cpp:54
CompilerFlags
Definition: yumec.cpp:43
auto __ubsan_default_options() -> const char *
Definition: yumec.cpp:39
void emit_version()
Definition: yumec.cpp:157
auto main(int argc, const char *argv[]) -> int
Definition: yumec.cpp:164
auto lib_dir() -> std::string
Definition: yumec.cpp:66
auto operator&(CompilerFlags a, CompilerFlags b) -> bool
Definition: yumec.cpp:62
auto operator|=(CompilerFlags &a, CompilerFlags b) -> CompilerFlags
Definition: yumec.cpp:58
auto operator~(CompilerFlags a) -> CompilerFlags
Definition: yumec.cpp:60
auto compile(const std::optional< std::string > &target_triple, std::vector< std::string > src_file_names, CompilerFlags flags) -> int
Definition: yumec.cpp:74