dcSpark / cddl-codegen

Codegen serialization logic for CBOR automatically from a CDDL specification
MIT License
40 stars 15 forks source link

Support non-table types as types #161

Open alistair23 opened 1 year ago

alistair23 commented 1 year ago

I'm hitting this TODO

https://github.com/dcSpark/cddl-codegen/blob/f0975bb34e36c0deaab5e7a4e21ea34e37988be3/src/parsing.rs#L1231

it would be great to have this implemented :)

I wanted to open this to help track implementing it

rooooooooob commented 1 year ago

@alistair23 can you post the CDDL file (or snippet from it) that encountered it? It's possible it could be rewritten to work around it in the meantime (and good to reference for the issue in general).

alistair23 commented 1 year ago

Yep!

So running

RUST_BACKTRACE=1 cargo run -- --input test.cddl --output rust-code

On

class-map = non-empty<{
  ? &(class-id: 0) => $class-id-type-choice
  ? &(vendor: 1) => tstr
  ? &(model: 2) => tstr
  ? &(layer: 3) => uint
  ? &(index: 4) => uint
}>

environment-map = non-empty<{
  ? &(class: 0) => class-map
  ? &(instance: 1) => $instance-id-type-choice
  ? &(group: 2) => $group-id-type-choice
}>

Gives me this

int
int
Switching from scope 'lib' to 'lib'
class-map
environment-map
_CDDL_CODEGEN_EXTERN_TYPE_
_CDDL_CODEGEN_RAW_BYTES_TYPE_

------------------------------------------
- Handling rule: lib:_CDDL_CODEGEN_EXTERN_TYPE_
------------------------------------
_CDDL_CODEGEN_EXTERN_TYPE_

------------------------------------------
- Handling rule: lib:_CDDL_CODEGEN_RAW_BYTES_TYPE_
------------------------------------
_CDDL_CODEGEN_RAW_BYTES_TYPE_

