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