Skip to main content

chipi_core/
codegen.rs

1//! Rust code generation from validated definitions and decision trees.
2//!
3//! Generates clean, idiomatic Rust code with an `impl` block containing a `decode()` function
4//! and an enum representing all instruction variants with their fields.
5
6use std::collections::HashMap;
7use std::fmt::Write;
8
9use crate::tree::DecodeNode;
10use crate::types::*;
11
12/// Check if any instruction in the decoder requires multiple units.
13fn needs_variable_length_decode(def: &ValidatedDef) -> bool {
14    def.instructions.iter().any(|i| i.unit_count() > 1)
15}
16
17/// Generate Rust source code from a validated definition and dispatch tree.
18///
19/// The output includes:
20/// - An enum with all instruction variants
21/// - A `decode()` function using efficient match statements
22/// - Proper type conversions and bit extraction
23pub fn generate_code(
24    def: &ValidatedDef,
25    tree: &DecodeNode,
26    type_maps: &HashMap<String, String>,
27    dispatch_overrides: &HashMap<String, crate::Dispatch>,
28) -> String {
29    let mut out = String::new();
30
31    writeln!(
32        out,
33        "// Auto-generated by https://github.com/ioncodes/chipi"
34    )
35    .unwrap();
36    writeln!(out, "// Do not edit.").unwrap();
37    writeln!(out).unwrap();
38    writeln!(out, "use std::fmt;").unwrap();
39    writeln!(out, "use std::marker::PhantomData;").unwrap();
40    writeln!(out).unwrap();
41
42    // Type map imports
43    let mut import_paths: Vec<&str> = type_maps.values().map(|v| v.as_str()).collect();
44    import_paths.sort();
45    import_paths.dedup();
46    for path in &import_paths {
47        // Only emit `use` for paths that look like Rust module paths (contain `::`)
48        if path.contains("::") {
49            writeln!(out, "use {};", path).unwrap();
50        }
51    }
52    if !import_paths.is_empty() {
53        writeln!(out).unwrap();
54    }
55
56    let word_type = word_type_for_width(def.config.width);
57    let unit_bytes = def.config.width / 8;
58    let variable_length = needs_variable_length_decode(def);
59    let enum_name = format!("{}Instruction", def.config.name);
60    let trait_name = format!("{}Format", def.config.name);
61    let default_struct = format!("Default{}Format", def.config.name);
62    let display_with = "DisplayWith";
63    let endian_suffix = match def.config.endian {
64        ByteEndian::Big => "be",
65        ByteEndian::Little => "le",
66    };
67
68    // Display format helpers
69    generate_display_helpers(&mut out, def);
70
71    // Map functions
72    generate_map_functions(&mut out, def);
73
74    // Sub-decoder types and dispatch
75    for sd in &def.sub_decoders {
76        let dispatch = dispatch_overrides
77            .get(&sd.name)
78            .copied()
79            .unwrap_or(crate::Dispatch::FnPtrLut);
80        generate_subdecoder(&mut out, sd, dispatch);
81    }
82
83    // Enum definition
84    writeln!(out, "#[derive(Debug, Clone, Copy, PartialEq, Eq)]").unwrap();
85    writeln!(out, "pub enum {} {{", enum_name).unwrap();
86
87    for instr in &def.instructions {
88        let variant_name = to_pascal_case(&instr.name);
89        if instr.resolved_fields.is_empty() {
90            writeln!(out, "    {},", variant_name).unwrap();
91        } else {
92            let fields: Vec<String> = instr
93                .resolved_fields
94                .iter()
95                .map(|f| {
96                    let rust_type = field_rust_type_with_maps(f, &type_maps);
97                    format!("{}: {}", f.name, rust_type)
98                })
99                .collect();
100            writeln!(out, "    {} {{ {} }},", variant_name, fields.join(", ")).unwrap();
101        }
102    }
103
104    writeln!(out, "}}").unwrap();
105    writeln!(out).unwrap();
106
107    // Formatting trait
108    generate_format_trait(&mut out, def, &trait_name, type_maps);
109
110    // impl block with decode() and write_asm() and display()
111    writeln!(out, "impl {} {{", enum_name).unwrap();
112    writeln!(out, "    #[inline]").unwrap();
113
114    // Always generate &[u8] signature, returning (Self, bytes_consumed)
115    writeln!(
116        out,
117        "    pub fn decode(data: &[u8]) -> Option<(Self, usize)> {{"
118    )
119    .unwrap();
120    writeln!(
121        out,
122        "        if data.len() < {} {{ return None; }}",
123        unit_bytes
124    )
125    .unwrap();
126    writeln!(
127        out,
128        "        let opcode = {}::from_{}_bytes(data[0..{}].try_into().unwrap());",
129        word_type, endian_suffix, unit_bytes
130    )
131    .unwrap();
132
133    emit_tree(
134        &mut out,
135        tree,
136        def,
137        &enum_name,
138        2,
139        variable_length,
140        &word_type,
141        type_maps,
142    );
143
144    writeln!(out, "    }}").unwrap();
145    writeln!(out).unwrap();
146
147    // write_asm method
148    generate_write_asm(&mut out, def, &enum_name, &trait_name);
149
150    // display method
151    writeln!(out).unwrap();
152    writeln!(out, "    #[allow(dead_code)]").unwrap();
153    writeln!(
154        out,
155        "    pub fn display<F: {}>(&self) -> {}<'_, F> {{",
156        trait_name, display_with
157    )
158    .unwrap();
159    writeln!(
160        out,
161        "        {} {{ insn: self, _phantom: PhantomData }}",
162        display_with
163    )
164    .unwrap();
165    writeln!(out, "    }}").unwrap();
166
167    writeln!(out, "}}").unwrap();
168    writeln!(out).unwrap();
169
170    // DisplayWith struct
171    writeln!(out, "#[allow(dead_code)]").unwrap();
172    writeln!(out, "pub struct {}<'a, F: {}> {{", display_with, trait_name).unwrap();
173    writeln!(out, "    insn: &'a {},", enum_name).unwrap();
174    writeln!(out, "    _phantom: PhantomData<F>,").unwrap();
175    writeln!(out, "}}").unwrap();
176    writeln!(out).unwrap();
177
178    writeln!(
179        out,
180        "impl<F: {}> fmt::Display for {}<'_, F> {{",
181        trait_name, display_with
182    )
183    .unwrap();
184    writeln!(
185        out,
186        "    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{"
187    )
188    .unwrap();
189    writeln!(out, "        self.insn.write_asm::<F>(f)").unwrap();
190    writeln!(out, "    }}").unwrap();
191    writeln!(out, "}}").unwrap();
192    writeln!(out).unwrap();
193
194    // Default format struct
195    writeln!(out, "pub struct {};", default_struct).unwrap();
196    writeln!(out, "impl {} for {} {{}}", trait_name, default_struct).unwrap();
197    writeln!(out).unwrap();
198
199    // Display impl using default format
200    writeln!(out, "impl fmt::Display for {} {{", enum_name).unwrap();
201    writeln!(
202        out,
203        "    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{"
204    )
205    .unwrap();
206    writeln!(out, "        self.write_asm::<{}>(f)", default_struct).unwrap();
207    writeln!(out, "    }}").unwrap();
208    writeln!(out, "}}").unwrap();
209
210    out
211}
212
213/// Emit a decode tree node in block context (i.e. as a statement/expression, not a match arm).
214fn emit_tree(
215    out: &mut String,
216    node: &DecodeNode,
217    def: &ValidatedDef,
218    enum_name: &str,
219    indent: usize,
220    variable_length: bool,
221    word_type: &str,
222    type_maps: &HashMap<String, String>,
223) {
224    let unit_bytes = def.config.width / 8;
225    let endian_suffix = match def.config.endian {
226        ByteEndian::Big => "be",
227        ByteEndian::Little => "le",
228    };
229    let pad = "    ".repeat(indent);
230    match node {
231        DecodeNode::Leaf { instruction_index } => {
232            let instr = &def.instructions[*instruction_index];
233            if let Some(guard) = leaf_guard(instr, word_type, unit_bytes, endian_suffix) {
234                writeln!(out, "{}if {} {{", pad, guard).unwrap();
235                emit_some(
236                    out,
237                    instr,
238                    enum_name,
239                    &format!("{}    ", pad),
240                    variable_length,
241                    word_type,
242                    unit_bytes,
243                    endian_suffix,
244                    &type_maps,
245                );
246                writeln!(out, "{}}} else {{", pad).unwrap();
247                writeln!(out, "{}    None", pad).unwrap();
248                writeln!(out, "{}}}", pad).unwrap();
249            } else {
250                emit_some(
251                    out,
252                    instr,
253                    enum_name,
254                    &pad,
255                    variable_length,
256                    word_type,
257                    unit_bytes,
258                    endian_suffix,
259                    &type_maps,
260                );
261            }
262        }
263        DecodeNode::PriorityLeaves { candidates } => {
264            // Try each candidate in priority order (most specific first)
265            for (i, &idx) in candidates.iter().enumerate() {
266                let instr = &def.instructions[idx];
267                let guard = leaf_guard(instr, word_type, unit_bytes, endian_suffix);
268
269                if i == 0 {
270                    // First candidate
271                    if let Some(guard_expr) = guard {
272                        writeln!(out, "{}if {} {{", pad, guard_expr).unwrap();
273                        emit_some(
274                            out,
275                            instr,
276                            enum_name,
277                            &format!("{}    ", pad),
278                            variable_length,
279                            word_type,
280                            unit_bytes,
281                            endian_suffix,
282                            &type_maps,
283                        );
284                    } else {
285                        // No guard needed - this should match unconditionally
286                        emit_some(
287                            out,
288                            instr,
289                            enum_name,
290                            &pad,
291                            variable_length,
292                            word_type,
293                            unit_bytes,
294                            endian_suffix,
295                            &type_maps,
296                        );
297                        break; // No need to check further candidates
298                    }
299                } else if i == candidates.len() - 1 {
300                    // Last candidate
301                    writeln!(out, "{}}} else {{", pad).unwrap();
302                    if let Some(guard_expr) = guard {
303                        writeln!(out, "{}    if {} {{", pad, guard_expr).unwrap();
304                        emit_some(
305                            out,
306                            instr,
307                            enum_name,
308                            &format!("{}        ", pad),
309                            variable_length,
310                            word_type,
311                            unit_bytes,
312                            endian_suffix,
313                            &type_maps,
314                        );
315                        writeln!(out, "{}    }} else {{", pad).unwrap();
316                        writeln!(out, "{}        None", pad).unwrap();
317                        writeln!(out, "{}    }}", pad).unwrap();
318                    } else {
319                        emit_some(
320                            out,
321                            instr,
322                            enum_name,
323                            &format!("{}    ", pad),
324                            variable_length,
325                            word_type,
326                            unit_bytes,
327                            endian_suffix,
328                            &type_maps,
329                        );
330                    }
331                    writeln!(out, "{}}}", pad).unwrap();
332                } else {
333                    // Middle candidates
334                    writeln!(
335                        out,
336                        "{}}} else if {} {{",
337                        pad,
338                        guard.unwrap_or_else(|| "true".to_string())
339                    )
340                    .unwrap();
341                    emit_some(
342                        out,
343                        instr,
344                        enum_name,
345                        &format!("{}    ", pad),
346                        variable_length,
347                        word_type,
348                        unit_bytes,
349                        endian_suffix,
350                        &type_maps,
351                    );
352                }
353            }
354        }
355        DecodeNode::Fail => {
356            writeln!(out, "{}None", pad).unwrap();
357        }
358        DecodeNode::Branch {
359            range,
360            arms,
361            default,
362        } => {
363            let extract_expr =
364                extract_expression("opcode", &[*range], word_type, unit_bytes, endian_suffix);
365            writeln!(out, "{}match {} {{", pad, extract_expr).unwrap();
366
367            for (value, child) in arms {
368                emit_arm(
369                    out,
370                    child,
371                    def,
372                    enum_name,
373                    indent + 1,
374                    &format!("{:#x}", value),
375                    variable_length,
376                    word_type,
377                    type_maps,
378                );
379            }
380
381            emit_arm(
382                out,
383                default,
384                def,
385                enum_name,
386                indent + 1,
387                "_",
388                variable_length,
389                word_type,
390                type_maps,
391            );
392
393            writeln!(out, "{}}}", pad).unwrap();
394        }
395    }
396}
397
398/// Emit a single match arm for a given pattern and child node.
399fn emit_arm(
400    out: &mut String,
401    node: &DecodeNode,
402    def: &ValidatedDef,
403    enum_name: &str,
404    indent: usize,
405    pattern: &str,
406    variable_length: bool,
407    word_type: &str,
408    type_maps: &HashMap<String, String>,
409) {
410    let unit_bytes = def.config.width / 8;
411    let endian_suffix = match def.config.endian {
412        ByteEndian::Big => "be",
413        ByteEndian::Little => "le",
414    };
415    let pad = "    ".repeat(indent);
416    match node {
417        DecodeNode::Fail => {
418            writeln!(out, "{}{} => None,", pad, pattern).unwrap();
419        }
420        DecodeNode::Leaf { instruction_index } => {
421            let instr = &def.instructions[*instruction_index];
422            if let Some(guard) = leaf_guard(instr, word_type, unit_bytes, endian_suffix) {
423                if pattern == "_" {
424                    // Default arm with guard: emit guarded arm then fallback
425                    write!(out, "{}{} if {} => ", pad, pattern, guard).unwrap();
426                    emit_some_inline(
427                        out,
428                        instr,
429                        enum_name,
430                        variable_length,
431                        word_type,
432                        unit_bytes,
433                        endian_suffix,
434                        &type_maps,
435                    );
436                    writeln!(out, "{}{} => None,", pad, pattern).unwrap();
437                } else {
438                    write!(out, "{}{} if {} => ", pad, pattern, guard).unwrap();
439                    emit_some_inline(
440                        out,
441                        instr,
442                        enum_name,
443                        variable_length,
444                        word_type,
445                        unit_bytes,
446                        endian_suffix,
447                        &type_maps,
448                    );
449                }
450            } else {
451                write!(out, "{}{} => ", pad, pattern).unwrap();
452                emit_some_inline(
453                    out,
454                    instr,
455                    enum_name,
456                    variable_length,
457                    word_type,
458                    unit_bytes,
459                    endian_suffix,
460                    &type_maps,
461                );
462            }
463        }
464        DecodeNode::PriorityLeaves { candidates } => {
465            // Emit as a block with if-else chain
466            writeln!(out, "{}{} => {{", pad, pattern).unwrap();
467            let inner_pad = "    ".repeat(indent + 1);
468
469            for (i, &idx) in candidates.iter().enumerate() {
470                let instr = &def.instructions[idx];
471                let guard = leaf_guard(instr, word_type, unit_bytes, endian_suffix);
472
473                if i == 0 {
474                    // First candidate
475                    if let Some(guard_expr) = guard {
476                        writeln!(out, "{}if {} {{", inner_pad, guard_expr).unwrap();
477                        emit_some(
478                            out,
479                            instr,
480                            enum_name,
481                            &format!("{}    ", inner_pad),
482                            variable_length,
483                            word_type,
484                            unit_bytes,
485                            endian_suffix,
486                            &type_maps,
487                        );
488                    } else {
489                        // No guard - matches unconditionally
490                        emit_some(
491                            out,
492                            instr,
493                            enum_name,
494                            &inner_pad,
495                            variable_length,
496                            word_type,
497                            unit_bytes,
498                            endian_suffix,
499                            &type_maps,
500                        );
501                        writeln!(out, "{}}}", pad).unwrap();
502                        return;
503                    }
504                } else if i == candidates.len() - 1 {
505                    // Last candidate
506                    writeln!(out, "{}}} else {{", inner_pad).unwrap();
507                    if let Some(guard_expr) = guard {
508                        writeln!(out, "{}    if {} {{", inner_pad, guard_expr).unwrap();
509                        emit_some(
510                            out,
511                            instr,
512                            enum_name,
513                            &format!("{}        ", inner_pad),
514                            variable_length,
515                            word_type,
516                            unit_bytes,
517                            endian_suffix,
518                            &type_maps,
519                        );
520                        writeln!(out, "{}    }} else {{", inner_pad).unwrap();
521                        writeln!(out, "{}        None", inner_pad).unwrap();
522                        writeln!(out, "{}    }}", inner_pad).unwrap();
523                    } else {
524                        emit_some(
525                            out,
526                            instr,
527                            enum_name,
528                            &format!("{}    ", inner_pad),
529                            variable_length,
530                            word_type,
531                            unit_bytes,
532                            endian_suffix,
533                            &type_maps,
534                        );
535                    }
536                    writeln!(out, "{}}}", inner_pad).unwrap();
537                    writeln!(out, "{}}}", pad).unwrap();
538                } else {
539                    // Middle candidates
540                    writeln!(
541                        out,
542                        "{}}} else if {} {{",
543                        inner_pad,
544                        guard.unwrap_or_else(|| "true".to_string())
545                    )
546                    .unwrap();
547                    emit_some(
548                        out,
549                        instr,
550                        enum_name,
551                        &format!("{}    ", inner_pad),
552                        variable_length,
553                        word_type,
554                        unit_bytes,
555                        endian_suffix,
556                        &type_maps,
557                    );
558                }
559            }
560        }
561        DecodeNode::Branch {
562            range,
563            arms,
564            default,
565        } => {
566            writeln!(out, "{}{} => {{", pad, pattern).unwrap();
567            let extract_expr =
568                extract_expression("opcode", &[*range], word_type, unit_bytes, endian_suffix);
569            let inner_pad = "    ".repeat(indent + 1);
570            writeln!(out, "{}match {} {{", inner_pad, extract_expr).unwrap();
571
572            for (value, child) in arms {
573                emit_arm(
574                    out,
575                    child,
576                    def,
577                    enum_name,
578                    indent + 2,
579                    &format!("{:#x}", value),
580                    variable_length,
581                    word_type,
582                    type_maps,
583                );
584            }
585
586            emit_arm(
587                out,
588                default,
589                def,
590                enum_name,
591                indent + 2,
592                "_",
593                variable_length,
594                word_type,
595                type_maps,
596            );
597
598            writeln!(out, "{}}}", inner_pad).unwrap();
599            writeln!(out, "{}}}", pad).unwrap();
600        }
601    }
602}
603
604/// Compute the guard condition string for a leaf instruction, if needed.
605/// Returns `None` if all fixed bits were already fully dispatched by the tree.
606/// For multi-unit instructions, generates guards for ALL units (unit 0 and unit 1+).
607fn leaf_guard(
608    instr: &ValidatedInstruction,
609    word_type: &str,
610    unit_bytes: u32,
611    endian_suffix: &str,
612) -> Option<String> {
613    let fixed_bits = instr.fixed_bits();
614    if fixed_bits.is_empty() {
615        return None;
616    }
617
618    // Group fixed bits by unit
619    let mut units_map: std::collections::HashMap<u32, Vec<(u32, Bit)>> =
620        std::collections::HashMap::new();
621    for (unit, hw_bit, bit) in fixed_bits {
622        units_map.entry(unit).or_default().push((hw_bit, bit));
623    }
624
625    let mut conditions = Vec::new();
626
627    for (unit, bits) in units_map {
628        let (mask, value) = compute_mask_value(&bits);
629        if mask != 0 {
630            let source = unit_read_expr(unit, word_type, unit_bytes, endian_suffix);
631            conditions.push(format!("{} & {:#x} == {:#x}", source, mask, value));
632        }
633    }
634
635    if conditions.is_empty() {
636        None
637    } else {
638        Some(conditions.join(" && "))
639    }
640}
641
642/// Write `Some((EnumName::Variant { ... }, bytes))` as an inline match arm value (terminated with comma+newline).
643fn emit_some_inline(
644    out: &mut String,
645    instr: &ValidatedInstruction,
646    enum_name: &str,
647    variable_length: bool,
648    word_type: &str,
649    unit_bytes: u32,
650    endian_suffix: &str,
651    type_maps: &HashMap<String, String>,
652) {
653    let variant_name = to_pascal_case(&instr.name);
654    let unit_count = instr.unit_count();
655    let bytes_consumed = unit_count * unit_bytes;
656
657    if variable_length && unit_count > 1 {
658        // Emit bounds check for multi-unit instruction
659        let cond = format!("data.len() >= {}", bytes_consumed);
660        write!(out, "if {} {{ ", cond).unwrap();
661    }
662
663    if instr.resolved_fields.is_empty() {
664        write!(
665            out,
666            "Some(({}::{}, {}))",
667            enum_name, variant_name, bytes_consumed
668        )
669        .unwrap();
670    } else {
671        let fields: Vec<String> = instr
672            .resolved_fields
673            .iter()
674            .map(|f| {
675                let extract =
676                    extract_expression("opcode", &f.ranges, word_type, unit_bytes, endian_suffix);
677                let expr = apply_transforms_with_maps(&extract, &f.resolved_type, type_maps);
678                format!("{}: {}", f.name, expr)
679            })
680            .collect();
681        write!(
682            out,
683            "Some(({}::{} {{ {} }}, {}))",
684            enum_name,
685            variant_name,
686            fields.join(", "),
687            bytes_consumed
688        )
689        .unwrap();
690    }
691
692    if variable_length && unit_count > 1 {
693        write!(out, " }} else {{ None }}").unwrap();
694    }
695    writeln!(out, ",").unwrap();
696}
697
698/// Write `Some((EnumName::Variant { ... }, bytes))` in block context (multi-line, no trailing comma).
699fn emit_some(
700    out: &mut String,
701    instr: &ValidatedInstruction,
702    enum_name: &str,
703    pad: &str,
704    variable_length: bool,
705    word_type: &str,
706    unit_bytes: u32,
707    endian_suffix: &str,
708    type_maps: &HashMap<String, String>,
709) {
710    let unit_count = instr.unit_count();
711    let bytes_consumed = unit_count * unit_bytes;
712
713    if variable_length && unit_count > 1 {
714        writeln!(out, "{}if data.len() >= {} {{", pad, bytes_consumed).unwrap();
715        let inner_pad = format!("{}    ", pad);
716        emit_some_inner(
717            out,
718            instr,
719            enum_name,
720            &inner_pad,
721            word_type,
722            unit_bytes,
723            endian_suffix,
724            bytes_consumed,
725            type_maps,
726        );
727        writeln!(out, "{}}} else {{", pad).unwrap();
728        writeln!(out, "{}    None", pad).unwrap();
729        writeln!(out, "{}}}", pad).unwrap();
730    } else {
731        emit_some_inner(
732            out,
733            instr,
734            enum_name,
735            pad,
736            word_type,
737            unit_bytes,
738            endian_suffix,
739            bytes_consumed,
740            type_maps,
741        );
742    }
743}
744
745/// Helper to emit the Some(...) part without bounds checking.
746fn emit_some_inner(
747    out: &mut String,
748    instr: &ValidatedInstruction,
749    enum_name: &str,
750    pad: &str,
751    word_type: &str,
752    unit_bytes: u32,
753    endian_suffix: &str,
754    bytes_consumed: u32,
755    type_maps: &HashMap<String, String>,
756) {
757    let variant_name = to_pascal_case(&instr.name);
758    if instr.resolved_fields.is_empty() {
759        writeln!(
760            out,
761            "{}Some(({}::{}, {}))",
762            pad, enum_name, variant_name, bytes_consumed
763        )
764        .unwrap();
765    } else {
766        writeln!(out, "{}Some(({}::{} {{", pad, enum_name, variant_name).unwrap();
767        for field in &instr.resolved_fields {
768            let extract = extract_expression(
769                "opcode",
770                &field.ranges,
771                word_type,
772                unit_bytes,
773                endian_suffix,
774            );
775            let expr = apply_transforms_with_maps(&extract, &field.resolved_type, type_maps);
776            writeln!(out, "{}    {}: {},", pad, field.name, expr).unwrap();
777        }
778        writeln!(out, "{}}}, {}))", pad, bytes_consumed).unwrap();
779    }
780}
781
782/// Compute a bitmask and expected value from a list of (hw_bit_position, Bit) pairs.
783/// Wildcard bits are skipped (not included in the mask).
784fn compute_mask_value(fixed_bits: &[(u32, Bit)]) -> (u64, u64) {
785    let mut mask: u64 = 0;
786    let mut value: u64 = 0;
787    for &(bit_pos, bit_val) in fixed_bits {
788        // Skip wildcard bits - they don't contribute to the mask
789        if bit_val == Bit::Wildcard {
790            continue;
791        }
792        mask |= 1u64 << bit_pos;
793        if bit_val == Bit::One {
794            value |= 1u64 << bit_pos;
795        }
796    }
797    (mask, value)
798}
799
800/// Generate an expression to read a unit from the byte slice.
801/// Unit 0 returns "opcode" (already decoded in preamble).
802/// Unit N>0 returns an inline byte read expression.
803fn unit_read_expr(unit: u32, word_type: &str, unit_bytes: u32, endian_suffix: &str) -> String {
804    if unit == 0 {
805        "opcode".to_string()
806    } else {
807        let start = unit * unit_bytes;
808        let end = start + unit_bytes;
809        format!(
810            "{}::from_{}_bytes(data[{}..{}].try_into().unwrap())",
811            word_type, endian_suffix, start, end
812        )
813    }
814}
815
816/// Generate an expression to extract bits from multiple ranges (potentially cross-unit).
817fn extract_expression(
818    var: &str,
819    ranges: &[BitRange],
820    word_type: &str,
821    unit_bytes: u32,
822    endian_suffix: &str,
823) -> String {
824    if ranges.is_empty() {
825        return "0".to_string();
826    }
827
828    if ranges.len() == 1 {
829        // Single range - simple extraction
830        let range = ranges[0];
831        let source = if range.unit == 0 {
832            var.to_string()
833        } else {
834            unit_read_expr(range.unit, word_type, unit_bytes, endian_suffix)
835        };
836
837        let width = range.width();
838        let shift = range.end;
839        let mask = (1u64 << width) - 1;
840
841        if shift == 0 {
842            format!("{} & {:#x}", source, mask)
843        } else {
844            format!("({} >> {}) & {:#x}", source, shift, mask)
845        }
846    } else {
847        // Multi-range extraction - combine bits from multiple ranges
848        let mut parts = Vec::new();
849        let mut accumulated_width = 0u32;
850
851        // Ranges are ordered from low-order to high-order bits
852        for range in ranges {
853            let source = if range.unit == 0 {
854                var.to_string()
855            } else {
856                unit_read_expr(range.unit, word_type, unit_bytes, endian_suffix)
857            };
858
859            let width = range.width();
860            let shift = range.end;
861            let mask = (1u64 << width) - 1;
862
863            let extracted = if shift == 0 {
864                format!("({} & {:#x})", source, mask)
865            } else {
866                format!("(({} >> {}) & {:#x})", source, shift, mask)
867            };
868
869            // Shift this part into its position in the final value
870            if accumulated_width > 0 {
871                parts.push(format!("({} << {})", extracted, accumulated_width));
872            } else {
873                parts.push(extracted);
874            }
875
876            accumulated_width += width;
877        }
878
879        parts.join(" | ")
880    }
881}
882
883/// Apply transforms and type conversions to extracted field values.
884fn apply_transforms(extract_expr: &str, resolved: &ResolvedFieldType) -> String {
885    apply_transforms_with_maps(extract_expr, resolved, &HashMap::new())
886}
887
888/// Apply transforms with type map support.
889fn apply_transforms_with_maps(
890    extract_expr: &str,
891    resolved: &ResolvedFieldType,
892    type_maps: &HashMap<String, String>,
893) -> String {
894    let mut expr = extract_expr.to_string();
895
896    for transform in &resolved.transforms {
897        match transform {
898            Transform::SignExtend(n) => {
899                let signed_type = signed_type_for(&resolved.base_type);
900                let bits = type_bits(&resolved.base_type);
901                expr = format!(
902                    "(((({}) as {}) << ({} - {})) >> ({} - {}))",
903                    expr, signed_type, bits, n, bits, n
904                );
905            }
906            Transform::ZeroExtend(_) => {
907                // No-op for unsigned types
908            }
909            Transform::ShiftLeft(n) => {
910                expr = format!("(({}) << {})", expr, n);
911            }
912        }
913    }
914
915    // Handle sub-decoder fields: call the sub-decoder dispatch function
916    if let Some(ref sd_name) = resolved.sub_decoder {
917        let decode_fn = format!("decode_{}", to_snake_case(sd_name));
918        // The field value is extracted and cast to the sub-decoder's word type, then dispatched
919        return format!(
920            "{}(({}) as {}).unwrap()",
921            decode_fn, expr, resolved.base_type
922        );
923    }
924
925    // Check if there's a type map for the wrapper type
926    let wrapper_type = resolved.alias_name.as_ref().and_then(|a| type_maps.get(a));
927
928    if let Some(wrapper) = wrapper_type {
929        let short_name = wrapper.rsplit("::").next().unwrap_or(wrapper);
930        expr = format!("{}::from(({}) as {})", short_name, expr, resolved.base_type);
931    } else if resolved.base_type == "bool" {
932        expr = format!("({}) != 0", expr);
933    } else {
934        expr = format!("({}) as {}", expr, resolved.base_type);
935    }
936
937    expr
938}
939
940/// Generate display format helper structs (SignedHex, Hex) if needed.
941fn generate_display_helpers(out: &mut String, def: &ValidatedDef) {
942    let mut need_signed_hex = false;
943    let mut need_hex = false;
944
945    for instr in &def.instructions {
946        for field in &instr.resolved_fields {
947            match field.resolved_type.display_format {
948                Some(DisplayFormat::SignedHex) => need_signed_hex = true,
949                Some(DisplayFormat::Hex) => need_hex = true,
950                None => {}
951            }
952        }
953    }
954
955    if need_signed_hex {
956        writeln!(out, "struct SignedHex<T>(T);").unwrap();
957        writeln!(out).unwrap();
958
959        for ty in &["i8", "i16", "i32"] {
960            writeln!(out, "impl fmt::Display for SignedHex<{}> {{", ty).unwrap();
961            writeln!(
962                out,
963                "    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{"
964            )
965            .unwrap();
966            writeln!(out, "        if self.0 == 0 {{ write!(f, \"0\") }}").unwrap();
967            writeln!(
968                out,
969                "        else if self.0 > 0 {{ write!(f, \"0x{{:X}}\", self.0) }}"
970            )
971            .unwrap();
972            writeln!(
973                out,
974                "        else {{ write!(f, \"-0x{{:X}}\", (self.0 as i64).wrapping_neg()) }}"
975            )
976            .unwrap();
977            writeln!(out, "    }}").unwrap();
978            writeln!(out, "}}").unwrap();
979            writeln!(out).unwrap();
980        }
981    }
982
983    if need_hex {
984        if !need_signed_hex {
985            writeln!(out, "struct SignedHex<T>(T);").unwrap();
986            writeln!(out).unwrap();
987        }
988
989        for ty in &["u8", "u16", "u32"] {
990            writeln!(out, "impl fmt::Display for SignedHex<{}> {{", ty).unwrap();
991            writeln!(
992                out,
993                "    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{"
994            )
995            .unwrap();
996            writeln!(out, "        if self.0 == 0 {{ write!(f, \"0\") }}").unwrap();
997            writeln!(out, "        else {{ write!(f, \"0x{{:X}}\", self.0) }}").unwrap();
998            writeln!(out, "    }}").unwrap();
999            writeln!(out, "}}").unwrap();
1000            writeln!(out).unwrap();
1001        }
1002    }
1003}
1004
1005/// Generate map lookup functions.
1006fn generate_map_functions(out: &mut String, def: &ValidatedDef) {
1007    if def.maps.is_empty() {
1008        return;
1009    }
1010
1011    // Collect map call sites to infer parameter types
1012    let map_param_types = infer_map_param_types(def);
1013
1014    for map in &def.maps {
1015        let has_interpolation = map.entries.iter().any(|entry| {
1016            entry
1017                .output
1018                .iter()
1019                .any(|p| matches!(p, FormatPiece::FieldRef { .. }))
1020        });
1021
1022        let param_types: Vec<String> = map
1023            .params
1024            .iter()
1025            .enumerate()
1026            .map(|(i, _)| {
1027                map_param_types
1028                    .get(&map.name)
1029                    .and_then(|types| types.get(i))
1030                    .cloned()
1031                    .unwrap_or_else(|| "i64".to_string())
1032            })
1033            .collect();
1034
1035        let return_type = if has_interpolation {
1036            "String"
1037        } else {
1038            "&'static str"
1039        };
1040
1041        let params: Vec<String> = map
1042            .params
1043            .iter()
1044            .zip(param_types.iter())
1045            .map(|(name, ty)| format!("{}: {}", name, ty))
1046            .collect();
1047
1048        writeln!(out, "#[allow(dead_code)]").unwrap();
1049        writeln!(
1050            out,
1051            "fn {}({}) -> {} {{",
1052            map.name,
1053            params.join(", "),
1054            return_type
1055        )
1056        .unwrap();
1057
1058        if map.params.len() == 1 {
1059            writeln!(out, "    match {} {{", map.params[0]).unwrap();
1060        } else {
1061            let tuple: Vec<&str> = map.params.iter().map(|s| s.as_str()).collect();
1062            writeln!(out, "    match ({}) {{", tuple.join(", ")).unwrap();
1063        }
1064
1065        // Separate wildcard (default) entries from specific entries
1066        let mut default_entry = None;
1067
1068        for entry in &map.entries {
1069            let is_all_wildcard = entry.keys.iter().all(|k| matches!(k, MapKey::Wildcard));
1070            if is_all_wildcard {
1071                default_entry = Some(entry);
1072                continue;
1073            }
1074
1075            let pattern = if map.params.len() == 1 {
1076                format_map_key(&entry.keys[0])
1077            } else {
1078                let keys: Vec<String> = entry.keys.iter().map(|k| format_map_key(k)).collect();
1079                format!("({})", keys.join(", "))
1080            };
1081
1082            let output = format_map_output(&entry.output, has_interpolation);
1083            writeln!(out, "        {} => {},", pattern, output).unwrap();
1084        }
1085
1086        // Default arm
1087        if let Some(entry) = default_entry {
1088            let output = format_map_output(&entry.output, has_interpolation);
1089            writeln!(out, "        _ => {},", output).unwrap();
1090        } else {
1091            if has_interpolation {
1092                writeln!(out, "        _ => String::from(\"???\"),").unwrap();
1093            } else {
1094                writeln!(out, "        _ => \"???\",").unwrap();
1095            }
1096        }
1097
1098        writeln!(out, "    }}").unwrap();
1099        writeln!(out, "}}").unwrap();
1100        writeln!(out).unwrap();
1101    }
1102}
1103
1104fn format_map_key(key: &MapKey) -> String {
1105    match key {
1106        MapKey::Value(v) => format!("{}", v),
1107        MapKey::Wildcard => "_".to_string(),
1108    }
1109}
1110
1111fn format_map_output(pieces: &[FormatPiece], has_interpolation: bool) -> String {
1112    if !has_interpolation {
1113        // All pieces should be literals
1114        let mut s = String::new();
1115        for piece in pieces {
1116            if let FormatPiece::Literal(lit) = piece {
1117                s.push_str(lit);
1118            }
1119        }
1120        return format!("\"{}\"", s);
1121    }
1122
1123    // Build a format!() call
1124    let mut fmt_str = String::new();
1125    let mut args = Vec::new();
1126
1127    for piece in pieces {
1128        match piece {
1129            FormatPiece::Literal(lit) => {
1130                // Escape `{` and `}` for format string
1131                for ch in lit.chars() {
1132                    match ch {
1133                        '{' => fmt_str.push_str("{{"),
1134                        '}' => fmt_str.push_str("}}"),
1135                        _ => fmt_str.push(ch),
1136                    }
1137                }
1138            }
1139            FormatPiece::FieldRef { expr, spec } => {
1140                if let Some(spec) = spec {
1141                    fmt_str.push_str(&format!("{{:{}}}", spec));
1142                } else {
1143                    fmt_str.push_str("{}");
1144                }
1145                args.push(expr_to_rust(expr, &[]));
1146            }
1147        }
1148    }
1149
1150    if args.is_empty() {
1151        format!("String::from(\"{}\")", fmt_str)
1152    } else {
1153        format!("format!(\"{}\", {})", fmt_str, args.join(", "))
1154    }
1155}
1156
1157/// Infer map parameter types from call sites across all instructions.
1158fn infer_map_param_types(def: &ValidatedDef) -> HashMap<String, Vec<String>> {
1159    let mut result: HashMap<String, Vec<String>> = HashMap::new();
1160
1161    // Build field type lookup per instruction
1162    for instr in &def.instructions {
1163        let field_types: HashMap<&str, &ResolvedFieldType> = instr
1164            .resolved_fields
1165            .iter()
1166            .map(|f| (f.name.as_str(), &f.resolved_type))
1167            .collect();
1168
1169        for fl in &instr.format_lines {
1170            for piece in &fl.pieces {
1171                if let FormatPiece::FieldRef { expr, .. } = piece {
1172                    collect_map_call_types(expr, &field_types, &mut result);
1173                }
1174            }
1175        }
1176    }
1177
1178    result
1179}
1180
1181/// Infer the Rust type of a format expression given field type information.
1182fn infer_expr_type(
1183    expr: &FormatExpr,
1184    field_types: &HashMap<&str, &ResolvedFieldType>,
1185) -> Option<String> {
1186    match expr {
1187        FormatExpr::Field(name) => field_types
1188            .get(name.as_str())
1189            .map(|ft| ft.base_type.clone()),
1190        FormatExpr::Arithmetic { left, .. } => infer_expr_type(left, field_types),
1191        FormatExpr::IntLiteral(_) => Some("i64".to_string()),
1192        _ => None,
1193    }
1194}
1195
1196fn collect_map_call_types(
1197    expr: &FormatExpr,
1198    field_types: &HashMap<&str, &ResolvedFieldType>,
1199    result: &mut HashMap<String, Vec<String>>,
1200) {
1201    match expr {
1202        FormatExpr::MapCall { map_name, args } => {
1203            let entry = result
1204                .entry(map_name.clone())
1205                .or_insert_with(|| vec!["i64".to_string(); args.len()]);
1206
1207            for (i, arg) in args.iter().enumerate() {
1208                if i < entry.len() {
1209                    if let Some(rust_type) = infer_expr_type(arg, field_types) {
1210                        entry[i] = rust_type;
1211                    }
1212                }
1213            }
1214
1215            // Also recurse into map call arguments to find nested map calls
1216            for arg in args {
1217                collect_map_call_types(arg, field_types, result);
1218            }
1219        }
1220        FormatExpr::Arithmetic { left, right, .. } => {
1221            collect_map_call_types(left, field_types, result);
1222            collect_map_call_types(right, field_types, result);
1223        }
1224        FormatExpr::BuiltinCall { args, .. } => {
1225            for arg in args {
1226                collect_map_call_types(arg, field_types, result);
1227            }
1228        }
1229        FormatExpr::SubDecoderAccess { .. }
1230        | FormatExpr::Field(_)
1231        | FormatExpr::Ternary { .. }
1232        | FormatExpr::IntLiteral(_) => {}
1233    }
1234}
1235
1236/// Generate the formatting trait with one method per instruction variant.
1237fn generate_format_trait(
1238    out: &mut String,
1239    def: &ValidatedDef,
1240    trait_name: &str,
1241    type_maps: &HashMap<String, String>,
1242) {
1243    writeln!(out, "pub trait {} {{", trait_name).unwrap();
1244
1245    for instr in &def.instructions {
1246        let method_name = format!("fmt_{}", instr.name);
1247        let params = trait_method_params(&instr.resolved_fields, &type_maps);
1248
1249        if params.is_empty() {
1250            writeln!(
1251                out,
1252                "    fn {}(f: &mut fmt::Formatter) -> fmt::Result {{",
1253                method_name
1254            )
1255            .unwrap();
1256        } else {
1257            writeln!(
1258                out,
1259                "    fn {}({}, f: &mut fmt::Formatter) -> fmt::Result {{",
1260                method_name, params
1261            )
1262            .unwrap();
1263        }
1264
1265        generate_format_body(out, instr, 2, &type_maps);
1266
1267        writeln!(out, "    }}").unwrap();
1268    }
1269
1270    writeln!(out, "}}").unwrap();
1271    writeln!(out).unwrap();
1272}
1273
1274/// Generate parameters for a trait method.
1275fn trait_method_params(fields: &[ResolvedField], type_maps: &HashMap<String, String>) -> String {
1276    let mut params = Vec::new();
1277    for field in fields {
1278        let rust_type = field_rust_type_with_maps(field, type_maps);
1279        params.push(format!("{}: {}", field.name, rust_type));
1280    }
1281    params.join(", ")
1282}
1283
1284/// Generate the body of a format trait method from format lines.
1285fn generate_format_body(
1286    out: &mut String,
1287    instr: &ValidatedInstruction,
1288    indent: usize,
1289    type_maps: &HashMap<String, String>,
1290) {
1291    let pad = "    ".repeat(indent);
1292
1293    if instr.format_lines.is_empty() {
1294        // Raw fallback
1295        if instr.resolved_fields.is_empty() {
1296            writeln!(out, "{}write!(f, \"{}\")", pad, instr.name).unwrap();
1297        } else {
1298            let field_names: Vec<&str> = instr
1299                .resolved_fields
1300                .iter()
1301                .map(|f| f.name.as_str())
1302                .collect();
1303            let placeholders: Vec<&str> = field_names.iter().map(|_| "{}").collect();
1304            let fmt_str = format!("{} {}", instr.name, placeholders.join(", "));
1305            let args: Vec<String> = instr
1306                .resolved_fields
1307                .iter()
1308                .map(|f| f.name.clone())
1309                .collect();
1310            writeln!(
1311                out,
1312                "{}write!(f, \"{}\", {})",
1313                pad,
1314                fmt_str,
1315                args.join(", ")
1316            )
1317            .unwrap();
1318        }
1319        return;
1320    }
1321
1322    if instr.format_lines.len() == 1 && instr.format_lines[0].guard.is_none() {
1323        // Single format line, no guard
1324        emit_write_call(
1325            out,
1326            &instr.format_lines[0].pieces,
1327            &instr.resolved_fields,
1328            &pad,
1329        );
1330        return;
1331    }
1332
1333    // Multiple format lines with guards
1334    for (i, fl) in instr.format_lines.iter().enumerate() {
1335        if let Some(guard) = &fl.guard {
1336            let guard_code = generate_guard_code(guard, &instr.resolved_fields, type_maps);
1337            if i == 0 {
1338                writeln!(out, "{}if {} {{", pad, guard_code).unwrap();
1339            } else {
1340                writeln!(out, "{}}} else if {} {{", pad, guard_code).unwrap();
1341            }
1342            emit_write_call(
1343                out,
1344                &fl.pieces,
1345                &instr.resolved_fields,
1346                &format!("{}    ", pad),
1347            );
1348        } else {
1349            // Last line without guard = else
1350            if i > 0 {
1351                writeln!(out, "{}}} else {{", pad).unwrap();
1352            }
1353            emit_write_call(
1354                out,
1355                &fl.pieces,
1356                &instr.resolved_fields,
1357                &format!("{}    ", pad),
1358            );
1359        }
1360    }
1361
1362    if instr.format_lines.len() > 1
1363        || instr
1364            .format_lines
1365            .first()
1366            .map_or(false, |fl| fl.guard.is_some())
1367    {
1368        writeln!(out, "{}}}", pad).unwrap();
1369    }
1370}
1371
1372/// Emit a `write!(f, ...)` call for a set of format pieces.
1373fn emit_write_call(out: &mut String, pieces: &[FormatPiece], fields: &[ResolvedField], pad: &str) {
1374    let mut fmt_str = String::new();
1375    let mut args = Vec::new();
1376
1377    for piece in pieces {
1378        match piece {
1379            FormatPiece::Literal(lit) => {
1380                for ch in lit.chars() {
1381                    match ch {
1382                        '{' => fmt_str.push_str("{{"),
1383                        '}' => fmt_str.push_str("}}"),
1384                        _ => fmt_str.push(ch),
1385                    }
1386                }
1387            }
1388            FormatPiece::FieldRef { expr, spec } => {
1389                if let Some(spec) = spec {
1390                    fmt_str.push_str(&format!("{{:{}}}", spec));
1391                    args.push(expr_to_rust(expr, fields));
1392                } else if let Some(display_fmt) = resolve_display_format(expr, fields) {
1393                    fmt_str.push_str("{}");
1394                    let rust_expr = expr_to_rust(expr, fields);
1395                    match display_fmt {
1396                        DisplayFormat::SignedHex | DisplayFormat::Hex => {
1397                            args.push(format!("SignedHex({})", rust_expr));
1398                        }
1399                    }
1400                } else {
1401                    fmt_str.push_str("{}");
1402                    args.push(expr_to_rust(expr, fields));
1403                }
1404            }
1405        }
1406    }
1407
1408    if args.is_empty() {
1409        writeln!(out, "{}write!(f, \"{}\")", pad, fmt_str).unwrap();
1410    } else {
1411        writeln!(
1412            out,
1413            "{}write!(f, \"{}\", {})",
1414            pad,
1415            fmt_str,
1416            args.join(", ")
1417        )
1418        .unwrap();
1419    }
1420}
1421
1422/// Resolve display format for a format expression, if the expression is a simple field reference.
1423fn resolve_display_format(expr: &FormatExpr, fields: &[ResolvedField]) -> Option<DisplayFormat> {
1424    if let FormatExpr::Field(name) = expr {
1425        fields
1426            .iter()
1427            .find(|f| f.name == *name)
1428            .and_then(|f| f.resolved_type.display_format)
1429    } else {
1430        None
1431    }
1432}
1433
1434/// Convert a format expression to Rust code.
1435fn expr_to_rust(expr: &FormatExpr, fields: &[ResolvedField]) -> String {
1436    match expr {
1437        FormatExpr::Field(name) => {
1438            let field = fields.iter().find(|f| f.name == *name);
1439            if let Some(f) = field {
1440                if f.resolved_type.base_type == "bool" {
1441                    format!("{} as u8", name)
1442                } else {
1443                    name.clone()
1444                }
1445            } else {
1446                name.clone()
1447            }
1448        }
1449        FormatExpr::Ternary {
1450            field,
1451            if_nonzero,
1452            if_zero,
1453        } => {
1454            let f = fields.iter().find(|f| f.name == *field);
1455            let cond = if let Some(f) = f {
1456                if f.resolved_type.base_type == "bool" {
1457                    field.clone()
1458                } else {
1459                    format!("{} != 0", field)
1460                }
1461            } else {
1462                format!("{} != 0", field)
1463            };
1464
1465            let else_val = if_zero
1466                .as_deref()
1467                .map(|s| format!("\"{}\"", s))
1468                .unwrap_or_else(|| "\"\"".to_string());
1469
1470            format!(
1471                "if {} {{ \"{}\" }} else {{ {} }}",
1472                cond, if_nonzero, else_val
1473            )
1474        }
1475        FormatExpr::Arithmetic { left, op, right } => {
1476            let l = expr_to_rust(left, fields);
1477            let r = expr_to_rust(right, fields);
1478            let op_str = match op {
1479                ArithOp::Add => "+",
1480                ArithOp::Sub => "-",
1481                ArithOp::Mul => "*",
1482                ArithOp::Div => "/",
1483                ArithOp::Mod => "%",
1484            };
1485            format!("{} {} {}", l, op_str, r)
1486        }
1487        FormatExpr::IntLiteral(val) => format!("{}", val),
1488        FormatExpr::MapCall { map_name, args } => {
1489            let arg_strs: Vec<String> = args.iter().map(|a| expr_to_rust(a, fields)).collect();
1490            format!("{}({})", map_name, arg_strs.join(", "))
1491        }
1492        FormatExpr::BuiltinCall { func, args } => {
1493            let arg_strs: Vec<String> = args.iter().map(|a| expr_to_rust(a, fields)).collect();
1494            match func {
1495                BuiltinFunc::RotateRight => {
1496                    format!(
1497                        "({} as u32).rotate_right({} as u32)",
1498                        arg_strs.get(0).map(|s| s.as_str()).unwrap_or("0"),
1499                        arg_strs.get(1).map(|s| s.as_str()).unwrap_or("0")
1500                    )
1501                }
1502                BuiltinFunc::RotateLeft => {
1503                    format!(
1504                        "({} as u32).rotate_left({} as u32)",
1505                        arg_strs.get(0).map(|s| s.as_str()).unwrap_or("0"),
1506                        arg_strs.get(1).map(|s| s.as_str()).unwrap_or("0")
1507                    )
1508                }
1509            }
1510        }
1511        FormatExpr::SubDecoderAccess { field, fragment } => {
1512            format!("{}.{}", field, fragment)
1513        }
1514    }
1515}
1516
1517/// Generate Rust code for a guard condition.
1518fn generate_guard_code(
1519    guard: &Guard,
1520    fields: &[ResolvedField],
1521    type_maps: &HashMap<String, String>,
1522) -> String {
1523    let conditions: Vec<String> = guard
1524        .conditions
1525        .iter()
1526        .map(|cond| {
1527            let left = guard_operand_to_rust(&cond.left, fields);
1528            let right = guard_operand_to_rust(&cond.right, fields);
1529            let op = match cond.op {
1530                CompareOp::Eq => "==",
1531                CompareOp::Ne => "!=",
1532                CompareOp::Lt => "<",
1533                CompareOp::Le => "<=",
1534                CompareOp::Gt => ">",
1535                CompareOp::Ge => ">=",
1536            };
1537
1538            let left_field = match &cond.left {
1539                GuardOperand::Field(name) => fields.iter().find(|f| f.name == *name),
1540                _ => None,
1541            };
1542
1543            if let Some(f) = left_field {
1544                // Check for type-mapped wrapper types
1545                let wrapper = f
1546                    .resolved_type
1547                    .alias_name
1548                    .as_ref()
1549                    .and_then(|a| type_maps.get(a));
1550                if let Some(wrapper_path) = wrapper {
1551                    let short_name = wrapper_path.rsplit("::").next().unwrap_or(wrapper_path);
1552                    if let GuardOperand::Literal(val) = &cond.right {
1553                        return format!(
1554                            "{} {} {}::from({}{})",
1555                            left, op, short_name, val, f.resolved_type.base_type
1556                        );
1557                    }
1558                } else if f.resolved_type.base_type == "bool" {
1559                    // For bool fields, generate simpler comparisons
1560                    if let GuardOperand::Literal(val) = &cond.right {
1561                        match (cond.op, *val) {
1562                            (CompareOp::Eq, 0) => return format!("!{}", left),
1563                            (CompareOp::Eq, _) => return left.clone(),
1564                            (CompareOp::Ne, 0) => return left.clone(),
1565                            (CompareOp::Ne, _) => return format!("!{}", left),
1566                            _ => {}
1567                        }
1568                    }
1569                }
1570            }
1571
1572            format!("{} {} {}", left, op, right)
1573        })
1574        .collect();
1575
1576    conditions.join(" && ")
1577}
1578
1579fn guard_operand_to_rust(operand: &GuardOperand, fields: &[ResolvedField]) -> String {
1580    match operand {
1581        GuardOperand::Field(name) => name.clone(),
1582        GuardOperand::Literal(val) => format!("{}", val),
1583        GuardOperand::Expr { left, op, right } => {
1584            let l = guard_operand_to_rust(left, fields);
1585            let r = guard_operand_to_rust(right, fields);
1586            let op_str = match op {
1587                ArithOp::Add => "+",
1588                ArithOp::Sub => "-",
1589                ArithOp::Mul => "*",
1590                ArithOp::Div => "/",
1591                ArithOp::Mod => "%",
1592            };
1593            format!("({} {} {})", l, op_str, r)
1594        }
1595    }
1596}
1597
1598/// Generate the `write_asm` method.
1599fn generate_write_asm(out: &mut String, def: &ValidatedDef, enum_name: &str, trait_name: &str) {
1600    writeln!(
1601        out,
1602        "    pub fn write_asm<F: {}>(&self, f: &mut fmt::Formatter) -> fmt::Result {{",
1603        trait_name
1604    )
1605    .unwrap();
1606    writeln!(out, "        match self {{").unwrap();
1607
1608    for instr in &def.instructions {
1609        let variant_name = to_pascal_case(&instr.name);
1610        let method_name = format!("fmt_{}", instr.name);
1611
1612        if instr.resolved_fields.is_empty() {
1613            writeln!(
1614                out,
1615                "            {}::{} => F::{}(f),",
1616                enum_name, variant_name, method_name
1617            )
1618            .unwrap();
1619        } else {
1620            let field_names: Vec<String> = instr
1621                .resolved_fields
1622                .iter()
1623                .map(|f| f.name.clone())
1624                .collect();
1625            let destructure = format!(
1626                "{}::{} {{ {} }}",
1627                enum_name,
1628                variant_name,
1629                field_names.join(", ")
1630            );
1631
1632            let args: Vec<String> = instr
1633                .resolved_fields
1634                .iter()
1635                .map(|f| format!("*{}", f.name))
1636                .collect();
1637
1638            writeln!(
1639                out,
1640                "            {} => F::{}({}, f),",
1641                destructure,
1642                method_name,
1643                args.join(", ")
1644            )
1645            .unwrap();
1646        }
1647    }
1648
1649    writeln!(out, "        }}").unwrap();
1650    writeln!(out, "    }}").unwrap();
1651}
1652
1653fn word_type_for_width(width: u32) -> &'static str {
1654    match width {
1655        8 => "u8",
1656        16 => "u16",
1657        32 => "u32",
1658        _ => "u32",
1659    }
1660}
1661
1662pub(crate) fn signed_type_for(base: &str) -> &'static str {
1663    match base {
1664        "u8" | "i8" => "i8",
1665        "u16" | "i16" => "i16",
1666        "u32" | "i32" => "i32",
1667        _ => "i32",
1668    }
1669}
1670
1671pub(crate) fn type_bits(base: &str) -> u32 {
1672    match base {
1673        "u8" | "i8" => 8,
1674        "u16" | "i16" => 16,
1675        "u32" | "i32" => 32,
1676        _ => 32,
1677    }
1678}
1679
1680fn field_rust_type_with_maps(field: &ResolvedField, type_maps: &HashMap<String, String>) -> String {
1681    if field.resolved_type.sub_decoder.is_some() {
1682        let sd_name = field.resolved_type.sub_decoder.as_ref().unwrap();
1683        format!("{}Insn", sd_name)
1684    } else if let Some(ref alias) = field.resolved_type.alias_name {
1685        if let Some(rust_type) = type_maps.get(alias) {
1686            // Use the last segment of the Rust path as the type name
1687            rust_type
1688                .rsplit("::")
1689                .next()
1690                .unwrap_or(rust_type)
1691                .to_string()
1692        } else {
1693            field.resolved_type.base_type.clone()
1694        }
1695    } else {
1696        field.resolved_type.base_type.clone()
1697    }
1698}
1699
1700/// Convert a DSL instruction name to PascalCase.
1701/// - `addi` -> `Addi`
1702/// - `ld_b_c` -> `LdBC`
1703/// - `ADD` -> `Add`
1704pub fn to_pascal_case(name: &str) -> String {
1705    let mut result = String::new();
1706    let mut capitalize_next = true;
1707
1708    for ch in name.chars() {
1709        if ch == '_' {
1710            capitalize_next = true;
1711        } else if capitalize_next {
1712            result.push(ch.to_ascii_uppercase());
1713            capitalize_next = false;
1714        } else {
1715            result.push(ch.to_ascii_lowercase());
1716        }
1717    }
1718
1719    result
1720}
1721
1722/// Generate sub-decoder types, pre-baked string arrays, and inline dispatch function.
1723fn generate_subdecoder(out: &mut String, sd: &ValidatedSubDecoder, dispatch: crate::Dispatch) {
1724    let opcode_enum = format!("{}Opcode", sd.name);
1725    let insn_struct = format!("{}Insn", sd.name);
1726    let decode_fn = format!("decode_{}", to_snake_case(&sd.name));
1727    let word_type = word_type_for_width(sd.width);
1728    let lut_size = 1usize << sd.width;
1729    let use_inline = dispatch == crate::Dispatch::JumpTable;
1730
1731    // Generate map functions for sub-decoder maps
1732    if !sd.maps.is_empty() {
1733        for map in &sd.maps {
1734            let has_interpolation = map.entries.iter().any(|entry| {
1735                entry
1736                    .output
1737                    .iter()
1738                    .any(|p| matches!(p, FormatPiece::FieldRef { .. }))
1739            });
1740            let return_type = if has_interpolation {
1741                "String"
1742            } else {
1743                "&'static str"
1744            };
1745            let params: Vec<String> = map
1746                .params
1747                .iter()
1748                .map(|name| format!("{}: {}", name, word_type))
1749                .collect();
1750            writeln!(out, "#[allow(dead_code)]").unwrap();
1751            writeln!(
1752                out,
1753                "fn {}({}) -> {} {{",
1754                map.name,
1755                params.join(", "),
1756                return_type
1757            )
1758            .unwrap();
1759            if map.params.len() == 1 {
1760                writeln!(out, "    match {} {{", map.params[0]).unwrap();
1761            } else {
1762                let tuple: Vec<&str> = map.params.iter().map(|s| s.as_str()).collect();
1763                writeln!(out, "    match ({}) {{", tuple.join(", ")).unwrap();
1764            }
1765            let mut default_entry = None;
1766            for entry in &map.entries {
1767                let is_all_wildcard = entry.keys.iter().all(|k| matches!(k, MapKey::Wildcard));
1768                if is_all_wildcard {
1769                    default_entry = Some(entry);
1770                    continue;
1771                }
1772                let pattern = if map.params.len() == 1 {
1773                    format_map_key(&entry.keys[0])
1774                } else {
1775                    let keys: Vec<String> = entry.keys.iter().map(|k| format_map_key(k)).collect();
1776                    format!("({})", keys.join(", "))
1777                };
1778                let output = format_map_output(&entry.output, has_interpolation);
1779                writeln!(out, "        {} => {},", pattern, output).unwrap();
1780            }
1781            if let Some(entry) = default_entry {
1782                let output = format_map_output(&entry.output, has_interpolation);
1783                writeln!(out, "        _ => {},", output).unwrap();
1784            } else if has_interpolation {
1785                writeln!(out, "        _ => String::from(\"???\"),").unwrap();
1786            } else {
1787                writeln!(out, "        _ => \"???\",").unwrap();
1788            }
1789            writeln!(out, "    }}").unwrap();
1790            writeln!(out, "}}").unwrap();
1791            writeln!(out).unwrap();
1792        }
1793    }
1794
1795    // Opcode enum
1796    writeln!(out, "#[derive(Debug, Clone, Copy, PartialEq, Eq)]").unwrap();
1797    writeln!(out, "pub enum {} {{", opcode_enum).unwrap();
1798    for instr in &sd.instructions {
1799        writeln!(out, "    {},", to_pascal_case(&instr.name)).unwrap();
1800    }
1801    writeln!(out, "}}").unwrap();
1802    writeln!(out).unwrap();
1803
1804    // Instruction struct
1805    writeln!(out, "#[derive(Debug, Clone, Copy, PartialEq, Eq)]").unwrap();
1806    writeln!(out, "pub struct {} {{", insn_struct).unwrap();
1807    writeln!(out, "    pub opcode: {},", opcode_enum).unwrap();
1808    for frag_name in &sd.fragment_names {
1809        writeln!(out, "    pub {}: &'static str,", frag_name).unwrap();
1810    }
1811    writeln!(out, "}}").unwrap();
1812    writeln!(out).unwrap();
1813
1814    // Pre-baked static string arrays and handler functions for each instruction
1815    for instr in &sd.instructions {
1816        let handler_name = format!("_sd_{}", instr.name);
1817
1818        // For each fragment, check if it has interpolation
1819        // If it does, we need to pre-compute all possible strings
1820        let has_fields = !instr.resolved_fields.is_empty();
1821
1822        if has_fields {
1823            // Generate static arrays for fragments that reference fields
1824            for frag in &instr.fragments {
1825                let has_field_refs = frag
1826                    .pieces
1827                    .iter()
1828                    .any(|p| matches!(p, FormatPiece::FieldRef { .. }));
1829                if has_field_refs {
1830                    // Compute all possible string values
1831                    generate_prebaked_fragment_array(out, sd, instr, frag);
1832                }
1833            }
1834        }
1835
1836        // Generate handler function
1837        if use_inline {
1838            writeln!(out, "#[inline(always)]").unwrap();
1839        }
1840        let param_name = if has_fields { "val" } else { "_val" };
1841        writeln!(
1842            out,
1843            "fn {}({}: {}) -> {} {{",
1844            handler_name, param_name, word_type, insn_struct
1845        )
1846        .unwrap();
1847
1848        // Extract fields
1849        for field in &instr.resolved_fields {
1850            let extract = extract_expression("val", &field.ranges, word_type, sd.width / 8, "be");
1851            let expr = apply_transforms(&extract, &field.resolved_type);
1852            writeln!(out, "    let {} = {};", field.name, expr).unwrap();
1853        }
1854
1855        // Build the struct
1856        writeln!(out, "    {} {{", insn_struct).unwrap();
1857        writeln!(
1858            out,
1859            "        opcode: {}::{},",
1860            opcode_enum,
1861            to_pascal_case(&instr.name)
1862        )
1863        .unwrap();
1864
1865        for frag in &instr.fragments {
1866            let has_field_refs = frag
1867                .pieces
1868                .iter()
1869                .any(|p| matches!(p, FormatPiece::FieldRef { .. }));
1870            if has_field_refs {
1871                // Use pre-baked array lookup
1872                let array_name = prebaked_array_name(&instr.name, &frag.name);
1873                let index_expr = prebaked_index_expr(instr);
1874                writeln!(
1875                    out,
1876                    "        {}: {}[{} as usize],",
1877                    frag.name, array_name, index_expr
1878                )
1879                .unwrap();
1880            } else {
1881                // Static literal string
1882                let literal = pieces_to_static_str(&frag.pieces);
1883                writeln!(out, "        {}: \"{}\",", frag.name, literal).unwrap();
1884            }
1885        }
1886
1887        writeln!(out, "    }}").unwrap();
1888        writeln!(out, "}}").unwrap();
1889        writeln!(out).unwrap();
1890    }
1891
1892    // Build the dispatch match table
1893    // First, build a mapping from each value 0..2^width to the instruction that matches it
1894    let mut dispatch_table: Vec<Option<usize>> = vec![None; lut_size];
1895
1896    for (instr_idx, instr) in sd.instructions.iter().enumerate() {
1897        // For each possible value, check if this instruction's fixed bits match
1898        for val in 0..lut_size {
1899            let matches = instr.segments.iter().all(|seg| {
1900                if let Segment::Fixed {
1901                    ranges, pattern, ..
1902                } = seg
1903                {
1904                    let mut bit_idx = 0;
1905                    for range in ranges {
1906                        for i in 0..range.width() as usize {
1907                            if bit_idx < pattern.len() {
1908                                let hw_bit = range.start - i as u32;
1909                                let bit_val = (val >> hw_bit) & 1;
1910                                match pattern[bit_idx] {
1911                                    Bit::Zero if bit_val != 0 => return false,
1912                                    Bit::One if bit_val != 1 => return false,
1913                                    _ => {}
1914                                }
1915                                bit_idx += 1;
1916                            }
1917                        }
1918                    }
1919                    true
1920                } else {
1921                    true
1922                }
1923            });
1924            if matches && dispatch_table[val].is_none() {
1925                dispatch_table[val] = Some(instr_idx);
1926            }
1927        }
1928    }
1929
1930    match dispatch {
1931        crate::Dispatch::JumpTable => {
1932            // Generate a match statement with #[inline(always)]
1933            writeln!(out, "#[inline(always)]").unwrap();
1934            writeln!(
1935                out,
1936                "pub fn {}(val: {}) -> Option<{}> {{",
1937                decode_fn, word_type, insn_struct
1938            )
1939            .unwrap();
1940            writeln!(out, "    match val {{").unwrap();
1941
1942            let mut i = 0;
1943            while i < lut_size {
1944                let current = dispatch_table[i];
1945                let start = i;
1946                while i < lut_size && dispatch_table[i] == current {
1947                    i += 1;
1948                }
1949                let end = i - 1;
1950
1951                let handler_name = current.map(|idx| format!("_sd_{}", sd.instructions[idx].name));
1952
1953                let pattern = if start == end {
1954                    format!("{:#x}", start)
1955                } else {
1956                    format!("{:#x}..={:#x}", start, end)
1957                };
1958
1959                match handler_name {
1960                    Some(h) => writeln!(out, "        {} => Some({}(val)),", pattern, h).unwrap(),
1961                    None => writeln!(out, "        {} => None,", pattern).unwrap(),
1962                }
1963            }
1964
1965            writeln!(out, "    }}").unwrap();
1966            writeln!(out, "}}").unwrap();
1967            writeln!(out).unwrap();
1968        }
1969        crate::Dispatch::FnPtrLut => {
1970            // Generate a static fn ptr array and a dispatch function that indexes into it
1971            let handler_type = format!("fn({}) -> {}", word_type, insn_struct);
1972            let table_name = format!("_SD_{}_LUT", sd.name.to_uppercase());
1973
1974            writeln!(
1975                out,
1976                "static {}: [Option<{}>; {}] = [",
1977                table_name, handler_type, lut_size
1978            )
1979            .unwrap();
1980            for val in 0..lut_size {
1981                match dispatch_table[val] {
1982                    Some(idx) => {
1983                        writeln!(out, "    Some(_sd_{}),", sd.instructions[idx].name).unwrap()
1984                    }
1985                    None => writeln!(out, "    None,").unwrap(),
1986                }
1987            }
1988            writeln!(out, "];").unwrap();
1989            writeln!(out).unwrap();
1990
1991            writeln!(
1992                out,
1993                "pub fn {}(val: {}) -> Option<{}> {{",
1994                decode_fn, word_type, insn_struct
1995            )
1996            .unwrap();
1997            writeln!(out, "    {}[val as usize].map(|f| f(val))", table_name).unwrap();
1998            writeln!(out, "}}").unwrap();
1999            writeln!(out).unwrap();
2000        }
2001    }
2002}
2003
2004/// Generate a pre-baked static string array for a fragment that contains field interpolation.
2005fn generate_prebaked_fragment_array(
2006    out: &mut String,
2007    sd: &ValidatedSubDecoder,
2008    instr: &ValidatedSubInstruction,
2009    frag: &FragmentLine,
2010) {
2011    let array_name = prebaked_array_name(&instr.name, &frag.name);
2012    // Total combinations = product of all field value counts
2013    let total = prebaked_total_entries(instr);
2014
2015    writeln!(out, "static {}: [&str; {}] = [", array_name, total).unwrap();
2016
2017    for combo_idx in 0..total {
2018        // Compute field values for this combination
2019        let field_values = prebaked_field_values(instr, combo_idx);
2020        let s = evaluate_fragment_pieces(&frag.pieces, &field_values, &sd.maps);
2021        writeln!(out, "    \"{}\",", s).unwrap();
2022    }
2023
2024    writeln!(out, "];").unwrap();
2025    writeln!(out).unwrap();
2026}
2027
2028/// Evaluate fragment pieces with concrete field values to produce a string.
2029fn evaluate_fragment_pieces(
2030    pieces: &[FormatPiece],
2031    field_values: &HashMap<String, i64>,
2032    maps: &[MapDef],
2033) -> String {
2034    evaluate_fragment_pieces_str(pieces, field_values, maps)
2035}
2036
2037/// Evaluate a format expression with concrete field values.
2038fn evaluate_format_expr(
2039    expr: &FormatExpr,
2040    field_values: &HashMap<String, i64>,
2041    maps: &[MapDef],
2042) -> i64 {
2043    match expr {
2044        FormatExpr::Field(name) => *field_values.get(name).unwrap_or(&0),
2045        FormatExpr::IntLiteral(v) => *v,
2046        FormatExpr::Arithmetic { left, op, right } => {
2047            let l = evaluate_format_expr(left, field_values, maps);
2048            let r = evaluate_format_expr(right, field_values, maps);
2049            match op {
2050                ArithOp::Add => l + r,
2051                ArithOp::Sub => l - r,
2052                ArithOp::Mul => l * r,
2053                ArithOp::Div => {
2054                    if r != 0 {
2055                        l / r
2056                    } else {
2057                        0
2058                    }
2059                }
2060                ArithOp::Mod => {
2061                    if r != 0 {
2062                        l % r
2063                    } else {
2064                        0
2065                    }
2066                }
2067            }
2068        }
2069        _ => 0,
2070    }
2071}
2072
2073/// Evaluate a format expression that produces a string (for map calls in fragments).
2074fn evaluate_fragment_pieces_str(
2075    pieces: &[FormatPiece],
2076    field_values: &HashMap<String, i64>,
2077    maps: &[MapDef],
2078) -> String {
2079    let mut result = String::new();
2080    for piece in pieces {
2081        match piece {
2082            FormatPiece::Literal(s) => result.push_str(s),
2083            FormatPiece::FieldRef { expr, spec } => {
2084                match expr {
2085                    FormatExpr::MapCall { map_name, args } => {
2086                        // Evaluate map call
2087                        if let Some(map) = maps.iter().find(|m| m.name == *map_name) {
2088                            let arg_vals: Vec<i64> = args
2089                                .iter()
2090                                .map(|a| evaluate_format_expr(a, field_values, maps))
2091                                .collect();
2092                            let map_result = evaluate_map(map, &arg_vals);
2093                            result.push_str(&map_result);
2094                        }
2095                    }
2096                    FormatExpr::Field(name) => {
2097                        let val = *field_values.get(name).unwrap_or(&0);
2098                        match spec {
2099                            Some(s) if s.contains('x') || s.contains('X') => {
2100                                result.push_str(&format!("{:x}", val));
2101                            }
2102                            _ => result.push_str(&val.to_string()),
2103                        }
2104                    }
2105                    _ => {
2106                        let val = evaluate_format_expr(expr, field_values, maps);
2107                        result.push_str(&val.to_string());
2108                    }
2109                }
2110            }
2111        }
2112    }
2113    result
2114}
2115
2116/// Evaluate a map lookup with concrete key values.
2117fn evaluate_map(map: &MapDef, keys: &[i64]) -> String {
2118    for entry in &map.entries {
2119        let matches = entry.keys.iter().zip(keys.iter()).all(|(k, v)| match k {
2120            MapKey::Value(expected) => *expected == *v,
2121            MapKey::Wildcard => true,
2122        });
2123        if matches {
2124            // Entry output is Vec<FormatPiece>, for static maps it should be all literals
2125            let mut s = String::new();
2126            for piece in &entry.output {
2127                if let FormatPiece::Literal(lit) = piece {
2128                    s.push_str(lit);
2129                }
2130            }
2131            return s;
2132        }
2133    }
2134    "???".to_string()
2135}
2136
2137/// Get total number of pre-baked entries (product of all field ranges).
2138fn prebaked_total_entries(instr: &ValidatedSubInstruction) -> usize {
2139    let mut total = 1usize;
2140    for field in &instr.resolved_fields {
2141        let field_bits: u32 = field.ranges.iter().map(|r| r.width()).sum();
2142        total *= 1 << field_bits;
2143    }
2144    total
2145}
2146
2147/// Compute field values for a given combination index.
2148fn prebaked_field_values(
2149    instr: &ValidatedSubInstruction,
2150    combo_idx: usize,
2151) -> HashMap<String, i64> {
2152    let mut values = HashMap::new();
2153    let mut remaining = combo_idx;
2154
2155    for field in instr.resolved_fields.iter().rev() {
2156        let field_bits: u32 = field.ranges.iter().map(|r| r.width()).sum();
2157        let field_range = 1 << field_bits;
2158        let val = remaining % field_range;
2159        remaining /= field_range;
2160        values.insert(field.name.clone(), val as i64);
2161    }
2162
2163    values
2164}
2165
2166/// Generate the index expression for looking up in the pre-baked array.
2167fn prebaked_index_expr(instr: &ValidatedSubInstruction) -> String {
2168    if instr.resolved_fields.len() == 1 {
2169        return instr.resolved_fields[0].name.clone();
2170    }
2171
2172    // Multi-field: combine into a single index
2173    let mut parts = Vec::new();
2174    let mut accumulated_bits = 0u32;
2175
2176    for field in instr.resolved_fields.iter().rev() {
2177        let field_bits: u32 = field.ranges.iter().map(|r| r.width()).sum();
2178        if accumulated_bits > 0 {
2179            parts.push(format!(
2180                "(({} as usize) << {})",
2181                field.name, accumulated_bits
2182            ));
2183        } else {
2184            parts.push(format!("({} as usize)", field.name));
2185        }
2186        accumulated_bits += field_bits;
2187    }
2188
2189    parts.reverse();
2190    parts.join(" | ")
2191}
2192
2193/// Get the name of a pre-baked array for a specific instruction and fragment.
2194fn prebaked_array_name(instr_name: &str, frag_name: &str) -> String {
2195    format!(
2196        "_SD_{}_{}",
2197        instr_name.to_uppercase(),
2198        frag_name.to_uppercase()
2199    )
2200}
2201
2202/// Convert pieces that are all literals into a single string.
2203fn pieces_to_static_str(pieces: &[FormatPiece]) -> String {
2204    let mut s = String::new();
2205    for piece in pieces {
2206        if let FormatPiece::Literal(lit) = piece {
2207            s.push_str(lit);
2208        }
2209    }
2210    s
2211}
2212
2213/// Convert a name to snake_case (for function names).
2214fn to_snake_case(name: &str) -> String {
2215    let mut result = String::new();
2216    for (i, ch) in name.chars().enumerate() {
2217        if ch.is_ascii_uppercase() && i > 0 {
2218            result.push('_');
2219        }
2220        result.push(ch.to_ascii_lowercase());
2221    }
2222    result
2223}
2224
2225#[cfg(test)]
2226mod tests {
2227    use super::*;
2228
2229    #[test]
2230    fn test_to_pascal_case() {
2231        assert_eq!(to_pascal_case("addi"), "Addi");
2232        assert_eq!(to_pascal_case("ld_b_c"), "LdBC");
2233        assert_eq!(to_pascal_case("ADD"), "Add");
2234        assert_eq!(to_pascal_case("nop"), "Nop");
2235    }
2236
2237    #[test]
2238    fn test_extract_expression() {
2239        // Extract bits [5:0] (6 bits from position 5 down to 0)
2240        let range = BitRange::new(5, 0);
2241        assert_eq!(
2242            extract_expression("opcode", &[range], "u32", 4, "be"),
2243            "opcode & 0x3f"
2244        );
2245
2246        // Extract bits [31:26] (6 bits from position 31 down to 26)
2247        let range = BitRange::new(31, 26);
2248        assert_eq!(
2249            extract_expression("opcode", &[range], "u32", 4, "be"),
2250            "(opcode >> 26) & 0x3f"
2251        );
2252
2253        // Extract bits from unit 1 in variable-length mode (width=16, unit_bytes=2)
2254        let range = BitRange::new_in_unit(1, 15, 0);
2255        assert_eq!(
2256            extract_expression("opcode", &[range], "u16", 2, "be"),
2257            "u16::from_be_bytes(data[2..4].try_into().unwrap()) & 0xffff"
2258        );
2259
2260        // Extract cross-unit field (width=16, bits [8:23] -> 2 units)
2261        // Unit 0: bits 8-15 (8 bits), Unit 1: bits 0-7 (8 bits)
2262        let range0 = BitRange::new_in_unit(0, 7, 0); // 8 bits from unit 0
2263        let range1 = BitRange::new_in_unit(1, 15, 8); // 8 bits from unit 1
2264        let result = extract_expression("opcode", &[range0, range1], "u16", 2, "be");
2265        assert!(result.contains("opcode & 0xff"));
2266        assert!(result.contains("u16::from_be_bytes(data[2..4].try_into().unwrap())"));
2267    }
2268}