// Step 461: x86 Assembly Parser Tests (12 tests) #include "ast/AssemblyNodes.h" #include "ast/EnumNamespaceNodes.h" #include "ast/X86AssemblyParser.h" #include static int passed = 0, failed = 0; #define TEST(name) { std::cout << " " << #name << "... "; } #define PASS() { std::cout << "PASS\n"; ++passed; } #define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; } #define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {} void test_parse_intel_basic_instructions() { TEST(parse_intel_basic_instructions); std::string src = ".text\nmain:\n mov rax, rbx\n add rax, 1\n ret\n"; auto mod = X86AssemblyParser::parseX86(src, X86SyntaxMode::Intel); CHECK(mod->getChildren("functions").size() == 1, "expected one function"); auto* fn = static_cast(mod->getChildren("functions")[0]); CHECK(fn->name == "main", "expected function name main"); CHECK(fn->getChildren("body").size() >= 3, "expected label + instructions"); PASS(); } void test_parse_att_basic_instructions() { TEST(parse_att_basic_instructions); std::string src = ".text\nmain:\n movq %rax, %rbx\n ret\n"; auto mod = X86AssemblyParser::parseX86(src, X86SyntaxMode::ATT); auto* fn = static_cast(mod->getChildren("functions")[0]); bool sawMov = false; for (auto* n : fn->getChildren("body")) { if (n->conceptType == "AssemblyInstruction") { auto* i = static_cast(n); if (i->opcode == "movq") sawMov = true; } } CHECK(sawMov, "expected movq opcode"); PASS(); } void test_parse_labels_and_global_flags() { TEST(parse_labels_and_global_flags); std::string src = ".global main\n.text\nmain:\n ret\n"; auto mod = X86AssemblyParser::parseX86(src); auto* fn = static_cast(mod->getChildren("functions")[0]); auto* lbl = static_cast(fn->getChildren("body")[0]); CHECK(lbl->conceptType == "AssemblyLabel", "expected label node"); CHECK(lbl->isGlobal, "expected global label"); PASS(); } void test_parse_directives_and_sections_as_namespaces() { TEST(parse_directives_and_sections_as_namespaces); std::string src = ".text\nmain:\n ret\n.data\nx: .word 1\n"; auto mod = X86AssemblyParser::parseX86(src); int nsCount = 0; for (auto* s : mod->getChildren("statements")) { if (s->conceptType == "NamespaceDeclaration") ++nsCount; } CHECK(nsCount >= 2, "expected namespace-like sections"); PASS(); } void test_register_recognition_sizes() { TEST(register_recognition_sizes); std::string src = ".text\nmain:\n mov rax, eax\n mov ax, al\n ret\n"; auto mod = X86AssemblyParser::parseX86(src); auto* fn = static_cast(mod->getChildren("functions")[0]); int has64 = 0, has32 = 0, has16 = 0, has8 = 0; for (auto* n : fn->getChildren("body")) { if (n->conceptType != "AssemblyInstruction") continue; auto* i = static_cast(n); for (const auto& r : i->registers) { if (r.sizeBits == 64) ++has64; if (r.sizeBits == 32) ++has32; if (r.sizeBits == 16) ++has16; if (r.sizeBits == 8) ++has8; } } CHECK(has64 > 0 && has32 > 0 && has16 > 0 && has8 > 0, "expected all register sizes"); PASS(); } void test_parse_intel_memory_base_offset() { TEST(parse_intel_memory_base_offset); std::string src = ".text\nmain:\n mov rax, [rbp-8]\n ret\n"; auto mod = X86AssemblyParser::parseX86(src, X86SyntaxMode::Intel); auto* fn = static_cast(mod->getChildren("functions")[0]); auto* i = static_cast(fn->getChildren("body")[1]); CHECK(i->memoryOperands.size() == 1, "expected one memory operand"); CHECK(i->memoryOperands[0].baseRegister == "rbp", "expected base rbp"); CHECK(i->memoryOperands[0].offset == -8, "expected offset -8"); PASS(); } void test_parse_intel_memory_base_index_scale_offset() { TEST(parse_intel_memory_base_index_scale_offset); std::string src = ".text\nmain:\n mov rax, [rax+rbx*4+8]\n ret\n"; auto mod = X86AssemblyParser::parseX86(src); auto* fn = static_cast(mod->getChildren("functions")[0]); auto* i = static_cast(fn->getChildren("body")[1]); CHECK(i->memoryOperands.size() == 1, "expected one memory operand"); CHECK(i->memoryOperands[0].baseRegister == "rax", "expected base rax"); CHECK(i->memoryOperands[0].indexRegister == "rbx", "expected index rbx"); CHECK(i->memoryOperands[0].scale == 4, "expected scale 4"); CHECK(i->memoryOperands[0].offset == 8, "expected offset 8"); PASS(); } void test_parse_att_memory_addressing() { TEST(parse_att_memory_addressing); std::string src = ".text\nmain:\n movq -8(%rbp,%rbx,4), %rax\n ret\n"; auto mod = X86AssemblyParser::parseX86(src, X86SyntaxMode::ATT); auto* fn = static_cast(mod->getChildren("functions")[0]); auto* i = static_cast(fn->getChildren("body")[1]); CHECK(i->memoryOperands.size() == 1, "expected one memory operand"); CHECK(i->memoryOperands[0].baseRegister == "rbp", "expected base rbp"); CHECK(i->memoryOperands[0].indexRegister == "rbx", "expected index rbx"); CHECK(i->memoryOperands[0].scale == 4, "expected scale 4"); CHECK(i->memoryOperands[0].offset == -8, "expected offset -8"); PASS(); } void test_parse_ignores_comments() { TEST(parse_ignores_comments); std::string src = ".text ; section\nmain: ; label\n mov rax, rbx ; comment\n ret\n"; auto mod = X86AssemblyParser::parseX86(src); auto* fn = static_cast(mod->getChildren("functions")[0]); CHECK(fn->getChildren("body").size() >= 3, "expected parsed nodes despite comments"); PASS(); } void test_known_opcode_set_covers_control_flow_and_stack() { TEST(known_opcode_set_covers_control_flow_and_stack); std::string src = ".text\nmain:\n push rax\n call foo\n jmp done\n pop rax\n ret\nfoo:\n ret\ndone:\n ret\n"; auto res = X86AssemblyParser::parseX86WithDiagnostics(src); CHECK(res.diagnostics.empty(), "expected no unknown opcode warnings"); PASS(); } void test_unknown_opcode_emits_warning() { TEST(unknown_opcode_emits_warning); std::string src = ".text\nmain:\n frobnicate rax, rbx\n ret\n"; auto res = X86AssemblyParser::parseX86WithDiagnostics(src); CHECK(!res.diagnostics.empty(), "expected warning diagnostics"); CHECK(res.diagnostics[0].severity == "warning", "expected warning severity"); PASS(); } void test_parse_text_data_bss_sections() { TEST(parse_text_data_bss_sections); std::string src = ".text\nmain:\n ret\n.data\nx: .word 1\n.bss\ny: .byte 0\n"; auto mod = X86AssemblyParser::parseX86(src); bool sawText = false, sawData = false; for (auto* s : mod->getChildren("statements")) { if (s->conceptType != "NamespaceDeclaration") continue; auto* ns = static_cast(s); if (ns->name == ".text") sawText = true; if (ns->name == ".data") sawData = true; } CHECK(sawText, "expected .text namespace"); CHECK(sawData, "expected .data namespace"); PASS(); } int main() { std::cout << "Step 461: x86 Assembly Parser Tests\n"; test_parse_intel_basic_instructions(); // 1 test_parse_att_basic_instructions(); // 2 test_parse_labels_and_global_flags(); // 3 test_parse_directives_and_sections_as_namespaces(); // 4 test_register_recognition_sizes(); // 5 test_parse_intel_memory_base_offset(); // 6 test_parse_intel_memory_base_index_scale_offset(); // 7 test_parse_att_memory_addressing(); // 8 test_parse_ignores_comments(); // 9 test_known_opcode_set_covers_control_flow_and_stack(); // 10 test_unknown_opcode_emits_warning(); // 11 test_parse_text_data_bss_sections(); // 12 std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n"; return failed == 0 ? 0 : 1; }