topazc
main.cpp
Go to the documentation of this file.
1
6
11#include <llvm/IR/LegacyPassManager.h>
12#include <llvm/Support/TargetSelect.h>
13#include <llvm/Target/TargetMachine.h>
14#include <llvm/Target/TargetOptions.h>
15#include <llvm/TargetParser/Triple.h>
16#include <llvm/Support/raw_ostream.h>
17#include <llvm/Support/FileSystem.h>
18#include <llvm/Passes/PassBuilder.h>
19#include <llvm/MC/TargetRegistry.h>
20#include <llvm/IR/GlobalVariable.h>
21#include <llvm/IRReader/IRReader.h>
22#include <llvm/Support/SourceMgr.h>
23#include <llvm/IR/LLVMContext.h>
24#include <llvm/IR/Constants.h>
25#include <llvm/IR/IRBuilder.h>
26#include <llvm/IR/Module.h>
27#include <filesystem>
28#include <iostream>
29#include <fstream>
30
31int main(int argc, const char *argv[]) {
32 bool print_tokens = false;
33 bool print_ir = false;
34 bool output_is_object = false;
35 bool debug = false;
36
37 if (argc < 2) {
38 std::cerr << "\033[33mUsage: topazc \"path/to/src.tp\"\033[0m\n";
39 return 1;
40 }
41
42
43 std::ifstream file(argv[1]);
44 if (!file.is_open()) {
45 std::cerr << "\033[31mCompilation error: Error openning file: does not exist!\033[0m\n";
46 return 1;
47 }
48
49 std::filesystem::path libs_path = "libs";
50 if (std::filesystem::exists(libs_path)) {
51 if (!std::filesystem::is_directory(libs_path)) {
52 std::cerr << "\033[31mCompilation error: Directory with libraries does not exists!\033[0m\n";
53 return 1;
54 }
55 }
56 else {
57 std::cerr << "\033[31mCompilation error: Directory with libraries does not exists!\033[0m\n";
58 return 1;
59 }
60
61 std::filesystem::path file_path = std::filesystem::absolute(argv[1]);
62 std::string executable_path = file_path;
63
64 for (int i = 1; i < argc; i++) {
65 if (strcmp(argv[i], "--tokens") == 0) {
66 print_tokens = true;
67 }
68 else if (strcmp(argv[i], "--ir") == 0) {
69 print_ir = true;
70 }
71 else if (strcmp(argv[i], "--obj") == 0) {
72 output_is_object = true;
73 }
74 else if (strcmp(argv[i], "--path") == 0) {
75 if (i == argc - 1) {
76 std::cerr << "\033[31mCompilation error: After \033[0m'--path'\033[31m option should be followed by the path to the output file!\033[0m\n";
77 return 1;
78 }
79 executable_path = argv[++i];
80 }
81 else if (strcmp(argv[i], "--debug") == 0) {
82 debug = true;
83 }
84 }
85
86 if (executable_path.find('.') != std::string::npos) {
87 for (int i = executable_path.size() - 1; executable_path[i] != '.'; i--) {
88 executable_path.pop_back();
89 }
90 executable_path.pop_back();
91 }
92
93 #if defined(_WIN32)
94 const char *obj_ext = ".obj";
95 const char *exe_ext = ".exe";
96 #else
97 const char *obj_ext = ".o";
98 const char *exe_ext = "";
99 #endif
100
101 #if defined(_WIN32)
102 executable_path += exe_ext;
103 #endif
104 const std::string object_path = executable_path + obj_ext;
105
106 std::ostringstream content;
107 content << file.rdbuf();
108 file.close();
109 Lexer lexer(content.str(), file_path.string(), debug);
110 std::vector<Token> tokens = lexer.tokenize();
111 if (print_tokens) {
112 std::cout << "\033[1m\033[32mTokens:\033[0m\n";
113 for (Token& token : tokens) {
114 std::cout << token.to_str() << '\n';
115 }
116 }
117
118 Parser parser(tokens, debug);
119 std::vector<AST::StmtPtr> stmts_for_semantic = parser.parse();
120
121 SemanticAnalyzer semantic(stmts_for_semantic, std::filesystem::absolute(libs_path), file_path.string(), debug);
122 semantic.analyze();
123
124 parser.reset();
125 std::vector<AST::StmtPtr> stmts_for_codegen = parser.parse();
126
127 CodeGenerator codegen(stmts_for_codegen, std::filesystem::absolute(libs_path), file_path.string(), debug);
128 codegen.generate();
129 if (print_ir) {
130 if (print_tokens) {
131 std::cout << '\n';
132 }
133 std::cout << "\033[1m\033[32mLLVM IR:\033[0m\n";
134 codegen.print_ir();
135 }
136
137 std::unique_ptr<llvm::Module> module = codegen.get_module();
138
139 llvm::InitializeNativeTarget();
140 llvm::InitializeNativeTargetAsmPrinter();
141 llvm::InitializeNativeTargetAsmParser();
142
143 if (module->getFunction("main") == nullptr) {
144 std::cerr << "\033[31mCompilation error: Program does not have entry point 'main'\033[0m" << '\n';
145 return 1;
146 }
147 auto getTriple = []() -> std::string {
148 const char *env_triple = std::getenv("TOPAZ_TRIPLE");
149 if (env_triple && *env_triple) {
150 return std::string(env_triple);
151 }
152
153 std::array<char, 256> buffer{};
154 std::string result;
155 #if defined(_WIN32)
156 std::unique_ptr<FILE, int(*)(FILE*)> pipe(_popen("clang -dumpmachine", "r"), _pclose);
157 #else
158 std::unique_ptr<FILE, int(*)(FILE*)> pipe(popen("clang -dumpmachine", "r"), pclose);
159 #endif
160 if (!pipe) {
161 return "";
162 }
163 while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get()) != nullptr) {
164 result += buffer.data();
165 }
166 while (!result.empty() && (result.back() == '\n' || result.back() == '\r')) {
167 result.pop_back();
168 }
169 return result;
170 };
171 auto defaultTripleForHost = []() -> std::string {
172 #if defined(_WIN32)
173 #if defined(__aarch64__) || defined(_M_ARM64)
174 return "aarch64-pc-windows-msvc";
175 #else
176 return "x86_64-pc-windows-msvc";
177 #endif
178 #elif defined(__APPLE__)
179 #if defined(__aarch64__)
180 return "arm64-apple-darwin";
181 #else
182 return "x86_64-apple-darwin";
183 #endif
184 #else
185 #if defined(__aarch64__)
186 return "aarch64-unknown-linux-gnu";
187 #else
188 return "x86_64-pc-linux-gnu";
189 #endif
190 #endif
191 };
192 std::string target_triple = getTriple();
193 if (target_triple.empty()) {
194 target_triple = defaultTripleForHost();
195 }
196 module->setTargetTriple(llvm::Triple(target_triple));
197
198 std::string error;
199 const llvm::Target *target = llvm::TargetRegistry::lookupTarget(target_triple, error);
200 if (!target) {
201 std::cerr << error << '\n';
202 return 1;
203 }
204
205 std::string CPU = "generic";
206 std::string features = "";
207 llvm::TargetOptions opt;
208 auto reloc_model = std::optional<llvm::Reloc::Model>();
209 std::unique_ptr<llvm::TargetMachine> target_machine(target->createTargetMachine(target_triple, CPU, features, opt, reloc_model));
210 if (!target_machine) {
211 std::cerr << "\033[31mCompilation error: Failed to create TargetMachine for triple '" << target_triple << "'\033[0m\n";
212 return 1;
213 }
214
215 module->setDataLayout(target_machine->createDataLayout());
216
217 std::error_code ec;
218 llvm::raw_fd_ostream dest(object_path, ec, llvm::sys::fs::OF_None);
219 if (ec) {
220 std::cerr << "\033[31mCompilation error: Could not open file '" << object_path << "': " << ec.message() << "\033[0m\n";
221 return 1;
222 }
223
224 llvm::legacy::PassManager pass;
225
226 auto fileType = static_cast<llvm::CodeGenFileType>(1); // 1 = Object file
227 if (target_machine->addPassesToEmitFile(pass, dest, nullptr, fileType)) {
228 std::cerr << "\033[31mCompilation error: TargetMachine can't emit a file of this type\033[0m\n";
229 return 1;
230 }
231
232 pass.run(*module);
233 dest.flush();
234 dest.close();
235
236 if (output_is_object) {
237 std::cout << "\033[36;1mCOMPILING SUCCESS. Built object:\033[0m " << object_path << '\n';
238 return 0;
239 }
240
241 const char *env_linker = std::getenv("TOPAZC_LINKER");
242 std::string linker = env_linker ? std::string(env_linker) : std::string("clang");
243 #if defined(_WIN32)
244 std::string link_cmd = linker + " \"" + object_path + "\" -o \"" + executable_path + "\" -fuse-ld=lld";
245 #elif defined(__APPLE__)
246 std::string link_cmd = linker + " \"" + object_path + "\" -o \"" + executable_path + "\"";
247 #else
248 std::string link_cmd = linker + " \"" + object_path + "\" -o \"" + executable_path + "\" -no-pie";
249 #endif
250 auto runAndCapture = [](const std::string& cmd) -> std::pair<int, std::string> {
251 std::string output;
252 #if defined(_WIN32)
253 std::unique_ptr<FILE, int(*)(FILE*)> pipe(_popen(cmd.c_str(), "r"), _pclose);
254 #else
255 std::unique_ptr<FILE, int(*)(FILE*)> pipe(popen(cmd.c_str(), "r"), pclose);
256 #endif
257 if (!pipe) {
258 return { -1, std::string("Failed to spawn: ") + cmd };
259 }
260 std::array<char, 512> buf{};
261 while (fgets(buf.data(), static_cast<int>(buf.size()), pipe.get()) != nullptr) {
262 output += buf.data();
263 }
264 int code = 0;
265 #if defined(_WIN32)
266 code = _pclose(pipe.release());
267 #else
268 code = pclose(pipe.release());
269 #endif
270 return { code, output };
271 };
272
273 auto [linkRes, link_out] = runAndCapture(link_cmd);
274 if (linkRes != 0) {
275 std::cerr << "\033[31mCompilation error: Link command: " << link_cmd << '\n';
276 std::cerr << link_out << '\n';
277 std::cerr << "Linking failed with code " << linkRes << "\033[0m\n";
278 return 1;
279 }
280
281 std::cout << "\033[36;1mCOMPILING SUCCESS. Built executable:\033[0m " << executable_path << '\n';
282
283 if (std::remove(object_path.c_str()) != 0) {
284 std::cerr << "\033[31mCompilation error: Warning: Failed to remove object file: " << object_path << "\033[0m\n";
285 return 1;
286 }
287
288 return 0;
289}
Code generator class.
Definition codegen.hpp:23
std::unique_ptr< llvm::Module > get_module()
Method for getting current LLVM Module.
Definition codegen.hpp:81
void print_ir()
Method for printing generated LLVM IR code.
Definition codegen.hpp:70
void generate()
Method for generating LLVM IR code.
Definition codegen.cpp:15
Lexer class.
Definition lexer.hpp:15
std::vector< Token > tokenize()
Method for tokenizing source code.
Definition lexer.cpp:10
Parser class.
Definition parser.hpp:14
std::vector< AST::StmtPtr > parse()
Method for parsing tokens into AST tree.
Definition parser.cpp:10
void reset()
Method for resetting all parameters in parser.
Definition parser.cpp:20
void analyze()
Method for analyze all statements.
Definition semantic.cpp:15
Header file for defining compiler code generator.
Header file for defining the lexer.
int main(int argc, const char *argv[])
Definition main.cpp:31
Header file for defining parser.
Header file for defining semantic analyzer.
Token structure.
Definition token.hpp:92