generator: Convert #define generation from vkxml to vk-parse (#708)

For upcoming `vk.xml` features (the new `api` attribute) some of our
codegen has to be converted to work on `vk-parse` types to make this
ergonomic (and there's a longstanding plan of factoring out `vkxml`
regardless).  Start with converting the `#define` code and showcasing
that it does not affect the output (beyond removing the unneeded
edgecase for `VK_HEADER_VERSION` resulting in a doc link).
This commit is contained in:
Marijn Suijten 2023-02-22 12:32:16 +01:00 committed by GitHub
parent 8c63c9e08b
commit 90ccc31f97
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 104 additions and 60 deletions

View file

@ -57,7 +57,8 @@ pub const API_VERSION_1_1: u32 = make_api_version(0, 1, 1, 0);
pub const API_VERSION_1_2: u32 = make_api_version(0, 1, 2, 0);
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_API_VERSION_1_3.html>"]
pub const API_VERSION_1_3: u32 = make_api_version(0, 1, 3, 0);
pub const HEADER_VERSION: u32 = 238u32;
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_HEADER_VERSION.html>"]
pub const HEADER_VERSION: u32 = 238;
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_HEADER_VERSION_COMPLETE.html>"]
pub const HEADER_VERSION_COMPLETE: u32 = make_api_version(0, 1, 3, HEADER_VERSION);
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSampleMask.html>"]

View file

