Complete step460 assembly AST nodes with serialization tests

This commit is contained in:
Bill
2026-02-16 20:06:47 -07:00
parent 596229a799
commit 95922f1ec6
4 changed files with 402 additions and 0 deletions

View File

@@ -3019,4 +3019,13 @@ target_link_libraries(step459_test PRIVATE
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
add_executable(step460_test tests/step460_test.cpp)
target_include_directories(step460_test PRIVATE src)
target_link_libraries(step460_test PRIVATE
nlohmann_json::nlohmann_json
unofficial::tree-sitter::tree-sitter
tree_sitter_python tree_sitter_cpp tree_sitter_elisp
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)

View File

@@ -0,0 +1,200 @@
#pragma once
// Step 460: Assembly AST Nodes
// Node types for assembly instructions, labels, directives, registers, and
// memory operands. These can be attached under Function nodes as statement-like
// children for assembly-backed function bodies.
#include "ASTNode.h"
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
using json = nlohmann::json;
enum class AssemblyDirectiveType {
Data,
Text,
Global,
Section,
Byte,
Word,
Align,
Unknown
};
inline std::string directiveTypeToString(AssemblyDirectiveType t) {
switch (t) {
case AssemblyDirectiveType::Data: return ".data";
case AssemblyDirectiveType::Text: return ".text";
case AssemblyDirectiveType::Global: return ".global";
case AssemblyDirectiveType::Section: return ".section";
case AssemblyDirectiveType::Byte: return ".byte";
case AssemblyDirectiveType::Word: return ".word";
case AssemblyDirectiveType::Align: return ".align";
case AssemblyDirectiveType::Unknown: return ".unknown";
}
return ".unknown";
}
inline AssemblyDirectiveType directiveTypeFromString(const std::string& s) {
if (s == ".data") return AssemblyDirectiveType::Data;
if (s == ".text") return AssemblyDirectiveType::Text;
if (s == ".global") return AssemblyDirectiveType::Global;
if (s == ".section") return AssemblyDirectiveType::Section;
if (s == ".byte") return AssemblyDirectiveType::Byte;
if (s == ".word") return AssemblyDirectiveType::Word;
if (s == ".align") return AssemblyDirectiveType::Align;
return AssemblyDirectiveType::Unknown;
}
class AssemblyRegister : public ASTNode {
public:
std::string name; // rax, eax, x0, r0, etc.
int sizeBits = 0; // 8/16/32/64
AssemblyRegister() { conceptType = "AssemblyRegister"; }
AssemblyRegister(std::string n, int bits) : name(std::move(n)), sizeBits(bits) {
conceptType = "AssemblyRegister";
}
};
class AssemblyMemoryOperand : public ASTNode {
public:
std::string baseRegister; // rbp
int offset = 0; // -8
std::string indexRegister; // rbx
int scale = 1; // 1/2/4/8
AssemblyMemoryOperand() { conceptType = "AssemblyMemoryOperand"; }
};
class AssemblyInstruction : public ASTNode {
public:
std::string opcode; // mov/add/sub...
std::vector<std::string> operands; // textual fallback operands
// Optional structured operand pieces for richer addressing semantics.
std::vector<AssemblyRegister> registers;
std::vector<AssemblyMemoryOperand> memoryOperands;
AssemblyInstruction() { conceptType = "AssemblyInstruction"; }
AssemblyInstruction(std::string op, std::vector<std::string> ops)
: opcode(std::move(op)), operands(std::move(ops)) {
conceptType = "AssemblyInstruction";
}
};
class AssemblyLabel : public ASTNode {
public:
std::string name;
bool isGlobal = false;
AssemblyLabel() { conceptType = "AssemblyLabel"; }
AssemblyLabel(std::string n, bool global) : name(std::move(n)), isGlobal(global) {
conceptType = "AssemblyLabel";
}
};
class AssemblyDirective : public ASTNode {
public:
AssemblyDirectiveType type = AssemblyDirectiveType::Unknown;
std::string value; // .section name, .word literal value, etc.
AssemblyDirective() { conceptType = "AssemblyDirective"; }
AssemblyDirective(AssemblyDirectiveType t, std::string v)
: type(t), value(std::move(v)) {
conceptType = "AssemblyDirective";
}
};
inline json toJson(const AssemblyRegister& r) {
return {
{"concept", r.conceptType},
{"name", r.name},
{"sizeBits", r.sizeBits}
};
}
inline AssemblyRegister registerFromJson(const json& j) {
AssemblyRegister r;
r.name = j.value("name", "");
r.sizeBits = j.value("sizeBits", 0);
return r;
}
inline json toJson(const AssemblyMemoryOperand& m) {
return {
{"concept", m.conceptType},
{"baseRegister", m.baseRegister},
{"offset", m.offset},
{"indexRegister", m.indexRegister},
{"scale", m.scale}
};
}
inline AssemblyMemoryOperand memoryOperandFromJson(const json& j) {
AssemblyMemoryOperand m;
m.baseRegister = j.value("baseRegister", "");
m.offset = j.value("offset", 0);
m.indexRegister = j.value("indexRegister", "");
m.scale = j.value("scale", 1);
return m;
}
inline json toJson(const AssemblyInstruction& i) {
json regs = json::array();
for (const auto& r : i.registers) regs.push_back(toJson(r));
json mems = json::array();
for (const auto& m : i.memoryOperands) mems.push_back(toJson(m));
return {
{"concept", i.conceptType},
{"opcode", i.opcode},
{"operands", i.operands},
{"registers", regs},
{"memoryOperands", mems}
};
}
inline AssemblyInstruction instructionFromJson(const json& j) {
AssemblyInstruction i;
i.opcode = j.value("opcode", "");
i.operands = j.value("operands", std::vector<std::string>{});
if (j.contains("registers")) {
for (const auto& r : j["registers"]) i.registers.push_back(registerFromJson(r));
}
if (j.contains("memoryOperands")) {
for (const auto& m : j["memoryOperands"]) i.memoryOperands.push_back(memoryOperandFromJson(m));
}
return i;
}
inline json toJson(const AssemblyLabel& l) {
return {
{"concept", l.conceptType},
{"name", l.name},
{"isGlobal", l.isGlobal}
};
}
inline AssemblyLabel labelFromJson(const json& j) {
AssemblyLabel l;
l.name = j.value("name", "");
l.isGlobal = j.value("isGlobal", false);
return l;
}
inline json toJson(const AssemblyDirective& d) {
return {
{"concept", d.conceptType},
{"type", directiveTypeToString(d.type)},
{"value", d.value}
};
}
inline AssemblyDirective directiveFromJson(const json& j) {
AssemblyDirective d;
d.type = directiveTypeFromString(j.value("type", ".unknown"));
d.value = j.value("value", "");
return d;
}

View File

@@ -0,0 +1,160 @@
// Step 460: Assembly AST Nodes Tests (12 tests)
#include "ast/AssemblyNodes.h"
#include <iostream>
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_instruction_node_fields() {
TEST(instruction_node_fields);
AssemblyInstruction ins("mov", {"rax", "rbx"});
CHECK(ins.conceptType == "AssemblyInstruction", "wrong conceptType");
CHECK(ins.opcode == "mov", "wrong opcode");
CHECK(ins.operands.size() == 2, "wrong operand count");
PASS();
}
void test_label_node_fields() {
TEST(label_node_fields);
AssemblyLabel label("main", true);
CHECK(label.conceptType == "AssemblyLabel", "wrong conceptType");
CHECK(label.name == "main", "wrong label name");
CHECK(label.isGlobal, "expected global label");
PASS();
}
void test_directive_node_fields() {
TEST(directive_node_fields);
AssemblyDirective d(AssemblyDirectiveType::Text, "");
CHECK(d.conceptType == "AssemblyDirective", "wrong conceptType");
CHECK(d.type == AssemblyDirectiveType::Text, "wrong directive type");
PASS();
}
void test_register_node_fields() {
TEST(register_node_fields);
AssemblyRegister r("rax", 64);
CHECK(r.conceptType == "AssemblyRegister", "wrong conceptType");
CHECK(r.name == "rax", "wrong register name");
CHECK(r.sizeBits == 64, "wrong register size");
PASS();
}
void test_memory_operand_fields() {
TEST(memory_operand_fields);
AssemblyMemoryOperand m;
m.baseRegister = "rbp";
m.offset = -8;
m.indexRegister = "rbx";
m.scale = 4;
CHECK(m.conceptType == "AssemblyMemoryOperand", "wrong conceptType");
CHECK(m.baseRegister == "rbp", "wrong base register");
CHECK(m.offset == -8, "wrong offset");
CHECK(m.scale == 4, "wrong scale");
PASS();
}
void test_directive_type_string_roundtrip() {
TEST(directive_type_string_roundtrip);
auto s = directiveTypeToString(AssemblyDirectiveType::Global);
auto t = directiveTypeFromString(s);
CHECK(s == ".global", "wrong directive string");
CHECK(t == AssemblyDirectiveType::Global, "wrong directive parse");
PASS();
}
void test_register_json_roundtrip() {
TEST(register_json_roundtrip);
AssemblyRegister r("eax", 32);
auto j = toJson(r);
auto out = registerFromJson(j);
CHECK(out.name == "eax", "wrong register from json");
CHECK(out.sizeBits == 32, "wrong register bits from json");
PASS();
}
void test_memory_operand_json_roundtrip() {
TEST(memory_operand_json_roundtrip);
AssemblyMemoryOperand m;
m.baseRegister = "rax";
m.offset = 8;
m.indexRegister = "rcx";
m.scale = 2;
auto j = toJson(m);
auto out = memoryOperandFromJson(j);
CHECK(out.baseRegister == "rax", "wrong base register from json");
CHECK(out.offset == 8, "wrong offset from json");
CHECK(out.indexRegister == "rcx", "wrong index register from json");
CHECK(out.scale == 2, "wrong scale from json");
PASS();
}
void test_instruction_json_roundtrip_with_structured_operands() {
TEST(instruction_json_roundtrip_with_structured_operands);
AssemblyInstruction i("add", {"rax", "[rbp-8]"});
i.registers.push_back(AssemblyRegister("rax", 64));
AssemblyMemoryOperand m;
m.baseRegister = "rbp";
m.offset = -8;
i.memoryOperands.push_back(m);
auto j = toJson(i);
auto out = instructionFromJson(j);
CHECK(out.opcode == "add", "wrong opcode from json");
CHECK(out.operands.size() == 2, "wrong operand count from json");
CHECK(out.registers.size() == 1, "wrong register count from json");
CHECK(out.memoryOperands.size() == 1, "wrong mem operand count from json");
PASS();
}
void test_label_json_roundtrip() {
TEST(label_json_roundtrip);
AssemblyLabel l("loop_start", false);
auto j = toJson(l);
auto out = labelFromJson(j);
CHECK(out.name == "loop_start", "wrong label from json");
CHECK(!out.isGlobal, "wrong global flag from json");
PASS();
}
void test_directive_json_roundtrip() {
TEST(directive_json_roundtrip);
AssemblyDirective d(AssemblyDirectiveType::Align, "16");
auto j = toJson(d);
auto out = directiveFromJson(j);
CHECK(out.type == AssemblyDirectiveType::Align, "wrong directive type from json");
CHECK(out.value == "16", "wrong directive value from json");
PASS();
}
void test_unknown_directive_parses_to_unknown() {
TEST(unknown_directive_parses_to_unknown);
auto t = directiveTypeFromString(".bogus");
CHECK(t == AssemblyDirectiveType::Unknown, "expected unknown directive type");
PASS();
}
int main() {
std::cout << "Step 460: Assembly AST Nodes Tests\n";
test_instruction_node_fields(); // 1
test_label_node_fields(); // 2
test_directive_node_fields(); // 3
test_register_node_fields(); // 4
test_memory_operand_fields(); // 5
test_directive_type_string_roundtrip(); // 6
test_register_json_roundtrip(); // 7
test_memory_operand_json_roundtrip(); // 8
test_instruction_json_roundtrip_with_structured_operands(); // 9
test_label_json_roundtrip(); // 10
test_directive_json_roundtrip(); // 11
test_unknown_directive_parses_to_unknown(); // 12
std::cout << "\nResults: " << passed << "/" << (passed + failed)
<< " passed\n";
return failed == 0 ? 0 : 1;
}

View File

@@ -6032,3 +6032,36 @@ generation, confidence scoring, equivalence checks, and RPC/MCP surface.
- deterministic confidence scoring and review gating
- per-function/project translation reporting
- RPC + MCP interfaces for transpilation pipeline operations
### Step 460: Assembly AST Nodes
**Status:** PASS (12/12 tests)
Introduces core assembly AST node model and per-node JSON serialization
round-trip helpers for instructions, labels, directives, registers, and
memory operands.
**Files added:**
- `editor/src/ast/AssemblyNodes.h` — new assembly node definitions:
- `AssemblyInstruction` (opcode, operands, structured register/memory operands)
- `AssemblyLabel` (name, `isGlobal`)
- `AssemblyDirective` (`.data`, `.text`, `.global`, `.section`, `.byte`, `.word`, `.align`)
- `AssemblyRegister` (name + bit width)
- `AssemblyMemoryOperand` (base/index/scale/offset addressing components)
- directive enum/string conversion helpers
- JSON serialization/deserialization helpers for all node types
- `editor/tests/step460_test.cpp` — 12 tests covering:
- node construction and field integrity for all assembly node types
- directive enum/string mapping
- JSON round-trip for each node type
- structured operand round-trip in instructions
- unknown directive fallback behavior
- `editor/CMakeLists.txt``step460_test` target
**Verification run:**
- `cmake --build editor/build-native --target step460_test` — PASS
- `./editor/build-native/step460_test` — PASS (12/12)
- `./editor/build-native/step459_test` — PASS (8/8) regression coverage
**Architecture gate check:**
- `editor/src/ast/AssemblyNodes.h` within header-size limit (`200` <= `600`)
- `editor/tests/step460_test.cpp` within test-file size guidance (`160` lines)