------------------------------------------
- Handling rule: lib:class-map
------------------------------------
class-map
non-empty
non-empty
thread 'main' panicked at 'not implemented: TODO: non-table types as types: Group { group_choices: [GroupChoice { group_entries: [(ValueMemberKey { ge: ValueMemberKeyEntry { occur: Some(Occurrence { occur: Optional { span: (64, 65, 4) }, comments: None, _a: PhantomData<&()> }), member_key: Some(Type1 { t1: Type1 { type2: ChoiceFromInlineGroup { group: Group { group_choices: [GroupChoice { group_entries: [(ValueMemberKey { ge: ValueMemberKeyEntry { occur: None, member_key: Some(Bareword { ident: Identifier { ident: "class-id", socket: None, span: (68, 76, 4) }, span: (68, 77, 4), comments: None, comments_after_colon: None }), entry_type: Type { type_choices: [TypeChoice { type1: Type1 { type2: UintValue { value: 0, span: (78, 79, 4) }, operator: None, span: (78, 79, 4), comments_after_type: None }, comments_before_type: None, comments_after_type: None }], span: (78, 79, 4) } }, span: (68, 79, 4), leading_comments: None, trailing_comments: None }, OptionalComma { optional_comma: false, trailing_comments: None, _a: PhantomData<&()> })], span: (68, 79, 4), comments_before_grpchoice: None }], span: (68, 79, 4) }, span: (66, 79, 4), comments: None, comments_before_group: None, comments_after_group: None }, operator: None, span: (66, 80, 4), comments_after_type: None }, is_cut: false, span: (66, 105, 4), comments_before_cut: None, comments_after_cut: None, comments_after_arrowmap: None }), entry_type: Type { type_choices: [TypeChoice { type1: Type1 { type2: Typename { ident: Identifier { ident: "class-id-type-choice", socket: Some(TYPE), span: (84, 105, 4) }, generic_args: None, span: (84, 105, 4) }, operator: None, span: (84, 105, 4), comments_after_type: None }, comments_before_type: None, comments_after_type: None }], span: (84, 105, 4) } }, span: (64, 105, 4), leading_comments: None, trailing_comments: None }, OptionalComma { optional_comma: false, trailing_comments: None, _a: PhantomData<&()> }), (ValueMemberKey { ge: ValueMemberKeyEntry { occur: Some(Occurrence { occur: Optional { span: (108, 109, 5) }, comments: None, _a: PhantomData<&()> }), member_key: Some(Type1 { t1: Type1 { type2: ChoiceFromInlineGroup { group: Group { group_choices: [GroupChoice { group_entries: [(ValueMemberKey { ge: ValueMemberKeyEntry { occur: None, member_key: Some(Bareword { ident: Identifier { ident: "vendor", socket: None, span: (112, 118, 5) }, span: (112, 119, 5), comments: None, comments_after_colon: None }), entry_type: Type { type_choices: [TypeChoice { type1: Type1 { type2: UintValue { value: 1, span: (120, 121, 5) }, operator: None, span: (120, 121, 5), comments_after_type: None }, comments_before_type: None, comments_after_type: None }], span: (120, 121, 5) } }, span: (112, 121, 5), leading_comments: None, trailing_comments: None }, OptionalComma { optional_comma: false, trailing_comments: None, _a: PhantomData<&()> })], span: (112, 121, 5), comments_before_grpchoice: None }], span: (112, 121, 5) }, span: (110, 121, 5), comments: None, comments_before_group: None, comments_after_group: None }, operator: None, span: (110, 122, 5), comments_after_type: None }, is_cut: false, span: (110, 130, 5), comments_before_cut: None, comments_after_cut: None, comments_after_arrowmap: None }), entry_type: Type { type_choices: [TypeChoice { type1: Type1 { type2: Typename { ident: Identifier { ident: "tstr", socket: None, span: (126, 130, 5) }, generic_args: None, span: (126, 130, 5) }, operator: None, span: (126, 130, 5), comments_after_type: None }, comments_before_type: None, comments_after_type: None }], span: (126, 130, 5) } }, span: (108, 130, 5), leading_comments: None, trailing_comments: None }, OptionalComma { optional_comma: false, trailing_comments: None, _a: PhantomData<&()> }), (ValueMemberKey { ge: ValueMemberKeyEntry { occur: Some(Occurrence { occur: Optional { span: (133, 134, 6) }, comments: None, _a: PhantomData<&()> }), member_key: Some(Type1 { t1: Type1 { type2: ChoiceFromInlineGroup { group: Group { group_choices: [GroupChoice { group_entries: [(ValueMemberKey { ge: ValueMemberKeyEntry { occur: None, member_key: Some(Bareword { ident: Identifier { ident: "model", socket: None, span: (137, 142, 6) }, span: (137, 143, 6), comments: None, comments_after_colon: None }), entry_type: Type { type_choices: [TypeChoice { type1: Type1 { type2: UintValue { value: 2, span: (144, 145, 6) }, operator: None, span: (144, 145, 6), comments_after_type: None }, comments_before_type: None, comments_after_type: None }], span: (144, 145, 6) } }, span: (137, 145, 6), leading_comments: None, trailing_comments: None }, OptionalComma { optional_comma: false, trailing_comments: None, _a: PhantomData<&()> })], span: (137, 145, 6), comments_before_grpchoice: None }], span: (137, 145, 6) }, span: (135, 145, 6), comments: None, comments_before_group: None, comments_after_group: None }, operator: None, span: (135, 146, 6), comments_after_type: None }, is_cut: false, span: (135, 154, 6), comments_before_cut: None, comments_after_cut: None, comments_after_arrowmap: None }), entry_type: Type { type_choices: [TypeChoice { type1: Type1 { type2: Typename { ident: Identifier { ident: "tstr", socket: None, span: (150, 154, 6) }, generic_args: None, span: (150, 154, 6) }, operator: None, span: (150, 154, 6), comments_after_type: None }, comments_before_type: None, comments_after_type: None }], span: (150, 154, 6) } }, span: (133, 154, 6), leading_comments: None, trailing_comments: None }, OptionalComma { optional_comma: false, trailing_comments: None, _a: PhantomData<&()> }), (ValueMemberKey { ge: ValueMemberKeyEntry { occur: Some(Occurrence { occur: Optional { span: (157, 158, 7) }, comments: None, _a: PhantomData<&()> }), member_key: Some(Type1 { t1: Type1 { type2: ChoiceFromInlineGroup { group: Group { group_choices: [GroupChoice { group_entries: [(ValueMemberKey { ge: ValueMemberKeyEntry { occur: None, member_key: Some(Bareword { ident: Identifier { ident: "layer", socket: None, span: (161, 166, 7) }, span: (161, 167, 7), comments: None, comments_after_colon: None }), entry_type: Type { type_choices: [TypeChoice { type1: Type1 { type2: UintValue { value: 3, span: (168, 169, 7) }, operator: None, span: (168, 169, 7), comments_after_type: None }, comments_before_type: None, comments_after_type: None }], span: (168, 169, 7) } }, span: (161, 169, 7), leading_comments: None, trailing_comments: None }, OptionalComma { optional_comma: false, trailing_comments: None, _a: PhantomData<&()> })], span: (161, 169, 7), comments_before_grpchoice: None }], span: (161, 169, 7) }, span: (159, 169, 7), comments: None, comments_before_group: None, comments_after_group: None }, operator: None, span: (159, 170, 7), comments_after_type: None }, is_cut: false, span: (159, 178, 7), comments_before_cut: None, comments_after_cut: None, comments_after_arrowmap: None }), entry_type: Type { type_choices: [TypeChoice { type1: Type1 { type2: Typename { ident: Identifier { ident: "uint", socket: None, span: (174, 178, 7) }, generic_args: None, span: (174, 178, 7) }, operator: None, span: (174, 178, 7), comments_after_type: None }, comments_before_type: None, comments_after_type: None }], span: (174, 178, 7) } }, span: (157, 178, 7), leading_comments: None, trailing_comments: None }, OptionalComma { optional_comma: false, trailing_comments: None, _a: PhantomData<&()> }), (ValueMemberKey { ge: ValueMemberKeyEntry { occur: Some(Occurrence { occur: Optional { span: (181, 182, 8) }, comments: None, _a: PhantomData<&()> }), member_key: Some(Type1 { t1: Type1 { type2: ChoiceFromInlineGroup { group: Group { group_choices: [GroupChoice { group_entries: [(ValueMemberKey { ge: ValueMemberKeyEntry { occur: None, member_key: Some(Bareword { ident: Identifier { ident: "index", socket: None, span: (185, 190, 8) }, span: (185, 191, 8), comments: None, comments_after_colon: None }), entry_type: Type { type_choices: [TypeChoice { type1: Type1 { type2: UintValue { value: 4, span: (192, 193, 8) }, operator: None, span: (192, 193, 8), comments_after_type: None }, comments_before_type: None, comments_after_type: None }], span: (192, 193, 8) } }, span: (185, 193, 8), leading_comments: None, trailing_comments: None }, OptionalComma { optional_comma: false, trailing_comments: None, _a: PhantomData<&()> })], span: (185, 193, 8), comments_before_grpchoice: None }], span: (185, 193, 8) }, span: (183, 193, 8), comments: None, comments_before_group: None, comments_after_group: None }, operator: None, span: (183, 194, 8), comments_after_type: None }, is_cut: false, span: (183, 202, 8), comments_before_cut: None, comments_after_cut: None, comments_after_arrowmap: None }), entry_type: Type { type_choices: [TypeChoice { type1: Type1 { type2: Typename { ident: Identifier { ident: "uint", socket: None, span: (198, 202, 8) }, generic_args: None, span: (198, 202, 8) }, operator: None, span: (198, 202, 8), comments_after_type: None }, comments_before_type: None, comments_after_type: None }], span: (198, 202, 8) } }, span: (181, 202, 8), leading_comments: None, trailing_comments: None }, OptionalComma { optional_comma: false, trailing_comments: None, _a: PhantomData<&()> })], span: (61, 202, 3), comments_before_grpchoice: None }], span: (61, 202, 3) }', src/parsing.rs:1231:33
stack backtrace:
   0: rust_begin_unwind
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/std/src/panicking.rs:575:5
   1: core::panicking::panic_fmt
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/panicking.rs:64:14
   2: cddl_codegen::parsing::rust_type_from_type2
             at ./src/parsing.rs:1231:33
   3: cddl_codegen::parsing::rust_type_from_type1
             at ./src/parsing.rs:1074:21
   4: cddl_codegen::parsing::parse_type::{{closure}}
             at ./src/parsing.rs:544:49
   5: core::iter::adapters::map::map_fold::{{closure}}
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/iter/adapters/map.rs:84:28
   6: core::iter::traits::iterator::Iterator::fold
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/iter/traits/iterator.rs:2438:21
   7: <core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::fold
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/iter/adapters/map.rs:124:9
   8: core::iter::traits::iterator::Iterator::for_each
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/iter/traits/iterator.rs:837:9
   9: alloc::vec::Vec<T,A>::extend_trusted
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/alloc/src/vec/mod.rs:2885:17
  10: <alloc::vec::Vec<T,A> as alloc::vec::spec_extend::SpecExtend<T,I>>::spec_extend
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/alloc/src/vec/spec_extend.rs:26:9
  11: <alloc::vec::Vec<T> as alloc::vec::spec_from_iter_nested::SpecFromIterNested<T,I>>::from_iter
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/alloc/src/vec/spec_from_iter_nested.rs:62:9
  12: <alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/alloc/src/vec/spec_from_iter.rs:33:9
  13: <alloc::vec::Vec<T> as core::iter::traits::collect::FromIterator<T>>::from_iter
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/alloc/src/vec/mod.rs:2753:9
  14: core::iter::traits::iterator::Iterator::collect
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/iter/traits/iterator.rs:1860:9
  15: cddl_codegen::parsing::parse_type
             at ./src/parsing.rs:540:60
  16: cddl_codegen::parsing::parse_rule
             at ./src/parsing.rs:82:21
  17: cddl_codegen::main
             at ./src/main.rs:121:9
  18: core::ops::function::FnOnce::call_once
             at /rustc/2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

