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