| use quickcheck::{Arbitrary, Gen}; |
| use std::fmt; |
| use std::fmt::Write as _; |
| |
| /// `BaseTypeC` is used in generation of C headers to represent the C language's |
| /// primitive types as well as `void*`. |
| #[derive(Debug, Clone)] |
| pub struct BaseTypeC { |
| /// String representation of C type. |
| pub def: String, |
| } |
| |
| /// `TypeQualifierC` is used in generation of C headers to represent qualifiers |
| /// such as `const`. |
| #[derive(Debug, Clone)] |
| pub struct TypeQualifierC { |
| /// String representation of C type qualifier. |
| pub def: String, |
| } |
| |
| /// `PointerLevelC` is used in generation of C headers to represent number of |
| /// `*` for pointer types. |
| #[derive(Debug, Clone)] |
| pub struct PointerLevelC { |
| /// String representation of C declaration's pointer level. |
| pub def: String, |
| } |
| |
| /// `ArrayDimensionC` is used in generation of C headers to represent number of |
| /// `[]` used to define array types. |
| #[derive(Debug, Clone)] |
| pub struct ArrayDimensionC { |
| /// String representation of C declaration's array dimension. |
| pub def: String, |
| } |
| |
| /// `BasicTypeDeclarationC` is used in generation of C headers to represent |
| /// declarations outside of function pointers that take the form |
| /// `BaseTypeC` + `TypeQualifierC` + `PointerLevelC` + `ident_id`. |
| #[derive(Debug, Clone)] |
| pub struct BasicTypeDeclarationC { |
| /// The declaration's base type, i.e. `int`. |
| pub type_name: BaseTypeC, |
| /// The declaration's type qualifier, i.e. `const`. |
| pub type_qualifier: TypeQualifierC, |
| /// The declaration's pointer level, i.e. `***`. |
| pub pointer_level: PointerLevelC, |
| /// The declaration's array dimension, i.e. [][][]. |
| pub array_dimension: ArrayDimensionC, |
| /// The declaration's identifier, i.e. `ident_N`. |
| pub ident_id: String, |
| } |
| |
| /// `StructDeclarationC` is used in generation of C headers to represent the |
| /// definition of a struct type. |
| #[derive(Debug, Clone)] |
| pub struct StructDeclarationC { |
| /// The declaration's fields. |
| pub fields: DeclarationListC, |
| /// The declaration's array dimension, i.e. [][][]. |
| pub array_dimension: ArrayDimensionC, |
| /// The declaration's identifier, i.e. `struct_N`. |
| pub ident_id: String, |
| } |
| |
| /// `UnionDeclarationC` is used in generation of C headers to represent the |
| /// definition of a union type. |
| #[derive(Debug, Clone)] |
| pub struct UnionDeclarationC { |
| /// The declaration's fields. |
| pub fields: DeclarationListC, |
| /// The declaration's array dimension, i.e. [][][]. |
| pub array_dimension: ArrayDimensionC, |
| /// The declaration's identifier, i.e. `union_N`. |
| pub ident_id: String, |
| } |
| |
| /// `FunctionPointerDeclarationC` is used in generation of C headers to represent |
| /// the definition of a function pointer type. |
| #[derive(Debug, Clone)] |
| pub struct FunctionPointerDeclarationC { |
| /// The function's type qualifier, i.e. `const`. |
| pub type_qualifier: TypeQualifierC, |
| /// The function's return type, i.e. `int`. |
| pub type_name: BaseTypeC, |
| /// The function's pointer level, i.e. `***`. |
| pub pointer_level: PointerLevelC, |
| /// The function's parameters. |
| pub params: ParameterListC, |
| /// The declaration's identifier, i.e. `func_ptr_N`. |
| pub ident_id: String, |
| } |
| |
| /// `FunctionPrototypeC` is used in generation of C headers to represent the |
| /// definition of a function prototype. |
| #[derive(Debug, Clone)] |
| pub struct FunctionPrototypeC { |
| /// The function's type qualifier, i.e. `const`. |
| pub type_qualifier: TypeQualifierC, |
| /// The function's return type, i.e. `int`. |
| pub type_name: BaseTypeC, |
| /// The function's pointer level, i.e. `***`. |
| pub pointer_level: PointerLevelC, |
| /// The function's parameters. |
| pub params: ParameterListC, |
| /// The prototype's identifier, i.e. `func_N`. |
| pub ident_id: String, |
| } |
| |
| /// `ParameterC` is used in generation of C headers to represent the |
| /// definition function parameters. |
| #[derive(Debug, Clone)] |
| pub struct ParameterC { |
| /// The parameter's type qualifier, i.e. `const`. |
| pub type_qualifier: TypeQualifierC, |
| /// The parameter's base type, i.e. `int`. |
| pub type_name: BaseTypeC, |
| /// The parameter's pointer level, i.e. `***`. |
| pub pointer_level: PointerLevelC, |
| } |
| |
| /// `ParameterListC` is used in generation of C headers to represent a list of |
| /// definitions of function parameters. |
| #[derive(Debug, Clone)] |
| pub struct ParameterListC { |
| /// Parameters that define a C function signature. |
| pub params: Vec<ParameterC>, |
| } |
| |
| /// `DeclarationC` is used in generation of C headers to represent all supported |
| /// C type declarations allowed in the generated header. |
| #[derive(Debug, Clone)] |
| pub enum DeclarationC { |
| /// Function prototype declaration kind. |
| FunctionDecl(FunctionPrototypeC), |
| /// Function pointer declaration kind. |
| FunctionPtrDecl(FunctionPointerDeclarationC), |
| /// Struct declaration kind. |
| StructDecl(StructDeclarationC), |
| /// Union declaration kind. |
| UnionDecl(UnionDeclarationC), |
| /// Basic type declaration kind. |
| VariableDecl(BasicTypeDeclarationC), |
| } |
| |
| /// `DeclarationListC` is used in generation of C headers to represent a list of |
| /// declarations. |
| #[derive(Debug, Clone)] |
| pub struct DeclarationListC { |
| /// Grouping of C declarations. |
| pub decls: Vec<DeclarationC>, |
| } |
| |
| /// `HeaderC` is used in generation of C headers to represent a collection of |
| /// declarations. |
| #[derive(Clone)] |
| pub struct HeaderC { |
| /// The header's declarations. |
| pub def: DeclarationListC, |
| } |
| |
| /// `MakeUnique` is used in generation of C headers to make declaration |
| /// identifiers unique by incorporating the `stamp` parameter into it's name. |
| trait MakeUnique { |
| fn make_unique(&mut self, stamp: usize); |
| } |
| |
| /// `MakeUnique` is used in generation of C headers to make `DeclarationC` |
| /// identifiers unique. |
| impl MakeUnique for DeclarationC { |
| fn make_unique(&mut self, stamp: usize) { |
| match *self { |
| DeclarationC::FunctionDecl(ref mut d) => d.make_unique(stamp), |
| DeclarationC::FunctionPtrDecl(ref mut d) => d.make_unique(stamp), |
| DeclarationC::StructDecl(ref mut d) => d.make_unique(stamp), |
| DeclarationC::UnionDecl(ref mut d) => d.make_unique(stamp), |
| DeclarationC::VariableDecl(ref mut d) => d.make_unique(stamp), |
| } |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `DeclarationC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for DeclarationC { |
| fn arbitrary(g: &mut Gen) -> DeclarationC { |
| match gen_range(g, 0, 5) { |
| 0 => DeclarationC::FunctionDecl(FunctionPrototypeC::arbitrary(g)), |
| 1 => DeclarationC::FunctionPtrDecl( |
| FunctionPointerDeclarationC::arbitrary(g), |
| ), |
| 2 => DeclarationC::StructDecl(StructDeclarationC::arbitrary(g)), |
| 3 => DeclarationC::UnionDecl(UnionDeclarationC::arbitrary(g)), |
| 4 => { |
| DeclarationC::VariableDecl(BasicTypeDeclarationC::arbitrary(g)) |
| } |
| _ => unreachable!(), |
| } |
| } |
| } |
| |
| /// Enables to string and format for `DeclarationC` types. |
| impl fmt::Display for DeclarationC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| match *self { |
| DeclarationC::FunctionPtrDecl(ref d) => write!(f, "{d}"), |
| DeclarationC::StructDecl(ref d) => write!(f, "{d}"), |
| DeclarationC::UnionDecl(ref d) => write!(f, "{d}"), |
| DeclarationC::VariableDecl(ref d) => write!(f, "{d}"), |
| DeclarationC::FunctionDecl(ref d) => write!(f, "{d}"), |
| } |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `DeclarationListC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for DeclarationListC { |
| fn arbitrary(g: &mut Gen) -> DeclarationListC { |
| DeclarationListC { |
| decls: Arbitrary::arbitrary(g), |
| } |
| } |
| } |
| |
| /// Enables to string and format for `DeclarationListC` types. |
| impl fmt::Display for DeclarationListC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| for decl in &self.decls { |
| write!(f, "{decl}")?; |
| } |
| Ok(()) |
| } |
| } |
| |
| /// A quickcheck trait for describing how `BaseTypeC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for BaseTypeC { |
| fn arbitrary(g: &mut Gen) -> BaseTypeC { |
| // Special case `long double` until issue #550 is resolved. |
| let base_type = vec![ |
| "char", |
| "signed char", |
| "unsigned char", |
| "short", |
| "short int", |
| "signed short", |
| "signed short int", |
| "unsigned short", |
| "unsigned short int", |
| "int", |
| "signed", |
| "signed int", |
| "unsigned", |
| "unsigned int", |
| "long", |
| "long int", |
| "signed long", |
| "signed long int", |
| "unsigned long", |
| "unsigned long int", |
| "long long", |
| "long long int", |
| "signed long long", |
| "signed long long int", |
| "unsigned long long", |
| "unsigned long long int", |
| "float", |
| "double", |
| #[cfg(feature = "long-doubles")] |
| "long double", |
| "void*", |
| ]; |
| BaseTypeC { |
| def: String::from(*g.choose(&base_type).unwrap()), |
| } |
| } |
| } |
| |
| /// Enables to string and format for `BaseTypeC` types, |
| impl fmt::Display for BaseTypeC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "{}", self.def) |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `TypeQualifierC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for TypeQualifierC { |
| fn arbitrary(g: &mut Gen) -> TypeQualifierC { |
| let qualifier = vec!["const", ""]; |
| TypeQualifierC { |
| def: String::from(*g.choose(&qualifier).unwrap()), |
| } |
| } |
| } |
| |
| /// Enables to string and format for `TypeQualifierC` types. |
| impl fmt::Display for TypeQualifierC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "{}", self.def) |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `PointerLevelC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for PointerLevelC { |
| fn arbitrary(g: &mut Gen) -> PointerLevelC { |
| PointerLevelC { |
| // 16 is an arbitrary "not too big" number for capping pointer level. |
| def: (0..gen_range(g, 0, 16)).map(|_| "*").collect::<String>(), |
| } |
| } |
| } |
| |
| /// Enables to string and format for `PointerLevelC` types. |
| impl fmt::Display for PointerLevelC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "{}", self.def) |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `ArrayDimensionC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for ArrayDimensionC { |
| fn arbitrary(g: &mut Gen) -> ArrayDimensionC { |
| // Keep these small, clang complains when they get too big. |
| let dimensions = gen_range(g, 0, 5); |
| let mut def = String::new(); |
| |
| let lower_bound = u64::from(cfg!(feature = "zero-sized-arrays")); |
| |
| for _ in 1..dimensions { |
| // 16 is an arbitrary "not too big" number for capping array size. |
| let _ = write!(def, "[{}]", gen_range(g, lower_bound, 16)); |
| } |
| ArrayDimensionC { def } |
| } |
| } |
| |
| /// Enables to string and format for `ArrayDimensionC` types. |
| impl fmt::Display for ArrayDimensionC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "{}", self.def) |
| } |
| } |
| |
| /// `MakeUnique` is used in generation of C headers to make `BasicTypeDeclarationC` |
| /// identifiers unique. |
| impl MakeUnique for BasicTypeDeclarationC { |
| fn make_unique(&mut self, stamp: usize) { |
| let _ = write!(self.ident_id, "_{stamp}"); |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `BasicTypeDeclarationC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for BasicTypeDeclarationC { |
| fn arbitrary(g: &mut Gen) -> BasicTypeDeclarationC { |
| BasicTypeDeclarationC { |
| type_qualifier: Arbitrary::arbitrary(g), |
| type_name: Arbitrary::arbitrary(g), |
| pointer_level: Arbitrary::arbitrary(g), |
| array_dimension: Arbitrary::arbitrary(g), |
| ident_id: format!("{}", usize::arbitrary(g)), |
| } |
| } |
| } |
| |
| /// Enables to string and format for `BasicTypeDeclarationC` types. |
| impl fmt::Display for BasicTypeDeclarationC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!( |
| f, |
| "{} {} {} ident_{}{};", |
| self.type_qualifier, |
| self.type_name, |
| self.pointer_level, |
| self.ident_id, |
| self.array_dimension |
| ) |
| } |
| } |
| |
| /// `MakeUnique` is used in generation of C headers to make `StructDeclarationC` |
| /// identifiers unique. |
| impl MakeUnique for StructDeclarationC { |
| fn make_unique(&mut self, stamp: usize) { |
| let _ = write!(self.ident_id, "_{stamp}"); |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `StructDeclarationC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for StructDeclarationC { |
| fn arbitrary(g: &mut Gen) -> StructDeclarationC { |
| // Reduce generator size as a method of putting a bound on recursion. |
| // When size < 1 the empty list is generated. |
| let reduced_size: usize = (g.size() / 2) + 1; |
| let mut decl_list: DeclarationListC = |
| Arbitrary::arbitrary(&mut Gen::new(reduced_size)); |
| let mut fields: DeclarationListC = DeclarationListC { decls: vec![] }; |
| |
| for (i, decl) in decl_list.decls.iter_mut().enumerate() { |
| match *decl { |
| DeclarationC::FunctionDecl(_) => {} |
| ref mut decl => { |
| decl.make_unique(i); |
| fields.decls.push(decl.clone()); |
| } |
| } |
| } |
| |
| StructDeclarationC { |
| fields, |
| ident_id: format!("{}", usize::arbitrary(g)), |
| array_dimension: Arbitrary::arbitrary(g), |
| } |
| } |
| } |
| |
| /// Enables to string and format for `StructDeclarationC` types. |
| impl fmt::Display for StructDeclarationC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!( |
| f, |
| "struct {{ {} }} struct_{}{};", |
| self.fields, self.ident_id, self.array_dimension |
| ) |
| } |
| } |
| |
| /// `MakeUnique` is used in generation of C headers to make `UnionDeclarationC` |
| /// identifiers unique. |
| impl MakeUnique for UnionDeclarationC { |
| fn make_unique(&mut self, stamp: usize) { |
| let _ = write!(self.ident_id, "_{stamp}"); |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `UnionDeclarationC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for UnionDeclarationC { |
| fn arbitrary(g: &mut Gen) -> UnionDeclarationC { |
| // Reduce generator size as a method of putting a bound on recursion. |
| // When size < 1 the empty list is generated. |
| let reduced_size: usize = (g.size() / 2) + 1; |
| let mut decl_list: DeclarationListC = |
| Arbitrary::arbitrary(&mut Gen::new(reduced_size)); |
| let mut fields: DeclarationListC = DeclarationListC { decls: vec![] }; |
| |
| for (i, decl) in decl_list.decls.iter_mut().enumerate() { |
| match *decl { |
| DeclarationC::FunctionDecl(_) => {} |
| ref mut decl => { |
| decl.make_unique(i); |
| fields.decls.push(decl.clone()); |
| } |
| } |
| } |
| |
| UnionDeclarationC { |
| fields, |
| ident_id: format!("{}", usize::arbitrary(g)), |
| array_dimension: Arbitrary::arbitrary(g), |
| } |
| } |
| } |
| |
| /// Enables to string and format for `UnionDeclarationC` types. |
| impl fmt::Display for UnionDeclarationC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!( |
| f, |
| "union {{ {} }} union_{}{};", |
| self.fields, self.ident_id, self.array_dimension |
| ) |
| } |
| } |
| |
| /// `MakeUnique` is used in generation of C headers to make |
| /// `FunctionPointerDeclarationC` identifiers unique. |
| impl MakeUnique for FunctionPointerDeclarationC { |
| fn make_unique(&mut self, stamp: usize) { |
| let _ = write!(self.ident_id, "_{stamp}"); |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `FunctionPointerDeclarationC` types can |
| /// be randomly generated and shrunk. |
| impl Arbitrary for FunctionPointerDeclarationC { |
| fn arbitrary(g: &mut Gen) -> FunctionPointerDeclarationC { |
| FunctionPointerDeclarationC { |
| type_qualifier: Arbitrary::arbitrary(g), |
| type_name: Arbitrary::arbitrary(g), |
| pointer_level: Arbitrary::arbitrary(g), |
| params: Arbitrary::arbitrary(g), |
| ident_id: format!("{}", usize::arbitrary(g)), |
| } |
| } |
| } |
| |
| /// Enables to string and format for `FunctionPointerDeclarationC` types. |
| impl fmt::Display for FunctionPointerDeclarationC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!( |
| f, |
| "{} {} {} (*func_ptr_{})({});", |
| self.type_qualifier, |
| self.type_name, |
| self.pointer_level, |
| self.ident_id, |
| self.params |
| ) |
| } |
| } |
| |
| /// `MakeUnique` is used in generation of C headers to make `FunctionPrototypeC` |
| /// identifiers unique. |
| impl MakeUnique for FunctionPrototypeC { |
| fn make_unique(&mut self, stamp: usize) { |
| let _ = write!(self.ident_id, "_{stamp}"); |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `FunctionPrototypeC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for FunctionPrototypeC { |
| fn arbitrary(g: &mut Gen) -> FunctionPrototypeC { |
| FunctionPrototypeC { |
| type_qualifier: Arbitrary::arbitrary(g), |
| type_name: Arbitrary::arbitrary(g), |
| pointer_level: Arbitrary::arbitrary(g), |
| params: Arbitrary::arbitrary(g), |
| ident_id: format!("{}", usize::arbitrary(g)), |
| } |
| } |
| } |
| |
| /// Enables to string and format for `FunctionPrototypeC` types. |
| impl fmt::Display for FunctionPrototypeC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!( |
| f, |
| "{} {} {} func_{}({});", |
| self.type_qualifier, |
| self.type_name, |
| self.pointer_level, |
| self.ident_id, |
| self.params |
| ) |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `ParameterC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for ParameterC { |
| fn arbitrary(g: &mut Gen) -> ParameterC { |
| ParameterC { |
| type_qualifier: Arbitrary::arbitrary(g), |
| type_name: Arbitrary::arbitrary(g), |
| pointer_level: Arbitrary::arbitrary(g), |
| } |
| } |
| } |
| |
| /// Enables to string and format for `ParameterC` types. |
| impl fmt::Display for ParameterC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!( |
| f, |
| "{} {} {}", |
| self.type_qualifier, self.type_name, self.pointer_level |
| ) |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `ParameterListC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for ParameterListC { |
| fn arbitrary(g: &mut Gen) -> ParameterListC { |
| ParameterListC { |
| params: Arbitrary::arbitrary(g), |
| } |
| } |
| } |
| |
| /// Enables to string and format for `ParameterListC` types. |
| impl fmt::Display for ParameterListC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| for (i, p) in self.params.iter().enumerate() { |
| match i { |
| 0 => write!(f, "{p}")?, |
| _ => write!(f, ",{p}")?, |
| } |
| } |
| Ok(()) |
| } |
| } |
| |
| /// A qucickcheck trait for describing how `HeaderC` types can be |
| /// randomly generated and shrunk. |
| impl Arbitrary for HeaderC { |
| fn arbitrary(g: &mut Gen) -> HeaderC { |
| let mut decl_list: DeclarationListC = Arbitrary::arbitrary(g); |
| for (i, decl) in decl_list.decls.iter_mut().enumerate() { |
| decl.make_unique(i); |
| } |
| HeaderC { def: decl_list } |
| } |
| } |
| |
| /// Enables to string and format for `HeaderC` types. |
| impl fmt::Display for HeaderC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| for decl in &self.def.decls { |
| write!(f, "{decl}")?; |
| } |
| Ok(()) |
| } |
| } |
| |
| /// Use Display trait for Debug so that any failing property tests report |
| /// generated C code rather than the data structures that contain it. |
| impl fmt::Debug for HeaderC { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "{self}") |
| } |
| } |
| |
| /// FIXME: is this actually uniform? |
| fn gen_range(gen: &mut Gen, lo: u64, hi: u64) -> u64 { |
| let len = hi - lo; |
| (u64::arbitrary(gen) % len) + lo |
| } |