If you have any ideas of how to re-write it please let me know :)

rooooooooob commented 1 year ago

What are you trying to achieve with &(class-id: 0)? We had never used & in the specs we've been generating from but what is the point in doing it on a basic group of only 1 value? Doesn't that create a type choice of only 1 option, that being 0? Or does this do something with the group encoding when you're putting it in the outer group? Forgive me if I'm not understanding things.

If those are just meant to be the uint values for each of those keys then try directly placing them as:

class-map = non-empty<{
  ?  0: $class-id-type-choice, ; @name class_id
  ? 1: tstr ; @name vendor
  ? 2: tstr, ; @name 
  ? 3: uint, ; @name layer
  ? 4: uint, ; @name index
}>

The generic support is also rather weak so you might need to avoid using it (what is the definition for non-empty<T>?) I stuck in those ; @name comments since it lets our parser name fields custom things instead of trying to name it after the key's value (key_0, key_1, key_2, etc here without those)

alistair23 commented 1 year ago

I have no idea, the class-map is taken from here:

https://github.com/ietf-rats-wg/draft-ietf-rats-corim/blob/b4c4b88550c2c2fbad47e9866bc6e0f1c6a303b3/cddl/class-map.cddl#L1

I'm not sure why they put it in a choice. I haven't yet wrapped my head around CDDL to have any idea why someone would do that.

As for the non-empty, it's also defined here: https://github.com/ietf-rats-wg/draft-ietf-rats-corim/blob/b4c4b88550c2c2fbad47e9866bc6e0f1c6a303b3/draft-ietf-rats-corim.md?plain=1#L151

rooooooooob commented 1 year ago

I don't think we can work around that non-empty restriction via cddl-codegen/changing the CDDL then. You can still try doing just:

class-map = {
  ? 0: $class-id-type-choice, ; @name class_id
  ? 1: tstr, ; @name vendor
  ? 2: tstr, ; @name model
  ? 3: uint, ; @name layer
  ? 4: uint, ; @name index
}

environment-map = {
  ? 0: class-map, ; @name class
  ? 1: $instance-id-type-choice, ; @name instance
  ? 2: $group-id-type-choice, ; @name group
}

and either not care that it doesn't enforce the non-empty requirement, or write that check yourself (should be like 1-2 lines of code).