device_driver_generation/dsl_hir/
mir_transform.rs

1use proc_macro2::Span;
2use quote::ToTokens;
3
4use crate::{dsl_hir, mir};
5
6pub fn transform(device: dsl_hir::Device) -> Result<mir::Device, syn::Error> {
7    let global_config = device.global_config_list.try_into()?;
8    let objects = transform_object_list(device.object_list, &global_config)?;
9
10    Ok(mir::Device {
11        name: None,
12        global_config,
13        objects,
14    })
15}
16
17impl From<dsl_hir::Access> for mir::Access {
18    fn from(value: dsl_hir::Access) -> Self {
19        match value {
20            dsl_hir::Access::RW => mir::Access::RW,
21            dsl_hir::Access::RO => mir::Access::RO,
22            dsl_hir::Access::WO => mir::Access::WO,
23        }
24    }
25}
26
27impl From<dsl_hir::ByteOrder> for mir::ByteOrder {
28    fn from(value: dsl_hir::ByteOrder) -> Self {
29        match value {
30            dsl_hir::ByteOrder::LE => mir::ByteOrder::LE,
31            dsl_hir::ByteOrder::BE => mir::ByteOrder::BE,
32        }
33    }
34}
35
36impl From<dsl_hir::BitOrder> for mir::BitOrder {
37    fn from(value: dsl_hir::BitOrder) -> Self {
38        match value {
39            dsl_hir::BitOrder::LSB0 => mir::BitOrder::LSB0,
40            dsl_hir::BitOrder::MSB0 => mir::BitOrder::MSB0,
41        }
42    }
43}
44
45impl TryFrom<syn::Ident> for mir::Integer {
46    type Error = syn::Error;
47
48    fn try_from(value: syn::Ident) -> Result<Self, Self::Error> {
49        match value.to_string().as_str() {
50            "u8" => Ok(mir::Integer::U8),
51            "u16" => Ok(mir::Integer::U16),
52            "u32" => Ok(mir::Integer::U32),
53            "i8" => Ok(mir::Integer::I8),
54            "i16" => Ok(mir::Integer::I16),
55            "i32" => Ok(mir::Integer::I32),
56            "i64" => Ok(mir::Integer::I64),
57            _ => Err(syn::Error::new(
58                value.span(),
59                "Must be an integer type: u8, u16, u32, i8, i16, i32, i64",
60            )),
61        }
62    }
63}
64
65impl TryFrom<dsl_hir::Repeat> for mir::Repeat {
66    type Error = syn::Error;
67
68    fn try_from(value: dsl_hir::Repeat) -> Result<Self, Self::Error> {
69        Ok(Self {
70            count: value.count.base10_parse()?,
71            stride: value.stride.base10_parse()?,
72        })
73    }
74}
75
76impl From<dsl_hir::BaseType> for mir::BaseType {
77    fn from(value: dsl_hir::BaseType) -> Self {
78        match value {
79            dsl_hir::BaseType::Bool => mir::BaseType::Bool,
80            dsl_hir::BaseType::Uint => mir::BaseType::Uint,
81            dsl_hir::BaseType::Int => mir::BaseType::Int,
82        }
83    }
84}
85
86impl TryFrom<dsl_hir::GlobalConfigList> for mir::GlobalConfig {
87    type Error = syn::Error;
88
89    fn try_from(value: dsl_hir::GlobalConfigList) -> Result<Self, Self::Error> {
90        let mut global_config = mir::GlobalConfig::default();
91
92        for config in value.configs.iter() {
93            let same_config_count = value
94                .configs
95                .iter()
96                .filter(|check_config| {
97                    std::mem::discriminant(*check_config) == std::mem::discriminant(config)
98                })
99                .count();
100
101            if same_config_count > 1 {
102                return Err(syn::Error::new(
103                    Span::call_site(),
104                    format!("Duplicate global config found: `{config:?}`"),
105                ));
106            }
107
108            match config.clone() {
109                dsl_hir::GlobalConfig::DefaultRegisterAccess(value) => {
110                    global_config.default_register_access = value.into()
111                }
112                dsl_hir::GlobalConfig::DefaultFieldAccess(value) => {
113                    global_config.default_field_access = value.into()
114                }
115                dsl_hir::GlobalConfig::DefaultBufferAccess(value) => {
116                    global_config.default_buffer_access = value.into()
117                }
118                dsl_hir::GlobalConfig::DefaultByteOrder(value) => {
119                    global_config.default_byte_order = Some(value.into())
120                }
121                dsl_hir::GlobalConfig::DefaultBitOrder(value) => {
122                    global_config.default_bit_order = value.into()
123                }
124                dsl_hir::GlobalConfig::RegisterAddressType(value) => {
125                    global_config.register_address_type = Some(value.try_into()?)
126                }
127                dsl_hir::GlobalConfig::CommandAddressType(value) => {
128                    global_config.command_address_type = Some(value.try_into()?)
129                }
130                dsl_hir::GlobalConfig::BufferAddressType(value) => {
131                    global_config.buffer_address_type = Some(value.try_into()?)
132                }
133                dsl_hir::GlobalConfig::NameWordBoundaries(value) => {
134                    global_config.name_word_boundaries = value
135                }
136                dsl_hir::GlobalConfig::DefmtFeature(lit_str) => {
137                    global_config.defmt_feature = Some(lit_str.value())
138                }
139            }
140        }
141
142        Ok(global_config)
143    }
144}
145
146fn get_description(attrs: &dsl_hir::AttributeList) -> Option<String> {
147    let str = attrs
148        .attributes
149        .iter()
150        .filter_map(|attr| match attr {
151            dsl_hir::Attribute::Doc(val) => Some(val.as_str()),
152            dsl_hir::Attribute::Cfg(_, _) => None,
153        })
154        .collect::<Vec<_>>()
155        .join("\n");
156
157    if str.is_empty() { None } else { Some(str) }
158}
159
160fn get_cfg_attr(attrs: &dsl_hir::AttributeList) -> Result<mir::Cfg, syn::Error> {
161    let mut cfg_attrs = attrs
162        .attributes
163        .iter()
164        .filter_map(|attr| match attr {
165            dsl_hir::Attribute::Cfg(val, span) => Some((val, span)),
166            dsl_hir::Attribute::Doc(_) => None,
167        })
168        .collect::<Vec<_>>();
169
170    match cfg_attrs.len() {
171        0 => Ok(mir::Cfg::new(None)),
172        1 => Ok(mir::Cfg::new(Some(&cfg_attrs.remove(0).0.clone()))),
173        n => Err(syn::Error::new(
174            *cfg_attrs.remove(1).1,
175            format!("Only one cfg attribute is allowed, but {n} are found"),
176        )),
177    }
178}
179
180fn transform_object_list(
181    list: dsl_hir::ObjectList,
182    global_config: &mir::GlobalConfig,
183) -> Result<Vec<mir::Object>, syn::Error> {
184    let mut objects = Vec::new();
185
186    for object in list.objects.into_iter() {
187        let object = match object {
188            dsl_hir::Object::Block(block) => {
189                mir::Object::Block(transform_block(block, global_config)?)
190            }
191            dsl_hir::Object::Register(register) => {
192                mir::Object::Register(transform_register(register, global_config)?)
193            }
194            dsl_hir::Object::Command(command) => {
195                mir::Object::Command(transform_command(command, global_config)?)
196            }
197            dsl_hir::Object::Buffer(buffer) => {
198                mir::Object::Buffer(transform_buffer(buffer, global_config)?)
199            }
200            dsl_hir::Object::Ref(ref_object) => mir::Object::Ref(transform_ref(ref_object)?),
201        };
202
203        objects.push(object);
204    }
205
206    Ok(objects)
207}
208
209fn transform_block(
210    block: dsl_hir::Block,
211    global_config: &mir::GlobalConfig,
212) -> Result<mir::Block, syn::Error> {
213    Ok(mir::Block {
214        cfg_attr: get_cfg_attr(&block.attribute_list)?,
215        description: get_description(&block.attribute_list).unwrap_or_default(),
216        name: block.identifier.to_string(),
217        address_offset: block
218            .block_item_list
219            .block_items
220            .iter()
221            .find_map(|item| match item {
222                dsl_hir::BlockItem::AddressOffset(address_offset) => {
223                    Some(address_offset.base10_parse())
224                }
225                _ => None,
226            })
227            .transpose()?
228            .unwrap_or(0),
229        repeat: block
230            .block_item_list
231            .block_items
232            .iter()
233            .find_map(|item| match item {
234                dsl_hir::BlockItem::Repeat(repeat) => Some(repeat.clone().try_into()),
235                _ => None,
236            })
237            .transpose()?,
238        objects: transform_object_list(block.object_list, global_config)?,
239    })
240}
241
242fn transform_register(
243    register: dsl_hir::Register,
244    global_config: &mir::GlobalConfig,
245) -> Result<mir::Register, syn::Error> {
246    Ok(mir::Register {
247        cfg_attr: get_cfg_attr(&register.attribute_list)?,
248        description: get_description(&register.attribute_list).unwrap_or_default(),
249        name: register.identifier.to_string(),
250        access: register
251            .register_item_list
252            .register_items
253            .iter()
254            .find_map(|i| match i {
255                dsl_hir::RegisterItem::Access(access) => Some((*access).into()),
256                _ => None,
257            })
258            .unwrap_or(global_config.default_register_access),
259        byte_order: register
260            .register_item_list
261            .register_items
262            .iter()
263            .find_map(|i| match i {
264                dsl_hir::RegisterItem::ByteOrder(bo) => Some((*bo).into()),
265                _ => None,
266            }),
267        bit_order: register
268            .register_item_list
269            .register_items
270            .iter()
271            .find_map(|i| match i {
272                dsl_hir::RegisterItem::BitOrder(bi) => Some((*bi).into()),
273                _ => None,
274            })
275            .unwrap_or(global_config.default_bit_order),
276        allow_bit_overlap: register
277            .register_item_list
278            .register_items
279            .iter()
280            .find_map(|i| match i {
281                dsl_hir::RegisterItem::AllowBitOverlap(b) => Some(b.value),
282                _ => None,
283            })
284            .unwrap_or_default(),
285        allow_address_overlap: register
286            .register_item_list
287            .register_items
288            .iter()
289            .find_map(|i| match i {
290                dsl_hir::RegisterItem::AllowAddressOverlap(b) => Some(b.value),
291                _ => None,
292            })
293            .unwrap_or_default(),
294        address: register
295            .register_item_list
296            .register_items
297            .iter()
298            .find_map(|i| match i {
299                dsl_hir::RegisterItem::Address(addr) => Some(addr.base10_parse()),
300                _ => None,
301            })
302            .transpose()?
303            .ok_or_else(|| {
304                syn::Error::new(
305                    register.identifier.span(),
306                    format!("Register `{}` must have an address", register.identifier),
307                )
308            })?,
309        size_bits: register
310            .register_item_list
311            .register_items
312            .iter()
313            .find_map(|i| match i {
314                dsl_hir::RegisterItem::SizeBits(sb) => Some(sb.base10_parse()),
315                _ => None,
316            })
317            .transpose()?
318            .ok_or_else(|| {
319                syn::Error::new(
320                    register.identifier.span(),
321                    format!(
322                        "Register `{}` must have size bits specified",
323                        register.identifier
324                    ),
325                )
326            })?,
327        reset_value: register
328            .register_item_list
329            .register_items
330            .iter()
331            .find_map(|i| match i {
332                dsl_hir::RegisterItem::ResetValueArray(arr) => {
333                    Some(Ok(mir::ResetValue::Array(arr.clone())))
334                }
335                dsl_hir::RegisterItem::ResetValueInt(int) => Some(
336                    int.base10_parse::<i128>()
337                        .map(|v| v as u128)
338                        .or_else(|_| int.base10_parse::<u128>())
339                        .map_err(|e| {
340                            syn::Error::new(
341                                int.span(),
342                                format!("{e}: number is parsed as an i128 or u128"),
343                            )
344                        })
345                        .map(mir::ResetValue::Integer),
346                ),
347                _ => None,
348            })
349            .transpose()?,
350        repeat: register
351            .register_item_list
352            .register_items
353            .iter()
354            .find_map(|i| match i {
355                dsl_hir::RegisterItem::Repeat(repeat) => Some(repeat.clone().try_into()),
356                _ => None,
357            })
358            .transpose()?,
359        fields: register
360            .field_list
361            .fields
362            .iter()
363            .map(|field| transform_field(field, global_config))
364            .collect::<Result<_, _>>()?,
365    })
366}
367
368fn transform_command(
369    command: dsl_hir::Command,
370    global_config: &mir::GlobalConfig,
371) -> Result<mir::Command, syn::Error> {
372    let command_value = command.value.ok_or_else(|| {
373        syn::Error::new(
374            command.identifier.span(),
375            format!("Command `{}` must have a value", command.identifier),
376        )
377    })?;
378    Ok(mir::Command {
379        cfg_attr: get_cfg_attr(&command.attribute_list)?,
380        description: get_description(&command.attribute_list).unwrap_or_default(),
381        name: command.identifier.to_string(),
382        address: match &command_value {
383            dsl_hir::CommandValue::Basic(lit) => lit,
384            dsl_hir::CommandValue::Extended {
385                command_item_list, ..
386            } => command_item_list
387                .items
388                .iter()
389                .find_map(|item| match item {
390                    dsl_hir::CommandItem::Address(lit) => Some(lit),
391                    _ => None,
392                })
393                .ok_or_else(|| {
394                    syn::Error::new(
395                        command.identifier.span(),
396                        format!("Command `{}` must have an address", command.identifier),
397                    )
398                })?,
399        }
400        .base10_parse()?,
401        byte_order: match &command_value {
402            dsl_hir::CommandValue::Basic(_) => None,
403            dsl_hir::CommandValue::Extended {
404                command_item_list, ..
405            } => command_item_list.items.iter().find_map(|item| match item {
406                dsl_hir::CommandItem::ByteOrder(order) => Some((*order).into()),
407                _ => None,
408            }),
409        },
410        bit_order: match &command_value {
411            dsl_hir::CommandValue::Basic(_) => None,
412            dsl_hir::CommandValue::Extended {
413                command_item_list, ..
414            } => command_item_list.items.iter().find_map(|item| match item {
415                dsl_hir::CommandItem::BitOrder(order) => Some((*order).into()),
416                _ => None,
417            }),
418        }
419        .unwrap_or(global_config.default_bit_order),
420        allow_bit_overlap: match &command_value {
421            dsl_hir::CommandValue::Basic(_) => None,
422            dsl_hir::CommandValue::Extended {
423                command_item_list, ..
424            } => command_item_list.items.iter().find_map(|item| match item {
425                dsl_hir::CommandItem::AllowBitOverlap(b) => Some(b.value),
426                _ => None,
427            }),
428        }
429        .unwrap_or_default(),
430        allow_address_overlap: match &command_value {
431            dsl_hir::CommandValue::Basic(_) => None,
432            dsl_hir::CommandValue::Extended {
433                command_item_list, ..
434            } => command_item_list.items.iter().find_map(|item| match item {
435                dsl_hir::CommandItem::AllowAddressOverlap(b) => Some(b.value),
436                _ => None,
437            }),
438        }
439        .unwrap_or_default(),
440        size_bits_in: match &command_value {
441            dsl_hir::CommandValue::Basic(_) => None,
442            dsl_hir::CommandValue::Extended {
443                command_item_list, ..
444            } => command_item_list.items.iter().find_map(|item| match item {
445                dsl_hir::CommandItem::SizeBitsIn(size) => Some(size.base10_parse()),
446                _ => None,
447            }),
448        }
449        .unwrap_or(Ok(0))?,
450        size_bits_out: match &command_value {
451            dsl_hir::CommandValue::Basic(_) => None,
452            dsl_hir::CommandValue::Extended {
453                command_item_list, ..
454            } => command_item_list.items.iter().find_map(|item| match item {
455                dsl_hir::CommandItem::SizeBitsOut(size) => Some(size.base10_parse()),
456                _ => None,
457            }),
458        }
459        .unwrap_or(Ok(0))?,
460        repeat: match &command_value {
461            dsl_hir::CommandValue::Basic(_) => None,
462            dsl_hir::CommandValue::Extended {
463                command_item_list, ..
464            } => command_item_list.items.iter().find_map(|item| match item {
465                dsl_hir::CommandItem::Repeat(repeat) => Some(repeat.clone().try_into()),
466                _ => None,
467            }),
468        }
469        .transpose()?,
470        in_fields: match &command_value {
471            dsl_hir::CommandValue::Basic(_)
472            | dsl_hir::CommandValue::Extended {
473                in_field_list: None,
474                ..
475            } => Vec::new(),
476            dsl_hir::CommandValue::Extended {
477                in_field_list: Some(in_field_list),
478                ..
479            } => in_field_list
480                .fields
481                .iter()
482                .map(|field| transform_field(field, global_config))
483                .collect::<Result<_, _>>()?,
484        },
485        out_fields: match &command_value {
486            dsl_hir::CommandValue::Basic(_)
487            | dsl_hir::CommandValue::Extended {
488                out_field_list: None,
489                ..
490            } => Vec::new(),
491            dsl_hir::CommandValue::Extended {
492                out_field_list: Some(out_field_list),
493                ..
494            } => out_field_list
495                .fields
496                .iter()
497                .map(|field| transform_field(field, global_config))
498                .collect::<Result<_, _>>()?,
499        },
500    })
501}
502
503fn transform_field(
504    field: &dsl_hir::Field,
505    global_config: &mir::GlobalConfig,
506) -> Result<mir::Field, syn::Error> {
507    let field_cfg_attr = get_cfg_attr(&field.attribute_list)?;
508    let field_description = get_description(&field.attribute_list).unwrap_or_default();
509
510    Ok(mir::Field {
511        cfg_attr: field_cfg_attr.clone(),
512        description: field_description.clone(),
513        name: field.identifier.to_string(),
514        access: field
515            .access
516            .map(Into::into)
517            .unwrap_or(global_config.default_field_access),
518        base_type: field.base_type.into(),
519        field_conversion: field
520            .field_conversion
521            .as_ref()
522            .map(|fc| transform_field_conversion(field_description, fc))
523            .transpose()?,
524        field_address: match &field.field_address {
525            dsl_hir::FieldAddress::Integer(start) if field.base_type.is_bool() => {
526                start.base10_parse()?..start.base10_parse()?
527            }
528            dsl_hir::FieldAddress::Integer(_) => {
529                return Err(syn::Error::new(
530                    field.identifier.span(),
531                    format!(
532                        "Field `{}` has a non-bool base type and must specify the start and the end address",
533                        field.identifier
534                    ),
535                ));
536            }
537            dsl_hir::FieldAddress::Range { start, end } => {
538                start.base10_parse()?..end.base10_parse()?
539            }
540            dsl_hir::FieldAddress::RangeInclusive { start, end } => {
541                start.base10_parse()?..(end.base10_parse::<u32>()? + 1)
542            }
543        },
544    })
545}
546
547fn transform_field_conversion(
548    field_description: String,
549    field_conversion: &dsl_hir::FieldConversion,
550) -> Result<mir::FieldConversion, syn::Error> {
551    match field_conversion {
552        dsl_hir::FieldConversion::Direct { path, use_try } => Ok(mir::FieldConversion::Direct {
553            type_name: path
554                .to_token_stream()
555                .to_string()
556                .replace(char::is_whitespace, ""),
557            use_try: *use_try,
558        }),
559        dsl_hir::FieldConversion::Enum {
560            identifier,
561            enum_variant_list,
562            use_try,
563        } => Ok(mir::FieldConversion::Enum {
564            enum_value: mir::Enum::new(
565                field_description,
566                identifier.to_string(),
567                enum_variant_list
568                    .variants
569                    .iter()
570                    .map(|v| {
571                        Ok(mir::EnumVariant {
572                            cfg_attr: get_cfg_attr(&v.attribute_list)?,
573                            description: get_description(&v.attribute_list).unwrap_or_default(),
574                            name: v.identifier.to_string(),
575                            value: match &v.enum_value {
576                                None => mir::EnumValue::Unspecified,
577                                Some(dsl_hir::EnumValue::Specified(val)) => {
578                                    mir::EnumValue::Specified(val.base10_parse()?)
579                                }
580                                Some(dsl_hir::EnumValue::Default) => mir::EnumValue::Default,
581                                Some(dsl_hir::EnumValue::CatchAll) => mir::EnumValue::CatchAll,
582                            },
583                        })
584                    })
585                    .collect::<Result<_, syn::Error>>()?,
586            ),
587            use_try: *use_try,
588        }),
589    }
590}
591
592fn transform_buffer(
593    buffer: dsl_hir::Buffer,
594    global_config: &mir::GlobalConfig,
595) -> Result<mir::Buffer, syn::Error> {
596    Ok(mir::Buffer {
597        cfg_attr: get_cfg_attr(&buffer.attribute_list)?,
598        description: get_description(&buffer.attribute_list).unwrap_or_default(),
599        name: buffer.identifier.to_string(),
600        access: buffer
601            .access
602            .map(Into::into)
603            .unwrap_or(global_config.default_buffer_access),
604        address: buffer
605            .address
606            .ok_or_else(|| {
607                syn::Error::new(
608                    buffer.identifier.span(),
609                    format!("Buffer `{}` must have an address", buffer.identifier),
610                )
611            })?
612            .base10_parse()?,
613    })
614}
615
616fn transform_ref(ref_object: dsl_hir::RefObject) -> Result<mir::RefObject, syn::Error> {
617    Ok(mir::RefObject {
618        cfg_attr: get_cfg_attr(&ref_object.attribute_list)?,
619        description: get_description(&ref_object.attribute_list).unwrap_or_default(),
620        name: ref_object.identifier.to_string(),
621        object_override: match *ref_object.object {
622            dsl_hir::Object::Block(block_override) => {
623                mir::ObjectOverride::Block(transform_block_override(block_override)?)
624            }
625            dsl_hir::Object::Register(register_override) => {
626                mir::ObjectOverride::Register(transform_register_override(register_override)?)
627            }
628            dsl_hir::Object::Command(command_override) => {
629                mir::ObjectOverride::Command(transform_command_override(command_override)?)
630            }
631            dsl_hir::Object::Buffer(_) => {
632                return Err(syn::Error::new(
633                    ref_object.identifier.span(),
634                    format!("Ref `{}` cannot ref a buffer", ref_object.identifier),
635                ));
636            }
637            dsl_hir::Object::Ref(_) => {
638                return Err(syn::Error::new(
639                    ref_object.identifier.span(),
640                    format!(
641                        "Ref `{}` cannot ref another ref object",
642                        ref_object.identifier
643                    ),
644                ));
645            }
646        },
647    })
648}
649
650fn transform_block_override(
651    block_override: dsl_hir::Block,
652) -> Result<mir::BlockOverride, syn::Error> {
653    if !block_override.attribute_list.attributes.is_empty() {
654        return Err(syn::Error::new(
655            block_override.identifier.span(),
656            "No attributes (cfg or doc) are allowed on block overrides",
657        ));
658    }
659
660    if !block_override.object_list.objects.is_empty() {
661        return Err(syn::Error::new(
662            block_override.identifier.span(),
663            "No objects may be defined on block overrides",
664        ));
665    }
666
667    Ok(mir::BlockOverride {
668        name: block_override.identifier.to_string(),
669        address_offset: block_override
670            .block_item_list
671            .block_items
672            .iter()
673            .find_map(|item| match item {
674                dsl_hir::BlockItem::AddressOffset(address_offset) => {
675                    Some(address_offset.base10_parse())
676                }
677                _ => None,
678            })
679            .transpose()?,
680        repeat: block_override
681            .block_item_list
682            .block_items
683            .iter()
684            .find_map(|item| match item {
685                dsl_hir::BlockItem::Repeat(repeat) => Some(repeat.clone().try_into()),
686                _ => None,
687            })
688            .transpose()?,
689    })
690}
691
692fn transform_register_override(
693    register_override: dsl_hir::Register,
694) -> Result<mir::RegisterOverride, syn::Error> {
695    if !register_override.attribute_list.attributes.is_empty() {
696        return Err(syn::Error::new(
697            register_override.identifier.span(),
698            "No attributes (cfg or doc) are allowed on register overrides",
699        ));
700    }
701
702    if !register_override.field_list.fields.is_empty() {
703        return Err(syn::Error::new(
704            register_override.identifier.span(),
705            "No fields are allowed on register overrides",
706        ));
707    }
708
709    for item in register_override.register_item_list.register_items.iter() {
710        match item {
711            dsl_hir::RegisterItem::ByteOrder(_) => {
712                return Err(syn::Error::new(
713                    register_override.identifier.span(),
714                    "No `ByteOrder` is allowed on register overrides",
715                ));
716            }
717            dsl_hir::RegisterItem::BitOrder(_) => {
718                return Err(syn::Error::new(
719                    register_override.identifier.span(),
720                    "No `BitOrder` is allowed on register overrides",
721                ));
722            }
723            dsl_hir::RegisterItem::SizeBits(_) => {
724                return Err(syn::Error::new(
725                    register_override.identifier.span(),
726                    "No `SizeBits` is allowed on register overrides",
727                ));
728            }
729            dsl_hir::RegisterItem::AllowBitOverlap(_) => {
730                return Err(syn::Error::new(
731                    register_override.identifier.span(),
732                    "No `AllowBitOverlap` is allowed on register overrides",
733                ));
734            }
735            dsl_hir::RegisterItem::Access(_) => {}
736            dsl_hir::RegisterItem::Address(_) => {}
737            dsl_hir::RegisterItem::ResetValueInt(_) => {}
738            dsl_hir::RegisterItem::ResetValueArray(_) => {}
739            dsl_hir::RegisterItem::Repeat(_) => {}
740            dsl_hir::RegisterItem::AllowAddressOverlap(_) => {}
741        }
742    }
743
744    Ok(mir::RegisterOverride {
745        name: register_override.identifier.to_string(),
746        access: register_override
747            .register_item_list
748            .register_items
749            .iter()
750            .find_map(|i| match i {
751                dsl_hir::RegisterItem::Access(access) => Some((*access).into()),
752                _ => None,
753            }),
754        address: register_override
755            .register_item_list
756            .register_items
757            .iter()
758            .find_map(|i| match i {
759                dsl_hir::RegisterItem::Address(addr) => Some(addr.base10_parse()),
760                _ => None,
761            })
762            .transpose()?,
763        allow_address_overlap: register_override
764            .register_item_list
765            .register_items
766            .iter()
767            .find_map(|i| match i {
768                dsl_hir::RegisterItem::AllowAddressOverlap(b) => Some(b.value),
769                _ => None,
770            })
771            .unwrap_or_default(),
772        reset_value: register_override
773            .register_item_list
774            .register_items
775            .iter()
776            .find_map(|i| match i {
777                dsl_hir::RegisterItem::ResetValueArray(arr) => {
778                    Some(Ok(mir::ResetValue::Array(arr.clone())))
779                }
780                dsl_hir::RegisterItem::ResetValueInt(int) => Some(
781                    int.base10_parse::<i128>()
782                        .map(|v| v as u128)
783                        .or_else(|_| int.base10_parse::<u128>())
784                        .map_err(|e| {
785                            syn::Error::new(
786                                int.span(),
787                                format!("{e}: number is parsed as an i128 or u128"),
788                            )
789                        })
790                        .map(mir::ResetValue::Integer),
791                ),
792                _ => None,
793            })
794            .transpose()?,
795        repeat: register_override
796            .register_item_list
797            .register_items
798            .iter()
799            .find_map(|i| match i {
800                dsl_hir::RegisterItem::Repeat(repeat) => Some(repeat.clone().try_into()),
801                _ => None,
802            })
803            .transpose()?,
804    })
805}
806
807fn transform_command_override(
808    command_override: dsl_hir::Command,
809) -> Result<mir::CommandOverride, syn::Error> {
810    if !command_override.attribute_list.attributes.is_empty() {
811        return Err(syn::Error::new(
812            command_override.identifier.span(),
813            "No attributes (cfg or doc) are allowed on command overrides",
814        ));
815    }
816
817    let command_item_list = match &command_override.value {
818        Some(dsl_hir::CommandValue::Extended {
819            in_field_list: Some(_),
820            ..
821        }) => {
822            return Err(syn::Error::new(
823                command_override.identifier.span(),
824                "No `in` field list is allowed on command overrides",
825            ));
826        }
827        Some(dsl_hir::CommandValue::Extended {
828            out_field_list: Some(_),
829            ..
830        }) => {
831            return Err(syn::Error::new(
832                command_override.identifier.span(),
833                "No `out` field list is allowed on command overrides",
834            ));
835        }
836        Some(dsl_hir::CommandValue::Extended {
837            command_item_list,
838            in_field_list: None,
839            out_field_list: None,
840        }) => {
841            for ci in command_item_list.items.iter() {
842                match ci {
843                    dsl_hir::CommandItem::ByteOrder(_) => {
844                        return Err(syn::Error::new(
845                            command_override.identifier.span(),
846                            "No `ByteOrder` is allowed on command overrides",
847                        ));
848                    }
849                    dsl_hir::CommandItem::BitOrder(_) => {
850                        return Err(syn::Error::new(
851                            command_override.identifier.span(),
852                            "No `BitOrder` is allowed on command overrides",
853                        ));
854                    }
855                    dsl_hir::CommandItem::SizeBitsIn(_) => {
856                        return Err(syn::Error::new(
857                            command_override.identifier.span(),
858                            "No `SizeBitsIn` is allowed on command overrides",
859                        ));
860                    }
861                    dsl_hir::CommandItem::SizeBitsOut(_) => {
862                        return Err(syn::Error::new(
863                            command_override.identifier.span(),
864                            "No `SizeBitsOut` is allowed on command overrides",
865                        ));
866                    }
867                    dsl_hir::CommandItem::AllowBitOverlap(_) => {
868                        return Err(syn::Error::new(
869                            command_override.identifier.span(),
870                            "No `AllowBitOverlap` is allowed on command overrides",
871                        ));
872                    }
873                    dsl_hir::CommandItem::AllowAddressOverlap(_) => {}
874                    dsl_hir::CommandItem::Repeat(_) => {}
875                    dsl_hir::CommandItem::Address(_) => {}
876                }
877            }
878
879            command_item_list
880        }
881        Some(dsl_hir::CommandValue::Basic(_)) => {
882            return Err(syn::Error::new(
883                command_override.identifier.span(),
884                "No basic address specifier is allowed on command overrides. Use the extended syntax with `{ }` instead",
885            ));
886        }
887        None => {
888            return Err(syn::Error::new(
889                command_override.identifier.span(),
890                "A value is required on command overrides",
891            ));
892        }
893    };
894
895    Ok(mir::CommandOverride {
896        name: command_override.identifier.to_string(),
897        address: command_item_list
898            .items
899            .iter()
900            .find_map(|item| match item {
901                dsl_hir::CommandItem::Address(lit) => Some(lit),
902                _ => None,
903            })
904            .map(|lit| lit.base10_parse())
905            .transpose()?,
906        allow_address_overlap: command_item_list
907            .items
908            .iter()
909            .find_map(|item| match item {
910                dsl_hir::CommandItem::AllowAddressOverlap(lit) => Some(lit.value),
911                _ => None,
912            })
913            .unwrap_or_default(),
914        repeat: command_item_list
915            .items
916            .iter()
917            .find_map(|item| match item {
918                dsl_hir::CommandItem::Repeat(repeat) => Some(mir::Repeat::try_from(repeat.clone())),
919                _ => None,
920            })
921            .transpose()?,
922    })
923}
924
925#[cfg(test)]
926mod tests {
927    use convert_case::Boundary;
928
929    use super::*;
930
931    #[test]
932    fn no_double_global_settings() {
933        let device = syn::parse_str::<dsl_hir::Device>(
934            "config { type DefaultRegisterAccess = RW; type DefaultRegisterAccess = RW; }",
935        )
936        .unwrap();
937
938        assert_eq!(
939            transform(device).unwrap_err().to_string(),
940            "Duplicate global config found: `DefaultRegisterAccess(RW)`"
941        );
942    }
943
944    #[test]
945    fn global_settings_correct() {
946        let device = syn::parse_str::<dsl_hir::Device>(
947            "config {
948                type DefaultRegisterAccess = RO;
949                type DefaultFieldAccess = RW;
950                type DefaultBufferAccess = WO;
951                type DefaultByteOrder = LE;
952                type DefaultBitOrder = MSB0;
953                type RegisterAddressType = i8;
954                type CommandAddressType = i64;
955                type BufferAddressType = u32;
956                type NameWordBoundaries = \"-\";
957                type DefmtFeature = \"defmt-03\";
958            }",
959        )
960        .unwrap();
961
962        let device = transform(device).unwrap();
963
964        assert_eq!(
965            device.global_config,
966            mir::GlobalConfig {
967                default_register_access: mir::Access::RO,
968                default_field_access: mir::Access::RW,
969                default_buffer_access: mir::Access::WO,
970                default_byte_order: Some(mir::ByteOrder::LE),
971                default_bit_order: mir::BitOrder::MSB0,
972                register_address_type: Some(mir::Integer::I8),
973                command_address_type: Some(mir::Integer::I64),
974                buffer_address_type: Some(mir::Integer::U32),
975                name_word_boundaries: vec![Boundary::Hyphen],
976                defmt_feature: Some("defmt-03".into()),
977            }
978        );
979    }
980
981    #[test]
982    fn buffer() {
983        assert_eq!(
984            transform(
985                syn::parse_str::<dsl_hir::Device>(
986                    "
987                    /// Hello world!
988                    #[cfg(feature = \"foo\")]
989                    /// This should be in order!
990                    buffer Foo: RW = 5
991                    ",
992                )
993                .unwrap()
994            )
995            .unwrap()
996            .objects,
997            &[mir::Object::Buffer(mir::Buffer {
998                cfg_attr: mir::Cfg::new(Some("feature = \"foo\"")),
999                description: " Hello world!\n This should be in order!".into(),
1000                name: "Foo".into(),
1001                access: mir::Access::RW,
1002                address: 5,
1003            })]
1004        );
1005
1006        assert_eq!(
1007            transform(
1008                syn::parse_str::<dsl_hir::Device>(
1009                    "
1010                    buffer Foo
1011                    ",
1012                )
1013                .unwrap()
1014            )
1015            .unwrap_err()
1016            .to_string(),
1017            "Buffer `Foo` must have an address"
1018        );
1019    }
1020
1021    #[test]
1022    fn command() {
1023        assert_eq!(
1024            transform(
1025                syn::parse_str::<dsl_hir::Device>(
1026                    "
1027                    command Foo
1028                    ",
1029                )
1030                .unwrap()
1031            )
1032            .unwrap_err()
1033            .to_string(),
1034            "Command `Foo` must have a value"
1035        );
1036
1037        assert_eq!(
1038            transform(
1039                syn::parse_str::<dsl_hir::Device>(
1040                    "
1041                    command Foo {}
1042                    ",
1043                )
1044                .unwrap()
1045            )
1046            .unwrap_err()
1047            .to_string(),
1048            "Command `Foo` must have an address"
1049        );
1050
1051        assert_eq!(
1052            transform(
1053                syn::parse_str::<dsl_hir::Device>(
1054                    "
1055                    /// Hello world!
1056                    #[cfg(feature = \"foo\")]
1057                    /// This should be in order!
1058                    command Foo = 5
1059                    ",
1060                )
1061                .unwrap()
1062            )
1063            .unwrap()
1064            .objects,
1065            &[mir::Object::Command(mir::Command {
1066                cfg_attr: mir::Cfg::new(Some("feature = \"foo\"")),
1067                description: " Hello world!\n This should be in order!".into(),
1068                name: "Foo".into(),
1069                address: 5,
1070                ..Default::default()
1071            })]
1072        );
1073
1074        assert_eq!(
1075            transform(
1076                syn::parse_str::<dsl_hir::Device>(
1077                    "
1078                    config {
1079                        type DefaultByteOrder = LE;
1080                        type DefaultFieldAccess = RO;
1081                    }
1082                    command Bar {
1083                        const SIZE_BITS_IN = 32;
1084                        const SIZE_BITS_OUT = 16;
1085                        const REPEAT = {
1086                            count: 4,
1087                            stride: 0x10,
1088                        };
1089                        const ADDRESS = 10;
1090
1091                        in {
1092                            /// Hello!
1093                            #[cfg(bla)]
1094                            val: WO bool = 0,
1095                            foo: uint as crate::my_mod::MyStruct = 1..=5,
1096                        }
1097                        out {
1098                            val: int as enum Val {
1099                                One,
1100                                /// Two!
1101                                Two = 2,
1102                                Three = default,
1103                                #[cfg(yes)]
1104                                Four = catch_all,
1105                            } = 0..16,
1106                        }
1107                    }
1108                    ",
1109                )
1110                .unwrap()
1111            )
1112            .unwrap()
1113            .objects,
1114            &[mir::Object::Command(mir::Command {
1115                name: "Bar".into(),
1116                address: 10,
1117                size_bits_in: 32,
1118                size_bits_out: 16,
1119                repeat: Some(mir::Repeat {
1120                    count: 4,
1121                    stride: 16
1122                }),
1123                in_fields: vec![
1124                    mir::Field {
1125                        cfg_attr: mir::Cfg::new(Some("bla")),
1126                        description: " Hello!".into(),
1127                        name: "val".into(),
1128                        access: mir::Access::WO,
1129                        base_type: mir::BaseType::Bool,
1130                        field_conversion: None,
1131                        field_address: 0..0,
1132                    },
1133                    mir::Field {
1134                        cfg_attr: mir::Cfg::new(None),
1135                        description: Default::default(),
1136                        name: "foo".into(),
1137                        access: mir::Access::RO,
1138                        base_type: mir::BaseType::Uint,
1139                        field_conversion: Some(mir::FieldConversion::Direct {
1140                            type_name: "crate::my_mod::MyStruct".into(),
1141                            use_try: false,
1142                        }),
1143                        field_address: 1..6,
1144                    }
1145                ],
1146                out_fields: vec![mir::Field {
1147                    cfg_attr: mir::Cfg::new(None),
1148                    description: Default::default(),
1149                    name: "val".into(),
1150                    access: mir::Access::RO,
1151                    base_type: mir::BaseType::Int,
1152                    field_conversion: Some(mir::FieldConversion::Enum {
1153                        enum_value: mir::Enum::new(
1154                            Default::default(),
1155                            "Val".into(),
1156                            vec![
1157                                mir::EnumVariant {
1158                                    cfg_attr: mir::Cfg::new(None),
1159                                    description: Default::default(),
1160                                    name: "One".into(),
1161                                    value: mir::EnumValue::Unspecified,
1162                                },
1163                                mir::EnumVariant {
1164                                    cfg_attr: mir::Cfg::new(None),
1165                                    description: " Two!".into(),
1166                                    name: "Two".into(),
1167                                    value: mir::EnumValue::Specified(2),
1168                                },
1169                                mir::EnumVariant {
1170                                    cfg_attr: mir::Cfg::new(None),
1171                                    description: Default::default(),
1172                                    name: "Three".into(),
1173                                    value: mir::EnumValue::Default,
1174                                },
1175                                mir::EnumVariant {
1176                                    cfg_attr: mir::Cfg::new(Some("yes")),
1177                                    description: Default::default(),
1178                                    name: "Four".into(),
1179                                    value: mir::EnumValue::CatchAll,
1180                                }
1181                            ],
1182                        ),
1183                        use_try: false
1184                    }),
1185                    field_address: 0..16,
1186                }],
1187                ..Default::default()
1188            })]
1189        );
1190
1191        assert_eq!(
1192            transform(
1193                syn::parse_str::<dsl_hir::Device>(
1194                    "
1195                    command Foo {
1196                        const ADDRESS = 0;
1197
1198                        in {
1199                            val: int = 0,
1200                        }
1201                    }
1202                    ",
1203                )
1204                .unwrap()
1205            )
1206            .unwrap_err()
1207            .to_string(),
1208            "Field `val` has a non-bool base type and must specify the start and the end address"
1209        );
1210
1211        assert_eq!(
1212            transform(
1213                syn::parse_str::<dsl_hir::Device>(
1214                    "
1215                    config {
1216                        type DefaultByteOrder = LE;
1217                        type DefaultBitOrder = MSB0;
1218                    }
1219                    command Bar {
1220                        type ByteOrder = BE;
1221                        type BitOrder = LSB0;
1222                        const ADDRESS = 10;
1223
1224                        in {
1225                            val: bool = 0,
1226                        }
1227                    }
1228                    ",
1229                )
1230                .unwrap()
1231            )
1232            .unwrap()
1233            .objects,
1234            &[mir::Object::Command(mir::Command {
1235                name: "Bar".into(),
1236                address: 10,
1237                byte_order: Some(mir::ByteOrder::BE),
1238                bit_order: mir::BitOrder::LSB0,
1239                in_fields: vec![mir::Field {
1240                    cfg_attr: mir::Cfg::new(None),
1241                    description: Default::default(),
1242                    name: "val".into(),
1243                    access: mir::Access::default(),
1244                    base_type: mir::BaseType::Bool,
1245                    field_conversion: None,
1246                    field_address: 0..0,
1247                },],
1248                ..Default::default()
1249            })]
1250        );
1251
1252        assert_eq!(
1253            transform(
1254                syn::parse_str::<dsl_hir::Device>(
1255                    "
1256                    command Foo {
1257                        const ADDRESS = 5;
1258                        const ALLOW_BIT_OVERLAP = true;
1259                        const ALLOW_ADDRESS_OVERLAP = true;
1260                    }
1261                    ",
1262                )
1263                .unwrap()
1264            )
1265            .unwrap()
1266            .objects,
1267            &[mir::Object::Command(mir::Command {
1268                name: "Foo".into(),
1269                address: 5,
1270                allow_bit_overlap: true,
1271                allow_address_overlap: true,
1272                ..Default::default()
1273            })]
1274        );
1275    }
1276
1277    #[test]
1278    fn max_one_cfg_attr() {
1279        assert_eq!(
1280            transform(
1281                syn::parse_str::<dsl_hir::Device>(
1282                    "
1283                    buffer Foo = 5
1284                    "
1285                )
1286                .unwrap()
1287            )
1288            .unwrap()
1289            .objects,
1290            &[mir::Object::Buffer(mir::Buffer {
1291                cfg_attr: mir::Cfg::new(None),
1292                description: "".into(),
1293                name: "Foo".into(),
1294                access: mir::Access::default(),
1295                address: 5,
1296            })]
1297        );
1298        assert_eq!(
1299            transform(
1300                syn::parse_str::<dsl_hir::Device>(
1301                    "
1302                    #[cfg(foo)]
1303                    buffer Foo = 5
1304                    "
1305                )
1306                .unwrap()
1307            )
1308            .unwrap()
1309            .objects,
1310            &[mir::Object::Buffer(mir::Buffer {
1311                cfg_attr: mir::Cfg::new(Some("foo")),
1312                description: "".into(),
1313                name: "Foo".into(),
1314                access: mir::Access::default(),
1315                address: 5,
1316            })]
1317        );
1318        assert_eq!(
1319            transform(
1320                syn::parse_str::<dsl_hir::Device>(
1321                    "
1322                    #[cfg(foo)]
1323                    #[cfg(too_many)]
1324                    buffer Foo = 5
1325                    "
1326                )
1327                .unwrap()
1328            )
1329            .unwrap_err()
1330            .to_string(),
1331            "Only one cfg attribute is allowed, but 2 are found"
1332        );
1333    }
1334
1335    #[test]
1336    fn ref_no_buffer_or_ref() {
1337        assert_eq!(
1338            transform(
1339                syn::parse_str::<dsl_hir::Device>(
1340                    "
1341                    ref Foo = buffer Bar
1342                    "
1343                )
1344                .unwrap()
1345            )
1346            .unwrap_err()
1347            .to_string(),
1348            "Ref `Foo` cannot ref a buffer"
1349        );
1350
1351        assert_eq!(
1352            transform(
1353                syn::parse_str::<dsl_hir::Device>(
1354                    "
1355                    ref Foo = ref Bar = buffer X
1356                    "
1357                )
1358                .unwrap()
1359            )
1360            .unwrap_err()
1361            .to_string(),
1362            "Ref `Foo` cannot ref another ref object"
1363        );
1364    }
1365
1366    #[test]
1367    fn ref_register() {
1368        assert_eq!(
1369            transform(
1370                syn::parse_str::<dsl_hir::Device>(
1371                    "
1372                    ref Foo = register Bar {}
1373                    "
1374                )
1375                .unwrap()
1376            )
1377            .unwrap()
1378            .objects,
1379            &[mir::Object::Ref(mir::RefObject {
1380                cfg_attr: mir::Cfg::new(None),
1381                description: "".into(),
1382                name: "Foo".into(),
1383                object_override: mir::ObjectOverride::Register(mir::RegisterOverride {
1384                    name: "Bar".into(),
1385                    ..Default::default()
1386                })
1387            })]
1388        );
1389
1390        assert_eq!(
1391            transform(
1392                syn::parse_str::<dsl_hir::Device>(
1393                    "
1394                    ref Foo = register Bar {
1395                        const ADDRESS = 5;
1396                        const REPEAT = {
1397                            count: 5,
1398                            stride: 1,
1399                        };
1400                        type Access = WO;
1401                        const RESET_VALUE = 123;
1402                        const ALLOW_ADDRESS_OVERLAP = false;
1403                    }
1404                    "
1405                )
1406                .unwrap()
1407            )
1408            .unwrap()
1409            .objects,
1410            &[mir::Object::Ref(mir::RefObject {
1411                cfg_attr: mir::Cfg::new(None),
1412                description: "".into(),
1413                name: "Foo".into(),
1414                object_override: mir::ObjectOverride::Register(mir::RegisterOverride {
1415                    name: "Bar".into(),
1416                    address: Some(5),
1417                    repeat: Some(mir::Repeat {
1418                        count: 5,
1419                        stride: 1
1420                    }),
1421                    access: Some(mir::Access::WO),
1422                    reset_value: Some(mir::ResetValue::Integer(123)),
1423                    ..Default::default()
1424                })
1425            })]
1426        );
1427
1428        assert_eq!(
1429            transform(
1430                syn::parse_str::<dsl_hir::Device>(
1431                    "
1432                    ref Foo = register Bar {
1433                        const RESET_VALUE = [1, 2, 3];
1434                    }
1435                    "
1436                )
1437                .unwrap()
1438            )
1439            .unwrap()
1440            .objects,
1441            &[mir::Object::Ref(mir::RefObject {
1442                cfg_attr: mir::Cfg::new(None),
1443                description: "".into(),
1444                name: "Foo".into(),
1445                object_override: mir::ObjectOverride::Register(mir::RegisterOverride {
1446                    name: "Bar".into(),
1447                    reset_value: Some(mir::ResetValue::Array(vec![1, 2, 3])),
1448                    ..Default::default()
1449                })
1450            })]
1451        );
1452
1453        assert_eq!(
1454            transform(
1455                syn::parse_str::<dsl_hir::Device>(
1456                    "
1457                    ref Foo = register Bar {
1458                        type ByteOrder = BE;
1459                    }
1460                    "
1461                )
1462                .unwrap()
1463            )
1464            .unwrap_err()
1465            .to_string(),
1466            "No `ByteOrder` is allowed on register overrides"
1467        );
1468
1469        assert_eq!(
1470            transform(
1471                syn::parse_str::<dsl_hir::Device>(
1472                    "
1473                    ref Foo = register Bar {
1474                        type BitOrder = LSB0;
1475                    }
1476                    "
1477                )
1478                .unwrap()
1479            )
1480            .unwrap_err()
1481            .to_string(),
1482            "No `BitOrder` is allowed on register overrides"
1483        );
1484
1485        assert_eq!(
1486            transform(
1487                syn::parse_str::<dsl_hir::Device>(
1488                    "
1489                    ref Foo = register Bar {
1490                        const SIZE_BITS = 5;
1491                    }
1492                    "
1493                )
1494                .unwrap()
1495            )
1496            .unwrap_err()
1497            .to_string(),
1498            "No `SizeBits` is allowed on register overrides"
1499        );
1500
1501        assert_eq!(
1502            transform(
1503                syn::parse_str::<dsl_hir::Device>(
1504                    "
1505                    ref Foo = register Bar {
1506                        const ALLOW_BIT_OVERLAP = false;
1507                    }
1508                    "
1509                )
1510                .unwrap()
1511            )
1512            .unwrap_err()
1513            .to_string(),
1514            "No `AllowBitOverlap` is allowed on register overrides"
1515        );
1516
1517        assert_eq!(
1518            transform(
1519                syn::parse_str::<dsl_hir::Device>(
1520                    "
1521                    ref Foo =
1522                        /// Hi!
1523                        register Bar { }
1524                    "
1525                )
1526                .unwrap()
1527            )
1528            .unwrap_err()
1529            .to_string(),
1530            "No attributes (cfg or doc) are allowed on register overrides"
1531        );
1532
1533        assert_eq!(
1534            transform(
1535                syn::parse_str::<dsl_hir::Device>(
1536                    "
1537                    ref Foo = register Bar {
1538                        val: bool = 0,
1539                    }
1540                    "
1541                )
1542                .unwrap()
1543            )
1544            .unwrap_err()
1545            .to_string(),
1546            "No fields are allowed on register overrides"
1547        );
1548
1549        assert_eq!(
1550            transform(
1551                syn::parse_str::<dsl_hir::Device>(
1552                    "
1553                    ref Foo = register Bar {
1554                        const RESET_VALUE = 0x123456789012345678901234567890123;
1555                    }
1556                    "
1557                )
1558                .unwrap()
1559            )
1560            .unwrap_err()
1561            .to_string(),
1562            "number too large to fit in target type: number is parsed as an i128 or u128"
1563        );
1564    }
1565
1566    #[test]
1567    fn ref_command() {
1568        assert_eq!(
1569            transform(
1570                syn::parse_str::<dsl_hir::Device>(
1571                    "
1572                    ref Foo = command Bar {}
1573                    "
1574                )
1575                .unwrap()
1576            )
1577            .unwrap()
1578            .objects,
1579            &[mir::Object::Ref(mir::RefObject {
1580                cfg_attr: mir::Cfg::new(None),
1581                description: "".into(),
1582                name: "Foo".into(),
1583                object_override: mir::ObjectOverride::Command(mir::CommandOverride {
1584                    name: "Bar".into(),
1585                    ..Default::default()
1586                })
1587            })]
1588        );
1589
1590        assert_eq!(
1591            transform(
1592                syn::parse_str::<dsl_hir::Device>(
1593                    "
1594                    ref Foo = command Bar {
1595                        const ADDRESS = 6;
1596                        const ALLOW_ADDRESS_OVERLAP = true;
1597                    }
1598                    "
1599                )
1600                .unwrap()
1601            )
1602            .unwrap()
1603            .objects,
1604            &[mir::Object::Ref(mir::RefObject {
1605                cfg_attr: mir::Cfg::new(None),
1606                description: "".into(),
1607                name: "Foo".into(),
1608                object_override: mir::ObjectOverride::Command(mir::CommandOverride {
1609                    name: "Bar".into(),
1610                    address: Some(6),
1611                    allow_address_overlap: true,
1612                    ..Default::default()
1613                })
1614            })]
1615        );
1616
1617        assert_eq!(
1618            transform(
1619                syn::parse_str::<dsl_hir::Device>(
1620                    "
1621                    ref Foo = command Bar {
1622                        const ADDRESS = 7;
1623                        const REPEAT = {
1624                            count: 4,
1625                            stride: 4,
1626                        };
1627                    }
1628                    "
1629                )
1630                .unwrap()
1631            )
1632            .unwrap()
1633            .objects,
1634            &[mir::Object::Ref(mir::RefObject {
1635                cfg_attr: mir::Cfg::new(None),
1636                description: "".into(),
1637                name: "Foo".into(),
1638                object_override: mir::ObjectOverride::Command(mir::CommandOverride {
1639                    name: "Bar".into(),
1640                    address: Some(7),
1641                    repeat: Some(mir::Repeat {
1642                        count: 4,
1643                        stride: 4
1644                    }),
1645                    ..Default::default()
1646                })
1647            })]
1648        );
1649
1650        assert_eq!(
1651            transform(
1652                syn::parse_str::<dsl_hir::Device>(
1653                    "
1654                    ref Foo = command Bar {
1655                        const REPEAT = {
1656                            count: 4,
1657                            stride: 4,
1658                        };
1659                    }
1660                    "
1661                )
1662                .unwrap()
1663            )
1664            .unwrap()
1665            .objects,
1666            &[mir::Object::Ref(mir::RefObject {
1667                cfg_attr: mir::Cfg::new(None),
1668                description: "".into(),
1669                name: "Foo".into(),
1670                object_override: mir::ObjectOverride::Command(mir::CommandOverride {
1671                    name: "Bar".into(),
1672                    repeat: Some(mir::Repeat {
1673                        count: 4,
1674                        stride: 4
1675                    }),
1676                    ..Default::default()
1677                })
1678            })]
1679        );
1680
1681        assert_eq!(
1682            transform(
1683                syn::parse_str::<dsl_hir::Device>(
1684                    "
1685                    ref Foo = command Bar
1686                    "
1687                )
1688                .unwrap()
1689            )
1690            .unwrap_err()
1691            .to_string(),
1692            "A value is required on command overrides"
1693        );
1694
1695        assert_eq!(
1696            transform(
1697                syn::parse_str::<dsl_hir::Device>(
1698                    "
1699                    ref Foo = command Bar = 6
1700                    "
1701                )
1702                .unwrap()
1703            )
1704            .unwrap_err()
1705            .to_string(),
1706            "No basic address specifier is allowed on command overrides. Use the extended syntax with `{ }` instead"
1707        );
1708
1709        assert_eq!(
1710            transform(
1711                syn::parse_str::<dsl_hir::Device>(
1712                    "
1713                    ref Foo =
1714                        /// Illegal attribute!
1715                        command Bar {}
1716                    "
1717                )
1718                .unwrap()
1719            )
1720            .unwrap_err()
1721            .to_string(),
1722            "No attributes (cfg or doc) are allowed on command overrides"
1723        );
1724
1725        assert_eq!(
1726            transform(
1727                syn::parse_str::<dsl_hir::Device>(
1728                    "
1729                    ref Foo = command Bar {
1730                        in {}
1731                    }
1732                    "
1733                )
1734                .unwrap()
1735            )
1736            .unwrap_err()
1737            .to_string(),
1738            "No `in` field list is allowed on command overrides"
1739        );
1740
1741        assert_eq!(
1742            transform(
1743                syn::parse_str::<dsl_hir::Device>(
1744                    "
1745                    ref Foo = command Bar {
1746                        out {}
1747                    }
1748                    "
1749                )
1750                .unwrap()
1751            )
1752            .unwrap_err()
1753            .to_string(),
1754            "No `out` field list is allowed on command overrides"
1755        );
1756
1757        assert_eq!(
1758            transform(
1759                syn::parse_str::<dsl_hir::Device>(
1760                    "
1761                    ref Foo = command Bar {
1762                        type ByteOrder = LE;
1763                    }
1764                    "
1765                )
1766                .unwrap()
1767            )
1768            .unwrap_err()
1769            .to_string(),
1770            "No `ByteOrder` is allowed on command overrides"
1771        );
1772
1773        assert_eq!(
1774            transform(
1775                syn::parse_str::<dsl_hir::Device>(
1776                    "
1777                    ref Foo = command Bar {
1778                        type BitOrder = LSB0;
1779                    }
1780                    "
1781                )
1782                .unwrap()
1783            )
1784            .unwrap_err()
1785            .to_string(),
1786            "No `BitOrder` is allowed on command overrides"
1787        );
1788
1789        assert_eq!(
1790            transform(
1791                syn::parse_str::<dsl_hir::Device>(
1792                    "
1793                    ref Foo = command Bar {
1794                        const SIZE_BITS_IN = 0;
1795                    }
1796                    "
1797                )
1798                .unwrap()
1799            )
1800            .unwrap_err()
1801            .to_string(),
1802            "No `SizeBitsIn` is allowed on command overrides"
1803        );
1804
1805        assert_eq!(
1806            transform(
1807                syn::parse_str::<dsl_hir::Device>(
1808                    "
1809                    ref Foo = command Bar {
1810                        const SIZE_BITS_OUT = 0;
1811                    }
1812                    "
1813                )
1814                .unwrap()
1815            )
1816            .unwrap_err()
1817            .to_string(),
1818            "No `SizeBitsOut` is allowed on command overrides"
1819        );
1820
1821        assert_eq!(
1822            transform(
1823                syn::parse_str::<dsl_hir::Device>(
1824                    "
1825                    ref Foo = command Bar {
1826                        const ALLOW_BIT_OVERLAP = true;
1827                    }
1828                    "
1829                )
1830                .unwrap()
1831            )
1832            .unwrap_err()
1833            .to_string(),
1834            "No `AllowBitOverlap` is allowed on command overrides"
1835        );
1836    }
1837
1838    #[test]
1839    fn ref_block() {
1840        assert_eq!(
1841            transform(
1842                syn::parse_str::<dsl_hir::Device>(
1843                    "
1844                    ref Foo = block Bar {}
1845                    "
1846                )
1847                .unwrap()
1848            )
1849            .unwrap()
1850            .objects,
1851            &[mir::Object::Ref(mir::RefObject {
1852                cfg_attr: mir::Cfg::new(None),
1853                description: "".into(),
1854                name: "Foo".into(),
1855                object_override: mir::ObjectOverride::Block(mir::BlockOverride {
1856                    name: "Bar".into(),
1857                    address_offset: None,
1858                    repeat: None
1859                })
1860            })]
1861        );
1862
1863        assert_eq!(
1864            transform(
1865                syn::parse_str::<dsl_hir::Device>(
1866                    "
1867                    ref Foo =
1868                        /// Illegal comment!
1869                        block Bar {}
1870                    "
1871                )
1872                .unwrap()
1873            )
1874            .unwrap_err()
1875            .to_string(),
1876            "No attributes (cfg or doc) are allowed on block overrides"
1877        );
1878
1879        assert_eq!(
1880            transform(
1881                syn::parse_str::<dsl_hir::Device>(
1882                    "
1883                    ref Foo = block Bar {
1884                        buffer Bla = 0,
1885                    }
1886                    "
1887                )
1888                .unwrap()
1889            )
1890            .unwrap_err()
1891            .to_string(),
1892            "No objects may be defined on block overrides"
1893        );
1894
1895        assert_eq!(
1896            transform(
1897                syn::parse_str::<dsl_hir::Device>(
1898                    "
1899                    /// Hi!
1900                    #[cfg(bla)]
1901                    ref Foo = block Bar {
1902                        const ADDRESS_OFFSET = 5;
1903                    }
1904                    "
1905                )
1906                .unwrap()
1907            )
1908            .unwrap()
1909            .objects,
1910            &[mir::Object::Ref(mir::RefObject {
1911                cfg_attr: mir::Cfg::new(Some("bla")),
1912                description: " Hi!".into(),
1913                name: "Foo".into(),
1914                object_override: mir::ObjectOverride::Block(mir::BlockOverride {
1915                    name: "Bar".into(),
1916                    address_offset: Some(5),
1917                    repeat: None
1918                })
1919            })]
1920        );
1921
1922        assert_eq!(
1923            transform(
1924                syn::parse_str::<dsl_hir::Device>(
1925                    "
1926                    ref Foo = block Bar {
1927                        const REPEAT = {
1928                            count: 6,
1929                            stride: 2
1930                        };
1931                    }
1932                    "
1933                )
1934                .unwrap()
1935            )
1936            .unwrap()
1937            .objects,
1938            &[mir::Object::Ref(mir::RefObject {
1939                cfg_attr: mir::Cfg::new(None),
1940                description: "".into(),
1941                name: "Foo".into(),
1942                object_override: mir::ObjectOverride::Block(mir::BlockOverride {
1943                    name: "Bar".into(),
1944                    address_offset: None,
1945                    repeat: Some(mir::Repeat {
1946                        count: 6,
1947                        stride: 2
1948                    })
1949                })
1950            })]
1951        );
1952    }
1953
1954    #[test]
1955    fn block() {
1956        assert_eq!(
1957            transform(
1958                syn::parse_str::<dsl_hir::Device>(
1959                    "
1960                    block Foo {
1961                        
1962                    }
1963                    "
1964                )
1965                .unwrap()
1966            )
1967            .unwrap()
1968            .objects,
1969            &[mir::Object::Block(mir::Block {
1970                cfg_attr: Default::default(),
1971                description: Default::default(),
1972                name: "Foo".into(),
1973                address_offset: 0,
1974                repeat: Default::default(),
1975                objects: Default::default(),
1976            })]
1977        );
1978
1979        assert_eq!(
1980            transform(
1981                syn::parse_str::<dsl_hir::Device>(
1982                    "
1983                    /// Hello!
1984                    #[cfg(bar)]
1985                    block Foo {
1986                        const ADDRESS_OFFSET = 0x500;
1987                        buffer Bla = 5,
1988                    }
1989                    "
1990                )
1991                .unwrap()
1992            )
1993            .unwrap()
1994            .objects,
1995            &[mir::Object::Block(mir::Block {
1996                cfg_attr: mir::Cfg::new(Some("bar")),
1997                description: " Hello!".into(),
1998                name: "Foo".into(),
1999                address_offset: 0x500,
2000                repeat: None,
2001                objects: vec![mir::Object::Buffer(mir::Buffer {
2002                    cfg_attr: Default::default(),
2003                    description: Default::default(),
2004                    name: "Bla".into(),
2005                    access: Default::default(),
2006                    address: 5
2007                })],
2008            })]
2009        );
2010
2011        assert_eq!(
2012            transform(
2013                syn::parse_str::<dsl_hir::Device>(
2014                    "
2015                    block Foo {
2016                        const REPEAT = {
2017                            count: 4,
2018                            stride: 4,
2019                        };
2020                    }
2021                    "
2022                )
2023                .unwrap()
2024            )
2025            .unwrap()
2026            .objects,
2027            &[mir::Object::Block(mir::Block {
2028                cfg_attr: Default::default(),
2029                description: Default::default(),
2030                name: "Foo".into(),
2031                address_offset: 0,
2032                repeat: Some(mir::Repeat {
2033                    count: 4,
2034                    stride: 4
2035                }),
2036                objects: Default::default(),
2037            })]
2038        );
2039    }
2040
2041    #[test]
2042    fn register() {
2043        assert_eq!(
2044            transform(
2045                syn::parse_str::<dsl_hir::Device>(
2046                    "
2047                    register Foo {
2048                        const SIZE_BITS = 16;
2049                    }
2050                    "
2051                )
2052                .unwrap()
2053            )
2054            .unwrap_err()
2055            .to_string(),
2056            "Register `Foo` must have an address"
2057        );
2058
2059        assert_eq!(
2060            transform(
2061                syn::parse_str::<dsl_hir::Device>(
2062                    "
2063                    register Foo {
2064                        const ADDRESS = 5;
2065                    }
2066                    "
2067                )
2068                .unwrap()
2069            )
2070            .unwrap_err()
2071            .to_string(),
2072            "Register `Foo` must have size bits specified"
2073        );
2074
2075        assert_eq!(
2076            transform(
2077                syn::parse_str::<dsl_hir::Device>(
2078                    "
2079                    register Foo {
2080                        const ADDRESS = 5;
2081                        const SIZE_BITS = 16;
2082                    }
2083                    "
2084                )
2085                .unwrap()
2086            )
2087            .unwrap()
2088            .objects,
2089            &[mir::Object::Register(mir::Register {
2090                name: "Foo".into(),
2091                address: 5,
2092                size_bits: 16,
2093                ..Default::default()
2094            })]
2095        );
2096
2097        assert_eq!(
2098            transform(
2099                syn::parse_str::<dsl_hir::Device>(
2100                    "
2101                    /// This is foo
2102                    register Foo {
2103                        const ADDRESS = 5;
2104                        type ByteOrder = LE;
2105                        type BitOrder = MSB0;
2106                        type Access = RO;
2107                        const SIZE_BITS = 16;
2108                        const REPEAT = {
2109                            count: 2,
2110                            stride: 120
2111                        };
2112                        const RESET_VALUE = 0x1234;
2113
2114                        val: int = 0..16
2115                    }
2116                    "
2117                )
2118                .unwrap()
2119            )
2120            .unwrap()
2121            .objects,
2122            &[mir::Object::Register(mir::Register {
2123                description: " This is foo".into(),
2124                name: "Foo".into(),
2125                access: mir::Access::RO,
2126                byte_order: Some(mir::ByteOrder::LE),
2127                bit_order: mir::BitOrder::MSB0,
2128                address: 5,
2129                size_bits: 16,
2130                reset_value: Some(mir::ResetValue::Integer(0x1234)),
2131                repeat: Some(mir::Repeat {
2132                    count: 2,
2133                    stride: 120
2134                }),
2135                fields: vec![mir::Field {
2136                    cfg_attr: Default::default(),
2137                    description: Default::default(),
2138                    name: "val".into(),
2139                    access: Default::default(),
2140                    base_type: mir::BaseType::Int,
2141                    field_conversion: Default::default(),
2142                    field_address: 0..16
2143                }],
2144                ..Default::default()
2145            })]
2146        );
2147
2148        assert_eq!(
2149            transform(
2150                syn::parse_str::<dsl_hir::Device>(
2151                    "
2152                    register Foo {
2153                        const ADDRESS = 16;
2154                        const SIZE_BITS = 136;
2155                        const RESET_VALUE = 0x123456789ABCDEF000000000000000000;
2156                    }
2157                    "
2158                )
2159                .unwrap()
2160            )
2161            .unwrap_err()
2162            .to_string(),
2163            "number too large to fit in target type: number is parsed as an i128 or u128"
2164        );
2165
2166        assert_eq!(
2167            transform(
2168                syn::parse_str::<dsl_hir::Device>(
2169                    "
2170                    register Foo {
2171                        const ADDRESS = 5;
2172                        const SIZE_BITS = 16;
2173                        const RESET_VALUE = [12, 34];
2174                    }
2175                    "
2176                )
2177                .unwrap()
2178            )
2179            .unwrap()
2180            .objects,
2181            &[mir::Object::Register(mir::Register {
2182                name: "Foo".into(),
2183                address: 5,
2184                reset_value: Some(mir::ResetValue::Array(vec![12, 34])),
2185                size_bits: 16,
2186                ..Default::default()
2187            })]
2188        );
2189
2190        assert_eq!(
2191            transform(
2192                syn::parse_str::<dsl_hir::Device>(
2193                    "
2194                    register Foo {
2195                        const ADDRESS = 5;
2196                        const SIZE_BITS = 16;
2197                        const ALLOW_BIT_OVERLAP = true;
2198                        const ALLOW_ADDRESS_OVERLAP = true;
2199                    }
2200                    "
2201                )
2202                .unwrap()
2203            )
2204            .unwrap()
2205            .objects,
2206            &[mir::Object::Register(mir::Register {
2207                name: "Foo".into(),
2208                address: 5,
2209                size_bits: 16,
2210                allow_bit_overlap: true,
2211                allow_address_overlap: true,
2212                ..Default::default()
2213            })]
2214        );
2215    }
2216
2217    #[test]
2218    fn test_integer_try_from_ident() {
2219        // Test for valid integer types
2220        let test_cases = vec![
2221            ("u8", mir::Integer::U8),
2222            ("u16", mir::Integer::U16),
2223            ("u32", mir::Integer::U32),
2224            ("i8", mir::Integer::I8),
2225            ("i16", mir::Integer::I16),
2226            ("i32", mir::Integer::I32),
2227            ("i64", mir::Integer::I64),
2228        ];
2229
2230        for (ident_str, expected) in test_cases {
2231            let ident = syn::Ident::new(ident_str, Span::call_site());
2232            let result = mir::Integer::try_from(ident);
2233            assert_eq!(result.unwrap(), expected);
2234        }
2235
2236        // Test for invalid identifier
2237        let invalid_ident: syn::Ident = syn::parse_quote! { foo };
2238        let result = mir::Integer::try_from(invalid_ident);
2239
2240        // Check the error string
2241        assert_eq!(
2242            result.unwrap_err().to_string(),
2243            "Must be an integer type: u8, u16, u32, i8, i16, i32, i64"
2244        );
2245    }
2246}