Skip to main content

chipi_core/
codegen_cpp.rs

1//! C++ code generation from validated definitions and decision trees.
2//!
3//! Generates a single-header C++ file with instruction enum, decode function,
4//! and disassembly formatting.
5
6use std::collections::HashMap;
7use std::fmt::Write;
8
9use crate::config::{CppGuardStyle as GuardStyle, CppOptions};
10use crate::tree::DecodeNode;
11use crate::types::*;
12
13/// Generate a complete C++ header file.
14pub fn generate_cpp_code(
15    def: &ValidatedDef,
16    tree: &DecodeNode,
17    opts: &CppOptions,
18    type_maps: &HashMap<String, String>,
19) -> String {
20    let mut out = String::new();
21
22    let default_ns = to_snake_case(&def.config.name);
23    let ns = opts.namespace.as_deref().unwrap_or(&default_ns);
24    let guard_name = format!("CHIPI_{}_HPP", ns.to_ascii_uppercase());
25    let unit_bytes = def.config.width / 8;
26    let word_type = cpp_word_type(def.config.width);
27    let endian = &def.config.endian;
28    let variable_length = def.instructions.iter().any(|i| i.unit_count() > 1);
29
30    // Header guard
31    match opts.guard_style {
32        GuardStyle::Pragma => writeln!(out, "#pragma once").unwrap(),
33        GuardStyle::Ifndef => {
34            writeln!(out, "#ifndef {}", guard_name).unwrap();
35            writeln!(out, "#define {}", guard_name).unwrap();
36        }
37    }
38    writeln!(out).unwrap();
39    writeln!(
40        out,
41        "// Auto-generated by https://github.com/ioncodes/chipi"
42    )
43    .unwrap();
44    writeln!(out, "// Do not edit.").unwrap();
45    writeln!(out).unwrap();
46
47    // Includes
48    writeln!(out, "#include <cstdint>").unwrap();
49    writeln!(out, "#include <cstddef>").unwrap();
50    writeln!(out, "#include <cstring>").unwrap();
51    writeln!(out, "#include <string>").unwrap();
52    writeln!(out, "#include <optional>").unwrap();
53    writeln!(out, "#include <format>").unwrap();
54    for inc in &opts.includes {
55        writeln!(out, "#include \"{}\"", inc).unwrap();
56    }
57    writeln!(out).unwrap();
58
59    writeln!(out, "namespace {} {{", ns).unwrap();
60    writeln!(out).unwrap();
61
62    // Built-in display wrapper types for type aliases with display hints
63    emit_display_types(&mut out, def, type_maps);
64
65    // Opcode enum
66    emit_opcode_enum(&mut out, def);
67
68    // Instruction struct
69    emit_instruction_struct(&mut out, def, type_maps);
70
71    // Sub-decoder types and functions
72    for sd in &def.sub_decoders {
73        emit_subdecoder(&mut out, sd, def.config.width);
74    }
75
76    // Map functions
77    emit_map_functions(&mut out, def);
78
79    // Decode function
80    emit_decode_function(
81        &mut out,
82        def,
83        tree,
84        word_type,
85        unit_bytes,
86        endian,
87        variable_length,
88        type_maps,
89    );
90
91    // Format function
92    emit_format_function(&mut out, def, type_maps, opts);
93
94    writeln!(out, "}} // namespace {}", ns).unwrap();
95
96    if opts.guard_style == GuardStyle::Ifndef {
97        writeln!(out).unwrap();
98        writeln!(out, "#endif // {}", guard_name).unwrap();
99    }
100
101    out
102}
103
104fn cpp_word_type(width: u32) -> &'static str {
105    match width {
106        8 => "uint8_t",
107        16 => "uint16_t",
108        32 => "uint32_t",
109        _ => "uint32_t",
110    }
111}
112
113fn cpp_signed_type(base: &str) -> &'static str {
114    match base {
115        "u8" | "i8" => "int8_t",
116        "u16" | "i16" => "int16_t",
117        "u32" | "i32" => "int32_t",
118        _ => "int32_t",
119    }
120}
121
122fn cpp_type_for(base: &str) -> &'static str {
123    match base {
124        "bool" => "bool",
125        "u1" | "u2" | "u3" | "u4" | "u5" | "u6" | "u7" | "u8" => "uint8_t",
126        "i8" => "int8_t",
127        "u16" => "uint16_t",
128        "i16" => "int16_t",
129        "u32" => "uint32_t",
130        "i32" => "int32_t",
131        _ => "uint32_t",
132    }
133}
134
135fn type_bits(base: &str) -> u32 {
136    match base {
137        "u8" | "i8" => 8,
138        "u16" | "i16" => 16,
139        "u32" | "i32" => 32,
140        _ => 32,
141    }
142}
143
144fn to_snake_case(name: &str) -> String {
145    let mut result = String::new();
146    for (i, ch) in name.chars().enumerate() {
147        if ch.is_ascii_uppercase() && i > 0 {
148            result.push('_');
149        }
150        result.push(ch.to_ascii_lowercase());
151    }
152    result
153}
154
155fn to_pascal_case(name: &str) -> String {
156    let mut result = String::new();
157    let mut cap_next = true;
158    for ch in name.chars() {
159        if ch == '_' {
160            cap_next = true;
161        } else if cap_next {
162            result.push(ch.to_ascii_uppercase());
163            cap_next = false;
164        } else {
165            result.push(ch.to_ascii_lowercase());
166        }
167    }
168    result
169}
170
171/// Emit built-in wrapper types for type aliases with `display(hex)` or `display(signed_hex)`.
172/// Only emits types that aren't already overridden via `type_map`.
173fn emit_display_types(out: &mut String, def: &ValidatedDef, type_maps: &HashMap<String, String>) {
174    let mut need_signed_hex = false;
175    let mut need_hex = false;
176
177    for alias in &def.type_aliases {
178        // Skip if the user provided a type_map override
179        if type_maps.contains_key(&alias.name) {
180            continue;
181        }
182        match alias.display_format {
183            Some(DisplayFormat::SignedHex) => need_signed_hex = true,
184            Some(DisplayFormat::Hex) => need_hex = true,
185            None => {}
186        }
187    }
188
189    if need_signed_hex {
190        writeln!(out, "struct SignedHex {{").unwrap();
191        writeln!(out, "    int32_t value;").unwrap();
192        writeln!(out, "    SignedHex() = default;").unwrap();
193        writeln!(out, "    constexpr SignedHex(int32_t v) : value(v) {{}}").unwrap();
194        writeln!(
195            out,
196            "    bool operator==(const SignedHex&) const = default;"
197        )
198        .unwrap();
199        writeln!(
200            out,
201            "    bool operator==(int other) const {{ return value == other; }}"
202        )
203        .unwrap();
204        writeln!(
205            out,
206            "    bool operator!=(int other) const {{ return value != other; }}"
207        )
208        .unwrap();
209        writeln!(
210            out,
211            "    bool operator<(int other) const {{ return value < other; }}"
212        )
213        .unwrap();
214        writeln!(
215            out,
216            "    bool operator<=(int other) const {{ return value <= other; }}"
217        )
218        .unwrap();
219        writeln!(
220            out,
221            "    bool operator>(int other) const {{ return value > other; }}"
222        )
223        .unwrap();
224        writeln!(
225            out,
226            "    bool operator>=(int other) const {{ return value >= other; }}"
227        )
228        .unwrap();
229        writeln!(
230            out,
231            "    SignedHex operator-() const {{ return SignedHex(-value); }}"
232        )
233        .unwrap();
234        writeln!(out, "    friend SignedHex operator-(int lhs, SignedHex rhs) {{ return SignedHex(lhs - rhs.value); }}").unwrap();
235        writeln!(out, "    friend SignedHex operator+(int lhs, SignedHex rhs) {{ return SignedHex(lhs + rhs.value); }}").unwrap();
236        writeln!(out, "    friend SignedHex operator+(SignedHex lhs, int rhs) {{ return SignedHex(lhs.value + rhs); }}").unwrap();
237        writeln!(out, "    friend SignedHex operator-(SignedHex lhs, int rhs) {{ return SignedHex(lhs.value - rhs); }}").unwrap();
238        writeln!(out, "    friend SignedHex operator*(SignedHex lhs, int rhs) {{ return SignedHex(lhs.value * rhs); }}").unwrap();
239        writeln!(out, "}};").unwrap();
240        writeln!(out).unwrap();
241    }
242
243    if need_hex {
244        writeln!(out, "struct Hex {{").unwrap();
245        writeln!(out, "    uint32_t value;").unwrap();
246        writeln!(out, "    Hex() = default;").unwrap();
247        writeln!(out, "    constexpr Hex(uint32_t v) : value(v) {{}}").unwrap();
248        writeln!(out, "    bool operator==(const Hex&) const = default;").unwrap();
249        writeln!(
250            out,
251            "    bool operator==(unsigned other) const {{ return value == other; }}"
252        )
253        .unwrap();
254        writeln!(out, "}};").unwrap();
255        writeln!(out).unwrap();
256    }
257
258    writeln!(out, "}} // namespace {}", to_snake_case(&def.config.name)).unwrap();
259    writeln!(out).unwrap();
260
261    // std::formatter specializations must be outside the namespace
262    if need_signed_hex {
263        let ns = to_snake_case(&def.config.name);
264        writeln!(
265            out,
266            "template <> struct std::formatter<{}::SignedHex> : std::formatter<std::string> {{",
267            ns
268        )
269        .unwrap();
270        writeln!(
271            out,
272            "    auto format({}::SignedHex v, auto& ctx) const {{",
273            ns
274        )
275        .unwrap();
276        writeln!(out, "        if (v.value < 0)").unwrap();
277        writeln!(out, "            return std::formatter<std::string>::format(std::format(\"-0x{{:x}}\", static_cast<unsigned>(-v.value)), ctx);").unwrap();
278        writeln!(out, "        return std::formatter<std::string>::format(std::format(\"0x{{:x}}\", static_cast<unsigned>(v.value)), ctx);").unwrap();
279        writeln!(out, "    }}").unwrap();
280        writeln!(out, "}};").unwrap();
281        writeln!(out).unwrap();
282    }
283
284    if need_hex {
285        let ns = to_snake_case(&def.config.name);
286        writeln!(
287            out,
288            "template <> struct std::formatter<{}::Hex> : std::formatter<std::string> {{",
289            ns
290        )
291        .unwrap();
292        writeln!(out, "    auto format({}::Hex v, auto& ctx) const {{", ns).unwrap();
293        writeln!(out, "        return std::formatter<std::string>::format(std::format(\"0x{{:x}}\", v.value), ctx);").unwrap();
294        writeln!(out, "    }}").unwrap();
295        writeln!(out, "}};").unwrap();
296        writeln!(out).unwrap();
297    }
298
299    // Re-open the namespace
300    if need_signed_hex || need_hex {
301        writeln!(out, "namespace {} {{", to_snake_case(&def.config.name)).unwrap();
302        writeln!(out).unwrap();
303    }
304}
305
306/// Emit the Opcode enum.
307fn emit_opcode_enum(out: &mut String, def: &ValidatedDef) {
308    writeln!(out, "enum class Opcode : uint32_t {{").unwrap();
309    for (i, instr) in def.instructions.iter().enumerate() {
310        writeln!(out, "    {} = {},", to_pascal_case(&instr.name), i).unwrap();
311    }
312    writeln!(out, "}};").unwrap();
313    writeln!(out).unwrap();
314}
315
316/// Emit the Instruction struct with a tagged union for fields.
317fn emit_instruction_struct(
318    out: &mut String,
319    def: &ValidatedDef,
320    type_maps: &HashMap<String, String>,
321) {
322    writeln!(out, "struct Instruction {{").unwrap();
323    writeln!(out, "    Opcode opcode;").unwrap();
324    writeln!(out, "    uint32_t size; // bytes consumed").unwrap();
325    writeln!(out).unwrap();
326
327    // Generate a union with per-instruction field structs
328    let has_fields = def
329        .instructions
330        .iter()
331        .any(|i| !i.resolved_fields.is_empty());
332    if has_fields {
333        writeln!(out, "    union {{").unwrap();
334        for instr in &def.instructions {
335            if instr.resolved_fields.is_empty() {
336                continue;
337            }
338            writeln!(out, "        struct {{").unwrap();
339            for field in &instr.resolved_fields {
340                let cpp_type = field_cpp_type(field, type_maps);
341                writeln!(out, "            {} {};", cpp_type, field.name).unwrap();
342            }
343            writeln!(out, "        }} {};", instr.name).unwrap();
344        }
345        writeln!(out, "    }};").unwrap();
346    }
347
348    writeln!(out, "}};").unwrap();
349    writeln!(out).unwrap();
350}
351
352/// Get the C++ type for a field.
353fn field_cpp_type(field: &ResolvedField, type_maps: &HashMap<String, String>) -> String {
354    // Check type map first (user override)
355    if let Some(alias) = &field.resolved_type.alias_name {
356        if let Some(mapped) = type_maps.get(alias) {
357            return mapped.clone();
358        }
359    }
360
361    // Check sub-decoder
362    if let Some(ref sd_name) = field.resolved_type.sub_decoder {
363        return format!("{}Insn", to_pascal_case(sd_name));
364    }
365
366    // Check display format -> built-in wrapper types
367    match field.resolved_type.display_format {
368        Some(DisplayFormat::SignedHex) => return "SignedHex".to_string(),
369        Some(DisplayFormat::Hex) => return "Hex".to_string(),
370        None => {}
371    }
372
373    cpp_type_for(&field.resolved_type.base_type).to_string()
374}
375
376/// Emit sub-decoder struct and dispatch function.
377fn emit_subdecoder(out: &mut String, sd: &ValidatedSubDecoder, _parent_width: u32) {
378    let type_name = format!("{}Insn", to_pascal_case(&sd.name));
379    let word_type = cpp_word_type(sd.width);
380
381    // Fragment struct
382    writeln!(out, "struct {} {{", type_name).unwrap();
383    for frag_name in &sd.fragment_names {
384        writeln!(out, "    const char* {};", frag_name).unwrap();
385    }
386    writeln!(out, "}};").unwrap();
387    writeln!(out).unwrap();
388
389    // Pre-baked fragment strings for each instruction
390    // Then dispatch function
391    let fn_name = format!("decode_{}", to_snake_case(&sd.name));
392    writeln!(
393        out,
394        "inline std::optional<{}> {}({} val) {{",
395        type_name, fn_name, word_type
396    )
397    .unwrap();
398
399    for (i, instr) in sd.instructions.iter().enumerate() {
400        let (mask, value) = compute_instruction_mask_value_sub(instr);
401        let keyword = if i == 0 { "if" } else { "} else if" };
402        writeln!(
403            out,
404            "    {} ((val & {:#x}) == {:#x}) {{",
405            keyword, mask, value
406        )
407        .unwrap();
408
409        // Build fragment values
410        // For simplicity, use string literals where possible; for field-dependent
411        // fragments, generate inline formatting
412        for frag in &instr.fragments {
413            let all_literal = frag
414                .pieces
415                .iter()
416                .all(|p| matches!(p, FormatPiece::Literal(_)));
417            if all_literal {
418                let s: String = frag
419                    .pieces
420                    .iter()
421                    .map(|p| {
422                        if let FormatPiece::Literal(lit) = p {
423                            lit.as_str()
424                        } else {
425                            ""
426                        }
427                    })
428                    .collect();
429                writeln!(out, "        // {}.{} = \"{}\"", instr.name, frag.name, s).unwrap();
430            }
431        }
432
433        // Return struct with fragment values
434        let frag_values: Vec<String> = instr
435            .fragments
436            .iter()
437            .map(|frag| {
438                let all_literal = frag
439                    .pieces
440                    .iter()
441                    .all(|p| matches!(p, FormatPiece::Literal(_)));
442                if all_literal {
443                    let s: String = frag
444                        .pieces
445                        .iter()
446                        .map(|p| {
447                            if let FormatPiece::Literal(lit) = p {
448                                lit.as_str()
449                            } else {
450                                ""
451                            }
452                        })
453                        .collect();
454                    format!("\"{}\"", s)
455                } else {
456                    // For dynamic fragments, we'd need snprintf; use empty for now
457                    "\"\"".to_string()
458                }
459            })
460            .collect();
461
462        writeln!(
463            out,
464            "        return {} {{ {} }};",
465            type_name,
466            frag_values.join(", ")
467        )
468        .unwrap();
469    }
470
471    if !sd.instructions.is_empty() {
472        writeln!(out, "    }}").unwrap();
473    }
474    writeln!(out, "    return std::nullopt;").unwrap();
475    writeln!(out, "}}").unwrap();
476    writeln!(out).unwrap();
477}
478
479fn compute_instruction_mask_value_sub(instr: &ValidatedSubInstruction) -> (u64, u64) {
480    let mut mask: u64 = 0;
481    let mut value: u64 = 0;
482    for seg in &instr.segments {
483        if let Segment::Fixed {
484            ranges, pattern, ..
485        } = seg
486        {
487            let mut bit_idx = 0;
488            for range in ranges {
489                for i in 0..range.width() {
490                    if bit_idx < pattern.len() {
491                        let bit = pattern[bit_idx];
492                        if bit != Bit::Wildcard {
493                            let hw_bit = range.start - i;
494                            mask |= 1u64 << hw_bit;
495                            if bit == Bit::One {
496                                value |= 1u64 << hw_bit;
497                            }
498                        }
499                        bit_idx += 1;
500                    }
501                }
502            }
503        }
504    }
505    (mask, value)
506}
507
508/// Emit map (lookup) functions.
509fn emit_map_functions(out: &mut String, def: &ValidatedDef) {
510    for map_def in &def.maps {
511        let params: Vec<String> = map_def
512            .params
513            .iter()
514            .map(|p| format!("int {}", p))
515            .collect();
516        writeln!(
517            out,
518            "inline const char* {}({}) {{",
519            map_def.name,
520            params.join(", ")
521        )
522        .unwrap();
523
524        let key_var = if map_def.params.len() == 1 {
525            map_def.params[0].clone()
526        } else {
527            // Multi-param: won't use switch
528            String::new()
529        };
530
531        if map_def.params.len() == 1 {
532            writeln!(out, "    switch ({}) {{", key_var).unwrap();
533            for entry in &map_def.entries {
534                if entry.keys.len() == 1 && entry.keys[0] == MapKey::Wildcard {
535                    continue;
536                }
537                if let Some(MapKey::Value(v)) = entry.keys.first() {
538                    let s = pieces_to_str(&entry.output);
539                    writeln!(out, "    case {}: return \"{}\";", v, s).unwrap();
540                }
541            }
542
543            let default_str = map_def
544                .entries
545                .iter()
546                .find(|e| e.keys.len() == 1 && e.keys[0] == MapKey::Wildcard)
547                .map(|e| pieces_to_str(&e.output))
548                .unwrap_or_else(|| "???".to_string());
549            writeln!(out, "    default: return \"{}\";", default_str).unwrap();
550            writeln!(out, "    }}").unwrap();
551        } else {
552            // Fallback for multi-param maps: if/else chain
553            for entry in &map_def.entries {
554                if entry.keys.iter().any(|k| *k == MapKey::Wildcard) {
555                    continue;
556                }
557                let conds: Vec<String> = entry
558                    .keys
559                    .iter()
560                    .zip(map_def.params.iter())
561                    .map(|(k, p)| {
562                        if let MapKey::Value(v) = k {
563                            format!("{} == {}", p, v)
564                        } else {
565                            "true".to_string()
566                        }
567                    })
568                    .collect();
569                let s = pieces_to_str(&entry.output);
570                writeln!(out, "    if ({}) return \"{}\";", conds.join(" && "), s).unwrap();
571            }
572            writeln!(out, "    return \"???\";").unwrap();
573        }
574
575        writeln!(out, "}}").unwrap();
576        writeln!(out).unwrap();
577    }
578
579    // Also emit sub-decoder maps
580    for sd in &def.sub_decoders {
581        for map_def in &sd.maps {
582            let params: Vec<String> = map_def
583                .params
584                .iter()
585                .map(|p| format!("int {}", p))
586                .collect();
587            writeln!(
588                out,
589                "inline const char* {}({}) {{",
590                map_def.name,
591                params.join(", ")
592            )
593            .unwrap();
594            writeln!(out, "    switch ({}) {{", map_def.params[0]).unwrap();
595            for entry in &map_def.entries {
596                if entry.keys.len() == 1 && entry.keys[0] == MapKey::Wildcard {
597                    continue;
598                }
599                if let Some(MapKey::Value(v)) = entry.keys.first() {
600                    let s = pieces_to_str(&entry.output);
601                    writeln!(out, "    case {}: return \"{}\";", v, s).unwrap();
602                }
603            }
604            let default_str = map_def
605                .entries
606                .iter()
607                .find(|e| e.keys.len() == 1 && e.keys[0] == MapKey::Wildcard)
608                .map(|e| pieces_to_str(&e.output))
609                .unwrap_or_else(|| "???".to_string());
610            writeln!(out, "    default: return \"{}\";", default_str).unwrap();
611            writeln!(out, "    }}").unwrap();
612            writeln!(out, "}}").unwrap();
613            writeln!(out).unwrap();
614        }
615    }
616}
617
618fn pieces_to_str(pieces: &[FormatPiece]) -> String {
619    let mut s = String::new();
620    for piece in pieces {
621        if let FormatPiece::Literal(lit) = piece {
622            s.push_str(lit);
623        }
624    }
625    s
626}
627
628/// Emit the decode function.
629fn emit_decode_function(
630    out: &mut String,
631    def: &ValidatedDef,
632    tree: &DecodeNode,
633    word_type: &str,
634    unit_bytes: u32,
635    endian: &ByteEndian,
636    variable_length: bool,
637    type_maps: &HashMap<String, String>,
638) {
639    writeln!(
640        out,
641        "inline std::optional<Instruction> decode(const uint8_t* data, size_t len) {{"
642    )
643    .unwrap();
644    writeln!(out, "    if (len < {}) return std::nullopt;", unit_bytes).unwrap();
645
646    // Read first unit
647    emit_word_read(out, "opcode", word_type, 0, unit_bytes, endian, 1);
648
649    emit_tree_cpp(
650        out,
651        tree,
652        def,
653        1,
654        word_type,
655        unit_bytes,
656        endian,
657        variable_length,
658        type_maps,
659    );
660
661    writeln!(out, "}}").unwrap();
662    writeln!(out).unwrap();
663}
664
665/// Emit a word read from the data buffer.
666fn emit_word_read(
667    out: &mut String,
668    var_name: &str,
669    word_type: &str,
670    offset: u32,
671    unit_bytes: u32,
672    endian: &ByteEndian,
673    indent: usize,
674) {
675    let pad = "    ".repeat(indent);
676    match (unit_bytes, endian) {
677        (1, _) => {
678            writeln!(out, "{}{} {} = data[{}];", pad, word_type, var_name, offset).unwrap();
679        }
680        (2, ByteEndian::Big) => {
681            writeln!(
682                out,
683                "{}{} {} = (static_cast<uint16_t>(data[{}]) << 8) | data[{}];",
684                pad,
685                word_type,
686                var_name,
687                offset,
688                offset + 1
689            )
690            .unwrap();
691        }
692        (2, ByteEndian::Little) => {
693            writeln!(
694                out,
695                "{}{} {} = data[{}] | (static_cast<uint16_t>(data[{}]) << 8);",
696                pad,
697                word_type,
698                var_name,
699                offset,
700                offset + 1
701            )
702            .unwrap();
703        }
704        (4, ByteEndian::Big) => {
705            writeln!(
706                out,
707                "{}{} {} = (static_cast<uint32_t>(data[{}]) << 24) | (static_cast<uint32_t>(data[{}]) << 16) | (static_cast<uint32_t>(data[{}]) << 8) | data[{}];",
708                pad, word_type, var_name, offset, offset + 1, offset + 2, offset + 3
709            ).unwrap();
710        }
711        (4, ByteEndian::Little) => {
712            writeln!(
713                out,
714                "{}{} {} = data[{}] | (static_cast<uint32_t>(data[{}]) << 8) | (static_cast<uint32_t>(data[{}]) << 16) | (static_cast<uint32_t>(data[{}]) << 24);",
715                pad, word_type, var_name, offset, offset + 1, offset + 2, offset + 3
716            ).unwrap();
717        }
718        _ => {
719            writeln!(out, "{}// unsupported width", pad).unwrap();
720        }
721    }
722}
723
724/// Unit read expression (inline).
725fn unit_read_expr(unit: u32, _word_type: &str, unit_bytes: u32, endian: &ByteEndian) -> String {
726    if unit == 0 {
727        return "opcode".to_string();
728    }
729    let offset = unit * unit_bytes;
730    match (unit_bytes, endian) {
731        (1, _) => format!("data[{}]", offset),
732        (2, ByteEndian::Big) => format!(
733            "(static_cast<uint16_t>(data[{}]) << 8 | data[{}])",
734            offset,
735            offset + 1
736        ),
737        (2, ByteEndian::Little) => format!(
738            "(data[{}] | static_cast<uint16_t>(data[{}]) << 8)",
739            offset,
740            offset + 1
741        ),
742        (4, ByteEndian::Big) => format!(
743            "(static_cast<uint32_t>(data[{}]) << 24 | static_cast<uint32_t>(data[{}]) << 16 | static_cast<uint32_t>(data[{}]) << 8 | data[{}])",
744            offset,
745            offset + 1,
746            offset + 2,
747            offset + 3
748        ),
749        (4, ByteEndian::Little) => format!(
750            "(data[{}] | static_cast<uint32_t>(data[{}]) << 8 | static_cast<uint32_t>(data[{}]) << 16 | static_cast<uint32_t>(data[{}]) << 24)",
751            offset,
752            offset + 1,
753            offset + 2,
754            offset + 3
755        ),
756        _ => "0".to_string(),
757    }
758}
759
760/// Extract bits expression.
761fn extract_expr(
762    var: &str,
763    ranges: &[BitRange],
764    word_type: &str,
765    unit_bytes: u32,
766    endian: &ByteEndian,
767) -> String {
768    if ranges.is_empty() {
769        return "0".to_string();
770    }
771
772    if ranges.len() == 1 {
773        let range = ranges[0];
774        let source = if range.unit == 0 {
775            var.to_string()
776        } else {
777            unit_read_expr(range.unit, word_type, unit_bytes, endian)
778        };
779        let width = range.width();
780        let shift = range.end;
781        let mask = (1u64 << width) - 1;
782        if shift == 0 {
783            format!("({} & {:#x})", source, mask)
784        } else {
785            format!("(({} >> {}) & {:#x})", source, shift, mask)
786        }
787    } else {
788        let mut parts = Vec::new();
789        let mut accumulated = 0u32;
790        for range in ranges {
791            let source = if range.unit == 0 {
792                var.to_string()
793            } else {
794                unit_read_expr(range.unit, word_type, unit_bytes, endian)
795            };
796            let width = range.width();
797            let shift = range.end;
798            let mask = (1u64 << width) - 1;
799            let extracted = if shift == 0 {
800                format!("({} & {:#x})", source, mask)
801            } else {
802                format!("(({} >> {}) & {:#x})", source, shift, mask)
803            };
804            if accumulated > 0 {
805                parts.push(format!("({} << {})", extracted, accumulated));
806            } else {
807                parts.push(extracted);
808            }
809            accumulated += width;
810        }
811        parts.join(" | ")
812    }
813}
814
815/// Compute mask/value for leaf guard.
816fn leaf_guard(
817    instr: &ValidatedInstruction,
818    word_type: &str,
819    unit_bytes: u32,
820    endian: &ByteEndian,
821) -> Option<String> {
822    let fixed_bits = instr.fixed_bits();
823    if fixed_bits.is_empty() {
824        return None;
825    }
826
827    let mut units_map: HashMap<u32, Vec<(u32, Bit)>> = HashMap::new();
828    for (unit, hw_bit, bit) in fixed_bits {
829        units_map.entry(unit).or_default().push((hw_bit, bit));
830    }
831
832    let mut conditions = Vec::new();
833    for (unit, bits) in &units_map {
834        let (mask, value) = compute_mask_value(bits);
835        if mask != 0 {
836            let source = if *unit == 0 {
837                "opcode".to_string()
838            } else {
839                unit_read_expr(*unit, word_type, unit_bytes, endian)
840            };
841            conditions.push(format!("({} & {:#x}) == {:#x}", source, mask, value));
842        }
843    }
844
845    if conditions.is_empty() {
846        None
847    } else {
848        Some(conditions.join(" && "))
849    }
850}
851
852fn compute_mask_value(fixed_bits: &[(u32, Bit)]) -> (u64, u64) {
853    let mut mask: u64 = 0;
854    let mut value: u64 = 0;
855    for &(bit_pos, bit_val) in fixed_bits {
856        if bit_val == Bit::Wildcard {
857            continue;
858        }
859        mask |= 1u64 << bit_pos;
860        if bit_val == Bit::One {
861            value |= 1u64 << bit_pos;
862        }
863    }
864    (mask, value)
865}
866
867/// Apply transforms to an extraction expression.
868fn apply_transforms(
869    extract: &str,
870    resolved: &ResolvedFieldType,
871    type_maps: &HashMap<String, String>,
872) -> String {
873    let mut expr = extract.to_string();
874
875    for transform in &resolved.transforms {
876        match transform {
877            Transform::SignExtend(n) => {
878                let signed = cpp_signed_type(&resolved.base_type);
879                let bits = type_bits(&resolved.base_type);
880                expr = format!(
881                    "static_cast<{}>(static_cast<{}>(({}) << ({} - {})) >> ({} - {}))",
882                    cpp_type_for(&resolved.base_type),
883                    signed,
884                    expr,
885                    bits,
886                    n,
887                    bits,
888                    n
889                );
890            }
891            Transform::ZeroExtend(_) => {}
892            Transform::ShiftLeft(n) => {
893                expr = format!("(({}) << {})", expr, n);
894            }
895        }
896    }
897
898    // Sub-decoder dispatch
899    if let Some(ref sd_name) = resolved.sub_decoder {
900        let decode_fn = format!("decode_{}", to_snake_case(sd_name));
901        return format!(
902            "{}(static_cast<{}>({})).value()",
903            decode_fn,
904            cpp_word_type(type_bits(&resolved.base_type).min(32)),
905            expr
906        );
907    }
908
909    // Type map wrapper (user override)
910    if let Some(alias) = &resolved.alias_name {
911        if let Some(mapped) = type_maps.get(alias) {
912            return format!("static_cast<{}>({})", mapped, expr);
913        }
914    }
915
916    // Display format -> built-in wrapper types
917    match resolved.display_format {
918        Some(DisplayFormat::SignedHex) => {
919            return format!("SignedHex(static_cast<int32_t>({}))", expr);
920        }
921        Some(DisplayFormat::Hex) => {
922            return format!("Hex(static_cast<uint32_t>({}))", expr);
923        }
924        None => {}
925    }
926
927    if resolved.base_type == "bool" {
928        format!("({}) != 0", expr)
929    } else {
930        format!(
931            "static_cast<{}>({})",
932            cpp_type_for(&resolved.base_type),
933            expr
934        )
935    }
936}
937
938/// Emit the decision tree as C++ switch/if-else.
939fn emit_tree_cpp(
940    out: &mut String,
941    node: &DecodeNode,
942    def: &ValidatedDef,
943    indent: usize,
944    word_type: &str,
945    unit_bytes: u32,
946    endian: &ByteEndian,
947    variable_length: bool,
948    type_maps: &HashMap<String, String>,
949) {
950    let pad = "    ".repeat(indent);
951    match node {
952        DecodeNode::Leaf { instruction_index } => {
953            let instr = &def.instructions[*instruction_index];
954            if let Some(guard) = leaf_guard(instr, word_type, unit_bytes, endian) {
955                writeln!(out, "{}if ({}) {{", pad, guard).unwrap();
956                emit_return_instruction(
957                    out,
958                    instr,
959                    indent + 1,
960                    word_type,
961                    unit_bytes,
962                    endian,
963                    variable_length,
964                    type_maps,
965                );
966                writeln!(out, "{}}} else {{", pad).unwrap();
967                writeln!(out, "{}    return std::nullopt;", pad).unwrap();
968                writeln!(out, "{}}}", pad).unwrap();
969            } else {
970                emit_return_instruction(
971                    out,
972                    instr,
973                    indent,
974                    word_type,
975                    unit_bytes,
976                    endian,
977                    variable_length,
978                    type_maps,
979                );
980            }
981        }
982        DecodeNode::PriorityLeaves { candidates } => {
983            for (i, &idx) in candidates.iter().enumerate() {
984                let instr = &def.instructions[idx];
985                let guard = leaf_guard(instr, word_type, unit_bytes, endian);
986                if i == 0 {
987                    if let Some(g) = guard {
988                        writeln!(out, "{}if ({}) {{", pad, g).unwrap();
989                        emit_return_instruction(
990                            out,
991                            instr,
992                            indent + 1,
993                            word_type,
994                            unit_bytes,
995                            endian,
996                            variable_length,
997                            type_maps,
998                        );
999                    } else {
1000                        emit_return_instruction(
1001                            out,
1002                            instr,
1003                            indent,
1004                            word_type,
1005                            unit_bytes,
1006                            endian,
1007                            variable_length,
1008                            type_maps,
1009                        );
1010                        break;
1011                    }
1012                } else if i == candidates.len() - 1 {
1013                    if let Some(g) = guard {
1014                        writeln!(out, "{}}} else if ({}) {{", pad, g).unwrap();
1015                        emit_return_instruction(
1016                            out,
1017                            instr,
1018                            indent + 1,
1019                            word_type,
1020                            unit_bytes,
1021                            endian,
1022                            variable_length,
1023                            type_maps,
1024                        );
1025                        writeln!(out, "{}}} else {{", pad).unwrap();
1026                        writeln!(out, "{}    return std::nullopt;", pad).unwrap();
1027                        writeln!(out, "{}}}", pad).unwrap();
1028                    } else {
1029                        writeln!(out, "{}}} else {{", pad).unwrap();
1030                        emit_return_instruction(
1031                            out,
1032                            instr,
1033                            indent + 1,
1034                            word_type,
1035                            unit_bytes,
1036                            endian,
1037                            variable_length,
1038                            type_maps,
1039                        );
1040                        writeln!(out, "{}}}", pad).unwrap();
1041                    }
1042                } else {
1043                    let g = guard.unwrap_or_else(|| "true".to_string());
1044                    writeln!(out, "{}}} else if ({}) {{", pad, g).unwrap();
1045                    emit_return_instruction(
1046                        out,
1047                        instr,
1048                        indent + 1,
1049                        word_type,
1050                        unit_bytes,
1051                        endian,
1052                        variable_length,
1053                        type_maps,
1054                    );
1055                }
1056            }
1057        }
1058        DecodeNode::Fail => {
1059            writeln!(out, "{}return std::nullopt;", pad).unwrap();
1060        }
1061        DecodeNode::Branch {
1062            range,
1063            arms,
1064            default,
1065        } => {
1066            let ext = extract_expr("opcode", &[*range], word_type, unit_bytes, endian);
1067            writeln!(out, "{}switch ({}) {{", pad, ext).unwrap();
1068            for (value, child) in arms {
1069                writeln!(out, "{}case {:#x}: {{", pad, value).unwrap();
1070                emit_tree_cpp(
1071                    out,
1072                    child,
1073                    def,
1074                    indent + 1,
1075                    word_type,
1076                    unit_bytes,
1077                    endian,
1078                    variable_length,
1079                    type_maps,
1080                );
1081                writeln!(out, "{}    break;", pad).unwrap();
1082                writeln!(out, "{}}}", pad).unwrap();
1083            }
1084            writeln!(out, "{}default: {{", pad).unwrap();
1085            emit_tree_cpp(
1086                out,
1087                default,
1088                def,
1089                indent + 1,
1090                word_type,
1091                unit_bytes,
1092                endian,
1093                variable_length,
1094                type_maps,
1095            );
1096            writeln!(out, "{}    break;", pad).unwrap();
1097            writeln!(out, "{}}}", pad).unwrap();
1098            writeln!(out, "{}}}", pad).unwrap();
1099        }
1100    }
1101}
1102
1103/// Emit code to return a decoded Instruction.
1104fn emit_return_instruction(
1105    out: &mut String,
1106    instr: &ValidatedInstruction,
1107    indent: usize,
1108    word_type: &str,
1109    unit_bytes: u32,
1110    endian: &ByteEndian,
1111    variable_length: bool,
1112    type_maps: &HashMap<String, String>,
1113) {
1114    let pad = "    ".repeat(indent);
1115    let unit_count = instr.unit_count();
1116    let bytes_consumed = unit_count * unit_bytes;
1117    let variant = to_pascal_case(&instr.name);
1118
1119    if variable_length && unit_count > 1 {
1120        writeln!(
1121            out,
1122            "{}if (len < {}) return std::nullopt;",
1123            pad, bytes_consumed
1124        )
1125        .unwrap();
1126    }
1127
1128    if instr.resolved_fields.is_empty() {
1129        writeln!(
1130            out,
1131            "{}return Instruction {{ Opcode::{}, {} }};",
1132            pad, variant, bytes_consumed
1133        )
1134        .unwrap();
1135    } else {
1136        writeln!(out, "{}{{", pad).unwrap();
1137        writeln!(out, "{}    Instruction insn{{}};", pad).unwrap();
1138        writeln!(out, "{}    insn.opcode = Opcode::{};", pad, variant).unwrap();
1139        writeln!(out, "{}    insn.size = {};", pad, bytes_consumed).unwrap();
1140        for field in &instr.resolved_fields {
1141            let ext = extract_expr("opcode", &field.ranges, word_type, unit_bytes, endian);
1142            let expr = apply_transforms(&ext, &field.resolved_type, type_maps);
1143            writeln!(
1144                out,
1145                "{}    insn.{}.{} = {};",
1146                pad, instr.name, field.name, expr
1147            )
1148            .unwrap();
1149        }
1150        writeln!(out, "{}    return insn;", pad).unwrap();
1151        writeln!(out, "{}}}", pad).unwrap();
1152    }
1153}
1154
1155/// Emit the format/disassemble function using std::format.
1156fn emit_format_function(
1157    out: &mut String,
1158    def: &ValidatedDef,
1159    _type_maps: &HashMap<String, String>,
1160    _opts: &CppOptions,
1161) {
1162    writeln!(out, "inline std::string format(const Instruction& insn) {{").unwrap();
1163    writeln!(out, "    switch (insn.opcode) {{").unwrap();
1164
1165    for instr in &def.instructions {
1166        let variant = to_pascal_case(&instr.name);
1167        writeln!(out, "    case Opcode::{}: {{", variant).unwrap();
1168
1169        if instr.format_lines.is_empty() {
1170            writeln!(out, "        return \"{}\";", instr.name).unwrap();
1171        } else {
1172            emit_format_lines_cpp(out, instr, 2);
1173        }
1174
1175        writeln!(out, "    }}").unwrap();
1176    }
1177
1178    writeln!(out, "    default: return \"???\";").unwrap();
1179    writeln!(out, "    }}").unwrap();
1180    writeln!(out, "}}").unwrap();
1181    writeln!(out).unwrap();
1182}
1183
1184/// Emit format lines for a single instruction using std::format.
1185fn emit_format_lines_cpp(out: &mut String, instr: &ValidatedInstruction, indent: usize) {
1186    let pad = "    ".repeat(indent);
1187
1188    if instr.format_lines.len() == 1 && instr.format_lines[0].guard.is_none() {
1189        let fl = &instr.format_lines[0];
1190        emit_std_format_call(out, &fl.pieces, instr, &pad);
1191        return;
1192    }
1193
1194    for (i, fl) in instr.format_lines.iter().enumerate() {
1195        if let Some(guard) = &fl.guard {
1196            let guard_code = guard_to_cpp(guard, instr);
1197            if i == 0 {
1198                writeln!(out, "{}if ({}) {{", pad, guard_code).unwrap();
1199            } else {
1200                writeln!(out, "{}}} else if ({}) {{", pad, guard_code).unwrap();
1201            }
1202            emit_std_format_call(out, &fl.pieces, instr, &format!("{}    ", pad));
1203        } else {
1204            if i > 0 {
1205                writeln!(out, "{}}} else {{", pad).unwrap();
1206            }
1207            emit_std_format_call(out, &fl.pieces, instr, &format!("{}    ", pad));
1208        }
1209    }
1210
1211    if instr.format_lines.len() > 1
1212        || instr
1213            .format_lines
1214            .first()
1215            .map_or(false, |fl| fl.guard.is_some())
1216    {
1217        writeln!(out, "{}}}", pad).unwrap();
1218    }
1219}
1220
1221/// Emit a return std::format(...) call for format pieces.
1222/// Fields are passed as arguments; user types participate via std::formatter specializations.
1223fn emit_std_format_call(
1224    out: &mut String,
1225    pieces: &[FormatPiece],
1226    instr: &ValidatedInstruction,
1227    pad: &str,
1228) {
1229    let mut fmt_str = String::new();
1230    let mut args: Vec<String> = Vec::new();
1231
1232    for piece in pieces {
1233        match piece {
1234            FormatPiece::Literal(lit) => {
1235                // Escape { and } for std::format
1236                for ch in lit.chars() {
1237                    match ch {
1238                        '{' => fmt_str.push_str("{{"),
1239                        '}' => fmt_str.push_str("}}"),
1240                        _ => fmt_str.push(ch),
1241                    }
1242                }
1243            }
1244            FormatPiece::FieldRef { expr, spec } => {
1245                let cpp_expr = expr_to_cpp(expr, instr);
1246                // Build std::format placeholder
1247                if let Some(spec) = spec {
1248                    fmt_str.push_str(&format!("{{:{}}}", translate_std_format_spec(spec)));
1249                } else {
1250                    fmt_str.push_str("{}");
1251                }
1252                args.push(cpp_expr);
1253            }
1254        }
1255    }
1256
1257    if args.is_empty() {
1258        writeln!(out, "{}return \"{}\";", pad, fmt_str).unwrap();
1259    } else {
1260        writeln!(
1261            out,
1262            "{}return std::format(\"{}\", {});",
1263            pad,
1264            fmt_str,
1265            args.join(", ")
1266        )
1267        .unwrap();
1268    }
1269}
1270
1271/// Translate chipi/Rust format specs to std::format specs.
1272/// Most are identical since std::format uses Python-style specs.
1273fn translate_std_format_spec(spec: &str) -> String {
1274    // chipi specs like "04x", "#06x", "#x" are already valid std::format specs
1275    spec.to_string()
1276}
1277
1278/// Convert a FormatExpr to a C++ expression string.
1279fn expr_to_cpp(expr: &FormatExpr, instr: &ValidatedInstruction) -> String {
1280    match expr {
1281        FormatExpr::Field(name) => {
1282            format!("insn.{}.{}", instr.name, name)
1283        }
1284        FormatExpr::Ternary {
1285            field,
1286            if_nonzero,
1287            if_zero,
1288        } => {
1289            let else_val = if_zero.as_deref().unwrap_or("");
1290            format!(
1291                "(insn.{}.{} ? \"{}\" : \"{}\")",
1292                instr.name, field, if_nonzero, else_val
1293            )
1294        }
1295        FormatExpr::Arithmetic { left, op, right } => {
1296            let l = expr_to_cpp(left, instr);
1297            let r = expr_to_cpp(right, instr);
1298            let op_str = match op {
1299                ArithOp::Add => "+",
1300                ArithOp::Sub => "-",
1301                ArithOp::Mul => "*",
1302                ArithOp::Div => "/",
1303                ArithOp::Mod => "%",
1304            };
1305            format!("({} {} {})", l, op_str, r)
1306        }
1307        FormatExpr::IntLiteral(val) => format!("{}", val),
1308        FormatExpr::MapCall { map_name, args } => {
1309            let arg_strs: Vec<String> = args.iter().map(|a| expr_to_cpp(a, instr)).collect();
1310            format!("{}({})", map_name, arg_strs.join(", "))
1311        }
1312        FormatExpr::BuiltinCall { func, args } => {
1313            let arg_strs: Vec<String> = args.iter().map(|a| expr_to_cpp(a, instr)).collect();
1314            match func {
1315                BuiltinFunc::RotateRight => {
1316                    format!(
1317                        "((static_cast<uint32_t>({}) >> {}) | (static_cast<uint32_t>({}) << (32 - {})))",
1318                        arg_strs.first().map(|s| s.as_str()).unwrap_or("0"),
1319                        arg_strs.get(1).map(|s| s.as_str()).unwrap_or("0"),
1320                        arg_strs.first().map(|s| s.as_str()).unwrap_or("0"),
1321                        arg_strs.get(1).map(|s| s.as_str()).unwrap_or("0"),
1322                    )
1323                }
1324                BuiltinFunc::RotateLeft => {
1325                    format!(
1326                        "((static_cast<uint32_t>({}) << {}) | (static_cast<uint32_t>({}) >> (32 - {})))",
1327                        arg_strs.first().map(|s| s.as_str()).unwrap_or("0"),
1328                        arg_strs.get(1).map(|s| s.as_str()).unwrap_or("0"),
1329                        arg_strs.first().map(|s| s.as_str()).unwrap_or("0"),
1330                        arg_strs.get(1).map(|s| s.as_str()).unwrap_or("0"),
1331                    )
1332                }
1333            }
1334        }
1335        FormatExpr::SubDecoderAccess { field, fragment } => {
1336            format!("insn.{}.{}.{}", instr.name, field, fragment)
1337        }
1338    }
1339}
1340
1341/// Convert a guard condition to C++ code.
1342fn guard_to_cpp(guard: &Guard, instr: &ValidatedInstruction) -> String {
1343    let conditions: Vec<String> = guard
1344        .conditions
1345        .iter()
1346        .map(|cond| {
1347            let left = guard_operand_cpp(&cond.left, instr);
1348            let right = guard_operand_cpp(&cond.right, instr);
1349            let op = match cond.op {
1350                CompareOp::Eq => "==",
1351                CompareOp::Ne => "!=",
1352                CompareOp::Lt => "<",
1353                CompareOp::Le => "<=",
1354                CompareOp::Gt => ">",
1355                CompareOp::Ge => ">=",
1356            };
1357            format!("{} {} {}", left, op, right)
1358        })
1359        .collect();
1360    conditions.join(" && ")
1361}
1362
1363fn guard_operand_cpp(operand: &GuardOperand, instr: &ValidatedInstruction) -> String {
1364    match operand {
1365        GuardOperand::Field(name) => format!("insn.{}.{}", instr.name, name),
1366        GuardOperand::Literal(val) => format!("{}", val),
1367        GuardOperand::Expr { left, op, right } => {
1368            let l = guard_operand_cpp(left, instr);
1369            let r = guard_operand_cpp(right, instr);
1370            let op_str = match op {
1371                ArithOp::Add => "+",
1372                ArithOp::Sub => "-",
1373                ArithOp::Mul => "*",
1374                ArithOp::Div => "/",
1375                ArithOp::Mod => "%",
1376            };
1377            format!("({} {} {})", l, op_str, r)
1378        }
1379    }
1380}