@ -3,15 +3,14 @@
use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
use itertools::Itertools;
use nom::sequence::pair;
use nom::{
branch::alt,
bytes::streaming::tag,
character::complete::{char, digit1, hex_digit1, multispace1, none_of, one_of},
combinator::{complete, map, opt, value},
bytes::complete::{tag, take_until, take_while1},
character::complete::{char, digit1, hex_digit1, multispace1, newline, none_of, one_of},
combinator::{map, opt, value},
error::{ParseError, VerboseError},
multi::many1,
sequence::{delimited, preceded, terminated},
multi::{many1, separated_list1},
sequence::{delimited, pair, preceded, terminated},
IResult, Parser,
};
use once_cell::sync::Lazy;
@ -72,10 +71,7 @@ impl quote::ToTokens for CType {
}
fn parse_ctype<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, CType, E> {
(alt((
value(CType::U64, complete(tag("ULL"))),
value(CType::U32, complete(tag("U"))),
)))(i)
(alt((value(CType::U64, tag("ULL")), value(CType::U32, tag("U")))))(i)
}
fn parse_cexpr<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (CType, String), E> {
@ -156,6 +152,41 @@ fn parse_hexadecimal_number<'a, E: ParseError<&'a str>>(
))(i)
}
fn parse_c_identifier<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
take_while1(|c: char| c == '_' || c.is_alphanumeric())(i)
}
fn parse_comment_suffix<'a, E: ParseError<&'a str>>(
i: &'a str,
) -> IResult<&'a str, Option<&'a str>, E> {
opt(delimited(tag("//"), take_until("\n"), newline))(i)
}
fn parse_parameter_names<'a, E: ParseError<&'a str>>(
i: &'a str,
) -> IResult<&'a str, Vec<&'a str>, E> {
delimited(
char('('),
separated_list1(tag(", "), parse_c_identifier),
char(')'),
)(i)
}
/// Parses a C macro define optionally prefixed by a comment and optionally
/// containing parameter names. The expression is left in the remainder
#[allow(clippy::type_complexity)]
fn parse_c_define_header<'a, E: ParseError<&'a str>>(
i: &'a str,
) -> IResult<&'a str, (Option<&'a str>, (&'a str, Option<Vec<&'a str>>)), E> {
(pair(
parse_comment_suffix,
preceded(
tag("#define "),
pair(parse_c_identifier, opt(parse_parameter_names)),
),
))(i)
}
fn khronos_link<S: Display + ?Sized>(name: &S) -> Literal {
Literal::string(&format!(
"<https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/{name}.html>"
@ -1094,35 +1125,33 @@ pub fn generate_extension<'a>(
Some(q)
}
pub fn generate_define(
define: &vkxml::Define,
define: &vk_parse::Type,
identifier_renames: &mut BTreeMap<String, Ident>,
) -> TokenStream {
let name = constant_name(&define.name);
let vk_parse::TypeSpec::Code(spec) = &define.spec else {
return quote!();
};
let [vk_parse::TypeCodeMarkup::Name(define_name), ..] = &spec.markup[..] else {
return quote!();
};
let name = constant_name(define_name);
let ident = format_ident!("{}", name);
if name == "NULL_HANDLE" {
quote!()
} else if let Some(value) = &define.value {
str::parse::<u32>(value).map_or(quote!(), |v| quote!(pub const #ident: u32 = #v;))
} else if let Some(c_expr) = &define.c_expression {
if define.name.contains(&"VERSION".to_string()) {
let link = khronos_link(&define.name);
let c_expr = c_expr.trim_start_matches('\\');
if define_name.contains("VERSION") && !spec.code.contains("//#define") {
let link = khronos_link(define_name);
let (c_expr, (comment, (_name, parameters))) =
parse_c_define_header::<VerboseError<&str>>(&spec.code).unwrap();
let c_expr = c_expr.trim().trim_start_matches('\\');
let c_expr = c_expr.replace("(uint32_t)", "");
let c_expr = convert_c_expression(&c_expr, identifier_renames);
let c_expr = discard_outmost_delimiter(c_expr);
let deprecated = define
.comment
.as_ref()
.and_then(|c| c.strip_prefix("DEPRECATED: "))
.map(|comment| quote!(#[deprecated = #comment]));
let deprecated = comment
.and_then(|c| c.trim().strip_prefix("DEPRECATED: "))
.map(|comment| (quote!(#[deprecated = #comment])));
let (code, ident) = if define.parameters.is_empty() {
(quote!(pub const #ident: u32 = #c_expr;), ident)
} else {
let params = define
.parameters
let (code, ident) = if let Some(parameters) = parameters {
let params = parameters
.iter()
.map(|param| format_ident!("{}", param))
.map(|i| quote!(#i: u32));
@ -1131,9 +1160,11 @@ pub fn generate_define(
quote!(pub const fn #ident(#(#params),*) -> u32 { #c_expr }),
ident,
)
} else {
(quote!(pub const #ident: u32 = #c_expr;), ident)
};
identifier_renames.insert(define.name.clone(), ident);
identifier_renames.insert(define_name.clone(), ident);
quote! {
#deprecated
@ -1143,9 +1174,6 @@ pub fn generate_define(
} else {
quote!()
}
} else {
quote!()
}
}
pub fn generate_typedef(typedef: &vkxml::Typedef) -> TokenStream {
if typedef.basetype.is_empty() {
@ -2133,6 +2161,15 @@ pub fn root_structs(definitions: &[&vkxml::DefinitionsElement]) -> HashSet<Ident
}
root_structs
}
pub fn generate_definition_vk_parse(
definition: &vk_parse::Type,
identifier_renames: &mut BTreeMap<String, Ident>,
) -> Option<TokenStream> {
match definition.category.as_deref() {
Some("define") => Some(generate_define(definition, identifier_renames)),
_ => None,
}
}
pub fn generate_definition(
definition: &vkxml::DefinitionsElement,
union_types: &HashSet<&str>,
@ -2140,12 +2177,8 @@ pub fn generate_definition(
has_lifetimes: &HashSet<Ident>,
bitflags_cache: &mut HashSet<Ident>,
const_values: &mut BTreeMap<Ident, ConstantTypeInfo>,
identifier_renames: &mut BTreeMap<String, Ident>,
) -> Option<TokenStream> {
match *definition {
vkxml::DefinitionsElement::Define(ref define) => {
Some(generate_define(define, identifier_renames))
}
vkxml::DefinitionsElement::Typedef(ref typedef) => Some(generate_typedef(typedef)),
vkxml::DefinitionsElement::Struct(ref struct_) => Some(generate_struct(
struct_,
@ -2576,11 +2609,22 @@ pub fn write_source_code<P: AsRef<Path>>(vk_headers_dir: &Path, src_dir: P) {
_ => continue,
};
}
let vk_parse_definitions: Vec<_> = spec2
.0
.iter()
.filter_map(get_variant!(vk_parse::RegistryChild::Types))
.flat_map(|ty| {
ty.children
.iter()
.filter_map(get_variant!(vk_parse::TypesChild::Type))
})
.filter_map(|def| generate_definition_vk_parse(def, &mut identifier_renames))
.collect();
let root_structs = root_structs(&definitions);
let definition_code: Vec<_> = definitions
let definition_code: Vec<_> = vk_parse_definitions
.into_iter()
.filter_map(|def| {
.chain(definitions.into_iter().filter_map(|def| {
generate_definition(
def,
&union_types,
@ -2588,9 +2632,8 @@ pub fn write_source_code<P: AsRef<Path>>(vk_headers_dir: &Path, src_dir: P) {
&has_lifetimes,
&mut bitflags_cache,
&mut const_values,
&mut identifier_renames,
)
})
}))
.collect();
let mut ty_cache = HashSet::new();