use std::collections::HashMap;
use std::fmt::Write;
use crate::config::{CppGuardStyle as GuardStyle, CppOptions};
use crate::tree::DecodeNode;
use crate::types::*;
pub fn generate_cpp_code(
def: &ValidatedDef,
tree: &DecodeNode,
opts: &CppOptions,
type_maps: &HashMap<String, String>,
) -> String {
let mut out = String::new();
let default_ns = to_snake_case(&def.config.name);
let ns = opts.namespace.as_deref().unwrap_or(&default_ns);
let guard_name = format!("CHIPI_{}_HPP", ns.to_ascii_uppercase());
let unit_bytes = def.config.width / 8;
let word_type = cpp_word_type(def.config.width);
let endian = &def.config.endian;
let variable_length = def.instructions.iter().any(|i| i.unit_count() > 1);
match opts.guard_style {
GuardStyle::Pragma => writeln!(out, "#pragma once").unwrap(),
GuardStyle::Ifndef => {
writeln!(out, "#ifndef {}", guard_name).unwrap();
writeln!(out, "#define {}", guard_name).unwrap();
}
}
writeln!(out).unwrap();
writeln!(
out,
"// Auto-generated by https://github.com/ioncodes/chipi"
)
.unwrap();
writeln!(out, "// Do not edit.").unwrap();
writeln!(out).unwrap();
writeln!(out, "#include <cstdint>").unwrap();
writeln!(out, "#include <cstddef>").unwrap();
writeln!(out, "#include <cstring>").unwrap();
writeln!(out, "#include <string>").unwrap();
writeln!(out, "#include <optional>").unwrap();
writeln!(out, "#include <format>").unwrap();
for inc in &opts.includes {
writeln!(out, "#include \"{}\"", inc).unwrap();
}
writeln!(out).unwrap();
writeln!(out, "namespace {} {{", ns).unwrap();
writeln!(out).unwrap();
emit_display_types(&mut out, def, type_maps);
emit_opcode_enum(&mut out, def);
emit_instruction_struct(&mut out, def, type_maps);
for sd in &def.sub_decoders {
emit_subdecoder(&mut out, sd, def.config.width);
}
emit_map_functions(&mut out, def);
emit_decode_function(
&mut out,
def,
tree,
word_type,
unit_bytes,
endian,
variable_length,
type_maps,
);
emit_format_function(&mut out, def, type_maps, opts);
writeln!(out, "}} // namespace {}", ns).unwrap();
if opts.guard_style == GuardStyle::Ifndef {
writeln!(out).unwrap();
writeln!(out, "#endif // {}", guard_name).unwrap();
}
out
}
fn cpp_word_type(width: u32) -> &'static str {
match width {
8 => "uint8_t",
16 => "uint16_t",
32 => "uint32_t",
_ => "uint32_t",
}
}
fn cpp_signed_type(base: &str) -> &'static str {
match base {
"u8" | "i8" => "int8_t",
"u16" | "i16" => "int16_t",
"u32" | "i32" => "int32_t",
_ => "int32_t",
}
}
fn cpp_type_for(base: &str) -> &'static str {
match base {
"bool" => "bool",
"u1" | "u2" | "u3" | "u4" | "u5" | "u6" | "u7" | "u8" => "uint8_t",
"i8" => "int8_t",
"u16" => "uint16_t",
"i16" => "int16_t",
"u32" => "uint32_t",
"i32" => "int32_t",
_ => "uint32_t",
}
}
fn type_bits(base: &str) -> u32 {
match base {
"u8" | "i8" => 8,
"u16" | "i16" => 16,
"u32" | "i32" => 32,
_ => 32,
}
}
fn to_snake_case(name: &str) -> String {
let mut result = String::new();
for (i, ch) in name.chars().enumerate() {
if ch.is_ascii_uppercase() && i > 0 {
result.push('_');
}
result.push(ch.to_ascii_lowercase());
}
result
}
fn to_pascal_case(name: &str) -> String {
let mut result = String::new();
let mut cap_next = true;
for ch in name.chars() {
if ch == '_' {
cap_next = true;
} else if cap_next {
result.push(ch.to_ascii_uppercase());
cap_next = false;
} else {
result.push(ch.to_ascii_lowercase());
}
}
result
}
fn emit_display_types(out: &mut String, def: &ValidatedDef, type_maps: &HashMap<String, String>) {
let mut need_signed_hex = false;
let mut need_hex = false;
for alias in &def.type_aliases {
if type_maps.contains_key(&alias.name) {
continue;
}
match alias.display_format {
Some(DisplayFormat::SignedHex) => need_signed_hex = true,
Some(DisplayFormat::Hex) => need_hex = true,
None => {}
}
}
if need_signed_hex {
writeln!(out, "struct SignedHex {{").unwrap();
writeln!(out, " int32_t value;").unwrap();
writeln!(out, " SignedHex() = default;").unwrap();
writeln!(out, " constexpr SignedHex(int32_t v) : value(v) {{}}").unwrap();
writeln!(
out,
" bool operator==(const SignedHex&) const = default;"
)
.unwrap();
writeln!(
out,
" bool operator==(int other) const {{ return value == other; }}"
)
.unwrap();
writeln!(
out,
" bool operator!=(int other) const {{ return value != other; }}"
)
.unwrap();
writeln!(
out,
" bool operator<(int other) const {{ return value < other; }}"
)
.unwrap();
writeln!(
out,
" bool operator<=(int other) const {{ return value <= other; }}"
)
.unwrap();
writeln!(
out,
" bool operator>(int other) const {{ return value > other; }}"
)
.unwrap();
writeln!(
out,
" bool operator>=(int other) const {{ return value >= other; }}"
)
.unwrap();
writeln!(
out,
" SignedHex operator-() const {{ return SignedHex(-value); }}"
)
.unwrap();
writeln!(out, " friend SignedHex operator-(int lhs, SignedHex rhs) {{ return SignedHex(lhs - rhs.value); }}").unwrap();
writeln!(out, " friend SignedHex operator+(int lhs, SignedHex rhs) {{ return SignedHex(lhs + rhs.value); }}").unwrap();
writeln!(out, " friend SignedHex operator+(SignedHex lhs, int rhs) {{ return SignedHex(lhs.value + rhs); }}").unwrap();
writeln!(out, " friend SignedHex operator-(SignedHex lhs, int rhs) {{ return SignedHex(lhs.value - rhs); }}").unwrap();
writeln!(out, " friend SignedHex operator*(SignedHex lhs, int rhs) {{ return SignedHex(lhs.value * rhs); }}").unwrap();
writeln!(out, "}};").unwrap();
writeln!(out).unwrap();
}
if need_hex {
writeln!(out, "struct Hex {{").unwrap();
writeln!(out, " uint32_t value;").unwrap();
writeln!(out, " Hex() = default;").unwrap();
writeln!(out, " constexpr Hex(uint32_t v) : value(v) {{}}").unwrap();
writeln!(out, " bool operator==(const Hex&) const = default;").unwrap();
writeln!(
out,
" bool operator==(unsigned other) const {{ return value == other; }}"
)
.unwrap();
writeln!(out, "}};").unwrap();
writeln!(out).unwrap();
}
writeln!(out, "}} // namespace {}", to_snake_case(&def.config.name)).unwrap();
writeln!(out).unwrap();
if need_signed_hex {
let ns = to_snake_case(&def.config.name);
writeln!(
out,
"template <> struct std::formatter<{}::SignedHex> : std::formatter<std::string> {{",
ns
)
.unwrap();
writeln!(
out,
" auto format({}::SignedHex v, auto& ctx) const {{",
ns
)
.unwrap();
writeln!(out, " if (v.value < 0)").unwrap();
writeln!(out, " return std::formatter<std::string>::format(std::format(\"-0x{{:x}}\", static_cast<unsigned>(-v.value)), ctx);").unwrap();
writeln!(out, " return std::formatter<std::string>::format(std::format(\"0x{{:x}}\", static_cast<unsigned>(v.value)), ctx);").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}};").unwrap();
writeln!(out).unwrap();
}
if need_hex {
let ns = to_snake_case(&def.config.name);
writeln!(
out,
"template <> struct std::formatter<{}::Hex> : std::formatter<std::string> {{",
ns
)
.unwrap();
writeln!(out, " auto format({}::Hex v, auto& ctx) const {{", ns).unwrap();
writeln!(out, " return std::formatter<std::string>::format(std::format(\"0x{{:x}}\", v.value), ctx);").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}};").unwrap();
writeln!(out).unwrap();
}
if need_signed_hex || need_hex {
writeln!(out, "namespace {} {{", to_snake_case(&def.config.name)).unwrap();
writeln!(out).unwrap();
}
}
fn emit_opcode_enum(out: &mut String, def: &ValidatedDef) {
writeln!(out, "enum class Opcode : uint32_t {{").unwrap();
for (i, instr) in def.instructions.iter().enumerate() {
writeln!(out, " {} = {},", to_pascal_case(&instr.name), i).unwrap();
}
writeln!(out, "}};").unwrap();
writeln!(out).unwrap();
}
fn emit_instruction_struct(
out: &mut String,
def: &ValidatedDef,
type_maps: &HashMap<String, String>,
) {
writeln!(out, "struct Instruction {{").unwrap();
writeln!(out, " Opcode opcode;").unwrap();
writeln!(out, " uint32_t size; // bytes consumed").unwrap();
writeln!(out).unwrap();
let has_fields = def
.instructions
.iter()
.any(|i| !i.resolved_fields.is_empty());
if has_fields {
writeln!(out, " union {{").unwrap();
for instr in &def.instructions {
if instr.resolved_fields.is_empty() {
continue;
}
writeln!(out, " struct {{").unwrap();
for field in &instr.resolved_fields {
let cpp_type = field_cpp_type(field, type_maps);
writeln!(out, " {} {};", cpp_type, field.name).unwrap();
}
writeln!(out, " }} {};", instr.name).unwrap();
}
writeln!(out, " }};").unwrap();
}
writeln!(out, "}};").unwrap();
writeln!(out).unwrap();
}
fn field_cpp_type(field: &ResolvedField, type_maps: &HashMap<String, String>) -> String {
if let Some(alias) = &field.resolved_type.alias_name {
if let Some(mapped) = type_maps.get(alias) {
return mapped.clone();
}
}
if let Some(ref sd_name) = field.resolved_type.sub_decoder {
return format!("{}Insn", to_pascal_case(sd_name));
}
match field.resolved_type.display_format {
Some(DisplayFormat::SignedHex) => return "SignedHex".to_string(),
Some(DisplayFormat::Hex) => return "Hex".to_string(),
None => {}
}
cpp_type_for(&field.resolved_type.base_type).to_string()
}
fn emit_subdecoder(out: &mut String, sd: &ValidatedSubDecoder, _parent_width: u32) {
let type_name = format!("{}Insn", to_pascal_case(&sd.name));
let word_type = cpp_word_type(sd.width);
writeln!(out, "struct {} {{", type_name).unwrap();
for frag_name in &sd.fragment_names {
writeln!(out, " const char* {};", frag_name).unwrap();
}
writeln!(out, "}};").unwrap();
writeln!(out).unwrap();
let fn_name = format!("decode_{}", to_snake_case(&sd.name));
writeln!(
out,
"inline std::optional<{}> {}({} val) {{",
type_name, fn_name, word_type
)
.unwrap();
for (i, instr) in sd.instructions.iter().enumerate() {
let (mask, value) = compute_instruction_mask_value_sub(instr);
let keyword = if i == 0 { "if" } else { "} else if" };
writeln!(
out,
" {} ((val & {:#x}) == {:#x}) {{",
keyword, mask, value
)
.unwrap();
for frag in &instr.fragments {
let all_literal = frag
.pieces
.iter()
.all(|p| matches!(p, FormatPiece::Literal(_)));
if all_literal {
let s: String = frag
.pieces
.iter()
.map(|p| {
if let FormatPiece::Literal(lit) = p {
lit.as_str()
} else {
""
}
})
.collect();
writeln!(out, " // {}.{} = \"{}\"", instr.name, frag.name, s).unwrap();
}
}
let frag_values: Vec<String> = instr
.fragments
.iter()
.map(|frag| {
let all_literal = frag
.pieces
.iter()
.all(|p| matches!(p, FormatPiece::Literal(_)));
if all_literal {
let s: String = frag
.pieces
.iter()
.map(|p| {
if let FormatPiece::Literal(lit) = p {
lit.as_str()
} else {
""
}
})
.collect();
format!("\"{}\"", s)
} else {
"\"\"".to_string()
}
})
.collect();
writeln!(
out,
" return {} {{ {} }};",
type_name,
frag_values.join(", ")
)
.unwrap();
}
if !sd.instructions.is_empty() {
writeln!(out, " }}").unwrap();
}
writeln!(out, " return std::nullopt;").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
fn compute_instruction_mask_value_sub(instr: &ValidatedSubInstruction) -> (u64, u64) {
let mut mask: u64 = 0;
let mut value: u64 = 0;
for seg in &instr.segments {
if let Segment::Fixed {
ranges, pattern, ..
} = seg
{
let mut bit_idx = 0;
for range in ranges {
for i in 0..range.width() {
if bit_idx < pattern.len() {
let bit = pattern[bit_idx];
if bit != Bit::Wildcard {
let hw_bit = range.start - i;
mask |= 1u64 << hw_bit;
if bit == Bit::One {
value |= 1u64 << hw_bit;
}
}
bit_idx += 1;
}
}
}
}
}
(mask, value)
}
fn emit_map_functions(out: &mut String, def: &ValidatedDef) {
for map_def in &def.maps {
let params: Vec<String> = map_def
.params
.iter()
.map(|p| format!("int {}", p))
.collect();
writeln!(
out,
"inline const char* {}({}) {{",
map_def.name,
params.join(", ")
)
.unwrap();
let key_var = if map_def.params.len() == 1 {
map_def.params[0].clone()
} else {
String::new()
};
if map_def.params.len() == 1 {
writeln!(out, " switch ({}) {{", key_var).unwrap();
for entry in &map_def.entries {
if entry.keys.len() == 1 && entry.keys[0] == MapKey::Wildcard {
continue;
}
if let Some(MapKey::Value(v)) = entry.keys.first() {
let s = pieces_to_str(&entry.output);
writeln!(out, " case {}: return \"{}\";", v, s).unwrap();
}
}
let default_str = map_def
.entries
.iter()
.find(|e| e.keys.len() == 1 && e.keys[0] == MapKey::Wildcard)
.map(|e| pieces_to_str(&e.output))
.unwrap_or_else(|| "???".to_string());
writeln!(out, " default: return \"{}\";", default_str).unwrap();
writeln!(out, " }}").unwrap();
} else {
for entry in &map_def.entries {
if entry.keys.iter().any(|k| *k == MapKey::Wildcard) {
continue;
}
let conds: Vec<String> = entry
.keys
.iter()
.zip(map_def.params.iter())
.map(|(k, p)| {
if let MapKey::Value(v) = k {
format!("{} == {}", p, v)
} else {
"true".to_string()
}
})
.collect();
let s = pieces_to_str(&entry.output);
writeln!(out, " if ({}) return \"{}\";", conds.join(" && "), s).unwrap();
}
writeln!(out, " return \"???\";").unwrap();
}
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
for sd in &def.sub_decoders {
for map_def in &sd.maps {
let params: Vec<String> = map_def
.params
.iter()
.map(|p| format!("int {}", p))
.collect();
writeln!(
out,
"inline const char* {}({}) {{",
map_def.name,
params.join(", ")
)
.unwrap();
writeln!(out, " switch ({}) {{", map_def.params[0]).unwrap();
for entry in &map_def.entries {
if entry.keys.len() == 1 && entry.keys[0] == MapKey::Wildcard {
continue;
}
if let Some(MapKey::Value(v)) = entry.keys.first() {
let s = pieces_to_str(&entry.output);
writeln!(out, " case {}: return \"{}\";", v, s).unwrap();
}
}
let default_str = map_def
.entries
.iter()
.find(|e| e.keys.len() == 1 && e.keys[0] == MapKey::Wildcard)
.map(|e| pieces_to_str(&e.output))
.unwrap_or_else(|| "???".to_string());
writeln!(out, " default: return \"{}\";", default_str).unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
}
}
fn pieces_to_str(pieces: &[FormatPiece]) -> String {
let mut s = String::new();
for piece in pieces {
if let FormatPiece::Literal(lit) = piece {
s.push_str(lit);
}
}
s
}
fn emit_decode_function(
out: &mut String,
def: &ValidatedDef,
tree: &DecodeNode,
word_type: &str,
unit_bytes: u32,
endian: &ByteEndian,
variable_length: bool,
type_maps: &HashMap<String, String>,
) {
writeln!(
out,
"inline std::optional<Instruction> decode(const uint8_t* data, size_t len) {{"
)
.unwrap();
writeln!(out, " if (len < {}) return std::nullopt;", unit_bytes).unwrap();
emit_word_read(out, "opcode", word_type, 0, unit_bytes, endian, 1);
emit_tree_cpp(
out,
tree,
def,
1,
word_type,
unit_bytes,
endian,
variable_length,
type_maps,
);
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
fn emit_word_read(
out: &mut String,
var_name: &str,
word_type: &str,
offset: u32,
unit_bytes: u32,
endian: &ByteEndian,
indent: usize,
) {
let pad = " ".repeat(indent);
match (unit_bytes, endian) {
(1, _) => {
writeln!(out, "{}{} {} = data[{}];", pad, word_type, var_name, offset).unwrap();
}
(2, ByteEndian::Big) => {
writeln!(
out,
"{}{} {} = (static_cast<uint16_t>(data[{}]) << 8) | data[{}];",
pad,
word_type,
var_name,
offset,
offset + 1
)
.unwrap();
}
(2, ByteEndian::Little) => {
writeln!(
out,
"{}{} {} = data[{}] | (static_cast<uint16_t>(data[{}]) << 8);",
pad,
word_type,
var_name,
offset,
offset + 1
)
.unwrap();
}
(4, ByteEndian::Big) => {
writeln!(
out,
"{}{} {} = (static_cast<uint32_t>(data[{}]) << 24) | (static_cast<uint32_t>(data[{}]) << 16) | (static_cast<uint32_t>(data[{}]) << 8) | data[{}];",
pad, word_type, var_name, offset, offset + 1, offset + 2, offset + 3
).unwrap();
}
(4, ByteEndian::Little) => {
writeln!(
out,
"{}{} {} = data[{}] | (static_cast<uint32_t>(data[{}]) << 8) | (static_cast<uint32_t>(data[{}]) << 16) | (static_cast<uint32_t>(data[{}]) << 24);",
pad, word_type, var_name, offset, offset + 1, offset + 2, offset + 3
).unwrap();
}
_ => {
writeln!(out, "{}// unsupported width", pad).unwrap();
}
}
}
fn unit_read_expr(unit: u32, _word_type: &str, unit_bytes: u32, endian: &ByteEndian) -> String {
if unit == 0 {
return "opcode".to_string();
}
let offset = unit * unit_bytes;
match (unit_bytes, endian) {
(1, _) => format!("data[{}]", offset),
(2, ByteEndian::Big) => format!(
"(static_cast<uint16_t>(data[{}]) << 8 | data[{}])",
offset,
offset + 1
),
(2, ByteEndian::Little) => format!(
"(data[{}] | static_cast<uint16_t>(data[{}]) << 8)",
offset,
offset + 1
),
(4, ByteEndian::Big) => format!(
"(static_cast<uint32_t>(data[{}]) << 24 | static_cast<uint32_t>(data[{}]) << 16 | static_cast<uint32_t>(data[{}]) << 8 | data[{}])",
offset,
offset + 1,
offset + 2,
offset + 3
),
(4, ByteEndian::Little) => format!(
"(data[{}] | static_cast<uint32_t>(data[{}]) << 8 | static_cast<uint32_t>(data[{}]) << 16 | static_cast<uint32_t>(data[{}]) << 24)",
offset,
offset + 1,
offset + 2,
offset + 3
),
_ => "0".to_string(),
}
}
fn extract_expr(
var: &str,
ranges: &[BitRange],
word_type: &str,
unit_bytes: u32,
endian: &ByteEndian,
) -> String {
if ranges.is_empty() {
return "0".to_string();
}
if ranges.len() == 1 {
let range = ranges[0];
let source = if range.unit == 0 {
var.to_string()
} else {
unit_read_expr(range.unit, word_type, unit_bytes, endian)
};
let width = range.width();
let shift = range.end;
let mask = (1u64 << width) - 1;
if shift == 0 {
format!("({} & {:#x})", source, mask)
} else {
format!("(({} >> {}) & {:#x})", source, shift, mask)
}
} else {
let mut parts = Vec::new();
let mut accumulated = 0u32;
for range in ranges {
let source = if range.unit == 0 {
var.to_string()
} else {
unit_read_expr(range.unit, word_type, unit_bytes, endian)
};
let width = range.width();
let shift = range.end;
let mask = (1u64 << width) - 1;
let extracted = if shift == 0 {
format!("({} & {:#x})", source, mask)
} else {
format!("(({} >> {}) & {:#x})", source, shift, mask)
};
if accumulated > 0 {
parts.push(format!("({} << {})", extracted, accumulated));
} else {
parts.push(extracted);
}
accumulated += width;
}
parts.join(" | ")
}
}
fn leaf_guard(
instr: &ValidatedInstruction,
word_type: &str,
unit_bytes: u32,
endian: &ByteEndian,
) -> Option<String> {
let fixed_bits = instr.fixed_bits();
if fixed_bits.is_empty() {
return None;
}
let mut units_map: HashMap<u32, Vec<(u32, Bit)>> = HashMap::new();
for (unit, hw_bit, bit) in fixed_bits {
units_map.entry(unit).or_default().push((hw_bit, bit));
}
let mut conditions = Vec::new();
for (unit, bits) in &units_map {
let (mask, value) = compute_mask_value(bits);
if mask != 0 {
let source = if *unit == 0 {
"opcode".to_string()
} else {
unit_read_expr(*unit, word_type, unit_bytes, endian)
};
conditions.push(format!("({} & {:#x}) == {:#x}", source, mask, value));
}
}
if conditions.is_empty() {
None
} else {
Some(conditions.join(" && "))
}
}
fn compute_mask_value(fixed_bits: &[(u32, Bit)]) -> (u64, u64) {
let mut mask: u64 = 0;
let mut value: u64 = 0;
for &(bit_pos, bit_val) in fixed_bits {
if bit_val == Bit::Wildcard {
continue;
}
mask |= 1u64 << bit_pos;
if bit_val == Bit::One {
value |= 1u64 << bit_pos;
}
}
(mask, value)
}
fn apply_transforms(
extract: &str,
resolved: &ResolvedFieldType,
type_maps: &HashMap<String, String>,
) -> String {
let mut expr = extract.to_string();
for transform in &resolved.transforms {
match transform {
Transform::SignExtend(n) => {
let signed = cpp_signed_type(&resolved.base_type);
let bits = type_bits(&resolved.base_type);
expr = format!(
"static_cast<{}>(static_cast<{}>(({}) << ({} - {})) >> ({} - {}))",
cpp_type_for(&resolved.base_type),
signed,
expr,
bits,
n,
bits,
n
);
}
Transform::ZeroExtend(_) => {}
Transform::ShiftLeft(n) => {
expr = format!("(({}) << {})", expr, n);
}
}
}
if let Some(ref sd_name) = resolved.sub_decoder {
let decode_fn = format!("decode_{}", to_snake_case(sd_name));
return format!(
"{}(static_cast<{}>({})).value()",
decode_fn,
cpp_word_type(type_bits(&resolved.base_type).min(32)),
expr
);
}
if let Some(alias) = &resolved.alias_name {
if let Some(mapped) = type_maps.get(alias) {
return format!("static_cast<{}>({})", mapped, expr);
}
}
match resolved.display_format {
Some(DisplayFormat::SignedHex) => {
return format!("SignedHex(static_cast<int32_t>({}))", expr);
}
Some(DisplayFormat::Hex) => {
return format!("Hex(static_cast<uint32_t>({}))", expr);
}
None => {}
}
if resolved.base_type == "bool" {
format!("({}) != 0", expr)
} else {
format!(
"static_cast<{}>({})",
cpp_type_for(&resolved.base_type),
expr
)
}
}
fn emit_tree_cpp(
out: &mut String,
node: &DecodeNode,
def: &ValidatedDef,
indent: usize,
word_type: &str,
unit_bytes: u32,
endian: &ByteEndian,
variable_length: bool,
type_maps: &HashMap<String, String>,
) {
let pad = " ".repeat(indent);
match node {
DecodeNode::Leaf { instruction_index } => {
let instr = &def.instructions[*instruction_index];
if let Some(guard) = leaf_guard(instr, word_type, unit_bytes, endian) {
writeln!(out, "{}if ({}) {{", pad, guard).unwrap();
emit_return_instruction(
out,
instr,
indent + 1,
word_type,
unit_bytes,
endian,
variable_length,
type_maps,
);
writeln!(out, "{}}} else {{", pad).unwrap();
writeln!(out, "{} return std::nullopt;", pad).unwrap();
writeln!(out, "{}}}", pad).unwrap();
} else {
emit_return_instruction(
out,
instr,
indent,
word_type,
unit_bytes,
endian,
variable_length,
type_maps,
);
}
}
DecodeNode::PriorityLeaves { candidates } => {
for (i, &idx) in candidates.iter().enumerate() {
let instr = &def.instructions[idx];
let guard = leaf_guard(instr, word_type, unit_bytes, endian);
if i == 0 {
if let Some(g) = guard {
writeln!(out, "{}if ({}) {{", pad, g).unwrap();
emit_return_instruction(
out,
instr,
indent + 1,
word_type,
unit_bytes,
endian,
variable_length,
type_maps,
);
} else {
emit_return_instruction(
out,
instr,
indent,
word_type,
unit_bytes,
endian,
variable_length,
type_maps,
);
break;
}
} else if i == candidates.len() - 1 {
if let Some(g) = guard {
writeln!(out, "{}}} else if ({}) {{", pad, g).unwrap();
emit_return_instruction(
out,
instr,
indent + 1,
word_type,
unit_bytes,
endian,
variable_length,
type_maps,
);
writeln!(out, "{}}} else {{", pad).unwrap();
writeln!(out, "{} return std::nullopt;", pad).unwrap();
writeln!(out, "{}}}", pad).unwrap();
} else {
writeln!(out, "{}}} else {{", pad).unwrap();
emit_return_instruction(
out,
instr,
indent + 1,
word_type,
unit_bytes,
endian,
variable_length,
type_maps,
);
writeln!(out, "{}}}", pad).unwrap();
}
} else {
let g = guard.unwrap_or_else(|| "true".to_string());
writeln!(out, "{}}} else if ({}) {{", pad, g).unwrap();
emit_return_instruction(
out,
instr,
indent + 1,
word_type,
unit_bytes,
endian,
variable_length,
type_maps,
);
}
}
}
DecodeNode::Fail => {
writeln!(out, "{}return std::nullopt;", pad).unwrap();
}
DecodeNode::Branch {
range,
arms,
default,
} => {
let ext = extract_expr("opcode", &[*range], word_type, unit_bytes, endian);
writeln!(out, "{}switch ({}) {{", pad, ext).unwrap();
for (value, child) in arms {
writeln!(out, "{}case {:#x}: {{", pad, value).unwrap();
emit_tree_cpp(
out,
child,
def,
indent + 1,
word_type,
unit_bytes,
endian,
variable_length,
type_maps,
);
writeln!(out, "{} break;", pad).unwrap();
writeln!(out, "{}}}", pad).unwrap();
}
writeln!(out, "{}default: {{", pad).unwrap();
emit_tree_cpp(
out,
default,
def,
indent + 1,
word_type,
unit_bytes,
endian,
variable_length,
type_maps,
);
writeln!(out, "{} break;", pad).unwrap();
writeln!(out, "{}}}", pad).unwrap();
writeln!(out, "{}}}", pad).unwrap();
}
}
}
fn emit_return_instruction(
out: &mut String,
instr: &ValidatedInstruction,
indent: usize,
word_type: &str,
unit_bytes: u32,
endian: &ByteEndian,
variable_length: bool,
type_maps: &HashMap<String, String>,
) {
let pad = " ".repeat(indent);
let unit_count = instr.unit_count();
let bytes_consumed = unit_count * unit_bytes;
let variant = to_pascal_case(&instr.name);
if variable_length && unit_count > 1 {
writeln!(
out,
"{}if (len < {}) return std::nullopt;",
pad, bytes_consumed
)
.unwrap();
}
if instr.resolved_fields.is_empty() {
writeln!(
out,
"{}return Instruction {{ Opcode::{}, {} }};",
pad, variant, bytes_consumed
)
.unwrap();
} else {
writeln!(out, "{}{{", pad).unwrap();
writeln!(out, "{} Instruction insn{{}};", pad).unwrap();
writeln!(out, "{} insn.opcode = Opcode::{};", pad, variant).unwrap();
writeln!(out, "{} insn.size = {};", pad, bytes_consumed).unwrap();
for field in &instr.resolved_fields {
let ext = extract_expr("opcode", &field.ranges, word_type, unit_bytes, endian);
let expr = apply_transforms(&ext, &field.resolved_type, type_maps);
writeln!(
out,
"{} insn.{}.{} = {};",
pad, instr.name, field.name, expr
)
.unwrap();
}
writeln!(out, "{} return insn;", pad).unwrap();
writeln!(out, "{}}}", pad).unwrap();
}
}
fn emit_format_function(
out: &mut String,
def: &ValidatedDef,
_type_maps: &HashMap<String, String>,
_opts: &CppOptions,
) {
writeln!(out, "inline std::string format(const Instruction& insn) {{").unwrap();
writeln!(out, " switch (insn.opcode) {{").unwrap();
for instr in &def.instructions {
let variant = to_pascal_case(&instr.name);
writeln!(out, " case Opcode::{}: {{", variant).unwrap();
if instr.format_lines.is_empty() {
writeln!(out, " return \"{}\";", instr.name).unwrap();
} else {
emit_format_lines_cpp(out, instr, 2);
}
writeln!(out, " }}").unwrap();
}
writeln!(out, " default: return \"???\";").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
fn emit_format_lines_cpp(out: &mut String, instr: &ValidatedInstruction, indent: usize) {
let pad = " ".repeat(indent);
if instr.format_lines.len() == 1 && instr.format_lines[0].guard.is_none() {
let fl = &instr.format_lines[0];
emit_std_format_call(out, &fl.pieces, instr, &pad);
return;
}
for (i, fl) in instr.format_lines.iter().enumerate() {
if let Some(guard) = &fl.guard {
let guard_code = guard_to_cpp(guard, instr);
if i == 0 {
writeln!(out, "{}if ({}) {{", pad, guard_code).unwrap();
} else {
writeln!(out, "{}}} else if ({}) {{", pad, guard_code).unwrap();
}
emit_std_format_call(out, &fl.pieces, instr, &format!("{} ", pad));
} else {
if i > 0 {
writeln!(out, "{}}} else {{", pad).unwrap();
}
emit_std_format_call(out, &fl.pieces, instr, &format!("{} ", pad));
}
}
if instr.format_lines.len() > 1
|| instr
.format_lines
.first()
.map_or(false, |fl| fl.guard.is_some())
{
writeln!(out, "{}}}", pad).unwrap();
}
}
fn emit_std_format_call(
out: &mut String,
pieces: &[FormatPiece],
instr: &ValidatedInstruction,
pad: &str,
) {
let mut fmt_str = String::new();
let mut args: Vec<String> = Vec::new();
for piece in pieces {
match piece {
FormatPiece::Literal(lit) => {
for ch in lit.chars() {
match ch {
'{' => fmt_str.push_str("{{"),
'}' => fmt_str.push_str("}}"),
_ => fmt_str.push(ch),
}
}
}
FormatPiece::FieldRef { expr, spec } => {
let cpp_expr = expr_to_cpp(expr, instr);
if let Some(spec) = spec {
fmt_str.push_str(&format!("{{:{}}}", translate_std_format_spec(spec)));
} else {
fmt_str.push_str("{}");
}
args.push(cpp_expr);
}
}
}
if args.is_empty() {
writeln!(out, "{}return \"{}\";", pad, fmt_str).unwrap();
} else {
writeln!(
out,
"{}return std::format(\"{}\", {});",
pad,
fmt_str,
args.join(", ")
)
.unwrap();
}
}
fn translate_std_format_spec(spec: &str) -> String {
spec.to_string()
}
fn expr_to_cpp(expr: &FormatExpr, instr: &ValidatedInstruction) -> String {
match expr {
FormatExpr::Field(name) => {
format!("insn.{}.{}", instr.name, name)
}
FormatExpr::Ternary {
field,
if_nonzero,
if_zero,
} => {
let else_val = if_zero.as_deref().unwrap_or("");
format!(
"(insn.{}.{} ? \"{}\" : \"{}\")",
instr.name, field, if_nonzero, else_val
)
}
FormatExpr::Arithmetic { left, op, right } => {
let l = expr_to_cpp(left, instr);
let r = expr_to_cpp(right, instr);
let op_str = match op {
ArithOp::Add => "+",
ArithOp::Sub => "-",
ArithOp::Mul => "*",
ArithOp::Div => "/",
ArithOp::Mod => "%",
};
format!("({} {} {})", l, op_str, r)
}
FormatExpr::IntLiteral(val) => format!("{}", val),
FormatExpr::MapCall { map_name, args } => {
let arg_strs: Vec<String> = args.iter().map(|a| expr_to_cpp(a, instr)).collect();
format!("{}({})", map_name, arg_strs.join(", "))
}
FormatExpr::BuiltinCall { func, args } => {
let arg_strs: Vec<String> = args.iter().map(|a| expr_to_cpp(a, instr)).collect();
match func {
BuiltinFunc::RotateRight => {
format!(
"((static_cast<uint32_t>({}) >> {}) | (static_cast<uint32_t>({}) << (32 - {})))",
arg_strs.first().map(|s| s.as_str()).unwrap_or("0"),
arg_strs.get(1).map(|s| s.as_str()).unwrap_or("0"),
arg_strs.first().map(|s| s.as_str()).unwrap_or("0"),
arg_strs.get(1).map(|s| s.as_str()).unwrap_or("0"),
)
}
BuiltinFunc::RotateLeft => {
format!(
"((static_cast<uint32_t>({}) << {}) | (static_cast<uint32_t>({}) >> (32 - {})))",
arg_strs.first().map(|s| s.as_str()).unwrap_or("0"),
arg_strs.get(1).map(|s| s.as_str()).unwrap_or("0"),
arg_strs.first().map(|s| s.as_str()).unwrap_or("0"),
arg_strs.get(1).map(|s| s.as_str()).unwrap_or("0"),
)
}
}
}
FormatExpr::SubDecoderAccess { field, fragment } => {
format!("insn.{}.{}.{}", instr.name, field, fragment)
}
}
}
fn guard_to_cpp(guard: &Guard, instr: &ValidatedInstruction) -> String {
let conditions: Vec<String> = guard
.conditions
.iter()
.map(|cond| {
let left = guard_operand_cpp(&cond.left, instr);
let right = guard_operand_cpp(&cond.right, instr);
let op = match cond.op {
CompareOp::Eq => "==",
CompareOp::Ne => "!=",
CompareOp::Lt => "<",
CompareOp::Le => "<=",
CompareOp::Gt => ">",
CompareOp::Ge => ">=",
};
format!("{} {} {}", left, op, right)
})
.collect();
conditions.join(" && ")
}
fn guard_operand_cpp(operand: &GuardOperand, instr: &ValidatedInstruction) -> String {
match operand {
GuardOperand::Field(name) => format!("insn.{}.{}", instr.name, name),
GuardOperand::Literal(val) => format!("{}", val),
GuardOperand::Expr { left, op, right } => {
let l = guard_operand_cpp(left, instr);
let r = guard_operand_cpp(right, instr);
let op_str = match op {
ArithOp::Add => "+",
ArithOp::Sub => "-",
ArithOp::Mul => "*",
ArithOp::Div => "/",
ArithOp::Mod => "%",
};
format!("({} {} {})", l, op_str, r)
}
}
}