* Replace formats.rs and features.rs regex with nom

* Fix missing eof in format rs

* Refactor parsing logic in extensions.rs

* Replace regex in mod.rs

* Replace regex in spriv_reqs.rs

* Improve nom usage for parsing get_header_version

* Remove stray dbg

* Remove final usage of regex

* Replace tag("single character") with char(...)

* Remove unused import

* Undo my testing changes to vk.xml
(I shouldn't have committed that)

* Sort cargo toml alphabetically

* Use nom for parse_depends

* Simplify parser again

* Parser cleanup

* Inline parser logic for shorter code

Thanks to marc for suggesting this

* Fix clippy violation

* Remove useless parse prefix
This commit is contained in:
stefnotch 2024-03-04 19:53:07 +01:00 committed by GitHub
parent 0375be7924
commit b34dbe9e09
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 190 additions and 192 deletions

56
Cargo.lock generated
View File

@ -46,15 +46,6 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "android-activity"
version = "0.5.1"
@ -1195,6 +1186,12 @@ dependencies = [
"winit 0.29.9",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.1"
@ -1375,6 +1372,16 @@ dependencies = [
"memoffset 0.7.1",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num_enum"
version = "0.5.11"
@ -1725,35 +1732,6 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "ron"
version = "0.8.1"
@ -2364,13 +2342,13 @@ dependencies = [
"heck",
"indexmap",
"libloading 0.8.1",
"nom",
"objc",
"once_cell",
"parking_lot",
"proc-macro2",
"quote",
"raw-window-handle 0.6.0",
"regex",
"serde",
"serde_json",
"smallvec",

View File

@ -39,6 +39,7 @@ half = "2.0"
heck = "0.4"
indexmap = "2.0"
libloading = "0.8"
nom = "7.1"
objc = "0.2.5"
once_cell = "1.17"
parking_lot = "0.12"
@ -46,7 +47,6 @@ proc-macro2 = "1.0"
proc-macro-crate = "2.0"
quote = "1.0"
raw-window-handle = "0.6"
regex = "1.8"
serde = "1.0"
serde_json = "1.0"
shaderc = "0.8"

View File

@ -36,10 +36,10 @@ core-graphics-types = { workspace = true }
ahash = { workspace = true }
heck = { workspace = true }
indexmap = { workspace = true }
nom = { workspace = true }
once_cell = { workspace = true }
proc-macro2 = { workspace = true }
quote = { workspace = true }
regex = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
vk-parse = { workspace = true }

View File

@ -1,9 +1,11 @@
use super::{write_file, IndexMap, RequiresOneOf, VkRegistryData};
use heck::ToSnakeCase;
use once_cell::sync::Lazy;
use nom::{
branch::alt, bytes::complete::take_while1, character::complete, combinator::all_consuming,
multi::separated_list1, sequence::delimited, IResult, Parser,
};
use proc_macro2::{Ident, Literal, TokenStream};
use quote::{format_ident, quote};
use regex::Regex;
use std::fmt::Write as _;
use vk_parse::Extension;
@ -968,109 +970,40 @@ enum DependsExpression<'a> {
AllOf(Vec<Self>),
}
fn parse_depends(mut depends: &str) -> Result<DependsExpression<'_>, String> {
#[derive(Debug, PartialEq)]
enum Token<'a> {
Name(&'a str),
Plus,
Comma,
POpen,
PClose,
fn parse_depends(depends: &str) -> Result<DependsExpression<'_>, String> {
fn name(input: &str) -> IResult<&str, &str> {
take_while1(|c: char| c.is_ascii_alphanumeric() || c == '_')(input)
}
static NAME: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[A-Za-z0-9_]+").unwrap());
fn term(input: &str) -> IResult<&str, DependsExpression> {
alt((
name.map(DependsExpression::Name),
delimited(complete::char('('), expression, complete::char(')')),
))(input)
}
let mut next_token = move || {
if let Some(m) = NAME.find(depends) {
depends = &depends[m.len()..];
Ok(Some(Token::Name(m.as_str())))
fn expression(input: &str) -> IResult<&str, DependsExpression> {
let (input, first) = term(input)?;
if let Some(input) = input.strip_prefix('+') {
let (input, mut all_of) = separated_list1(complete::char('+'), term)(input)?;
all_of.insert(0, first);
Ok((input, DependsExpression::AllOf(all_of)))
} else if let Some(input) = input.strip_prefix(',') {
let (input, mut one_of) = separated_list1(complete::char(','), term)(input)?;
one_of.insert(0, first);
Ok((input, DependsExpression::OneOf(one_of)))
} else {
depends
.chars()
.next()
.map(|c| {
let token = match c {
'+' => Token::Plus,
',' => Token::Comma,
'(' => Token::POpen,
')' => Token::PClose,
_ => return Err(format!("unexpected character: {}", c)),
};
depends = &depends[1..];
Ok(token)
})
.transpose()
}
};
fn parse_expression<'a>(
next_token: &mut impl FnMut() -> Result<Option<Token<'a>>, String>,
expect_pclose: bool,
) -> Result<DependsExpression<'a>, String> {
let first = match next_token()? {
Some(Token::Name(name)) => DependsExpression::Name(name),
Some(Token::POpen) => parse_expression(next_token, true)?,
Some(token) => return Err(format!("unexpected token: {:?}", token)),
None => return Err("unexpected end of string".into()),
};
match next_token()? {
Some(separator @ (Token::Plus | Token::Comma)) => {
let mut subexpr = vec![first];
loop {
match next_token()? {
Some(Token::Name(name)) => subexpr.push(DependsExpression::Name(name)),
Some(Token::POpen) => subexpr.push(parse_expression(next_token, true)?),
Some(token) => return Err(format!("unexpected token: {:?}", token)),
None => return Err("unexpected end of string".into()),
}
match next_token()? {
Some(Token::PClose) => {
if expect_pclose {
break;
} else {
return Err(format!("unexpected token: {:?}", Token::PClose));
}
}
Some(token) if token == separator => (),
Some(token) => return Err(format!("unexpected token: {:?}", token)),
None => {
if expect_pclose {
return Err("unexpected end of string".into());
} else {
break;
}
}
Ok((input, first))
}
}
Ok(match separator {
Token::Plus => DependsExpression::AllOf(subexpr),
Token::Comma => DependsExpression::OneOf(subexpr),
_ => unreachable!(),
})
match all_consuming(expression)(depends) {
Ok((_, expr)) => Ok(expr),
Err(err) => Err(format!("{:?}", err)),
}
Some(Token::PClose) => {
if expect_pclose {
Ok(first)
} else {
Err(format!("unexpected token: {:?}", Token::PClose))
}
}
Some(token) => Err(format!("unexpected token: {:?}", token)),
None => {
if expect_pclose {
Err("unexpected end of string".into())
} else {
Ok(first)
}
}
}
}
parse_expression(&mut next_token, false)
}
fn make_doc(ext: &mut ExtensionsMember) {

View File

@ -1,9 +1,9 @@
use super::{write_file, IndexMap, VkRegistryData};
use ahash::HashMap;
use heck::ToSnakeCase;
use nom::{bytes::complete::tag, character::complete::digit1, combinator::eof, sequence::tuple};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use regex::Regex;
use std::{collections::hash_map::Entry, fmt::Write as _};
use vk_parse::{Extension, Type, TypeMember, TypeMemberMarkup, TypeSpec};
@ -740,11 +740,21 @@ fn sorted_structs<'a>(
ty.structextends.as_deref() == Some("VkPhysicalDeviceFeatures2,VkDeviceCreateInfo")
})
.collect();
let regex = Regex::new(r"^VkPhysicalDeviceVulkan\d+Features$").unwrap();
fn is_physical_device_features(name: &str) -> bool {
tuple((
tag::<_, &str, ()>("VkPhysicalDeviceVulkan"),
digit1,
tag("Features"),
eof,
))(name)
.is_ok()
}
structs.sort_unstable_by_key(|&(ty, provided_by)| {
let name = ty.name.as_ref().unwrap();
(
!regex.is_match(name),
!is_physical_device_features(name),
if let Some(version) = provided_by
.iter()
.find_map(|s| s.strip_prefix("VK_VERSION_"))

View File

@ -1,9 +1,8 @@
use super::{write_file, IndexMap, RequiresOneOf, VkRegistryData};
use heck::ToSnakeCase;
use once_cell::sync::Lazy;
use nom::{character::complete, combinator::eof, sequence::tuple};
use proc_macro2::{Ident, Literal, TokenStream};
use quote::{format_ident, quote};
use regex::Regex;
use std::iter;
use vk_parse::{
Enum, EnumSpec, Extension, ExtensionChild, Feature, Format, FormatChild, InterfaceItem,
@ -606,8 +605,17 @@ fn formats_members(
features: &IndexMap<&str, &Feature>,
extensions: &IndexMap<&str, &Extension>,
) -> Vec<FormatMember> {
static BLOCK_EXTENT_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^(\d+),(\d+),(\d+)$").unwrap());
fn parse_block_extent(input: &str) -> Result<[u32; 3], nom::Err<()>> {
tuple((
complete::u32::<_, ()>,
complete::char(','),
complete::u32,
complete::char(','),
complete::u32,
eof,
))(input)
.map(|(_, (a, _, b, _, c, _))| [a, b, c])
}
iter::once(
FormatMember {
@ -763,12 +771,7 @@ fn formats_members(
}
if let Some(block_extent) = format.blockExtent.as_ref() {
let captures = BLOCK_EXTENT_REGEX.captures(block_extent).unwrap();
member.block_extent = [
captures.get(1).unwrap().as_str().parse().unwrap(),
captures.get(2).unwrap().as_str().parse().unwrap(),
captures.get(3).unwrap().as_str().parse().unwrap(),
];
member.block_extent = parse_block_extent(block_extent).unwrap();
} else {
match format.chroma.as_deref() {
Some("420") => member.block_extent = [2, 2, 1],

View File

@ -1,7 +1,12 @@
use self::spirv_grammar::SpirvGrammar;
use ahash::HashMap;
use once_cell::sync::Lazy;
use regex::Regex;
use nom::{
bytes::complete::{tag, take_until},
character::complete::{self, multispace0, multispace1},
combinator::eof,
sequence::{delimited, tuple},
IResult, Parser,
};
use std::{
cmp::min,
env,
@ -114,12 +119,52 @@ impl<'r> VkRegistryData<'r> {
}
}
/// Returns the Vulkan header version in the vk.xml file.
fn get_header_version(registry: &Registry) -> (u16, u16, u16) {
static VK_HEADER_VERSION: Lazy<Regex> =
Lazy::new(|| Regex::new(r"#define\s+VK_HEADER_VERSION\s+(\d+)\s*$").unwrap());
static VK_HEADER_VERSION_COMPLETE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"#define\s+VK_HEADER_VERSION_COMPLETE\s+VK_MAKE_API_VERSION\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*VK_HEADER_VERSION\s*\)").unwrap()
});
fn spaced_comma(input: &str) -> IResult<&str, char> {
delimited(multispace0, complete::char(','), multispace0)(input)
}
fn vk_header_patch(input: &str) -> IResult<&str, u16> {
let (input, _) = take_until("#define")(input)?;
delimited(
tuple((
tag("#define"),
multispace1,
tag("VK_HEADER_VERSION"),
multispace0,
)),
complete::u16,
tuple((multispace0, eof)),
)(input)
}
fn vk_header_major_minor(input: &str) -> IResult<&str, (u16, u16)> {
let (input, _) = take_until("#define")(input)?;
delimited(
tuple((
tag("#define"),
multispace1,
tag("VK_HEADER_VERSION_COMPLETE"),
multispace1,
tag("VK_MAKE_API_VERSION"),
multispace0,
complete::char('('),
multispace0,
)),
tuple((
complete::u16,
spaced_comma,
complete::u16,
spaced_comma,
complete::u16,
spaced_comma,
tag("VK_HEADER_VERSION"),
))
.map(|(_ignored, _, major, _, minor, _, _)| (major, minor)),
tuple((multispace0, complete::char(')'), multispace0)),
)(input)
}
let mut major = None;
let mut minor = None;
@ -129,14 +174,17 @@ impl<'r> VkRegistryData<'r> {
if let RegistryChild::Types(types) = child {
for ty in types.children.iter() {
if let TypesChild::Type(ty) = ty {
if ty.api.as_deref() != Some("vulkan") {
continue;
}
if let TypeSpec::Code(code) = &ty.spec {
if let Some(captures) = VK_HEADER_VERSION.captures(&code.code) {
patch = Some(captures.get(1).unwrap().as_str().parse().unwrap());
} else if let Some(captures) =
VK_HEADER_VERSION_COMPLETE.captures(&code.code)
{
major = Some(captures.get(2).unwrap().as_str().parse().unwrap());
minor = Some(captures.get(3).unwrap().as_str().parse().unwrap());
if let Ok((_, p)) = vk_header_patch(&code.code) {
assert!(patch.is_none());
patch = Some(p);
} else if let Ok((_, (m, n))) = vk_header_major_minor(&code.code) {
assert!(major.is_none());
major = Some(m);
minor = Some(n);
}
}
}
@ -430,11 +478,12 @@ pub fn get_spirv_grammar<P: AsRef<Path> + ?Sized>(path: &P) -> SpirvGrammar {
}
fn suffix_key(name: &str) -> u32 {
static VENDOR_SUFFIXES: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?:AMD|GOOGLE|INTEL|NV)$").unwrap());
#[allow(clippy::bool_to_int_with_if)]
if VENDOR_SUFFIXES.is_match(name) {
if name.ends_with("AMD")
|| name.ends_with("GOOGLE")
|| name.ends_with("INTEL")
|| name.ends_with("NV")
{
3
} else if name.ends_with("EXT") {
2

View File

@ -1,9 +1,15 @@
use super::{write_file, IndexMap, VkRegistryData};
use ahash::HashMap;
use heck::ToSnakeCase;
use nom::{
bytes::complete::{tag, take_until, take_while1},
character::complete::{self, digit1},
combinator::{all_consuming, eof},
sequence::{delimited, tuple},
IResult,
};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use regex::Regex;
use std::{collections::hash_map::Entry, fmt::Write as _};
use vk_parse::{Extension, Type, TypeMember, TypeMemberMarkup, TypeSpec};
@ -374,11 +380,21 @@ fn sorted_structs<'a>(
.values()
.filter(|(ty, _)| ty.structextends.as_deref() == Some("VkPhysicalDeviceProperties2"))
.collect();
let regex = Regex::new(r"^VkPhysicalDeviceVulkan\d+Properties$").unwrap();
fn is_physical_device_properties(name: &str) -> bool {
tuple((
tag::<_, &str, ()>("VkPhysicalDeviceVulkan"),
digit1,
tag("Properties"),
eof,
))(name)
.is_ok()
}
structs.sort_unstable_by_key(|&(ty, provided_by)| {
let name = ty.name.as_ref().unwrap();
(
!regex.is_match(name),
!is_physical_device_properties(name),
if let Some(version) = provided_by
.iter()
.find_map(|s| s.strip_prefix("VK_VERSION_"))
@ -415,7 +431,15 @@ struct Member<'a> {
}
fn members(ty: &Type) -> Vec<Member> {
let regex = Regex::new(r"\[([A-Za-z0-9_]+)\]\s*$").unwrap();
fn array_len(input: &str) -> IResult<&str, &str> {
let (input, _) = take_until("[")(input)?;
all_consuming(delimited(
complete::char('['),
take_while1(|c: char| c.is_ascii_alphanumeric() || c == '_'),
complete::char(']'),
))(input)
}
if let TypeSpec::Members(members) = &ty.spec {
members
.iter()
@ -436,12 +460,7 @@ fn members(ty: &Type) -> Vec<Member> {
TypeMemberMarkup::Enum(len) => Some(len.as_str()),
_ => None,
})
.or_else(|| {
regex
.captures(&def.code)
.and_then(|cap| cap.get(1))
.map(|m| m.as_str())
});
.or_else(|| array_len(&def.code).map(|(_, len)| len).ok());
if name != Some("sType") && name != Some("pNext") {
return name.map(|name| Member {
name,

View File

@ -4,10 +4,15 @@ use super::{
};
use heck::ToSnakeCase;
use indexmap::map::Entry;
use once_cell::sync::Lazy;
use nom::{
bytes::complete::tag,
character::complete,
combinator::all_consuming,
sequence::{preceded, separated_pair},
IResult, Parser,
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use regex::Regex;
use vk_parse::SpirvExtOrCap;
pub fn write(vk_data: &VkRegistryData, grammar: &SpirvGrammar) {
@ -303,9 +308,12 @@ fn spirv_extensions_members(extensions: &[&SpirvExtOrCap]) -> Vec<SpirvReqsMembe
}
fn make_requires(enables: &[vk_parse::Enable]) -> (RequiresOneOf, Vec<RequiresProperty>) {
static VK_API_VERSION: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^VK_(?:API_)?VERSION_(\d+)_(\d+)$").unwrap());
static BIT: Lazy<Regex> = Lazy::new(|| Regex::new(r"_BIT(?:_NV)?$").unwrap());
fn vk_api_version(input: &str) -> IResult<&str, (u32, u32)> {
all_consuming(preceded(
tag("VK_API_VERSION_").or(tag("VK_VERSION_")),
separated_pair(complete::u32, complete::char('_'), complete::u32),
))(input)
}
let mut requires_one_of = RequiresOneOf::default();
let mut requires_properties = vec![];
@ -314,12 +322,7 @@ fn make_requires(enables: &[vk_parse::Enable]) -> (RequiresOneOf, Vec<RequiresPr
match enable {
vk_parse::Enable::Version(version) => {
if version != "VK_VERSION_1_0" {
let captures = VK_API_VERSION.captures(version).unwrap();
let major = captures.get(1).unwrap().as_str();
let minor = captures.get(1).unwrap().as_str();
requires_one_of.api_version =
Some((major.parse().unwrap(), minor.parse().unwrap()));
requires_one_of.api_version = Some(vk_api_version(version).unwrap().1);
}
}
vk_parse::Enable::Extension(extension) => {
@ -341,7 +344,10 @@ fn make_requires(enables: &[vk_parse::Enable]) -> (RequiresOneOf, Vec<RequiresPr
PropertyValue::FlagsIntersects {
path: quote! { crate::device::physical },
ty: "SubgroupFeatures".to_string(),
flag: BIT.replace(member, "").to_string(),
flag: member
.trim_end_matches("_BIT_NV")
.trim_end_matches("_BIT")
.to_string(),
}
} else {
unimplemented!()