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