Phasor 3.3.0
Stack VM based Programming Language
Loading...
Searching...
No Matches
CppCompiler.cpp
Go to the documentation of this file.
1#include "CppCompiler.hpp"
7#include <version.h>
8#include <filesystem>
9#include <fstream>
10#include <print>
11#include <sstream>
12
13namespace Phasor
14{
15
16CppCompiler::CppCompiler(int argc, char *argv[])
17{
18 parseArguments(argc, argv);
19}
20
22{
23 if (m_args.showHelp)
24 {
25 showHelp("phasornative");
26 return 0;
27 }
28
29 if (m_args.inputFile.empty() && !(m_args.headerOnly || m_args.generateOnly))
30 {
31 std::println(std::cerr, "Error: No input file provided\nUse --help for usage information");
32 return 1;
33 }
34
35 if (m_args.mainFile.empty() && !m_args.headerOnly)
36#ifdef _WIN32
37 m_args.mainFile = "C:\\Program Files\\Phasor VM\\Development\\nativestub.cpp";
38#else
39 m_args.mainFile = "/usr/local/share/phasor/dev/nativestub.cpp";
40#endif
41
42 if (m_args.moduleName.empty())
43 {
44 std::filesystem::path inputPath(m_args.inputFile);
45 m_args.moduleName = inputPath.stem().string();
46 }
47
48 // Default output file if not specified
49 if (m_args.outputFile.empty())
50 {
51#ifdef _WIN32
52 m_args.outputFile = m_args.moduleName + ".exe";
53#else
54 m_args.outputFile = m_args.moduleName;
55#endif
56 }
57
58 if (m_args.verbose)
59 {
60 std::println("Input file: {}\nOutput file: {}", m_args.inputFile.string(), m_args.outputFile.string());
61 if (!m_args.moduleName.empty())
62 std::println("Module name: {}", m_args.moduleName);
63 }
64
65 if (m_args.headerOnly)
66 {
67 generateHeader(m_args.inputFile, m_args.outputFile);
68 return 0;
69 }
70
71 if (m_args.generateOnly)
72 {
73 generateHeader(m_args.inputFile, m_args.moduleName + ".h");
74 generateSource(m_args.moduleName + ".h", m_args.outputFile);
75 return 0;
76 }
77
78 if (m_args.objectOnly)
79 {
80 generateHeader(m_args.inputFile, m_args.moduleName + ".h");
81 generateSource(m_args.moduleName + ".h", m_args.moduleName + ".cpp");
82 compileSource(m_args.moduleName + ".cpp", m_args.outputFile);
83 return 0;
84 }
85
86 std::println("Generating wrapper...");
87
88 if (generateHeader(m_args.inputFile, m_args.moduleName + ".h"))
89 std::println("{} -> {}.h", m_args.inputFile.string(), m_args.moduleName);
90 else
91 {
92 std::println(std::cerr, "Error: Could not generate header file");
93 return 1;
94 }
95
96 if (generateSource(m_args.mainFile, m_args.moduleName + ".cpp"))
97 std::println("{} -> {}.cpp\n", m_args.mainFile.filename().string(), m_args.moduleName);
98 else
99 {
100 std::println(std::cerr, "Could not generate source file");
101 return 1;
102 }
103
104 std::println("Compiling...");
105 std::print("[COMPILER] ");
106 if (compileSource(m_args.moduleName + ".cpp", m_args.moduleName + ".obj"))
107 std::println("{}.cpp -> {}.obj\n", m_args.moduleName, m_args.moduleName);
108 else
109 {
110 std::println(std::cerr, "Could not compile program");
111 return 1;
112 }
113
114 std::println("Linking...");
115 std::print("[LINKER] ");
116 if (linkObject(m_args.moduleName + ".obj", m_args.outputFile))
117 std::println("{}.obj -> {}", m_args.moduleName, m_args.outputFile.string());
118 else
119 {
120 std::println(std::cerr, "Could not link program");
121 return 1;
122 }
123
124 return 0;
125}
126
127bool CppCompiler::parseArguments(int argc, char *argv[])
128{
129 for (int i = 1; i < argc; i++)
130 {
131 std::string arg = argv[i];
132
133 if (arg == "-h" || arg == "--help")
134 {
135 m_args.showHelp = true;
136 return true;
137 }
138 else if (arg == "-v" || arg == "--verbose")
139 {
140 m_args.verbose = true;
141 }
142 else if (arg == "-o" || arg == "--output")
143 {
144 if (i + 1 < argc)
145 {
146 m_args.outputFile = argv[++i];
147 }
148 else
149 {
150 std::println(std::cerr, "Error: {} requires an argument", arg);
151 m_args.showHelp = true;
152 return true;
153 }
154 }
155 else if (arg == "-H" || arg == "--header-only")
156 {
157 m_args.headerOnly = true;
158 }
159 else if (arg == "-g" || arg == "--generate-only")
160 {
161 m_args.generateOnly = true;
162 }
163 else if (arg == "-O" || arg == "--object-only")
164 {
165 m_args.objectOnly = true;
166 }
167 else if (arg == "-m" || arg == "--module")
168 {
169 if (i + 1 < argc)
170 {
171 m_args.moduleName = argv[++i];
172 }
173 else
174 {
175 std::println(std::cerr, "Error: {} requires an argument", arg);
176 m_args.showHelp = true;
177 return true;
178 }
179 }
180 else if (arg == "-c" || arg == "--compiler")
181 {
182 if (i + 1 < argc)
183 {
184 m_args.compiler = argv[++i];
185 if (m_args.compiler == "cl" && m_args.linker.empty())
186 {
187 m_args.linker = "link";
188 }
189 else if ((m_args.compiler == "clang" || m_args.compiler == "clang++") && m_args.linker.empty())
190 {
191 m_args.linker = "clang";
192 }
193 else if ((m_args.compiler == "gcc" || m_args.compiler == "g++") && m_args.linker.empty())
194 {
195 m_args.linker = "gcc";
196 }
197 }
198 else
199 {
200 std::println(std::cerr, "Error: {} requires an argument", arg);
201 m_args.showHelp = true;
202 return true;
203 }
204 }
205 else if (arg == "-l" || arg == "--linker")
206 {
207 if (i + 1 < argc)
208 {
209 m_args.linker = argv[++i];
210 }
211 else
212 {
213 std::println(std::cerr, "Error: {} requires an argument", arg);
214 m_args.showHelp = true;
215 return true;
216 }
217 }
218 else if (arg == "-s" || arg == "--source")
219 {
220 m_args.mainFile = argv[++i];
221 }
222 else if (arg[0] == '-')
223 {
224 std::println(std::cerr, "Error: Unknown option: {}", arg);
225 m_args.showHelp = true;
226 return true;
227 }
228 else
229 {
230 // First non-option argument is the input file
231 if (m_args.inputFile.empty())
232 m_args.inputFile = arg;
233 else
234 {
235 std::println(std::cerr, "Error: Multiple input files specified");
236 m_args.showHelp = true;
237 return true;
238 }
239 }
240 }
241 return false;
242}
243
244bool CppCompiler::showHelp(const std::string &programName)
245{
246 std::println("Phasor C++ Bytecode Embedder v{}\n"
247 "(C) 2026 Daniel McGuire - Licensed under Apache 2.0\n\n"
248 "Usage:\n"
249 " {} [options] <input.phs>\n\n"
250 "Options:\n"
251 " -c, --compiler <name> Compiler to use (default: g++)\n"
252 " -l, --linker <name> Linker to use (default: g++)\n"
253 " -s, --source <name> The source file to compile with\n"
254 " -o, --output <file> Output file\n"
255 " -m, --module <name> Module name for generated code (default: input filename)\n"
256 " -H, --header-only Generate header file only\n"
257 " -g, --generate-only Generate source file only\n"
258 " -O, --object-only Generate and compile to object only\n"
259 " -v, --verbose Enable verbose output\n"
260 " -h, --help Show this help message\n"
261 "Example:\n"
262 " {} program.phs -o program.exe -c clang++ -l lld\n"
263 " {} -O program.phs -o program.obj -c clang++\n"
264 " {} -H program.phs -o program.hpp\n"
265 " {} -g program.phs -o program.cpp",
266 PHASOR_VERSION_STRING, programName, programName, programName, programName, programName);
267 return true;
268}
269
270bool CppCompiler::generateHeader(const std::filesystem::path &sourcePath, const std::filesystem::path &outputPath)
271{
272 try
273 {
274 Bytecode bytecode;
275 if (sourcePath.extension() == ".phir")
276 {
277 PhasorIR phasorIR;
278 bytecode = phasorIR.loadFromFile(sourcePath);
279 }
280 else
281 {
282 // Read source file
283 if (m_args.verbose)
284 std::println("Reading source file...");
285
286 std::ifstream file(sourcePath);
287 if (!file.is_open())
288 {
289 std::println(std::cerr, "Error: Could not open input file: {}", sourcePath.string());
290 return false;
291 }
292
293 std::stringstream buffer;
294 buffer << file.rdbuf();
295 std::string source = buffer.str();
296 file.close();
297
298 // Lex
299 if (m_args.verbose)
300 std::println("Lexing...");
301
302 Lexer lexer(source);
303 auto tokens = lexer.tokenize();
304
305 // Parse
306 if (m_args.verbose)
307 std::println("Parsing...");
308
309 Parser parser(tokens, sourcePath);
310 auto program = parser.parse();
311
312 // Generate bytecode
313 if (m_args.verbose)
314 std::println("Generating bytecode...");
315
316 CodeGenerator codegen;
317 bytecode = codegen.generate(*program);
318 }
319
320 if (bytecode.instructions.empty())
321 {
322 std::println(std::cerr, "Error: No instructions generated");
323 return false;
324 }
325
326 if (m_args.verbose)
327 {
328 std::println("Bytecode statistics:\n"
329 " Instructions: {}\n"
330 " Constants: {}\n"
331 " Variables: {}\n"
332 " Functions: {}",
333 bytecode.instructions.size(), bytecode.constants.size(), bytecode.variables.size(),
334 bytecode.functionEntries.size());
335 }
336
337 // Generate C++ code
338 if (m_args.verbose)
339 std::println("Generating C++ code...");
340
341 CppCodeGenerator cppGen;
342 bool success = cppGen.generate(bytecode, outputPath, m_args.moduleName);
343
344 if (!success)
345 {
346 std::println(std::cerr, "Error: Failed to generate C++ code");
347 return false;
348 }
349
350 if (m_args.verbose)
351 std::println("Successfully generated: {}", outputPath.string());
352 }
353 catch (const std::exception &e)
354 {
355 std::println(std::cerr, "Compilation Error: {}", e.what());
356 return false;
357 }
358 return true;
359}
360
361bool CppCompiler::generateSource(const std::filesystem::path &sourcePath, const std::filesystem::path &outputPath)
362{
363 std::ifstream file(sourcePath);
364 if (!file.is_open())
365 {
366 std::println(std::cerr, "Error: Could not open input file: {}", sourcePath.string());
367 return false;
368 }
369
370 std::stringstream buffer;
371 buffer << file.rdbuf();
372 std::string source = buffer.str();
373 file.close();
374
375 std::ofstream outputFile(outputPath);
376 if (!outputFile.is_open())
377 {
378 std::println(std::cerr, "Error: Could not open output file: {}", outputPath.string());
379 return false;
380 }
381
382 // Include the generated header file (which is the output filename with .h extension)
383 std::filesystem::path headerPath = outputPath;
384 headerPath.replace_extension(".h");
385
386 outputFile << "#include \"" << headerPath.filename().string() << "\"\n";
387 outputFile << source;
388 outputFile.close();
389
390 return true;
391}
392
393bool CppCompiler::compileSource(const std::filesystem::path &sourcePath, const std::filesystem::path &outputPath)
394{
395 std::vector<std::string> flags;
396 if (m_args.compiler == "cl")
397 flags = {"/std:c++20", "/Ox", "/D", "NDEBUG", "/MD", "/GL", "/Gy-",
398 "/GS-", "/Gw", "/EHsc", "/WX-", "/nologo", "/c", ("/Fo" + outputPath.string())};
399 else if (m_args.compiler == "g++" || m_args.compiler == "clang++")
400 flags = {"-std=c++20",
401 "-O3",
402 "-DNDEBUG",
403 "-fPIC",
404 "-flto",
405 "-fno-function-sections",
406 "-fno-stack-protector",
407 "-fwhole-program",
408 "-fexceptions",
409 "-Wno-error",
410 "-c",
411 ("-o" + outputPath.string())};
412
413 else
414 {
415 std::println(std::cerr, "Error: Unknown compiler: {}", m_args.compiler);
416 return false;
417 }
418
419 std::string command = m_args.compiler;
420 for (const auto &flag : flags)
421 {
422 command += " " + flag;
423 }
424
425 command += " " + sourcePath.string();
426 if (std::system(command.c_str()) != 0)
427 {
428 std::println(std::cerr, "Error: Compilation failed");
429 return false;
430 }
431
432 return true;
433}
434
435bool CppCompiler::linkObject(const std::filesystem::path &objectPath, const std::filesystem::path &outputPath)
436{
437 std::string command = m_args.linker;
438 command += " " + objectPath.string();
439 if (m_args.linker == "link")
440 command += " /NOLOGO /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO /out:" + outputPath.string();
441 else if (m_args.linker == "ld" || m_args.linker == "clang++" || m_args.linker == "clang")
442 command += "-flto -pthread -Wl,--gc-sections -o " + outputPath.string();
443 else
444 {
445 std::println(std::cerr, "Error: Unknown linker: {}", m_args.linker);
446 return false;
447 }
448 return (std::system(command.c_str()) == 0);
449}
450
451} // namespace Phasor
Code generator for Phasor VM.
Definition CodeGen.hpp:108
Bytecode generate(const AST::Program &program, const std::unordered_map< std::string, int > &existingVars={}, int nextVarIdx=0, bool replMode=false)
Generate bytecode from program.
Definition CodeGen.cpp:8
Generates C++ header files with embedded Phasor bytecode.
bool generate(const Bytecode &bytecode, const std::filesystem::path &outputPath, const std::string &moduleName="")
Generate C++ header file from bytecode.
bool generateSource(const std::filesystem::path &sourcePath, const std::filesystem::path &outputPath)
struct Phasor::CppCompiler::Args m_args
bool parseArguments(int argc, char *argv[])
bool showHelp(const std::string &programName)
bool generateHeader(const std::filesystem::path &sourcePath, const std::filesystem::path &outputPath)
bool linkObject(const std::filesystem::path &objectPath, const std::filesystem::path &outputPath)
bool compileSource(const std::filesystem::path &sourcePath, const std::filesystem::path &outputPath)
CppCompiler(int argc, char *argv[])
Lexer.
Definition Lexer.hpp:13
std::vector< Token > tokenize()
Definition Lexer.cpp:27
Parser.
Definition Parser.hpp:13
std::unique_ptr< AST::Program > parse()
Definition Parser.cpp:74
Phasor IR Serializer/Deserializer.
Definition PhasorIR.hpp:18
static Bytecode loadFromFile(const std::filesystem::path &filename)
Load bytecode from .phir file.
Definition PhasorIR.cpp:666
The Phasor Programming Language and Runtime.
Definition AST.hpp:12
Complete bytecode structure.
Definition CodeGen.hpp:50
std::unordered_map< std::string, int > variables
Variable name -> index mapping.
Definition CodeGen.hpp:53
std::vector< Instruction > instructions
List of instructions.
Definition CodeGen.hpp:51
std::unordered_map< std::string, int > functionEntries
Function name -> instruction index mapping.
Definition CodeGen.hpp:54
std::vector< Value > constants
Constant pool.
Definition CodeGen.hpp:52