From 8f6190843721a1baf07c053560fd2929b18bb77d Mon Sep 17 00:00:00 2001 From: Eval EXEC Date: Tue, 14 Feb 2023 13:39:17 +0800 Subject: [PATCH 1/5] feat: union type customize ID --- tools/codegen/src/ast/mod.rs | 2 +- tools/codegen/src/ast/raw/mod.rs | 8 +++- tools/codegen/src/ast/raw/utils.rs | 49 +++++++++++++++++++++- tools/codegen/src/ast/verified/complete.rs | 5 ++- tools/codegen/src/ast/verified/mod.rs | 18 +++++++- tools/codegen/src/ast/verified/recover.rs | 6 ++- tools/codegen/src/grammar.pest | 15 +++++-- tools/codegen/src/ir/from_ast.rs | 9 ++++ tools/codegen/src/ir/mod.rs | 38 ++++++++++++++++- tools/codegen/src/utils.rs | 1 + 10 files changed, 140 insertions(+), 11 deletions(-) diff --git a/tools/codegen/src/ast/mod.rs b/tools/codegen/src/ast/mod.rs index 6bad0cb..933abd9 100644 --- a/tools/codegen/src/ast/mod.rs +++ b/tools/codegen/src/ast/mod.rs @@ -3,5 +3,5 @@ pub(crate) mod verified; pub use verified::{ Array, Ast, DefaultContent, DynVec, FieldDecl, FixVec, HasName, ImportStmt, ItemDecl, Option_, - Primitive, Struct, Table, TopDecl, Union, + Primitive, Struct, Table, TopDecl, Union, UnionItemDecl, }; diff --git a/tools/codegen/src/ast/raw/mod.rs b/tools/codegen/src/ast/raw/mod.rs index 6b97274..c3d8dac 100644 --- a/tools/codegen/src/ast/raw/mod.rs +++ b/tools/codegen/src/ast/raw/mod.rs @@ -40,7 +40,7 @@ pub(crate) struct OptionDecl { #[derive(Debug, Property)] pub(crate) struct UnionDecl { name: String, - items: Vec, + items: Vec, imported_depth: usize, } @@ -78,6 +78,12 @@ pub(crate) struct ItemDecl { typ: String, } +#[derive(Debug, Property)] +pub(crate) struct CustomUnionItemDecl { + typ: String, + id: usize, +} + #[derive(Debug, Property)] pub(crate) struct FieldDecl { name: String, diff --git a/tools/codegen/src/ast/raw/utils.rs b/tools/codegen/src/ast/raw/utils.rs index e27735e..aa19efc 100644 --- a/tools/codegen/src/ast/raw/utils.rs +++ b/tools/codegen/src/ast/raw/utils.rs @@ -1,8 +1,10 @@ +use std::collections::HashSet; use std::{ffi, fs, io::Read as _, path::Path, str::FromStr}; use pest::{error::Error as PestError, iterators::Pairs, Parser as _}; use same_file::is_same_file; +use crate::ast::raw::CustomUnionItemDecl; use crate::{ ast::raw as ast, parser, @@ -40,6 +42,51 @@ impl<'i> utils::PairsUtils for Pairs<'i, parser::Rule> { ret } + fn next_custom_union_items(&mut self) -> Vec { + let mut previous_id: Option = None; + let mut ret = Vec::new(); + + let mut custom_ids = HashSet::new(); + for item in self { + match item.as_rule() { + parser::Rule::item_decl => { + let mut pair = item.into_inner(); + let node = ast::CustomUnionItemDecl { + typ: pair.next_string(), + id: if let Some(pre_id) = previous_id { + pre_id + 1 + } else { + 0 + }, + }; + pair.next_should_be_none(); + ret.push(node); + } + parser::Rule::custom_union_item_decl => { + let mut pair = item.into_inner(); + let node = ast::CustomUnionItemDecl { + typ: pair.next_string(), + id: pair.next_usize(), + }; + pair.next_should_be_none(); + ret.push(node); + } + _ => unreachable!(), + } + + if !custom_ids.insert(ret.last().unwrap().id) { + panic!( + "Custom Union Item ID {} is duplicated", + ret.last().unwrap().id + ); + } + previous_id = Some(ret.last().unwrap().id); + } + // union items should be sorted by custom ID + ret.sort_by_key(|item| item.id); + ret + } + fn next_fields(&mut self) -> Vec { let mut ret = Vec::new(); for field in self { @@ -204,7 +251,7 @@ impl parser::Parser { let mut pair = pair.into_inner(); let node = ast::UnionDecl { name: pair.next_string(), - items: pair.next_items(), + items: pair.next_custom_union_items(), imported_depth, }; pair.next_should_be_none(); diff --git a/tools/codegen/src/ast/verified/complete.rs b/tools/codegen/src/ast/verified/complete.rs index 70f6ac5..9970c51 100644 --- a/tools/codegen/src/ast/verified/complete.rs +++ b/tools/codegen/src/ast/verified/complete.rs @@ -31,7 +31,10 @@ impl CompleteRawDecl for raw::UnionDecl { } self.items() .iter() - .map(|raw_item| deps.get(raw_item.typ()).map(super::ItemDecl::new)) + .map(|raw_item| { + deps.get(raw_item.typ()) + .map(|typ| super::UnionItemDecl::new(typ, raw_item.id())) + }) .collect::>>() .map(|items| { let name = self.name().to_owned(); diff --git a/tools/codegen/src/ast/verified/mod.rs b/tools/codegen/src/ast/verified/mod.rs index 3f49e40..6e4e7e0 100644 --- a/tools/codegen/src/ast/verified/mod.rs +++ b/tools/codegen/src/ast/verified/mod.rs @@ -61,7 +61,7 @@ pub struct Option_ { #[property(get(public))] pub struct Union { name: String, - items: Vec, + items: Vec, imported_depth: usize, } @@ -115,6 +115,13 @@ pub struct ItemDecl { typ: Rc, } +#[derive(Debug, Property)] +#[property(get(public))] +pub struct UnionItemDecl { + typ: Rc, + id: usize, +} + #[derive(Debug, Property)] #[property(get(public))] pub struct FieldDecl { @@ -218,6 +225,15 @@ impl ItemDecl { } } +impl UnionItemDecl { + fn new(top_decl: &Rc, customize_id: usize) -> Self { + Self { + typ: Rc::clone(top_decl), + id: customize_id, + } + } +} + impl FieldDecl { fn new(name: &str, top_decl: &Rc) -> Self { Self { diff --git a/tools/codegen/src/ast/verified/recover.rs b/tools/codegen/src/ast/verified/recover.rs index 84b6096..9401774 100644 --- a/tools/codegen/src/ast/verified/recover.rs +++ b/tools/codegen/src/ast/verified/recover.rs @@ -29,9 +29,13 @@ impl RecoverFromIr for ir::Union { if self.items().is_empty() { panic!("the union ({}) is empty", self.name()); } + self.items() .iter() - .map(|ir_item| deps.get(ir_item.typ()).map(super::ItemDecl::new)) + .map(|ir_item| { + deps.get(ir_item.typ()) + .map(|item| super::UnionItemDecl::new(item, ir_item.id())) + }) .collect::>>() .map(|items| { let name = self.name().to_owned(); diff --git a/tools/codegen/src/grammar.pest b/tools/codegen/src/grammar.pest index 15c073b..cecc163 100644 --- a/tools/codegen/src/grammar.pest +++ b/tools/codegen/src/grammar.pest @@ -8,7 +8,9 @@ ifs = _{ " " | "\t" } newline = _{ "\n" | "\r\n" } identifier = @{ letter ~ (letter | digit | "_")* } -number = @{ nonzero ~ digit* } + +number_greater_than_zero = @{ nonzero ~ digit* } +number_greater_or_equal_than_zero = @{ zero | number_greater_than_zero} block_comment = _{ "/*" ~ (block_comment | !"*/" ~ ANY)* ~ "*/" } line_comment = _{ ("//" | "#") ~(!newline ~ ANY)* } @@ -25,6 +27,13 @@ item_decl = { identifier ~ (brk)* ~ item_end } + +custom_union_item_decl = { + identifier ~ (brk)* ~ ":" ~ (brk)* ~ + number_greater_or_equal_than_zero ~ (brk)* ~ + field_end + } + field_decl = { identifier ~ (brk)* ~ ":" ~ (brk)* ~ identifier ~ (brk)* ~ @@ -40,13 +49,13 @@ option_decl = { union_decl = { "union" ~ (brk)+ ~ identifier ~ (brk)* ~ "{" ~ (brk)* ~ - (item_decl ~ (brk)*)+ ~ + ((item_decl | custom_union_item_decl) ~ (brk)*)+ ~ "}" } array_decl = { "array" ~ (brk)+ ~ identifier ~ (brk)* ~ "[" ~ (brk)* ~ - identifier ~ (brk)* ~ ";" ~ (brk)* ~ number ~ (brk)* ~ + identifier ~ (brk)* ~ ";" ~ (brk)* ~ number_greater_than_zero ~ (brk)* ~ "]" ~ (brk)* ~ stmt_end } diff --git a/tools/codegen/src/ir/from_ast.rs b/tools/codegen/src/ir/from_ast.rs index d57726a..9e85ee7 100644 --- a/tools/codegen/src/ir/from_ast.rs +++ b/tools/codegen/src/ir/from_ast.rs @@ -129,6 +129,15 @@ impl ToIntermediate for ast::ItemDecl { } } } +impl ToIntermediate for ast::UnionItemDecl { + type Ir = super::UnionItemDecl; + fn to_ir(&self) -> Self::Ir { + Self::Ir { + typ: self.typ().name().to_owned(), + id: self.id().to_owned(), + } + } +} impl ToIntermediate for ast::FieldDecl { type Ir = super::FieldDecl; diff --git a/tools/codegen/src/ir/mod.rs b/tools/codegen/src/ir/mod.rs index 90aa9b6..f3a3d36 100644 --- a/tools/codegen/src/ir/mod.rs +++ b/tools/codegen/src/ir/mod.rs @@ -1,7 +1,7 @@ mod format; mod from_ast; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use property::Property; @@ -52,7 +52,9 @@ pub(crate) struct Option_ { #[serde(deny_unknown_fields)] pub(crate) struct Union { name: String, - items: Vec, + + #[serde(deserialize_with = "deserialize_union_items")] + items: Vec, #[serde(default = "zero", skip_serializing_if = "is_zero")] imported_depth: usize, } @@ -109,6 +111,38 @@ pub(crate) struct ItemDecl { typ: String, } +#[derive(Debug, Property, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct UnionItemDecl { + typ: String, + id: usize, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +enum UnionItemsForCompatibility { + ItemsForCompatibility(Vec), + Items(Vec), +} + +fn deserialize_union_items<'de, D>(d: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let union_com = UnionItemsForCompatibility::deserialize(d)?; + Ok(match union_com { + UnionItemsForCompatibility::ItemsForCompatibility(items) => items + .iter() + .enumerate() + .map(|(id, item)| UnionItemDecl { + typ: item.typ.clone(), + id, + }) + .collect(), + UnionItemsForCompatibility::Items(items) => items, + }) +} + #[derive(Debug, Property, Deserialize, Serialize)] #[serde(deny_unknown_fields)] pub(crate) struct FieldDecl { diff --git a/tools/codegen/src/utils.rs b/tools/codegen/src/utils.rs index 2fd219b..87163d5 100644 --- a/tools/codegen/src/utils.rs +++ b/tools/codegen/src/utils.rs @@ -9,6 +9,7 @@ pub(crate) trait PairsUtils { fn next_usize(&mut self) -> usize; fn next_item(&mut self) -> ast::ItemDecl; fn next_items(&mut self) -> Vec; + fn next_custom_union_items(&mut self) -> Vec; fn next_fields(&mut self) -> Vec; fn next_import>( &mut self, From e09c7411a9eb18fe9027d72f75d903ac2e1d23ce Mon Sep 17 00:00:00 2001 From: Eval EXEC Date: Tue, 14 Feb 2023 13:43:57 +0800 Subject: [PATCH 2/5] add code generator for Rust --- tools/codegen/src/generator/languages/rust/enumerator.rs | 8 ++++---- tools/codegen/src/generator/languages/rust/getters.rs | 8 ++++---- .../src/generator/languages/rust/reader/implementation.rs | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/codegen/src/generator/languages/rust/enumerator.rs b/tools/codegen/src/generator/languages/rust/enumerator.rs index c4062d5..de986fc 100644 --- a/tools/codegen/src/generator/languages/rust/enumerator.rs +++ b/tools/codegen/src/generator/languages/rust/enumerator.rs @@ -26,7 +26,7 @@ impl GenEnumerator for ast::Union { ref entity_union_item_paths, ref reader_union_item_paths, ) = { - self.items().iter().enumerate().fold( + self.items().iter().fold( ( Vec::with_capacity(inner_len), Vec::with_capacity(inner_len), @@ -43,12 +43,12 @@ impl GenEnumerator for ast::Union { mut entity_union_item_paths, mut reader_union_item_paths, ), - (index, inner)| { - let inner_name = inner.typ().name(); + item| { + let inner_name = item.typ().name(); let entity_name = entity_name(inner_name); let reader_name = reader_name(inner_name); let item_name = union_item_name(inner_name); - let item_id = usize_lit(index); + let item_id = usize_lit(item.id()); let entity_union_item_path = quote!(#entity_union::#item_name); let reader_union_item_path = quote!(#reader_union::#item_name); entity_inners.push(entity_name); diff --git a/tools/codegen/src/generator/languages/rust/getters.rs b/tools/codegen/src/generator/languages/rust/getters.rs index 1a04eea..2ba6593 100644 --- a/tools/codegen/src/generator/languages/rust/getters.rs +++ b/tools/codegen/src/generator/languages/rust/getters.rs @@ -63,12 +63,12 @@ impl ImplGetters for ast::Union { let getter_stmt = quote!(&self.as_slice()[molecule::NUMBER_SIZE..]); (getter_ret, getter_stmt) }; - let match_stmts = self.items().iter().enumerate().map(|(index, inner)| { - let item_id = usize_lit(index); + let match_stmts = self.items().iter().map(|item| { + let item_id = usize_lit(item.id()); let inner = if is_entity { - entity_name(inner.typ().name()) + entity_name(item.typ().name()) } else { - reader_name(inner.typ().name()) + reader_name(item.typ().name()) }; quote!(#item_id => #inner::new_unchecked(inner).into(),) }); diff --git a/tools/codegen/src/generator/languages/rust/reader/implementation.rs b/tools/codegen/src/generator/languages/rust/reader/implementation.rs index b495ce8..ca23d5e 100644 --- a/tools/codegen/src/generator/languages/rust/reader/implementation.rs +++ b/tools/codegen/src/generator/languages/rust/reader/implementation.rs @@ -47,9 +47,9 @@ impl ImplReader for ast::Option_ { impl ImplReader for ast::Union { fn impl_reader_internal(&self) -> m4::TokenStream { - let verify_inners = self.items().iter().enumerate().map(|(index, inner)| { - let item_id = usize_lit(index); - let inner = reader_name(inner.typ().name()); + let verify_inners = self.items().iter().map(|item| { + let item_id = usize_lit(item.id()); + let inner = reader_name(item.typ().name()); quote!( #item_id => #inner::verify(inner_slice, compatible), ) From 4427cecfc54eeed06e8b8163ea6982af133d077d Mon Sep 17 00:00:00 2001 From: Eval EXEC Date: Tue, 14 Feb 2023 13:35:03 +0800 Subject: [PATCH 3/5] add code generator for C --- tools/codegen/src/generator/languages/c/reader.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/codegen/src/generator/languages/c/reader.rs b/tools/codegen/src/generator/languages/c/reader.rs index feaca4b..26ec6ff 100644 --- a/tools/codegen/src/generator/languages/c/reader.rs +++ b/tools/codegen/src/generator/languages/c/reader.rs @@ -95,8 +95,12 @@ impl GenReader for ast::Union { w!(o, " inner.ptr = input->ptr + MOL_NUM_T_SIZE; "); w!(o, " inner.size = input->size - MOL_NUM_T_SIZE; "); w!(o, " switch(item_id) {{ "); - for (item_id, item) in self.items().iter().enumerate() { - w!(o, " case {}: ", item_id); + for item in self.items().iter() { + w!( + o, + " case {}: ", + item.id() + ); if item.typ().is_byte() { w!(o, " return inner.size == 1 ? MOL_OK : MOL_ERR; "); } else { From 8e5cfad3f489290fde93da7ec111bc9d546d1b5d Mon Sep 17 00:00:00 2001 From: Eval EXEC Date: Mon, 13 Feb 2023 12:59:03 +0800 Subject: [PATCH 4/5] add unit test for custom union id feature Signed-off-by: Eval EXEC --- tools/codegen/Cargo.toml | 3 + tools/codegen/src/parser/mod.rs | 303 ++++++++++++++++++++++++++++++++ 2 files changed, 306 insertions(+) diff --git a/tools/codegen/Cargo.toml b/tools/codegen/Cargo.toml index 4c1202c..96e21ff 100644 --- a/tools/codegen/Cargo.toml +++ b/tools/codegen/Cargo.toml @@ -29,6 +29,9 @@ serde = { version = "1.0.118", features = ["derive", "rc"], optional = true } serde_json = { version = "1.0.61", optional = true } serde_yaml = { version = "0.8.15", optional = true } +[dev-dependencies] +tempfile = "3" + [features] default = [] compiler-plugin = ["serde", "serde_json", "serde_yaml"] diff --git a/tools/codegen/src/parser/mod.rs b/tools/codegen/src/parser/mod.rs index 25e0300..0cc04e2 100644 --- a/tools/codegen/src/parser/mod.rs +++ b/tools/codegen/src/parser/mod.rs @@ -13,3 +13,306 @@ impl Parser { ast::Ast::complete(ast_raw) } } + +#[cfg(test)] +mod tests { + use crate::ast::{HasName, TopDecl}; + use crate::*; + use std::io::Write; + + #[test] + fn test_parse_custom_union_id() { + let mut schema_file = tempfile::NamedTempFile::new().unwrap(); + schema_file + .write_all( + b" +array a0 [byte;1]; +array a1 [byte;1]; +array a2 [byte;1]; +array a3 [byte;1]; +array a4 [byte;1]; + +union UnionWithoutCustomId { + a0, + a1, + a2, + a3, +} + +union UninoWithFullContinuousCustomIdFrom0 { + a0 : 0, + a1 : 1, + a2 : 2, + a3 : 3, +} + +union UninoWithFullContinuousCustomIdFrom5 { + a0 : 5, + a1 : 6, + a2 : 7, + a3 : 8, +} + +union UninoWithFullDiscontinuousCustomId { + a0 : 2, + a1 : 3, + a2 : 7, + a3 : 8, +} + +union UninoWithPartialCustomId_0 { + a0 : 3, + a1, + a2, + a3, +} + +union UninoWithPartialCustomId_1 { + a0, + a1 : 3, + a2, + a3, +} + +union UninoWithPartialCustomId_2 { + a0 : 3, + a1, + a2 : 5, + a3, +} + +union UninoWithPartialCustomId_Reverse { + a0 : 5, + a1, + a2 : 3, + a3, +} + + +", + ) + .unwrap(); + schema_file.flush().unwrap(); + + let ast = Parser::parse(&schema_file.into_temp_path()); + ast.decls().iter().for_each(|decl| { + if let TopDecl::Union(union) = decl.as_ref() { + match union.name() { + "UnionWithoutCustomId" => { + assert_eq!(union.items().len(), 4); + for union_item_decl in union.items() { + match union_item_decl.typ().name() { + "a0" => assert_eq!(union_item_decl.id(), 0), + "a1" => assert_eq!(union_item_decl.id(), 1), + "a2" => assert_eq!(union_item_decl.id(), 2), + "a3" => assert_eq!(union_item_decl.id(), 3), + _ => unreachable!(), + } + } + } + "UninoWithFullContinuousCustomIdFrom0" => { + assert_eq!(union.items().len(), 4); + for union_item_decl in union.items() { + match union_item_decl.typ().name() { + "a0" => assert_eq!(union_item_decl.id(), 0), + "a1" => assert_eq!(union_item_decl.id(), 1), + "a2" => assert_eq!(union_item_decl.id(), 2), + "a3" => assert_eq!(union_item_decl.id(), 3), + _ => unreachable!(), + } + } + } + "UninoWithFullContinuousCustomIdFrom5" => { + assert_eq!(union.items().len(), 4); + for union_item_decl in union.items() { + match union_item_decl.typ().name() { + "a0" => assert_eq!(union_item_decl.id(), 5), + "a1" => assert_eq!(union_item_decl.id(), 6), + "a2" => assert_eq!(union_item_decl.id(), 7), + "a3" => assert_eq!(union_item_decl.id(), 8), + _ => unreachable!(), + } + } + } + "UninoWithFullDiscontinuousCustomId" => { + assert_eq!(union.items().len(), 4); + for union_item_decl in union.items() { + match union_item_decl.typ().name() { + "a0" => assert_eq!(union_item_decl.id(), 2), + "a1" => assert_eq!(union_item_decl.id(), 3), + "a2" => assert_eq!(union_item_decl.id(), 7), + "a3" => assert_eq!(union_item_decl.id(), 8), + _ => unreachable!(), + } + } + } + "UninoWithPartialCustomId_0" => { + assert_eq!(union.items().len(), 4); + for union_item_decl in union.items() { + match union_item_decl.typ().name() { + "a0" => assert_eq!(union_item_decl.id(), 3), + "a1" => assert_eq!(union_item_decl.id(), 4), + "a2" => assert_eq!(union_item_decl.id(), 5), + "a3" => assert_eq!(union_item_decl.id(), 6), + _ => unreachable!(), + } + } + } + "UninoWithPartialCustomId_1" => { + assert_eq!(union.items().len(), 4); + for union_item_decl in union.items() { + match union_item_decl.typ().name() { + "a0" => assert_eq!(union_item_decl.id(), 0), + "a1" => assert_eq!(union_item_decl.id(), 3), + "a2" => assert_eq!(union_item_decl.id(), 4), + "a3" => assert_eq!(union_item_decl.id(), 5), + _ => unreachable!(), + } + } + } + "UninoWithPartialCustomId_2" => { + assert_eq!(union.items().len(), 4); + for union_item_decl in union.items() { + match union_item_decl.typ().name() { + "a0" => assert_eq!(union_item_decl.id(), 3), + "a1" => assert_eq!(union_item_decl.id(), 4), + "a2" => assert_eq!(union_item_decl.id(), 5), + "a3" => assert_eq!(union_item_decl.id(), 6), + _ => unreachable!(), + } + } + } + "UninoWithPartialCustomId_Reverse" => { + assert_eq!(union.items().len(), 4); + for union_item_decl in union.items() { + match union_item_decl.typ().name() { + "a0" => assert_eq!(union_item_decl.id(), 5), + "a1" => assert_eq!(union_item_decl.id(), 6), + "a2" => assert_eq!(union_item_decl.id(), 3), + "a3" => assert_eq!(union_item_decl.id(), 4), + _ => unreachable!(), + } + } + } + + _ => unreachable!(), + } + } + }); + } + + #[test] + fn test_union_items_should_ordered_by_custom_id() { + let mut schema_file0 = tempfile::NamedTempFile::new().unwrap(); + schema_file0 + .write_all( + b" +array a0 [byte;1]; +array a1 [byte;2]; +array a2 [byte;3]; +array a3 [byte;4]; +union Foo { + a0 : 1, + a1, + a2 : 10, + a3, +} +", + ) + .unwrap(); + + schema_file0.flush().unwrap(); + + let mut schema_file1 = tempfile::NamedTempFile::new().unwrap(); + schema_file1 + .write_all( + b" +array a0 [byte;1]; +array a1 [byte;2]; +array a2 [byte;3]; +array a3 [byte;4]; +union Foo { + a2 : 10, + a3, + a0 : 1, + a1, +} +", + ) + .unwrap(); + + schema_file1.flush().unwrap(); + + let ast0 = Parser::parse(&schema_file0.into_temp_path()); + let ast1 = Parser::parse(&schema_file1.into_temp_path()); + + for ast in vec![ast0, ast1] { + // get union items + if let TopDecl::Union(union) = ast + .decls() + .iter() + .find(|decl| decl.name() == "Foo") + .unwrap() + .as_ref() + { + let custom_ids: Vec = union.items().iter().map(|item| item.id()).collect(); + assert_eq!(custom_ids, vec![1, 2, 10, 11]); + } + } + } + + #[should_panic] + #[test] + fn test_bad_explicit_duplicate_union_schema() { + let mut schema_file = tempfile::NamedTempFile::new().unwrap(); + schema_file + .write_all( + b" +array a0 [byte;1]; +array a1 [byte;1]; +array a2 [byte;1]; +array a3 [byte;1]; +union UninoWithPartialDuplicateCustomId { + a0, + a1 : 3, + a2 : 3, + a3, +} +", + ) + .unwrap(); + + schema_file.flush().unwrap(); + + let _should_panic = Parser::parse(&schema_file.into_temp_path()); + } + + #[should_panic] + #[test] + fn test_bad_implicit_duplicate_union_schema() { + let mut schema_file = tempfile::NamedTempFile::new().unwrap(); + schema_file + .write_all( + b" +array a0 [byte;1]; +array a1 [byte;1]; +array a2 [byte;1]; +array a3 [byte;1]; +array a4 [byte;1]; + +union UninoWithPartialDuplicateCustomIdInReverseOrder { + a0 : 10, + a1, + a2, + a3 : 11, + a4, +} +", + ) + .unwrap(); + + schema_file.flush().unwrap(); + + let _should_panic = Parser::parse(&schema_file.into_temp_path()); + } +} From 27a349abdbeddda3e2972eb1c97e9aed18f12896 Mon Sep 17 00:00:00 2001 From: Eval EXEC Date: Tue, 14 Feb 2023 07:17:51 +0800 Subject: [PATCH 5/5] add compatibility test for custom union ID feature Signed-off-by: Eval EXEC --- Makefile | 2 +- tests/.gitignore | 2 + tests/Cargo.toml | 18 +++++++ tests/build.rs | 66 ++++++++++++++++++++++++ tests/src/main.rs | 3 ++ tests/src/union_compatibility_test.rs | 73 +++++++++++++++++++++++++++ tests/union_foo_0_7_3.mol | 11 ++++ tests/union_foo_with_custom_id.mol | 7 +++ 8 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 tests/.gitignore create mode 100644 tests/Cargo.toml create mode 100644 tests/build.rs create mode 100644 tests/src/main.rs create mode 100644 tests/src/union_compatibility_test.rs create mode 100644 tests/union_foo_0_7_3.mol create mode 100644 tests/union_foo_with_custom_id.mol diff --git a/Makefile b/Makefile index c9375ba..8e39093 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ ci: make ci-examples ci-crates; \ echo "Success!" -RUST_DEV_PROJS = examples/ci-tests +RUST_DEV_PROJS = examples/ci-tests tests RUST_PROD_PROJS = bindings/rust tools/codegen tools/compiler RUST_PROJS = ${RUST_DEV_PROJS} ${RUST_PROD_PROJS} C_PROJS = examples/ci-tests diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/tests/Cargo.toml b/tests/Cargo.toml new file mode 100644 index 0000000..e3e8695 --- /dev/null +++ b/tests/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "tests" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[dev-dependencies] +codegen-0_7_3 = {package = "molecule-codegen", version = "0.7.3", features = ["compiler-plugin"]} +codegen-dev = {package = "molecule-codegen", path = "../tools/codegen", features = ["compiler-plugin"]} +molecule = "0.7.3" + +[build-dependencies] +codegen-0_7_3 = {package = "molecule-codegen", version = "0.7.3", features = ["compiler-plugin"]} +codegen-dev = {package = "molecule-codegen", path = "../tools/codegen", features = ["compiler-plugin"]} +molecule = "0.7.3" diff --git a/tests/build.rs b/tests/build.rs new file mode 100644 index 0000000..e332dae --- /dev/null +++ b/tests/build.rs @@ -0,0 +1,66 @@ +fn compile_schema_0_7_3(schema: &str) { + let out_dir = std::path::PathBuf::from(&std::env::var("OUT_DIR").unwrap()).join("0_7_3"); + std::fs::create_dir_all(&out_dir).unwrap(); + + let mut compiler = codegen_0_7_3::Compiler::new(); + compiler + .input_schema_file(schema) + .generate_code(codegen_0_7_3::Language::Rust) + .output_dir(out_dir) + .run() + .unwrap(); + println!("cargo:rerun-if-changed={}", schema); +} + +fn compile_schema_dev(schema: &str) { + let out_dir = std::path::PathBuf::from(&std::env::var("OUT_DIR").unwrap()).join("dev"); + std::fs::create_dir_all(&out_dir).unwrap(); + + let mut compiler = codegen_dev::Compiler::new(); + compiler + .input_schema_file(schema) + .generate_code(codegen_dev::Language::Rust) + .output_dir(out_dir) + .run() + .unwrap(); + println!("cargo:rerun-if-changed={}", schema); +} + +fn compile_intermediate_0_7_3(schema: &str) { + let out_dir = std::path::PathBuf::from(&std::env::var("OUT_DIR").unwrap()).join("0_7_3"); + std::fs::create_dir_all(&out_dir).unwrap(); + + let mut compiler = codegen_0_7_3::Compiler::new(); + compiler + .input_schema_file(schema) + .generate_intermediate(codegen_0_7_3::IntermediateFormat::JSON) + .output_dir(out_dir) + .run() + .unwrap(); + println!("cargo:rerun-if-changed={}", schema); +} + +fn compile_intermediate_dev(schema: &str) { + let out_dir = std::path::PathBuf::from(&std::env::var("OUT_DIR").unwrap()).join("dev"); + std::fs::create_dir_all(&out_dir).unwrap(); + + let mut compiler = codegen_dev::Compiler::new(); + compiler + .input_schema_file(schema) + .generate_intermediate(codegen_dev::IntermediateFormat::JSON) + .output_dir(out_dir) + .run() + .unwrap(); + println!("cargo:rerun-if-changed={}", schema); +} + +fn main() { + println!("cargo:rerun-if-changed=./union_foo_0_7_3.mol"); + println!("cargo:rerun-if-changed=./union_foo_with_custom_id.mol"); + + compile_intermediate_0_7_3("./union_foo_0_7_3.mol"); + compile_intermediate_dev("./union_foo_with_custom_id.mol"); + + compile_schema_0_7_3("./union_foo_0_7_3.mol"); + compile_schema_dev("./union_foo_with_custom_id.mol"); +} diff --git a/tests/src/main.rs b/tests/src/main.rs new file mode 100644 index 0000000..fd6491b --- /dev/null +++ b/tests/src/main.rs @@ -0,0 +1,3 @@ +mod union_compatibility_test; + +fn main() {} diff --git a/tests/src/union_compatibility_test.rs b/tests/src/union_compatibility_test.rs new file mode 100644 index 0000000..ad6b0d3 --- /dev/null +++ b/tests/src/union_compatibility_test.rs @@ -0,0 +1,73 @@ +#[cfg(test)] +mod tests { + use molecule::prelude::*; + + static UNION_FOO_0_7_3_JSON_INTERMEDIATE: &str = + include_str!(concat!(env!("OUT_DIR"), "/0_7_3/union_foo_0_7_3.json")); + + static UNION_FOO_DEV_JSON_INTERMEDIATE: &str = include_str!(concat!( + env!("OUT_DIR"), + "/dev/union_foo_with_custom_id.json" + )); + + #[test] + fn test_recover_0_7_3_intermediate_by_current_ir_recover() { + let format = codegen_dev::IntermediateFormat::JSON; + let ast_result = format.recover(UNION_FOO_0_7_3_JSON_INTERMEDIATE.as_bytes()); + assert!(ast_result.is_ok()); + } + + #[test] + fn test_recover_ir() { + let format = codegen_dev::IntermediateFormat::JSON; + let ast_result = format.recover(UNION_FOO_DEV_JSON_INTERMEDIATE.as_bytes()); + assert!(ast_result.is_ok()); + } + + mod union_foo_0_7_3 { + #![allow(clippy::all, dead_code)] + include!(concat!(env!("OUT_DIR"), "/0_7_3/union_foo_0_7_3.rs")); + } + + mod union_foo_dev { + #![allow(clippy::all, dead_code)] + include!(concat!(env!("OUT_DIR"), "/dev/union_foo_with_custom_id.rs")); + } + + #[test] + fn test_decode_0_7_3_generated_rust_bytes_by_current_version() { + let a2_0_7_3 = union_foo_0_7_3::A2::new_builder() + .nth0(Byte::from(17)) + .build(); + + let foo_0_7_3 = union_foo_0_7_3::Foo::new_builder() + .set(a2_0_7_3.clone()) + .build(); + let foo_0_7_3_slice = foo_0_7_3.as_slice(); + + let foo_dev_result = union_foo_dev::FooOnlyReserveA2AndA3::from_slice(foo_0_7_3_slice); + assert!(foo_dev_result.is_ok()); + let foo_dev = foo_dev_result.unwrap(); + + let foo_union_dev = foo_dev.to_enum(); + + if let union_foo_dev::FooOnlyReserveA2AndA3Union::A2(a2_dev) = foo_union_dev { + assert_eq!(a2_0_7_3.as_slice(), a2_dev.as_slice()); + } else { + panic!("foo_union_dev should be A2"); + } + } + + #[test] + fn test_decode_0_7_3_generated_deprecated_rust_bytes_by_current_version() { + let a0_0_7_3 = union_foo_0_7_3::A0::new_builder() + .nth0(Byte::from(133)) + .build(); + + let foo_0_7_3 = union_foo_0_7_3::Foo::new_builder().set(a0_0_7_3).build(); + let foo_0_7_3_slice = foo_0_7_3.as_slice(); + + let foo_dev_result = union_foo_dev::FooOnlyReserveA2AndA3::from_slice(foo_0_7_3_slice); + assert!(foo_dev_result.is_err()); + } +} diff --git a/tests/union_foo_0_7_3.mol b/tests/union_foo_0_7_3.mol new file mode 100644 index 0000000..332eea0 --- /dev/null +++ b/tests/union_foo_0_7_3.mol @@ -0,0 +1,11 @@ +array a0 [byte;1]; +array a1 [byte;2]; +array a2 [byte;3]; +array a3 [byte;4]; + +union Foo { + a0, + a1, + a2, + a3, +} diff --git a/tests/union_foo_with_custom_id.mol b/tests/union_foo_with_custom_id.mol new file mode 100644 index 0000000..2482e47 --- /dev/null +++ b/tests/union_foo_with_custom_id.mol @@ -0,0 +1,7 @@ +array a2 [byte;3]; +array a3 [byte;4]; + +union Foo_Only_Reserve_a2_and_a3{ + a2 : 2, + a3 : 3, +}