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