Phasor 3.3.0
Stack VM based Programming Language
Loading...
Searching...
No Matches
CodeGen.cpp
Go to the documentation of this file.
1#include "CodeGen.hpp"
2#include <iostream>
3#include <unordered_map>
4
5namespace Phasor
6{
7
8Bytecode CodeGenerator::generate(const AST::Program &program, const std::unordered_map<std::string, int> &existingVars,
9 int nextVarIdx, bool replMode)
10{
11 bytecode = Bytecode(); // Reset bytecode
12 bytecode.variables = existingVars;
13 bytecode.nextVarIndex = nextVarIdx;
14 isRepl = replMode;
15
16 for (const auto &stmt : program.statements)
17 {
18 generateStatement(stmt.get());
19 }
21 return bytecode;
22}
23
25{
26 if (const auto *numExpr = dynamic_cast<const AST::NumberExpr *>(expr))
27 {
28 try
29 {
30 if (numExpr->value.find('.') != std::string::npos)
31 {
32 outValue = Value(std::stod(numExpr->value));
33 }
34 else
35 {
36 outValue = Value(static_cast<int64_t>(std::stoll(numExpr->value)));
37 }
38 return true;
39 }
40 catch (...)
41 {
42 return false;
43 }
44 }
45 if (const auto *strExpr = dynamic_cast<const AST::StringExpr *>(expr))
46 {
47 outValue = Value(strExpr->value);
48 return true;
49 }
50 if (const auto *boolExpr = dynamic_cast<const AST::BooleanExpr *>(expr))
51 {
52 outValue = Value(boolExpr->value);
53 return true;
54 }
55 if (dynamic_cast<const AST::NullExpr *>(expr) != nullptr)
56 {
57 outValue = Value();
58 return true;
59 }
60 return false;
61}
62
64{
65 // If literal, we know the type immediately
66 Value lit;
67 if (isLiteralExpression(expr, lit))
68 {
69 known = true;
70 return lit.getType();
71 }
72
73 // If identifier and we've inferred its type previously, return that
74 if (const auto *ident = dynamic_cast<const AST::IdentifierExpr *>(expr))
75 {
76 auto it = inferredTypes.find(ident->name);
77 if (it != inferredTypes.end())
78 {
79 known = true;
80 return it->second;
81 }
82 }
83
84 // Unknown
85 known = false;
86 return ValueType::Float; // default when unknown (not used unless known==true)
87}
88
90{
91 if (const auto *varDecl = dynamic_cast<const AST::VarDecl *>(stmt))
92 {
93 generateVarDecl(varDecl);
94 }
95 else if (const auto *exprStmt = dynamic_cast<const AST::ExpressionStmt *>(stmt))
96 {
97 generateExpressionStmt(exprStmt);
98 }
99 else if (const auto *printStmt = dynamic_cast<const AST::PrintStmt *>(stmt))
100 {
101 generatePrintStmt(printStmt);
102 }
103 else if (dynamic_cast<const AST::IncludeStmt *>(stmt) != nullptr)
104 {
105 // preprocessor include
106 }
107 else if (const auto *importStmt = dynamic_cast<const AST::ImportStmt *>(stmt))
108 {
109 generateImportStmt(importStmt);
110 }
111 else if (const auto *exportStmt = dynamic_cast<const AST::ExportStmt *>(stmt))
112 {
113 generateExportStmt(exportStmt);
114 }
115 else if (const auto *blockStmt = dynamic_cast<const AST::BlockStmt *>(stmt))
116 {
117 generateBlockStmt(blockStmt);
118 }
119 else if (const auto *ifStmt = dynamic_cast<const AST::IfStmt *>(stmt))
120 {
121 generateIfStmt(ifStmt);
122 }
123 else if (const auto *whileStmt = dynamic_cast<const AST::WhileStmt *>(stmt))
124 {
125 generateWhileStmt(whileStmt);
126 }
127 else if (const auto *forStmt = dynamic_cast<const AST::ForStmt *>(stmt))
128 {
129 generateForStmt(forStmt);
130 }
131 else if (const auto *returnStmt = dynamic_cast<const AST::ReturnStmt *>(stmt))
132 {
133 generateReturnStmt(returnStmt);
134 }
135 else if (const auto *unsafeStmt = dynamic_cast<const AST::UnsafeBlockStmt *>(stmt))
136 {
137 generateUnsafeBlockStmt(unsafeStmt);
138 }
139 else if (const auto *funcDecl = dynamic_cast<const AST::FunctionDecl *>(stmt))
140 {
141 generateFunctionDecl(funcDecl);
142 }
143 else if (const auto *structDecl = dynamic_cast<const AST::StructDecl *>(stmt))
144 {
145 generateStructDecl(structDecl);
146 }
147 else if (dynamic_cast<const AST::BreakStmt *>(stmt) != nullptr)
148 {
150 }
151 else if (dynamic_cast<const AST::ContinueStmt *>(stmt) != nullptr)
152 {
154 }
155 else if (const auto *switchStmt = dynamic_cast<const AST::SwitchStmt *>(stmt))
156 {
157 generateSwitchStmt(switchStmt);
158 }
159 else
160 {
161 throw std::runtime_error("Unknown statement type in code generation");
162 }
163}
164
165void CodeGenerator::generateExpression(const AST::Expression *expr, bool resultNeeded)
166{
167 if (const auto *numExpr = dynamic_cast<const AST::NumberExpr *>(expr))
168 {
169 generateNumberExpr(numExpr);
170 }
171 else if (const auto *strExpr = dynamic_cast<const AST::StringExpr *>(expr))
172 {
173 generateStringExpr(strExpr);
174 }
175 else if (const auto *identExpr = dynamic_cast<const AST::IdentifierExpr *>(expr))
176 {
177 generateIdentifierExpr(identExpr);
178 }
179 else if (const auto *unaryExpr = dynamic_cast<const AST::UnaryExpr *>(expr))
180 {
181 generateUnaryExpr(unaryExpr);
182 }
183 else if (const auto *callExpr = dynamic_cast<const AST::CallExpr *>(expr))
184 {
185 generateCallExpr(callExpr);
186 }
187 else if (const auto *binExpr = dynamic_cast<const AST::BinaryExpr *>(expr))
188 {
189 generateBinaryExpr(binExpr);
190 }
191 else if (const auto *boolExpr = dynamic_cast<const AST::BooleanExpr *>(expr))
192 {
193 generateBooleanExpr(boolExpr);
194 }
195 else if (const auto *nullExpr = dynamic_cast<const AST::NullExpr *>(expr))
196 {
197 generateNullExpr(nullExpr);
198 }
199 else if (const auto *assignExpr = dynamic_cast<const AST::AssignmentExpr *>(expr))
200 {
201 generateAssignmentExpr(assignExpr);
202 }
203 else if (const auto *structExpr = dynamic_cast<const AST::StructInstanceExpr *>(expr))
204 {
205 generateStructInstanceExpr(structExpr);
206 }
207 else if (const auto *fieldAccessExpr = dynamic_cast<const AST::FieldAccessExpr *>(expr))
208 {
209 generateFieldAccessExpr(fieldAccessExpr);
210 }
211 else if (const auto *postfixExpr = dynamic_cast<const AST::PostfixExpr *>(expr))
212 {
213 generatePostfixExpr(postfixExpr, resultNeeded);
214 }
215 else
216 {
217 throw std::runtime_error("Unknown expression type in code generation");
218 }
219}
220
222{
223 if (varDecl->initializer)
224 {
225 // Generate initializer code
226 generateExpression(varDecl->initializer.get());
227 // Try to infer type from initializer
228 Value initVal;
229 if (isLiteralExpression(varDecl->initializer.get(), initVal))
230 {
231 inferredTypes[varDecl->name] = initVal.getType();
232 }
233 else if (const auto *ident = dynamic_cast<const AST::IdentifierExpr *>(varDecl->initializer.get()))
234 {
235 // propagate known type from another variable
236 auto it = inferredTypes.find(ident->name);
237 if (it != inferredTypes.end())
238 {
239 inferredTypes[varDecl->name] = it->second;
240 }
241 }
242
243 int varIndex = bytecode.getOrCreateVar(varDecl->name);
244 bytecode.emit(OpCode::STORE_VAR, varIndex);
245 }
246 else
247 {
248 // Store null value for uninitialized variable
249 int constIndex = bytecode.addConstant(Value());
250 bytecode.emit(OpCode::PUSH_CONST, constIndex);
251 int varIndex = bytecode.getOrCreateVar(varDecl->name);
252 bytecode.emit(OpCode::STORE_VAR, varIndex);
253 }
254}
255
257{
258 if (isRepl)
259 {
260 generateExpression(exprStmt->expression.get(), true);
262 return;
263 }
264
265 // In statement context the result is always discarded.
266 // For postfix specifically: resultNeeded=false skips saving the old value,
267 // and STORE_VAR already pops the result — stack is clean, no POP needed.
268 if (const auto *postfix = dynamic_cast<const AST::PostfixExpr *>(exprStmt->expression.get()))
269 {
270 generatePostfixExpr(postfix, false);
271 }
272 else
273 {
274 generateExpression(exprStmt->expression.get());
275 bytecode.emit(OpCode::POP);
276 }
277}
278
280{
281 generateExpression(printStmt->expression.get());
283}
284
286{
287 int constIndex = bytecode.addStringConstant(importStmt->modulePath);
288 bytecode.emit(OpCode::IMPORT, constIndex);
289}
290
292{
293 // For now, export just executes the declaration in the current scope
294 generateStatement(exportStmt->declaration.get());
295}
296
298{
299 // Try to parse as integer first, then as float
300 try
301 {
302 // Check if it has a decimal point
303 if (numExpr->value.find('.') != std::string::npos)
304 {
305 double d = std::stod(numExpr->value);
306 int constIndex = bytecode.addConstant(Value(d));
307 bytecode.emit(OpCode::PUSH_CONST, constIndex);
308 }
309 else
310 {
311 int64_t i = std::stoll(numExpr->value);
312 int constIndex = bytecode.addConstant(Value(i));
313 bytecode.emit(OpCode::PUSH_CONST, constIndex);
314 }
315 }
316 catch (...)
317 {
318 throw std::runtime_error("Invalid number format: " + numExpr->value);
319 }
320}
321
323{
324 int constIndex = bytecode.addConstant(Value(strExpr->value));
325 bytecode.emit(OpCode::PUSH_CONST, constIndex);
326}
327
329{
330 int varIndex = bytecode.getOrCreateVar(identExpr->name);
331 bytecode.emit(OpCode::LOAD_VAR, varIndex);
332}
333
335{
336 // Generate operand
337 generateExpression(unaryExpr->operand.get());
338
339 // Emit operation
340 switch (unaryExpr->op)
341 {
344 break;
346 bytecode.emit(OpCode::NOT);
347 break;
349 // Not implemented
350 // bytecode.emit(OpCode::ADROF);
351 [[fallthrough]]; // for now
353 // bytecode.emit(OpCode::DREF);
354 break;
355 }
356}
357
359{
360 // Optimizations
361 if (callExpr->callee == "len" && callExpr->arguments.size() == 1)
362 {
363 if (const auto *strExpr = dynamic_cast<const AST::StringExpr *>(callExpr->arguments[0].get()))
364 {
365 // Constant fold len("literal")
366 auto len = (int64_t)strExpr->value.length();
367 int constIndex = bytecode.addConstant(Value(len));
368 bytecode.emit(OpCode::PUSH_CONST, constIndex);
369 return;
370 }
371 // Emit specialized opcode for variable strings
372 generateExpression(callExpr->arguments[0].get());
373 bytecode.emit(OpCode::LEN);
374 return;
375 }
376
377 if (callExpr->callee == "substr" && callExpr->arguments.size() == 3)
378 {
379 // Check for substr(s, i, 1) -> char_at(s, i)
380 if (const auto *numExpr = dynamic_cast<const AST::NumberExpr *>(callExpr->arguments[2].get()))
381 {
382 if (numExpr->value == "1" || numExpr->value == "1.0")
383 {
384 // Redirect to char_at opcode
385 generateExpression(callExpr->arguments[0].get());
386 generateExpression(callExpr->arguments[1].get());
388 return;
389 }
390 }
391 }
392
393 if (callExpr->callee == "char_at" && callExpr->arguments.size() == 2)
394 {
395 generateExpression(callExpr->arguments[0].get());
396 generateExpression(callExpr->arguments[1].get());
398 return;
399 }
400
401 if (callExpr->callee == "starts_with" && callExpr->arguments.size() == 2)
402 {
403 const auto *s = dynamic_cast<const AST::StringExpr *>(callExpr->arguments[0].get());
404 const auto *p = dynamic_cast<const AST::StringExpr *>(callExpr->arguments[1].get());
405 if ((s != nullptr) && (p != nullptr))
406 {
407 bool result = s->value.length() >= p->value.length() && s->value.starts_with(p->value);
408 bytecode.emit(result ? OpCode::TRUE_P : OpCode::FALSE_P);
409 return;
410 }
411 }
412
413 if (callExpr->callee == "ends_with" && callExpr->arguments.size() == 2)
414 {
415 const auto *s = dynamic_cast<const AST::StringExpr *>(callExpr->arguments[0].get());
416 const auto *suffix = dynamic_cast<const AST::StringExpr *>(callExpr->arguments[1].get());
417 if ((s != nullptr) && (suffix != nullptr))
418 {
419 bool result = s->value.length() >= suffix->value.length() && s->value.ends_with(suffix->value);
420 bytecode.emit(result ? OpCode::TRUE_P : OpCode::FALSE_P);
421 return;
422 }
423 }
424 // Push arguments
425 for (const auto &arg : callExpr->arguments)
426 {
427 generateExpression(arg.get());
428 }
429
430 // Push argument count
431 int constIndex = bytecode.addConstant(Value(static_cast<int64_t>(callExpr->arguments.size())));
432 bytecode.emit(OpCode::PUSH_CONST, constIndex);
433
434 // Check if it's a user function
435 auto entryIt = bytecode.functionEntries.find(callExpr->callee);
436 if (entryIt != bytecode.functionEntries.end())
437 {
438 int nameIndex = bytecode.addStringConstant(callExpr->callee);
439 auto itParam = bytecode.functionParamCounts.find(callExpr->callee);
440 if (itParam != bytecode.functionParamCounts.end())
441 {
442 int expected = itParam->second;
443 int got = static_cast<int>(callExpr->arguments.size());
444 if (expected != got)
445 {
446 std::cerr << "ERROR: calling function '" << callExpr->callee << "' with " << got
447 << " arguments but it expects " << expected << "\n";
448 std::exit(1);
449 }
450 }
451 bytecode.emit(OpCode::CALL, nameIndex);
452 }
453 else
454 {
455 int nameIndex = bytecode.addStringConstant(callExpr->callee);
456 bytecode.emit(OpCode::CALL_NATIVE, nameIndex);
457 }
458}
459
461{
462 Value leftVal;
463 Value rightVal;
464 if (isLiteralExpression(binExpr->left.get(), leftVal) && isLiteralExpression(binExpr->right.get(), rightVal))
465 {
466 try
467 {
468 Value result;
469 switch (binExpr->op)
470 {
472 result = leftVal + rightVal;
473 break;
475 result = leftVal - rightVal;
476 break;
478 result = leftVal * rightVal;
479 break;
481 result = leftVal / rightVal;
482 break;
484 result = leftVal % rightVal;
485 break;
487 result = leftVal.logicalAnd(rightVal);
488 break;
490 result = leftVal.logicalOr(rightVal);
491 break;
493 result = Value(leftVal == rightVal);
494 break;
496 result = Value(leftVal != rightVal);
497 break;
499 result = Value(leftVal < rightVal);
500 break;
502 result = Value(leftVal > rightVal);
503 break;
505 result = Value(leftVal <= rightVal);
506 break;
508 result = Value(leftVal >= rightVal);
509 break;
510 }
511 if (result.isBool())
512 {
513 if (result.asBool())
514 {
516 }
517 else
518 {
520 }
521 }
522 else if (result.isNull())
523 {
525 }
526 else
527 {
528 int constIndex = bytecode.addConstant(result);
529 bytecode.emit(OpCode::PUSH_CONST, constIndex);
530 }
531 return;
532 }
533 catch (...)
534 {
535 std::cerr << "Unknown error in Phasor::CodeGenerator::generateBinaryExpr().\n";
536 }
537 }
538
539 if (binExpr->op == AST::BinaryOp::And)
540 {
541 generateExpression(binExpr->left.get());
542 int jumpToFalseIndex = static_cast<int>(bytecode.instructions.size());
544 generateExpression(binExpr->right.get());
545 int jumpToEndIndex = static_cast<int>(bytecode.instructions.size());
546 bytecode.emit(OpCode::JUMP, 0);
547 bytecode.instructions[jumpToFalseIndex].operand1 = static_cast<int>(bytecode.instructions.size());
549 bytecode.instructions[jumpToEndIndex].operand1 = static_cast<int>(bytecode.instructions.size());
550 return;
551 }
552 if (binExpr->op == AST::BinaryOp::Or)
553 {
554 generateExpression(binExpr->left.get());
555 int jumpToTrueIndex = static_cast<int>(bytecode.instructions.size());
557 generateExpression(binExpr->right.get());
558 int jumpToEndIndex = static_cast<int>(bytecode.instructions.size());
559 bytecode.emit(OpCode::JUMP, 0);
560 bytecode.instructions[jumpToTrueIndex].operand1 = static_cast<int>(bytecode.instructions.size());
562 bytecode.instructions[jumpToEndIndex].operand1 = static_cast<int>(bytecode.instructions.size());
563 return;
564 }
565
566 uint8_t rLeft = allocateRegister();
567 uint8_t rRight = allocateRegister();
568 uint8_t rResult = allocateRegister();
569
570 // Reuse literal detection done here to choose appropriate register opcodes.
571 Value leftLiteral;
572 bool leftIsLiteral = isLiteralExpression(binExpr->left.get(), leftLiteral);
573 if (leftIsLiteral)
574 {
575 int constIndex = bytecode.addConstant(leftLiteral);
576 bytecode.emit(OpCode::LOAD_CONST_R, rLeft, constIndex);
577 }
578 else
579 {
580 generateExpression(binExpr->left.get());
581 bytecode.emit(OpCode::POP_R, rLeft);
582 }
583
584 Value rightLiteral;
585 bool rightIsLiteral = isLiteralExpression(binExpr->right.get(), rightLiteral);
586 if (rightIsLiteral)
587 {
588 int constIndex = bytecode.addConstant(rightLiteral);
589 bytecode.emit(OpCode::LOAD_CONST_R, rRight, constIndex);
590 }
591 else
592 {
593 generateExpression(binExpr->right.get());
594 bytecode.emit(OpCode::POP_R, rRight);
595 }
596
597 // Conservative integer decision:
598 // treat operand as known-int if it's an integer literal or a variable previously inferred as Int.
599 auto exprIsKnownInt = [&](const AST::Expression *e, bool isLiteral, const Value &lit) -> bool {
600 if (isLiteral)
601 {
602 return lit.isInt();
603 }
604 // variable case
605 if (const auto *ident = dynamic_cast<const AST::IdentifierExpr *>(e))
606 {
607 auto it = inferredTypes.find(ident->name);
608 return it != inferredTypes.end() && it->second == ValueType::Int;
609 }
610 return false;
611 };
612
613 bool leftKnownInt = exprIsKnownInt(binExpr->left.get(), leftIsLiteral, leftLiteral);
614 bool rightKnownInt = exprIsKnownInt(binExpr->right.get(), rightIsLiteral, rightLiteral);
615
616 // Choose integer ops only when both operands are known ints.
617 if (leftKnownInt && rightKnownInt)
618 {
619 switch (binExpr->op)
620 {
622 bytecode.emit(OpCode::IADD_R, rResult, rLeft, rRight);
623 break;
625 bytecode.emit(OpCode::ISUB_R, rResult, rLeft, rRight);
626 break;
628 bytecode.emit(OpCode::IMUL_R, rResult, rLeft, rRight);
629 break;
631 bytecode.emit(OpCode::IDIV_R, rResult, rLeft, rRight);
632 break;
634 bytecode.emit(OpCode::IMOD_R, rResult, rLeft, rRight);
635 break;
637 bytecode.emit(OpCode::IAND_R, rResult, rLeft, rRight);
638 break;
640 bytecode.emit(OpCode::IOR_R, rResult, rLeft, rRight);
641 break;
643 bytecode.emit(OpCode::IEQ_R, rResult, rLeft, rRight);
644 break;
646 bytecode.emit(OpCode::INE_R, rResult, rLeft, rRight);
647 break;
649 bytecode.emit(OpCode::ILT_R, rResult, rLeft, rRight);
650 break;
652 bytecode.emit(OpCode::IGT_R, rResult, rLeft, rRight);
653 break;
655 bytecode.emit(OpCode::ILE_R, rResult, rLeft, rRight);
656 break;
658 bytecode.emit(OpCode::IGE_R, rResult, rLeft, rRight);
659 break;
660 }
661 }
662 else
663 {
664 // Fallback to float ops when we don't know both operands are integers.
665 switch (binExpr->op)
666 {
668 bytecode.emit(OpCode::FLADD_R, rResult, rLeft, rRight);
669 break;
671 bytecode.emit(OpCode::FLSUB_R, rResult, rLeft, rRight);
672 break;
674 bytecode.emit(OpCode::FLMUL_R, rResult, rLeft, rRight);
675 break;
677 bytecode.emit(OpCode::FLDIV_R, rResult, rLeft, rRight);
678 break;
680 bytecode.emit(OpCode::FLMOD_R, rResult, rLeft, rRight);
681 break;
683 bytecode.emit(OpCode::FLAND_R, rResult, rLeft, rRight);
684 break;
686 bytecode.emit(OpCode::FLOR_R, rResult, rLeft, rRight);
687 break;
689 bytecode.emit(OpCode::FLEQ_R, rResult, rLeft, rRight);
690 break;
692 bytecode.emit(OpCode::FLNE_R, rResult, rLeft, rRight);
693 break;
695 bytecode.emit(OpCode::FLLT_R, rResult, rLeft, rRight);
696 break;
698 bytecode.emit(OpCode::FLGT_R, rResult, rLeft, rRight);
699 break;
701 bytecode.emit(OpCode::FLLE_R, rResult, rLeft, rRight);
702 break;
704 bytecode.emit(OpCode::FLGE_R, rResult, rLeft, rRight);
705 break;
706 }
707 }
708
709 freeRegister(rLeft);
710 freeRegister(rRight);
711 bytecode.emit(OpCode::PUSH_R, rResult);
712 freeRegister(rResult);
713}
714
716{
717 for (const auto &stmt : blockStmt->statements)
718 {
719 generateStatement(stmt.get());
720 }
721}
722
724{
725 generateExpression(ifStmt->condition.get());
726
727 // Jump to else if false
728 int jumpToElseIndex = static_cast<int>(bytecode.instructions.size());
730
731 generateStatement(ifStmt->thenBranch.get());
732
733 int jumpToEndIndex = static_cast<int>(bytecode.instructions.size());
734 bytecode.emit(OpCode::JUMP, 0);
735
736 // Patch jump to else
737 bytecode.instructions[jumpToElseIndex].operand1 = static_cast<int>(bytecode.instructions.size());
738
739 if (ifStmt->elseBranch)
740 {
741 generateStatement(ifStmt->elseBranch.get());
742 }
743
744 // Patch jump to end
745 bytecode.instructions[jumpToEndIndex].operand1 = static_cast<int>(bytecode.instructions.size());
746}
747
749{
750 int loopStartIndex = static_cast<int>(bytecode.instructions.size());
751
752 // Push loop context
753 loopStartStack.push_back(loopStartIndex);
754 breakJumpsStack.emplace_back();
755 continueJumpsStack.emplace_back();
756
757 generateExpression(whileStmt->condition.get());
758
759 int jumpToEndIndex = static_cast<int>(bytecode.instructions.size());
761
762 generateStatement(whileStmt->body.get());
763
764 // Patch continue jumps to loop start
765 for (int continueJump : continueJumpsStack.back())
766 {
767 bytecode.instructions[continueJump].operand1 = loopStartIndex;
768 }
769
770 bytecode.emit(OpCode::JUMP_BACK, loopStartIndex);
771
772 // Patch jump to end and break jumps
773 int endIndex = static_cast<int>(bytecode.instructions.size());
774 bytecode.instructions[jumpToEndIndex].operand1 = endIndex;
775 for (int breakJump : breakJumpsStack.back())
776 {
777 bytecode.instructions[breakJump].operand1 = endIndex;
778 }
779
780 // Pop loop context
781 loopStartStack.pop_back();
782 breakJumpsStack.pop_back();
783 continueJumpsStack.pop_back();
784}
785
787{
788 // Generate initializer
789 if (forStmt->initializer)
790 {
791 generateStatement(forStmt->initializer.get());
792 }
793
794 int loopStartIndex = static_cast<int>(bytecode.instructions.size());
795
796 // Push loop context
797 loopStartStack.push_back(loopStartIndex);
798 breakJumpsStack.emplace_back();
799 continueJumpsStack.emplace_back();
800
801 // Generate condition (if present)
802 int jumpToEndIndex = -1;
803 if (forStmt->condition)
804 {
805 generateExpression(forStmt->condition.get());
806 jumpToEndIndex = static_cast<int>(bytecode.instructions.size());
808 }
809
810 // Generate body
811 generateStatement(forStmt->body.get());
812
813 // Continue jumps to increment (or loop start if no increment)
814 int incrementIndex = static_cast<int>(bytecode.instructions.size());
815 for (int continueJump : continueJumpsStack.back())
816 {
817 bytecode.instructions[continueJump].operand1 = incrementIndex;
818 }
819
820 // Generate increment
821 if (forStmt->increment)
822 {
823 if (const auto *postfix = dynamic_cast<const AST::PostfixExpr *>(forStmt->increment.get()))
824 {
825 // resultNeeded=false: skips the old-value LOAD_VAR, and STORE_VAR
826 // already pops the result — stack is clean, no POP needed.
827 generatePostfixExpr(postfix, false);
828 }
829 else
830 {
831 generateExpression(forStmt->increment.get());
832 bytecode.emit(OpCode::POP); // discard result
833 }
834 }
835
836 // Jump back to condition check
837 bytecode.emit(OpCode::JUMP_BACK, loopStartIndex);
838
839 // Patch jump to end and break jumps
840 int endIndex = static_cast<int>(bytecode.instructions.size());
841 if (jumpToEndIndex != -1)
842 {
843 bytecode.instructions[jumpToEndIndex].operand1 = endIndex;
844 }
845 for (int breakJump : breakJumpsStack.back())
846 {
847 bytecode.instructions[breakJump].operand1 = endIndex;
848 }
849
850 // Pop loop context
851 loopStartStack.pop_back();
852 breakJumpsStack.pop_back();
853 continueJumpsStack.pop_back();
854}
855
857{
858 if (breakJumpsStack.empty())
859 {
860 throw std::runtime_error("'break' statement outside of loop");
861 }
862 // Emit jump with placeholder, will be patched later
863 int jumpIndex = static_cast<int>(bytecode.instructions.size());
864 bytecode.emit(OpCode::JUMP, 0);
865 breakJumpsStack.back().push_back(jumpIndex);
866}
867
869{
870 if (continueJumpsStack.empty())
871 {
872 throw std::runtime_error("'continue' statement outside of loop");
873 }
874 // Emit jump with placeholder, will be patched later
875 int jumpIndex = static_cast<int>(bytecode.instructions.size());
876 bytecode.emit(OpCode::JUMP, 0);
877 continueJumpsStack.back().push_back(jumpIndex);
878}
879
881{
882 if (returnStmt->value)
883 {
884 generateExpression(returnStmt->value.get());
885 }
886 else
887 {
889 }
891}
892
894{
895 generateBlockStmt(unsafeStmt->block.get());
896}
897
899{
900 // Jump over function body
901 int jumpOverIndex = static_cast<int>(bytecode.instructions.size());
902 bytecode.emit(OpCode::JUMP, 0);
903
904 // Record entry point
905 int entryPoint = static_cast<int>(bytecode.instructions.size());
906 bytecode.functionEntries[funcDecl->name] = entryPoint;
907
908 // Record parameter count
909 bytecode.functionParamCounts[funcDecl->name] = static_cast<int>(funcDecl->params.size());
910
911 // Pop argument count (it's on the stack when function is called)
912 bytecode.emit(OpCode::POP);
913
914 // Pop parameters in reverse order and store in variables
915 for (auto it = funcDecl->params.rbegin(); it != funcDecl->params.rend(); ++it)
916 {
917 int varIndex = bytecode.getOrCreateVar(it->name);
918 bytecode.emit(OpCode::STORE_VAR, varIndex);
919 }
920
921 // Generate body
922 generateBlockStmt(funcDecl->body.get());
923
924 // Ensure return
927
928 // Patch jump over
929 bytecode.instructions[jumpOverIndex].operand1 = static_cast<int>(bytecode.instructions.size());
930}
931
933{
934 if (boolExpr->value)
935 {
937 }
938 else
939 {
941 }
942}
943
948
950{
951 // Support assignments to variables and struct fields.
952 if (const auto *identExpr = dynamic_cast<const AST::IdentifierExpr *>(assignExpr->target.get()))
953 {
954 // Variable assignment: a = value
955 // 1. Generate value (pushes value to stack)
956 generateExpression(assignExpr->value.get());
957
958 // Try to infer and record type
959 Value val;
960 if (isLiteralExpression(assignExpr->value.get(), val))
961 {
962 inferredTypes[identExpr->name] = val.getType();
963 }
964 else if (const auto *identRhs = dynamic_cast<const AST::IdentifierExpr *>(assignExpr->value.get()))
965 {
966 auto it = inferredTypes.find(identRhs->name);
967 if (it != inferredTypes.end())
968 {
969 inferredTypes[identExpr->name] = it->second;
970 }
971 }
972
973 // 2. Store in variable (pops value)
974 int varIndex = bytecode.getOrCreateVar(identExpr->name);
975 bytecode.emit(OpCode::STORE_VAR, varIndex);
976
977 // 3. Load it back (assignment is an expression that returns the value)
978 bytecode.emit(OpCode::LOAD_VAR, varIndex);
979 }
980 else if (const auto *fieldExpr = dynamic_cast<const AST::FieldAccessExpr *>(assignExpr->target.get()))
981 {
982 // Generate object (pushes struct)
983 generateExpression(fieldExpr->object.get());
984 // Generate value (pushes value on top)
985 generateExpression(assignExpr->value.get());
986
987 // Use dynamic field access since we don't have type information
988 int fieldNameIndex = bytecode.addStringConstant(fieldExpr->fieldName);
989 bytecode.emit(OpCode::SET_FIELD, fieldNameIndex);
990
991 // Reload the assigned field value so the assignment expression evaluates to it
992 generateExpression(fieldExpr->object.get());
993 bytecode.emit(OpCode::GET_FIELD, fieldNameIndex);
994 }
995 else
996 {
997 throw std::runtime_error("Invalid assignment target. Only variables and struct fields are supported.");
998 }
999}
1000
1002{
1003 // Prefer static struct instantiation when we have metadata in the struct section.
1004 auto it = bytecode.structEntries.find(expr->structName);
1005 if (it != bytecode.structEntries.end())
1006 {
1007 int structIndex = it->second;
1008 // Create instance with defaults from Bytecode::structs / constants
1010
1011 // Apply any explicit field initializers as overrides using dynamic SET_FIELD.
1012 for (const auto &[fieldName, fieldValue] : expr->fieldValues)
1013 {
1014 generateExpression(fieldValue.get());
1015 int fieldNameIndex = bytecode.addStringConstant(fieldName);
1016 bytecode.emit(OpCode::SET_FIELD, fieldNameIndex);
1017 }
1018 }
1019 else
1020 {
1021 // Fallback
1022 int structNameIndex = bytecode.addStringConstant(expr->structName);
1023 bytecode.emit(OpCode::NEW_STRUCT, structNameIndex);
1024
1025 for (const auto &[fieldName, fieldValue] : expr->fieldValues)
1026 {
1027 generateExpression(fieldValue.get());
1028 int fieldNameIndex = bytecode.addStringConstant(fieldName);
1029 bytecode.emit(OpCode::SET_FIELD, fieldNameIndex);
1030 }
1031 }
1032}
1033
1035{
1036 generateExpression(expr->object.get());
1037 int fieldNameIndex = bytecode.addStringConstant(expr->fieldName);
1038 bytecode.emit(OpCode::GET_FIELD, fieldNameIndex);
1039}
1040
1041void CodeGenerator::generatePostfixExpr(const AST::PostfixExpr *expr, bool resultNeeded)
1042{
1043 const auto *identExpr = dynamic_cast<const AST::IdentifierExpr *>(expr->operand.get());
1044 if (identExpr == nullptr)
1045 throw std::runtime_error("Postfix operators only supported on variables");
1046
1047 int varIndex = bytecode.getOrCreateVar(identExpr->name);
1048
1049 if (resultNeeded)
1050 bytecode.emit(OpCode::LOAD_VAR, varIndex);
1051
1052 bytecode.emit(OpCode::LOAD_VAR, varIndex);
1053
1054 int oneIndex = bytecode.addConstant(Value(static_cast<int64_t>(1)));
1055 bytecode.emit(OpCode::PUSH_CONST, oneIndex);
1056
1057 auto it = inferredTypes.find(identExpr->name);
1058 bool varIsInt = (it != inferredTypes.end() && it->second == ValueType::Int);
1059 if (expr->op == AST::PostfixOp::Increment)
1060 bytecode.emit(varIsInt ? OpCode::IADD : OpCode::FLADD);
1061 else
1063
1064 bytecode.emit(OpCode::STORE_VAR, varIndex);
1065}
1066
1068{
1069 std::string structDef = "struct " + decl->name + " {";
1070 for (const auto &field : decl->fields)
1071 {
1072 structDef += " " + field.name + ":" + field.type->name + ",";
1073 }
1074 if (!decl->fields.empty())
1075 {
1076 structDef.pop_back(); // Remove trailing comma
1077 }
1078 structDef += " }";
1079 bytecode.addConstant(Value(structDef));
1080 // reg metadata
1081 int firstConstIndex = static_cast<int>(bytecode.constants.size());
1082 for (const auto &field : decl->fields)
1083 {
1084 (void)field;
1085 bytecode.addConstant(Value());
1086 }
1087
1088 StructInfo info;
1089 info.name = decl->name;
1090 info.firstConstIndex = firstConstIndex;
1091 info.fieldCount = static_cast<int>(decl->fields.size());
1092 for (const auto &field : decl->fields)
1093 {
1094 info.fieldNames.push_back(field.name);
1095 }
1096
1097 // If a struct with this name already exists, do not overwrite it.
1098 if (!bytecode.structEntries.contains(decl->name))
1099 {
1100 int index = static_cast<int>(bytecode.structs.size());
1101 bytecode.structs.push_back(std::move(info));
1102 bytecode.structEntries[decl->name] = index;
1103 }
1104}
1105
1107{
1108 generateExpression(switchStmt->expr.get());
1109 std::string tempName = "__switch_" + std::to_string(switchCounter++);
1110 int tempVarIndex = bytecode.getOrCreateVar(tempName);
1111 bytecode.emit(OpCode::STORE_VAR, tempVarIndex);
1112
1113 std::vector<int> endJumps; // one per case, all patched to end
1114
1115 for (const auto &caseClause : switchStmt->cases)
1116 {
1117 // Reload switch value for every comparison
1118 bytecode.emit(OpCode::LOAD_VAR, tempVarIndex);
1119 generateExpression(caseClause.value.get());
1121
1122 int skipJump = static_cast<int>(bytecode.instructions.size());
1123 bytecode.emit(OpCode::JUMP_IF_FALSE, 0); // skip this case if no match
1124
1125 for (const auto &stmt : caseClause.statements)
1126 {
1127 generateStatement(stmt.get());
1128 }
1129
1130 int endJump = static_cast<int>(bytecode.instructions.size());
1131 bytecode.emit(OpCode::JUMP, 0); // after executing, jump to end
1132 endJumps.push_back(endJump);
1133
1134 // Patch skip jump to point at the next case
1135 bytecode.instructions[skipJump].operand1 = static_cast<int>(bytecode.instructions.size());
1136 }
1137
1138 for (const auto &stmt : switchStmt->defaultStmts)
1139 {
1140 generateStatement(stmt.get());
1141 }
1142
1143 int endIndex = static_cast<int>(bytecode.instructions.size());
1144 for (int jumpIdx : endJumps)
1145 {
1146 bytecode.instructions[jumpIdx].operand1 = endIndex;
1147 }
1148}
1149} // namespace Phasor
void generateForStmt(const AST::ForStmt *forStmt)
Generate bytecode from For Statement.
Definition CodeGen.cpp:786
Bytecode bytecode
Generated bytecode.
Definition CodeGen.hpp:123
void generateCallExpr(const AST::CallExpr *callExpr)
Generate bytecode from Call Expression.
Definition CodeGen.cpp:358
void generateVarDecl(const AST::VarDecl *varDecl)
Generate bytecode from Variable Declaration.
Definition CodeGen.cpp:221
void generateIfStmt(const AST::IfStmt *ifStmt)
Generate bytecode from If Statement.
Definition CodeGen.cpp:723
void generatePostfixExpr(const AST::PostfixExpr *expr, bool resultNeeded=true)
Definition CodeGen.cpp:1041
std::vector< std::vector< int > > breakJumpsStack
Definition CodeGen.hpp:210
void generateUnaryExpr(const AST::UnaryExpr *unaryExpr)
Generate bytecode from Unary Expression.
Definition CodeGen.cpp:334
void generateStructInstanceExpr(const AST::StructInstanceExpr *expr)
Definition CodeGen.cpp:1001
void generateNullExpr(const AST::NullExpr *nullExpr)
Generate bytecode from Null Expression.
Definition CodeGen.cpp:944
void generateFunctionDecl(const AST::FunctionDecl *funcDecl)
Generate bytecode from Function Declaration.
Definition CodeGen.cpp:898
std::unordered_map< std::string, ValueType > inferredTypes
Definition CodeGen.hpp:126
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
void generateWhileStmt(const AST::WhileStmt *whileStmt)
Generate bytecode from While Statement.
Definition CodeGen.cpp:748
void generateSwitchStmt(const AST::SwitchStmt *switchStmt)
Definition CodeGen.cpp:1106
void generateBlockStmt(const AST::BlockStmt *blockStmt)
Generate bytecode from Block Statement.
Definition CodeGen.cpp:715
void generateBooleanExpr(const AST::BooleanExpr *boolExpr)
Generate bytecode from Boolean Expression.
Definition CodeGen.cpp:932
void generateStructDecl(const AST::StructDecl *decl)
Definition CodeGen.cpp:1067
uint8_t allocateRegister()
Allocate a new register.
Definition CodeGen.hpp:133
std::vector< int > loopStartStack
Definition CodeGen.hpp:209
void generatePrintStmt(const AST::PrintStmt *printStmt)
Generate bytecode from Print Statement.
Definition CodeGen.cpp:279
void generateStringExpr(const AST::StringExpr *strExpr)
Generate bytecode from String Expression.
Definition CodeGen.cpp:322
void generateExpression(const AST::Expression *expr, bool resultNeeded=true)
Generate bytecode from Expression.
Definition CodeGen.cpp:165
void freeRegister(uint8_t reg)
Free a register.
Definition CodeGen.hpp:151
ValueType inferExpressionType(const AST::Expression *expr, bool &known)
Simple expression type inference (conservative).
Definition CodeGen.cpp:63
std::vector< std::vector< int > > continueJumpsStack
Definition CodeGen.hpp:211
void generateExportStmt(const AST::ExportStmt *exportStmt)
Generate bytecode from Export Statement.
Definition CodeGen.cpp:291
void generateUnsafeBlockStmt(const AST::UnsafeBlockStmt *unsafeStmt)
Generate bytecode from Unsafe Block Statement.
Definition CodeGen.cpp:893
void generateBinaryExpr(const AST::BinaryExpr *binExpr)
Generate bytecode from Binary Expression.
Definition CodeGen.cpp:460
void generateExpressionStmt(const AST::ExpressionStmt *exprStmt)
Generate bytecode from Expression Statement.
Definition CodeGen.cpp:256
void generateImportStmt(const AST::ImportStmt *importStmt)
Generate bytecode from Import Statement.
Definition CodeGen.cpp:285
void generateStatement(const AST::Statement *stmt)
Generate bytecode from Statement.
Definition CodeGen.cpp:89
static bool isLiteralExpression(const AST::Expression *expr, Value &outValue)
Check if expression is a compile-time literal.
Definition CodeGen.cpp:24
void generateIdentifierExpr(const AST::IdentifierExpr *identExpr)
Generate bytecode from Identifier Expression.
Definition CodeGen.cpp:328
bool isRepl
REPL mode.
Definition CodeGen.hpp:124
void generateAssignmentExpr(const AST::AssignmentExpr *assignExpr)
Generate bytecode from Assignment Expression.
Definition CodeGen.cpp:949
void generateNumberExpr(const AST::NumberExpr *numExpr)
Generate bytecode from Numeral Expression.
Definition CodeGen.cpp:297
void generateFieldAccessExpr(const AST::FieldAccessExpr *expr)
Definition CodeGen.cpp:1034
void generateReturnStmt(const AST::ReturnStmt *returnStmt)
Generate bytecode from Return Statement.
Definition CodeGen.cpp:880
A value in the Phasor VM.
Definition Value.hpp:58
bool isNull() const noexcept
Check if the value is null.
Definition Value.hpp:141
ValueType getType() const noexcept
Get the type of the value.
Definition Value.hpp:113
bool asBool() const noexcept
Get the value as a boolean.
Definition Value.hpp:154
Value logicalOr(const Value &other) const noexcept
Logical OR.
Definition Value.hpp:330
Value logicalAnd(const Value &other) const noexcept
Logical AND.
Definition Value.hpp:324
bool isBool() const noexcept
Definition Value.hpp:142
static uint64_t s[2]
Definition random.cpp:6
The Phasor Programming Language and Runtime.
Definition AST.hpp:12
@ FLMOD_R
R[rA] = R[rB] % R[rC].
Definition ISA.hpp:122
@ NOT
Pop a, push !a.
Definition ISA.hpp:38
@ NULL_VAL
Push null.
Definition ISA.hpp:86
@ IAND_R
R[rA] = R[rB] && R[rC].
Definition ISA.hpp:132
@ FLMUL_R
R[rA] = R[rB] * R[rC].
Definition ISA.hpp:120
@ IADD
Pop b, pop a, push a + b.
Definition ISA.hpp:18
@ FLGE_R
R[rA] = R[rB] >= R[rC].
Definition ISA.hpp:147
@ PUSH_CONST
Push constant from constant pool.
Definition ISA.hpp:14
@ JUMP_IF_TRUE
Jump if top of stack is true (pops value).
Definition ISA.hpp:63
@ FLGT_R
R[rA] = R[rB] > R[rC].
Definition ISA.hpp:145
@ PUSH_R
Push register to stack: push(R[rA]).
Definition ISA.hpp:107
@ POP_R
Pop stack to register: R[rA] = pop().
Definition ISA.hpp:109
@ FLEQUAL
Pop b, pop a, push a == b.
Definition ISA.hpp:53
@ FLADD_R
R[rA] = R[rB] + R[rC].
Definition ISA.hpp:118
@ LOAD_CONST_R
Load constant to register: R[rA] = constants[immediate].
Definition ISA.hpp:104
@ FLADD
Pop b, pop a, push a + b.
Definition ISA.hpp:23
@ JUMP
Unconditional jump to offset.
Definition ISA.hpp:61
@ FLLT_R
R[rA] = R[rB] < R[rC].
Definition ISA.hpp:144
@ SET_FIELD
Pop struct, pop field name, pop value, set field value.
Definition ISA.hpp:95
@ CHAR_AT
Pop index, pop s, push s[index].
Definition ISA.hpp:90
@ IMUL_R
R[rA] = R[rB] * R[rC].
Definition ISA.hpp:115
@ FLLE_R
R[rA] = R[rB] <= R[rC].
Definition ISA.hpp:146
@ ISUB_R
R[rA] = R[rB] - R[rC].
Definition ISA.hpp:114
@ FLDIV_R
R[rA] = R[rB] / R[rC].
Definition ISA.hpp:121
@ CALL_NATIVE
Call a native function: operand is index of function name in constants.
Definition ISA.hpp:76
@ ISUBTRACT
Pop b, pop a, push a - b.
Definition ISA.hpp:19
@ LEN
Pop s, push len(s).
Definition ISA.hpp:89
@ FALSE_P
Push false.
Definition ISA.hpp:85
@ NEW_STRUCT_INSTANCE_STATIC
Create new struct instance using struct section metadata (structIndex).
Definition ISA.hpp:97
@ STORE_VAR
Pop top of stack, store in variable slot.
Definition ISA.hpp:67
@ ILE_R
R[rA] = R[rB] <= R[rC].
Definition ISA.hpp:138
@ FLAND_R
R[rA] = R[rB] && R[rC].
Definition ISA.hpp:140
@ JUMP_IF_FALSE
Jump if top of stack is false (pops value).
Definition ISA.hpp:62
@ FLEQ_R
R[rA] = R[rB] == R[rC].
Definition ISA.hpp:142
@ LOAD_VAR
Push variable value onto stack.
Definition ISA.hpp:68
@ RETURN
Return from function.
Definition ISA.hpp:81
@ IGT_R
R[rA] = R[rB] > R[rC].
Definition ISA.hpp:137
@ FLSUBTRACT
Pop b, pop a, push a - b.
Definition ISA.hpp:24
@ IADD_R
R[rA] = R[rB] + R[rC].
Definition ISA.hpp:113
@ IDIV_R
R[rA] = R[rB] / R[rC].
Definition ISA.hpp:116
@ HALT
Stop execution.
Definition ISA.hpp:75
@ FLSUB_R
R[rA] = R[rB] - R[rC].
Definition ISA.hpp:119
@ JUMP_BACK
Jump backwards (for loops).
Definition ISA.hpp:64
@ FLOR_R
R[rA] = R[rB] || R[rC].
Definition ISA.hpp:141
@ CALL
Call a user function: operand is index of function name in constants.
Definition ISA.hpp:77
@ INE_R
R[rA] = R[rB] != R[rC].
Definition ISA.hpp:135
@ NEGATE
Pop a, push -a.
Definition ISA.hpp:37
@ FLNE_R
R[rA] = R[rB] != R[rC].
Definition ISA.hpp:143
@ GET_FIELD
Pop struct, pop field name, push field value.
Definition ISA.hpp:94
@ IMPORT
Import a module: operand is index of module path in constants.
Definition ISA.hpp:74
@ NEW_STRUCT
Create new struct: operand is index of struct name in constants.
Definition ISA.hpp:93
@ IOR_R
R[rA] = R[rB] || R[rC].
Definition ISA.hpp:133
@ ILT_R
R[rA] = R[rB] < R[rC].
Definition ISA.hpp:136
@ TRUE_P
Push true.
Definition ISA.hpp:84
@ POP
Pop top of stack.
Definition ISA.hpp:15
@ IMOD_R
R[rA] = R[rB] % R[rC].
Definition ISA.hpp:117
@ PRINT
Pop top of stack and print.
Definition ISA.hpp:71
@ IEQ_R
R[rA] = R[rB] == R[rC].
Definition ISA.hpp:134
@ IGE_R
R[rA] = R[rB] >= R[rC].
Definition ISA.hpp:139
ValueType
Runtime value types for the VM.
Definition Value.hpp:42
Assignment Expression Node.
Definition AST.hpp:389
std::unique_ptr< Expression > target
Definition AST.hpp:390
std::unique_ptr< Expression > value
Definition AST.hpp:391
Binary Expression Node.
Definition AST.hpp:252
std::unique_ptr< Expression > right
Definition AST.hpp:255
std::unique_ptr< Expression > left
Definition AST.hpp:253
Block Statement Node.
Definition AST.hpp:495
std::vector< std::unique_ptr< Statement > > statements
Definition AST.hpp:496
Boolean Expression Node.
Definition AST.hpp:138
Break Statement Node.
Definition AST.hpp:529
Call Expression Node.
Definition AST.hpp:368
std::string callee
Definition AST.hpp:369
std::vector< std::unique_ptr< Expression > > arguments
Definition AST.hpp:370
Continue Statement Node.
Definition AST.hpp:538
Export Statement Node.
Definition AST.hpp:481
std::unique_ptr< Statement > declaration
Definition AST.hpp:482
Expression Statement Node.
Definition AST.hpp:428
std::unique_ptr< Expression > expression
Definition AST.hpp:429
Expression Node.
Definition AST.hpp:54
Field Access Expression Node.
Definition AST.hpp:734
std::unique_ptr< Expression > object
Definition AST.hpp:735
For Statement Node.
Definition AST.hpp:591
std::unique_ptr< Statement > initializer
Definition AST.hpp:592
std::unique_ptr< Expression > increment
Definition AST.hpp:594
std::unique_ptr< Statement > body
Definition AST.hpp:595
std::unique_ptr< Expression > condition
Definition AST.hpp:593
Function Declaration Node.
Definition AST.hpp:638
std::vector< Param > params
Definition AST.hpp:645
std::unique_ptr< BlockStmt > body
Definition AST.hpp:647
Identifier Expression Node.
Definition AST.hpp:125
If Statement Node.
Definition AST.hpp:547
std::unique_ptr< Statement > elseBranch
Definition AST.hpp:550
std::unique_ptr< Expression > condition
Definition AST.hpp:548
std::unique_ptr< Statement > thenBranch
Definition AST.hpp:549
Import Statement Node.
Definition AST.hpp:468
std::string modulePath
Definition AST.hpp:469
Include Statement Node.
Definition AST.hpp:455
NULL Expression Node.
Definition AST.hpp:151
Numeral Expression Node.
Definition AST.hpp:98
std::string value
Definition AST.hpp:99
Postfix Expression Node.
Definition AST.hpp:194
std::unique_ptr< Expression > operand
Definition AST.hpp:196
Print Statement Node.
Definition AST.hpp:441
std::unique_ptr< Expression > expression
Definition AST.hpp:442
Program Node.
Definition AST.hpp:64
std::vector< std::unique_ptr< Statement > > statements
Definition AST.hpp:65
Return Statement Node.
Definition AST.hpp:512
std::unique_ptr< Expression > value
Definition AST.hpp:513
Statement Node.
Definition AST.hpp:59
String Expression Node.
Definition AST.hpp:111
std::string value
Definition AST.hpp:112
Struct Declaration Node.
Definition AST.hpp:691
std::vector< StructField > fields
Definition AST.hpp:693
std::string name
Definition AST.hpp:692
Struct Instance Expression Node.
Definition AST.hpp:712
std::vector< std::pair< std::string, std::unique_ptr< Expression > > > fieldValues
Definition AST.hpp:714
Switch Statement Node.
Definition AST.hpp:764
std::vector< CaseClause > cases
Definition AST.hpp:766
std::vector< std::unique_ptr< Statement > > defaultStmts
Definition AST.hpp:767
std::unique_ptr< Expression > expr
Definition AST.hpp:765
Unary Expression Node.
Definition AST.hpp:220
std::unique_ptr< Expression > operand
Definition AST.hpp:222
Unsafe Block Statement Node.
Definition AST.hpp:624
std::unique_ptr< BlockStmt > block
Definition AST.hpp:625
Variable Declaration Node.
Definition AST.hpp:410
std::string name
Definition AST.hpp:411
std::unique_ptr< Expression > initializer
Definition AST.hpp:412
While Statement Node.
Definition AST.hpp:572
std::unique_ptr< Expression > condition
Definition AST.hpp:573
std::unique_ptr< Statement > body
Definition AST.hpp:574
Complete bytecode structure.
Definition CodeGen.hpp:50
Struct metadata stored alongside bytecode (struct section).
Definition CodeGen.hpp:41
int firstConstIndex
Index into constants for the first default value.
Definition CodeGen.hpp:43
std::vector< std::string > fieldNames
Field names in declaration order.
Definition CodeGen.hpp:45
int fieldCount
Number of fields in this struct.
Definition CodeGen.hpp:44
std::string name
Struct name.
Definition CodeGen.hpp:42