From 932e6a1fb061d9d77c1a3dec9c90746de03430b2 Mon Sep 17 00:00:00 2001 From: Julia Tatz Date: Sat, 20 Aug 2022 01:09:52 -0400 Subject: [PATCH 01/17] Fully parse type info parse_name_with_type still has some leftovers from early iterations only preprocessor commands are un-parsed If possible using nom would make this much better --- vk-parse/src/convert.rs | 264 +++++---------------- vk-parse/src/parse.rs | 496 +++++++++++++++++++++++++++++++++------- vk-parse/src/types.rs | 62 ++++- 3 files changed, 528 insertions(+), 294 deletions(-) diff --git a/vk-parse/src/convert.rs b/vk-parse/src/convert.rs index d0f7c82..087d1e3 100644 --- a/vk-parse/src/convert.rs +++ b/vk-parse/src/convert.rs @@ -342,19 +342,18 @@ impl From for Option { } "funcpointer" => { - let mut fnptr = vkxml::FunctionPointer { - name: vkxml::Identifier::new(), - notation: t.comment, - return_type: new_field(), - param: Vec::new(), - }; - let code = match t.spec { - TypeSpec::Code(TypeCode { code, .. }) => code, - _ => panic!("Unexpected contents of handle {:?}", t.spec), - }; - - parse_type_funcptr(&mut fnptr, &code); - return Some(vkxml::DefinitionsElement::FuncPtr(fnptr)); + if let TypeSpec::FunctionPointer(defn, params) = t.spec { + return Some(vkxml::DefinitionsElement::FuncPtr( + vkxml::FunctionPointer { + name: defn.name.clone().into(), + notation: t.comment, + return_type: defn.into(), + param: params.into_iter().map(|p| p.into()).collect(), + }, + )); + } else { + panic!("Unexpected contents of handle {:?}", t.spec) + } } "struct" => { @@ -392,14 +391,7 @@ impl From for Option { match member { TypeMember::Comment(..) => (), TypeMember::Definition(def) => { - let mut iter = def - .code - .split_whitespace() - .flat_map(|s| c::TokenIter::new(s)) - .peekable(); - - let field = parse_c_field(&mut iter).unwrap(); - u.elements.push(field); + u.elements.push(def.definition.into()); } } } @@ -422,13 +414,7 @@ impl From for vkxml::StructElement { match orig { TypeMember::Comment(comment) => vkxml::StructElement::Notation(comment), TypeMember::Definition(def) => { - let mut iter = def - .code - .split_whitespace() - .flat_map(|s| c::TokenIter::new(s)) - .peekable(); - - let mut field = parse_c_field(&mut iter).unwrap(); + let mut field: vkxml::Field = def.definition.into(); field.c_size = def.altlen; field.sync = def.externsync; field.optional = def.optional; @@ -453,9 +439,7 @@ impl From for vkxml::StructElement { } for tag in def.markup { match tag { - TypeMemberMarkup::Enum(value) => field.size_enumref = Some(value), TypeMemberMarkup::Comment(comment) => field.notation = Some(comment), - _ => (), } } @@ -689,61 +673,6 @@ fn process_define_code(r: &mut vkxml::Define, code: String) { } } -fn parse_type_funcptr(r: &mut vkxml::FunctionPointer, code: &str) { - let mut iter = code - .split_whitespace() - .flat_map(|s| c::TokenIter::new(s)) - .peekable(); - let token = iter.next().unwrap(); - if token != "typedef" { - panic!("Unexpected token {:?}", token); - } - - r.return_type = parse_c_field(&mut iter).unwrap(); - - let token = iter.next().unwrap(); - if token != "(" { - panic!("Unexpected token {:?}", token); - } - - let token = iter.next().unwrap(); - if token != "VKAPI_PTR" { - panic!("Unexpected token {:?}", token); - } - - let token = iter.next().unwrap(); - if token != "*" { - panic!("Unexpected token {:?}", token); - } - - r.name.push_str(iter.next().unwrap()); - - let token = iter.next().unwrap(); - if token != ")" { - panic!("Unexpected token {:?}", token); - } - - while let Some(token) = iter.next() { - match token { - "(" | "," => (), - ")" => break, - _ => panic!("Unexpected token {:?}", token), - } - - let field = if let Some(field) = parse_c_field(&mut iter) { - field - } else { - continue; - }; - - if field.basetype == "void" && field.reference.is_none() && field.name.is_none() { - continue; - } - - r.param.push(field); - } -} - impl From for Option { fn from(orig: EnumsChild) -> Self { match orig { @@ -808,69 +737,52 @@ impl From for Option { } } -fn parse_c_field<'a, I: Iterator>( - iter: &mut std::iter::Peekable, -) -> Option { - match iter.peek() { - Some(&")") => return None, - None => return None, - _ => (), - } - - let mut r = new_field(); - - let mut token = iter.next().unwrap(); - loop { - match token { - "const" => { - r.is_const = true; - token = iter.next().unwrap(); - } - "struct" => { - r.is_struct = true; - token = iter.next().unwrap(); - } - _ => break, - } - } - - r.basetype = String::from(token); - - while let Some(&token) = iter.peek() { - match token { - "," | ")" | "(" | ";" => break, - "*" => match r.reference { - None => r.reference = Some(vkxml::ReferenceType::Pointer), - _ => (), - }, - "const" => r.reference = Some(vkxml::ReferenceType::PointerToConstPointer), - "[" => { - r.array = Some(vkxml::ArrayType::Static); - } - "]" => { - break; - } - t => { - if r.array.is_some() { - let mut is_number = true; - for c in t.chars() { - if c < '0' || '9' < c { - is_number = false; - break; - } - } - if is_number { - r.size = Some(String::from(t)); - } - } else { - r.name = Some(String::from(token)) +impl From for vkxml::Field { + fn from(nt: NameWithType) -> Self { + let NameWithType { + type_name, + pointer_kind, + is_struct, + bitfield_size: _, + array_shape, + name, + } = nt; + let mut r = new_field(); + r.name = Some(name); + r.is_const = matches!( + pointer_kind, + Some( + PointerKind::Single { is_const: true } | PointerKind::Double { is_const: true, .. } + ) + ); + r.is_struct = is_struct; + r.basetype = type_name; + r.reference = match pointer_kind { + Some(PointerKind::Single { .. }) => Some(vkxml::ReferenceType::Pointer), + Some(PointerKind::Double { + inner_is_const: true, + .. + }) => Some(vkxml::ReferenceType::PointerToConstPointer), + Some(PointerKind::Double { + inner_is_const: false, + .. + }) => Some(vkxml::ReferenceType::PointerToPointer), + None => None, + }; + if let Some(shape) = array_shape.as_deref() { + r.array = Some(vkxml::ArrayType::Static); + match shape { + [ArrayLength::Static(n), ..] => { + r.size = Some(n.to_string()); } - } + [ArrayLength::Constant(c), ..] => { + r.size_enumref = Some(c.clone()); + } + [] => unreachable!(), + }; } - iter.next().unwrap(); + r } - - Some(r) } //-------------------------------------------------------------------------------------------------- @@ -1240,10 +1152,7 @@ impl From for Option { queues: def.queues, renderpass: None, }; - match def.proto.type_name { - Some(type_name) => r.return_type.basetype = type_name, - None => (), - } + r.return_type.basetype = def.proto.type_name; r.return_type.successcodes = def.successcodes; r.return_type.errorcodes = def.errorcodes; for text in def.implicitexternsyncparams { @@ -1268,11 +1177,7 @@ impl From for Option { r.param.reserve(def.params.len()); for param in def.params { - let mut p = new_field(); - p.name = Some(param.definition.name); - if let Some(v) = param.definition.type_name { - p.basetype = v; - } + let mut p: vkxml::Field = param.definition.into(); p.optional = param.optional; p.sync = param.externsync; if let Some(mut value) = param.len { @@ -1293,57 +1198,6 @@ impl From for Option { r.param.push(p); } - let mut tokens = c::TokenIter::new(&def.code); - while let Some(token) = tokens.next() { - if token == "(" { - break; - } - } - - let mut is_array = false; - let mut array_size = String::new(); - let mut param_idx = 0; - let mut ptr_count = 0; - let mut const_count = 0; - while let Some(token) = tokens.next() { - match token { - "," | ")" => { - let p = &mut r.param[param_idx]; - if const_count > 0 { - p.is_const = true; - } - if ptr_count == 2 { - if const_count >= 1 { - p.reference = Some(vkxml::ReferenceType::PointerToConstPointer); - } else { - p.reference = Some(vkxml::ReferenceType::PointerToPointer); - } - } else if ptr_count == 1 { - p.reference = Some(vkxml::ReferenceType::Pointer); - } - param_idx += 1; - ptr_count = 0; - const_count = 0; - } - "*" => ptr_count += 1, - "const" => const_count += 1, - "struct" => r.param[param_idx].is_struct = true, - "[" => is_array = true, - "]" => { - is_array = false; - let p = &mut r.param[param_idx]; - p.array = Some(vkxml::ArrayType::Static); - p.size = Some(array_size); - array_size = String::new(); - } - t => { - if is_array { - array_size.push_str(t); - } - } - } - } - Some(r) } } diff --git a/vk-parse/src/parse.rs b/vk-parse/src/parse.rs index bac45b7..8be1f9f 100644 --- a/vk-parse/src/parse.rs +++ b/vk-parse/src/parse.rs @@ -2,7 +2,10 @@ extern crate xml; use std; use std::io::Read; +use std::ops::ControlFlow; use std::str::FromStr; +use xml::attribute::OwnedAttribute; +use xml::name::OwnedName; use xml::reader::XmlEvent; use types::*; @@ -134,44 +137,12 @@ macro_rules! match_elements { } macro_rules! match_elements_combine_text { - ( $ctx:expr, $buffer:ident, $($p:pat => $e:expr),+) => { - while let Some(Ok(e)) = $ctx.events.next() { - match e { - XmlEvent::Characters(text) => $buffer.push_str(&text), - XmlEvent::Whitespace(text) => $buffer.push_str(&text), - XmlEvent::StartElement { name, .. } => { - $buffer.push(' '); - let name = name.local_name.as_str(); - $ctx.push_element(name); - match name { - $( - $p => $e, - )+ - _ => { - $ctx.errors.push(Error::UnexpectedElement { - xpath: $ctx.xpath.clone(), - name: String::from(name), - }); - consume_current_element($ctx); - } - } - } - XmlEvent::EndElement { .. } => { - $buffer.push(' '); - $ctx.pop_element(); - break; - }, - _ => {} - } - } - }; - ( $ctx:expr, $attributes:ident, $buffer:ident, $($p:pat => $e:expr),+) => { while let Some(Ok(e)) = $ctx.events.next() { match e { XmlEvent::Characters(text) => $buffer.push_str(&text), XmlEvent::Whitespace(text) => $buffer.push_str(&text), - XmlEvent::StartElement { name, $attributes, .. } => { + XmlEvent::StartElement { name, attributes: $attributes, .. } => { let name = name.local_name.as_str(); $ctx.push_element(name); match name { @@ -195,6 +166,10 @@ macro_rules! match_elements_combine_text { } } }; + + ( $ctx:expr, $buffer:ident, $($p:pat => $e:expr),+) => { + match_elements_combine_text!{ $ctx, _attributes, $buffer, $($p => $e),+ } + }; } //-------------------------------------------------------------------------------------------------- @@ -452,6 +427,391 @@ fn parse_tag(ctx: &mut ParseCtx, attributes: Vec) -> O }) } +#[derive(Debug, Default, Clone, Copy)] +struct ParsedPreTypeTag { + is_const: bool, + is_struct: bool, +} + +fn parse_pre_type_tag_text(text: &str) -> ParsedPreTypeTag { + // handle const_ptr / struct info, can be 'const', 'struct', or 'const struct' + let trimmed_text = text.trim(); + let (is_const, trimmed_text) = trimmed_text + .strip_prefix("const") + .map_or((false, trimmed_text), |t| (true, t.trim_start())); + let (is_struct, trimmed_text) = trimmed_text + .strip_prefix("struct") + .map_or((false, trimmed_text), |t| (true, t.trim_start())); + assert_eq!(trimmed_text, ""); + ParsedPreTypeTag { + is_const, + is_struct, + } +} + +fn parse_post_type_tag_text( + text: &str, + parsed_pre: ParsedPreTypeTag, +) -> (Option, &str) { + let ParsedPreTypeTag { is_const, .. } = parsed_pre; + // handle pointer info, can be '*' or '**' or '* const*' + let trimmed_text = text.trim(); + if let Some(trimmed_text) = trimmed_text.strip_prefix('*') { + let trimmed_text = trimmed_text.trim_start(); + if let Some(trimmed_text) = trimmed_text.strip_prefix("const") { + ( + Some(PointerKind::Double { + is_const, + inner_is_const: true, + }), + trimmed_text + .trim_start() + .strip_prefix('*') + .expect("In post type tag text a '* const' must be followed by '*'") + .trim_start(), + ) + } else if let Some(trimmed_text) = trimmed_text.strip_prefix('*') { + ( + Some(PointerKind::Double { + is_const, + inner_is_const: false, + }), + trimmed_text.trim_start(), + ) + } else { + (Some(PointerKind::Single { is_const }), trimmed_text) + } + } else { + (None, trimmed_text) + } +} + +fn parse_array_shape_text(text: &str, shape_vec: &mut Vec) -> bool { + if let Some(mut trimmed_text) = text.trim().strip_prefix('[') { + while let Some((n, rest)) = trimmed_text.split_once(']') { + shape_vec.push(ArrayLength::Static(n.parse().unwrap())); + if let Some(rest) = rest.trim_start().strip_prefix('[') { + trimmed_text = rest; + } else { + assert_eq!(rest, ""); + return false; + } + } + true + } else { + false + } +} + +fn parse_name_with_type< + R: Read, + F: FnMut(&mut ParseCtx, &mut String, &str, &[OwnedAttribute]) -> ControlFlow<()>, +>( + ctx: &mut ParseCtx, + code: &mut String, + mut handle_extra: F, +) -> Option { + let mut event = ctx.events.next(); + if let Some(Ok(XmlEvent::Whitespace(text))) = event { + code.push_str(&text); + event = ctx.events.next(); + } + let parsed_pre = if let Some(Ok(XmlEvent::Characters(text))) = event { + code.push_str(&text); + event = ctx.events.next(); + + parse_pre_type_tag_text(&text) + } else { + Default::default() + }; + + let type_name = match event { + Some(Ok(XmlEvent::StartElement { + name: OwnedName { local_name, .. }, + .. + })) if local_name == "type" => { + ctx.push_element(&local_name); + let text = parse_text_element(ctx); + code.push_str(&text); + event = ctx.events.next(); + + text + } + _ => { + ctx.errors.push(Error::MissingElement { + xpath: ctx.xpath.clone(), + name: String::from("type"), + }); + return None; + } + }; + + if let Some(Ok(XmlEvent::Whitespace(text))) = event { + code.push_str(&text); + event = ctx.events.next(); + } + let pointer_kind = if let Some(Ok(XmlEvent::Characters(text))) = event { + code.push_str(&text); + event = ctx.events.next(); + + let (kind, rest) = parse_post_type_tag_text(&text, parsed_pre); + assert_eq!(rest, ""); + kind + } else { + None + }; + + let name = match event { + Some(Ok(XmlEvent::StartElement { + name: OwnedName { local_name, .. }, + .. + })) if local_name == "name" => { + ctx.push_element(&local_name); + let text = parse_text_element(ctx); + code.push_str(&text); + event = ctx.events.next(); + + text + } + _ => { + ctx.errors.push(Error::MissingElement { + xpath: ctx.xpath.clone(), + name: String::from("name"), + }); + return None; + } + }; + + let (bitfield_size, mut array_shape, mut hungry) = + if let Some(Ok(XmlEvent::Characters(text))) = event { + code.push_str(&text); + event = ctx.events.next(); + + let trimmed_text = text.trim(); + // handle bitfield / statically-sized arrays + if let Some(rest) = trimmed_text.strip_prefix(':') { + ( + Some( + rest.parse() + .expect("after bitfield's ':' only a non-zero integer is expected"), + ), + None, + false, + ) + } + // TODO is there anything else that could be here + else { + let mut shape = Vec::new(); + let hungry = parse_array_shape_text(trimmed_text, &mut shape); + (None, Some(shape), hungry) + } + } else { + (None, None, false) + }; + + while hungry { + let array_shape_vec = array_shape.as_mut().unwrap(); + // + match event { + Some(Ok(XmlEvent::StartElement { + name: OwnedName { local_name, .. }, + .. + })) if local_name == "enum" => { + ctx.push_element(&local_name); + let text = parse_text_element(ctx); + code.push_str(&text); + event = ctx.events.next(); + + array_shape_vec.push(ArrayLength::Constant(text)); + } + _ => { + ctx.errors.push(Error::MissingElement { + xpath: ctx.xpath.clone(), + name: String::from("enum"), + }); + return None; + } + }; + // + if let Some(Ok(XmlEvent::Characters(text))) = event { + code.push_str(&text); + event = ctx.events.next(); + + let trimmed_text = text + .trim() + .strip_prefix("]") + .expect("Expected a ']' to denote the end of an element of a shape"); + hungry = parse_array_shape_text(trimmed_text.trim_start(), array_shape_vec); + } else { + ctx.errors.push(Error::MissingCharacters { + xpath: ctx.xpath.clone(), + }); + return None; + }; + } + + while let Some(Ok(e)) = event { + match e { + XmlEvent::Whitespace(text) => code.push_str(&text), + XmlEvent::Characters(text) => code.push_str(&text), + XmlEvent::StartElement { + name: elem_name, + attributes, + .. + } => { + let elem_name = elem_name.local_name.as_str(); + ctx.push_element(elem_name); + if let ControlFlow::Break(()) = + handle_extra(ctx, code, elem_name, attributes.as_ref()) + { + ctx.errors.push(Error::UnexpectedElement { + xpath: ctx.xpath.clone(), + name: String::from(elem_name), + }); + consume_current_element(ctx); + } + } + XmlEvent::EndElement { .. } => { + ctx.pop_element(); + break; + } + _ => {} + } + event = ctx.events.next(); + } + + Some(NameWithType { + type_name, + pointer_kind, + is_struct: parsed_pre.is_struct, + bitfield_size, + name, + array_shape, + }) +} + +fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option<(NameWithType, Vec)> { + let (type_name, pointer_kind) = if let Some(Ok(XmlEvent::Characters(text))) = ctx.events.next() + { + let trimmed_text = text.trim(); + let trimmed_text = trimmed_text.strip_prefix("typedef").expect("").trim_start(); + let trimmed_text = trimmed_text.strip_suffix('*').expect("").trim_end(); + let trimmed_text = trimmed_text.strip_suffix("VKAPI_PTR").expect("").trim_end(); + let type_str = trimmed_text.strip_suffix('(').expect("").trim_end(); + // FIXME `type_str` can be any C type and needs full parsing, but for now just assuming is either a basic type or a non-const pointer to one + let (type_str, pointer_kind) = + type_str.strip_suffix('*').map_or((type_str, None), |rest| { + ( + rest.trim_end(), + Some(PointerKind::Single { is_const: false }), + ) + }); + (type_str.to_string(), pointer_kind) + } else { + ctx.errors.push(Error::MissingCharacters { + xpath: ctx.xpath.clone(), + }); + return None; + }; + + let name = match ctx.events.next() { + Some(Ok(XmlEvent::StartElement { + name: OwnedName { local_name, .. }, + .. + })) if local_name == "name" => { + ctx.push_element(&local_name); + let text = parse_text_element(ctx); + + text + } + _ => { + ctx.errors.push(Error::MissingElement { + xpath: ctx.xpath.clone(), + name: String::from("name"), + }); + return None; + } + }; + + let fnptr_defn = NameWithType { + name, + type_name, + pointer_kind, + is_struct: false, + bitfield_size: None, + array_shape: None, + }; + + let mut params = Vec::new(); + + let mut parsed_pre = if let Some(Ok(XmlEvent::Characters(text))) = ctx.events.next() { + let trimmed_text = text.trim(); + // empty params will have text be `)(void);` + if trimmed_text.ends_with(';') { + return Some((fnptr_defn, params)); + } + + let trimmed_text = trimmed_text.strip_prefix(')').expect("").trim_start(); + let trimmed_text = trimmed_text.strip_prefix('(').expect("").trim_start(); + parse_pre_type_tag_text(trimmed_text) + } else { + ctx.errors.push(Error::MissingCharacters { + xpath: ctx.xpath.clone(), + }); + return None; + }; + + loop { + let type_name = match ctx.events.next() { + Some(Ok(XmlEvent::StartElement { + name: OwnedName { local_name, .. }, + .. + })) if local_name == "type" => { + ctx.push_element(&local_name); + let text = parse_text_element(ctx); + + text + } + _ => { + ctx.errors.push(Error::MissingElement { + xpath: ctx.xpath.clone(), + name: String::from("type"), + }); + return None; + } + }; + + if let Some(Ok(XmlEvent::Characters(text))) = ctx.events.next() { + let (pointer_kind, rest) = parse_post_type_tag_text(&text, parsed_pre); + + let (name, rest) = rest.split_once(',').map_or_else( + || (rest.strip_suffix(");").unwrap_or(rest), None), + |(n, r)| (n, Some(r)), + ); + params.push(NameWithType { + type_name, + pointer_kind, + is_struct: parsed_pre.is_struct, + bitfield_size: None, + array_shape: None, + name: name.to_string(), + }); + if let Some(rest) = rest { + parsed_pre = parse_pre_type_tag_text(rest.trim()); + } else { + break; + } + } else { + ctx.errors.push(Error::MissingCharacters { + xpath: ctx.xpath.clone(), + }); + return None; + }; + } + + Some((fnptr_defn, params)) +} + fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> TypesChild { let mut api = None; let mut alias = None; @@ -485,6 +845,12 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> "comment" => comment = Some(a.value) } + let fn_ptr_spec = if let Some("funcpointer") = category.as_deref() { + parse_type_funcptr(ctx) + } else { + None + }; + match_elements_combine_text! {ctx, attributes, code, "member" => { let mut len = None; @@ -513,27 +879,14 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> "limittype" => limittype = Some(a.value), "objecttype" => objecttype = Some(a.value) } - match_elements_combine_text!{ctx, code, - "type" => { - let text = parse_text_element(ctx); - code.push_str(&text); - markup.push(TypeMemberMarkup::Type(text)); - }, - "name" => { - let text = parse_text_element(ctx); - code.push_str(&text); - markup.push(TypeMemberMarkup::Name(text)); - }, - "enum" => { - let text = parse_text_element(ctx); - code.push_str(&text); - markup.push(TypeMemberMarkup::Enum(text)); - }, + if let Some(definition) = parse_name_with_type(ctx, &mut code, |ctx, _code, local_name, _| match local_name { "comment" => { let text = parse_text_element(ctx); markup.push(TypeMemberMarkup::Comment(text)); - } - } + ControlFlow::Continue(()) + }, + _ => ControlFlow::Break(()) + }) { members.push(TypeMember::Definition(TypeMemberDefinition { len, altlen, @@ -547,8 +900,10 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> limittype, objecttype, code, + definition, markup, })) + } }, "comment" => members.push(TypeMember::Comment(parse_text_element(ctx))), "name" => { @@ -581,7 +936,9 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> objtypeenum, bitvalues, comment, - spec: if members.len() > 0 { + spec: if let Some((defn, params)) = fn_ptr_spec { + TypeSpec::FunctionPointer(defn, params) + } else if members.len() > 0 { TypeSpec::Members(members) } else if code.len() > 0 { TypeSpec::Code(TypeCode { code, markup }) @@ -627,40 +984,9 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) let mut description = None; let mut implicitexternsyncparams = Vec::new(); - fn parse_name_with_type( - ctx: &mut ParseCtx, - buffer: &mut String, - ) -> Option { - let mut name = None; - let mut type_name = None; - match_elements_combine_text! {ctx, buffer, - "type" => { - let text = parse_text_element(ctx); - buffer.push_str(&text); - type_name = Some(text); - }, - "name" => { - let text = parse_text_element(ctx); - buffer.push_str(&text); - name = Some(text); - } - } - let name = if let Some(v) = name { - v - } else { - ctx.errors.push(Error::MissingElement { - xpath: ctx.xpath.clone(), - name: String::from("name"), - }); - return None; - }; - - Some(NameWithType { name, type_name }) - } - match_elements! {ctx, attributes, "proto" => { - proto = parse_name_with_type(ctx, &mut code); + proto = parse_name_with_type(ctx, &mut code, |_, _, _, _| ControlFlow::Break(())); code.push('('); }, @@ -691,7 +1017,7 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) if !params.is_empty() { code.push_str(", "); } - if let Some(definition) = parse_name_with_type(ctx, &mut code) { + if let Some(definition) = parse_name_with_type(ctx, &mut code, |_, _, _, _| ControlFlow::Break(())) { params.push(CommandParam { len, altlen, diff --git a/vk-parse/src/types.rs b/vk-parse/src/types.rs index 93c074c..af9427c 100644 --- a/vk-parse/src/types.rs +++ b/vk-parse/src/types.rs @@ -39,6 +39,9 @@ pub enum Error { xpath: String, name: String, }, + MissingCharacters { + xpath: String, + }, MissingAttribute { xpath: String, name: String, @@ -277,6 +280,7 @@ pub enum TypeSpec { None, Code(TypeCode), Members(Vec), + FunctionPointer(NameWithType, Vec), } impl Default for TypeSpec { @@ -395,6 +399,13 @@ pub struct TypeMemberDefinition { )] pub code: String, + /// The definition of this member. + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub definition: NameWithType, + #[cfg_attr( feature = "serialize", serde(default, skip_serializing_if = "is_default") @@ -406,9 +417,6 @@ pub struct TypeMemberDefinition { #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] pub enum TypeMemberMarkup { - Name(String), - Type(String), - Enum(String), Comment(String), } @@ -1125,6 +1133,28 @@ pub enum FormatChild { SpirvImageFormat { name: String }, } +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum PointerKind { + Single { + is_const: bool, + }, + Double { + is_const: bool, + inner_is_const: bool, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum ArrayLength { + // TODO could probably make this smaller and/or NonZero + Static(usize), + Constant(String), +} + #[derive(Debug, Clone, PartialEq, Eq, Default)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] @@ -1133,7 +1163,31 @@ pub struct NameWithType { feature = "serialize", serde(default, skip_serializing_if = "is_default") )] - pub type_name: Option, + pub type_name: String, + + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub pointer_kind: Option, + + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub is_struct: bool, + + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub bitfield_size: Option, + + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub array_shape: Option>, #[cfg_attr( feature = "serialize", From 0af944c0c393fd07461e21fc49038d0cb155ba58 Mon Sep 17 00:00:00 2001 From: Julia Tatz Date: Sat, 20 Aug 2022 23:23:28 -0400 Subject: [PATCH 02/17] Strongly typed tag by category TODO convert stringly-typed attributes FIXME still not a bijective parsing removed `TypeCode`, but `TypeCodeMarkup` still needs to go --- vk-parse/src/convert.rs | 446 +++++++--------------------------------- vk-parse/src/parse.rs | 387 ++++++++++++++++++++++++++++++++-- vk-parse/src/types.rs | 95 ++++++--- 3 files changed, 519 insertions(+), 409 deletions(-) diff --git a/vk-parse/src/convert.rs b/vk-parse/src/convert.rs index 087d1e3..3605e12 100644 --- a/vk-parse/src/convert.rs +++ b/vk-parse/src/convert.rs @@ -1,6 +1,5 @@ extern crate vkxml; -use c; use parse::*; use std; use types::*; @@ -179,9 +178,8 @@ impl From for Option { match orig { TypesChild::Comment(text) => Some(vkxml::DefinitionsElement::Notation(text)), TypesChild::Type(t) => { - let category = match t.category { - Some(c) => c, - None => { + match t.spec { + TypeSpec::None => { let name = t.name.unwrap_or(String::new()); return Some(vkxml::DefinitionsElement::Reference(vkxml::Reference { name, @@ -189,147 +187,96 @@ impl From for Option { include: t.requires, })); } - }; - - match category.as_str() { - "include" => { - let mut include = vkxml::Include { - name: t.name.unwrap_or(String::new()), + TypeSpec::Include { name, quoted } => { + let name = t.name.or(name).unwrap_or(String::new()); + let need_ext = !name.ends_with(".h"); + let include = vkxml::Include { + name, notation: t.comment, - style: vkxml::IncludeStyle::Quote, - need_ext: false, + style: match quoted { + true => vkxml::IncludeStyle::Quote, + false => vkxml::IncludeStyle::Bracket, + }, + need_ext, }; - match t.spec { - TypeSpec::Code(TypeCode { code, markup }) => { - let mut iter = code.split_whitespace(); - let token = iter.next().unwrap(); - if token != "#include" { - panic!("Unexpected token {:?}", token); - } - let token = iter.next().unwrap(); - if token.starts_with('<') { - include.style = vkxml::IncludeStyle::Bracket; - } - for tag in markup { - match tag { - TypeCodeMarkup::Name(name) => include.name = name, - _ => (), - } - } - } - _ => (), - } - - include.need_ext = !include.name.ends_with(".h"); return Some(vkxml::DefinitionsElement::Include(include)); } - "define" => { + TypeSpec::Define(TypeDefine { + name, + value, + comment, + defref, + is_disabled, + replace, + }) => { let mut define = vkxml::Define { - name: t.name.unwrap_or(String::new()), + name: name, notation: t.comment, - is_disabled: true, - comment: None, - replace: false, - defref: Vec::new(), + is_disabled, + comment, + replace, + defref, parameters: Vec::new(), c_expression: None, value: None, }; - match t.spec { - TypeSpec::Code(TypeCode { code, markup }) => { - for tag in markup { - match tag { - TypeCodeMarkup::Type(val) => define.defref.push(val), - TypeCodeMarkup::Name(val) => define.name = val, - _ => panic!("Unexpected tag in define {:?}", tag), - } - } - process_define_code(&mut define, code); + match value { + TypeDefineValue::Empty => {} + TypeDefineValue::Value(v) => { + define.value = Some(v); + } + TypeDefineValue::Expression(e) => { + define.c_expression = Some(e); + } + TypeDefineValue::Function { params, expression } => { + define.parameters = params; + define.c_expression = Some(expression); } - _ => panic!("Unexpected contents of define {:?}", t.spec), } return Some(vkxml::DefinitionsElement::Define(define)); } - "basetype" => { - let mut typedef = vkxml::Typedef { - name: String::new(), + TypeSpec::Typedef { name, basetype } => { + let typedef = vkxml::Typedef { + name, notation: t.comment, - basetype: String::new(), - }; - let markup = match t.spec { - TypeSpec::Code(TypeCode { markup, .. }) => markup, - _ => panic!("Unexpected contents of typedef {:?}", t.spec), + basetype: basetype.unwrap_or_default(), }; - for tag in markup { - match tag { - TypeCodeMarkup::Type(val) => typedef.basetype = val, - TypeCodeMarkup::Name(val) => typedef.name = val, - _ => panic!("Unexpected tag in typedef {:?}", tag), - } - } return Some(vkxml::DefinitionsElement::Typedef(typedef)); } - "bitmask" => { - if t.name.is_some() || t.alias.is_some() { - return None; - } - let mut bitmask = vkxml::Bitmask { - name: vkxml::Identifier::new(), + TypeSpec::Bitmask(None) => { + return None; + } + TypeSpec::Bitmask(Some(name_val)) => { + let NameWithType { + type_name, name, .. + } = name_val; + let bitmask = vkxml::Bitmask { + name, notation: t.comment, - basetype: vkxml::Identifier::new(), + basetype: type_name, enumref: t.requires, }; - let markup = match t.spec { - TypeSpec::Code(TypeCode { markup, .. }) => markup, - _ => panic!("Unexpected contents of bitmaks {:?}", t.spec), - }; - for tag in markup { - match tag { - TypeCodeMarkup::Type(val) => bitmask.basetype = val, - TypeCodeMarkup::Name(val) => bitmask.name = val, - _ => panic!("Unexpected tag in typedef {:?}", tag), - } - } return Some(vkxml::DefinitionsElement::Bitmask(bitmask)); } - "handle" => { - if t.name.is_some() || t.alias.is_some() { - return None; - } - let mut handle = vkxml::Handle { - name: String::new(), + TypeSpec::Handle(TypeHandle { name, handle_type }) => { + let handle = vkxml::Handle { + name, notation: t.comment, parent: t.parent, - ty: vkxml::HandleType::Dispatch, - }; - let markup = match t.spec { - TypeSpec::Code(TypeCode { markup, .. }) => markup, - _ => panic!("Unexpected contents of handle {:?}", t.spec), + ty: match handle_type { + HandleType::Dispatch => vkxml::HandleType::Dispatch, + HandleType::NoDispatch => vkxml::HandleType::NoDispatch, + }, }; - for tag in markup { - match tag { - TypeCodeMarkup::Name(val) => handle.name = val, - TypeCodeMarkup::Type(val) => { - handle.ty = match val.as_str() { - "VK_DEFINE_HANDLE" => vkxml::HandleType::Dispatch, - "VK_DEFINE_NON_DISPATCHABLE_HANDLE" => { - vkxml::HandleType::NoDispatch - } - _ => panic!("Unexpected handle type: {}", val), - } - } - _ => panic!("Unexpected tag in typedef {:?}", tag), - } - } return Some(vkxml::DefinitionsElement::Handle(handle)); } - "enum" => { + TypeSpec::Enumeration => { // if alias.is_some() { // return None; // } @@ -341,22 +288,19 @@ impl From for Option { )); } - "funcpointer" => { - if let TypeSpec::FunctionPointer(defn, params) = t.spec { - return Some(vkxml::DefinitionsElement::FuncPtr( - vkxml::FunctionPointer { - name: defn.name.clone().into(), - notation: t.comment, - return_type: defn.into(), - param: params.into_iter().map(|p| p.into()).collect(), - }, - )); - } else { - panic!("Unexpected contents of handle {:?}", t.spec) - } + TypeSpec::FunctionPointer(TypeFunctionPointer { + proto: defn, + params, + }) => { + return Some(vkxml::DefinitionsElement::FuncPtr(vkxml::FunctionPointer { + name: defn.name.clone().into(), + notation: t.comment, + return_type: defn.into(), + param: params.into_iter().map(|p| p.into()).collect(), + })); } - "struct" => { + TypeSpec::Struct(members) => { if t.alias.is_some() { return None; } @@ -367,42 +311,30 @@ impl From for Option { extends: t.structextends, elements: Vec::new(), }; - match t.spec { - TypeSpec::Members(members) => { - for member in members { - s.elements.push(member.into()); - } - } - _ => panic!("Unexpected contents of struct {:?}: {:?}", s.name, t.spec), + for member in members { + s.elements.push(member.into()); } return Some(vkxml::DefinitionsElement::Struct(s)); } - "union" => { + TypeSpec::Union(members) => { let mut u = vkxml::Union { name: t.name.unwrap_or(String::new()), notation: t.comment, elements: Vec::new(), }; - match t.spec { - TypeSpec::Members(members) => { - for member in members { - match member { - TypeMember::Comment(..) => (), - TypeMember::Definition(def) => { - u.elements.push(def.definition.into()); - } - } + for member in members { + match member { + TypeMember::Comment(..) => (), + TypeMember::Definition(def) => { + u.elements.push(def.definition.into()); } } - _ => panic!("Unexpected contents of union {:?}: {:?}", u.name, t.spec), } return Some(vkxml::DefinitionsElement::Union(u)); } - - _ => panic!("Unexpected category of type {:?}", category), } } } @@ -449,230 +381,6 @@ impl From for vkxml::StructElement { } } -fn process_define_code(r: &mut vkxml::Define, code: String) { - fn consume_whitespace(chars: &mut std::str::Chars, mut current: Option) -> Option { - while let Some(c) = current { - if !c.is_whitespace() { - break; - } - current = chars.next(); - } - current - } - - { - enum State { - Initial, - LineComment, - BlockComment, - DefineName, - DefineArgs, - DefineExpression, - DefineValue, - } - let mut state = State::Initial; - let mut chars = code.chars(); - loop { - match state { - State::Initial => { - let mut current = chars.next(); - current = consume_whitespace(&mut chars, current); - - match current { - Some('/') => { - current = chars.next(); - match current { - Some('/') => state = State::LineComment, - Some('*') => state = State::BlockComment, - Some(c) => panic!("Unexpected symbol {:?}", c), - None => panic!("Unexpected end of code."), - } - } - - Some('#') => { - let text = chars.as_str(); - let mut directive_len = 0; - while let Some(c) = chars.next() { - if c.is_whitespace() { - break; - } - if 'a' <= c && c <= 'z' { - directive_len += 1; - } else { - panic!("Unexpected symbol in preprocessor directive: {:?}", c); - } - } - - let directive = &text[..directive_len]; - match directive { - "define" => state = State::DefineName, - _ => { - // Different directive. Whole text treated as c expression and replace set to true. - r.replace = true; - r.is_disabled = false; - break; - } - } - } - - Some('s') => { - let expected = "truct "; - - let text = chars.as_str(); - if text.starts_with(expected) { - // mk:TODO Less hacky handling of define which is actually forward declaration. - r.replace = true; - break; - } else { - println!("Unexpected code segment {:?}", code); - } - } - - Some(c) => panic!("Unexpected symbol {:?}", c), - None => panic!("Unexpected end of code."), - } - } - - State::LineComment => { - let text = chars.as_str(); - if let Some(idx) = text.find('\n') { - let comment = text[..idx].trim(); - if r.comment.is_none() { - r.comment = Some(String::from(comment)); - } - chars = text[idx + 1..].chars(); - state = State::Initial; - } else { - if r.comment.is_none() { - r.comment = Some(String::from(text.trim())); - } - - break; - } - } - - State::BlockComment => { - let text = chars.as_str(); - if let Some(idx) = text.find("*/") { - let comment = &text[..idx]; - if r.comment.is_none() { - r.comment = Some(String::from(comment)); - } - chars = text[idx + 2..].chars(); - state = State::Initial; - } else { - panic!("Unterminated block comment {:?}", text); - } - } - - State::DefineName => { - r.is_disabled = false; - let text = chars.as_str(); - let mut current = chars.next(); - let mut whitespace_len = 0; - while let Some(c) = current { - if !c.is_whitespace() { - break; - } - current = chars.next(); - whitespace_len += 1; - } - - let mut name_len = 0; - while let Some(c) = current { - if !c::is_c_identifier_char(c) { - break; - } - name_len += 1; - current = chars.next(); - } - - let name = &text[whitespace_len..whitespace_len + name_len]; - if name != r.name.as_str() { - panic!("#define name mismatch. {:?} vs. {:?}", name, r.name); - } - - match current { - Some('(') => state = State::DefineArgs, - Some(c) => { - if c.is_whitespace() { - state = State::DefineValue; - } else { - panic!("Unexpected char after #define name: {:?}", c); - } - } - None => break, - } - } - - State::DefineArgs => { - let mut text = chars.as_str(); - let mut current = chars.next(); - loop { - let mut whitespace_len = 0; - while let Some(c) = current { - if !c.is_whitespace() { - break; - } - whitespace_len += 1; - current = chars.next(); - } - - let mut name_len = 0; - while let Some(c) = current { - if !c::is_c_identifier_char(c) { - break; - } - current = chars.next(); - name_len += 1; - } - let name = &text[whitespace_len..whitespace_len + name_len]; - r.parameters.push(String::from(name)); - - current = consume_whitespace(&mut chars, current); - match current { - Some(',') => { - text = chars.as_str(); - current = chars.next(); - } - Some(')') => { - chars.next(); - break; - } - Some(c) => { - panic!("Unexpected character in #define argument list: {:?}", c) - } - None => { - panic!("End of text while in the middle of #define argument list.") - } - } - } - state = State::DefineExpression; - } - - State::DefineExpression => { - r.c_expression = Some(String::from(chars.as_str().trim())); - break; - } - - State::DefineValue => { - let v = Some(String::from(chars.as_str().trim())); - if r.defref.len() > 0 { - r.c_expression = v; - } else { - r.value = v; - } - break; - } - } - } - } - - if r.replace { - r.c_expression = Some(code); - } -} - impl From for Option { fn from(orig: EnumsChild) -> Self { match orig { diff --git a/vk-parse/src/parse.rs b/vk-parse/src/parse.rs index 8be1f9f..cc71f07 100644 --- a/vk-parse/src/parse.rs +++ b/vk-parse/src/parse.rs @@ -137,7 +137,7 @@ macro_rules! match_elements { } macro_rules! match_elements_combine_text { - ( $ctx:expr, $attributes:ident, $buffer:ident, $($p:pat => $e:expr),+) => { + ( $ctx:expr, $attributes:ident, $buffer:ident, $($p:pat $(if $g:expr)? => $e:expr),+) => { while let Some(Ok(e)) = $ctx.events.next() { match e { XmlEvent::Characters(text) => $buffer.push_str(&text), @@ -147,7 +147,7 @@ macro_rules! match_elements_combine_text { $ctx.push_element(name); match name { $( - $p => $e, + $p $(if $g)? => $e, )+ _ => { $ctx.errors.push(Error::UnexpectedElement { @@ -690,7 +690,7 @@ fn parse_name_with_type< }) } -fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option<(NameWithType, Vec)> { +fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option { let (type_name, pointer_kind) = if let Some(Ok(XmlEvent::Characters(text))) = ctx.events.next() { let trimmed_text = text.trim(); @@ -748,7 +748,10 @@ fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option<(NameWithType, V let trimmed_text = text.trim(); // empty params will have text be `)(void);` if trimmed_text.ends_with(';') { - return Some((fnptr_defn, params)); + return Some(TypeFunctionPointer { + proto: fnptr_defn, + params, + }); } let trimmed_text = trimmed_text.strip_prefix(')').expect("").trim_start(); @@ -809,7 +812,263 @@ fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option<(NameWithType, V }; } - Some((fnptr_defn, params)) + Some(TypeFunctionPointer { + proto: fnptr_defn, + params, + }) +} + +fn process_define_code(code: String, name_: String, defref: Vec) -> TypeDefine { + fn consume_whitespace(chars: &mut std::str::Chars, mut current: Option) -> Option { + while let Some(c) = current { + if !c.is_whitespace() { + break; + } + current = chars.next(); + } + current + } + + let mut is_disabled = true; + let mut replace = false; + let mut parameters = Vec::new(); + let mut value_ = None; + let mut c_expr_ = None; + let mut comment_ = None; + + { + enum State { + Initial, + LineComment, + BlockComment, + DefineName, + DefineArgs, + DefineExpression, + DefineValue, + } + let mut state = State::Initial; + let mut chars = code.chars(); + loop { + match state { + State::Initial => { + let mut current = chars.next(); + current = consume_whitespace(&mut chars, current); + + match current { + Some('/') => { + current = chars.next(); + match current { + Some('/') => state = State::LineComment, + Some('*') => state = State::BlockComment, + Some(c) => panic!("Unexpected symbol {:?}", c), + None => panic!("Unexpected end of code."), + } + } + + Some('#') => { + let text = chars.as_str(); + let mut directive_len = 0; + while let Some(c) = chars.next() { + if c.is_whitespace() { + break; + } + if 'a' <= c && c <= 'z' { + directive_len += 1; + } else { + panic!("Unexpected symbol in preprocessor directive: {:?}", c); + } + } + + let directive = &text[..directive_len]; + match directive { + "define" => state = State::DefineName, + _ => { + // Different directive. Whole text treated as c expression and replace set to true. + replace = true; + is_disabled = false; + break; + } + } + } + + Some('s') => { + let expected = "truct "; + + let text = chars.as_str(); + if text.starts_with(expected) { + // mk:TODO Less hacky handling of define which is actually forward declaration. + replace = true; + break; + } else { + println!("Unexpected code segment {:?}", code); + } + } + + Some(c) => panic!("Unexpected symbol {:?}", c), + None => panic!("Unexpected end of code."), + } + } + + State::LineComment => { + let text = chars.as_str(); + if let Some(idx) = text.find('\n') { + let comment = text[..idx].trim(); + if comment_.is_none() { + comment_.replace(String::from(comment)); + } + chars = text[idx + 1..].chars(); + state = State::Initial; + } else { + if comment_.is_none() { + comment_.replace(String::from(text.trim())); + } + + break; + } + } + + State::BlockComment => { + let text = chars.as_str(); + if let Some(idx) = text.find("*/") { + let comment = &text[..idx]; + if comment_.is_none() { + comment_.replace(String::from(comment)); + } + chars = text[idx + 2..].chars(); + state = State::Initial; + } else { + panic!("Unterminated block comment {:?}", text); + } + } + + State::DefineName => { + is_disabled = false; + let text = chars.as_str(); + let mut current = chars.next(); + let mut whitespace_len = 0; + while let Some(c) = current { + if !c.is_whitespace() { + break; + } + current = chars.next(); + whitespace_len += 1; + } + + let mut name_len = 0; + while let Some(c) = current { + if !crate::c::is_c_identifier_char(c) { + break; + } + name_len += 1; + current = chars.next(); + } + + let name = &text[whitespace_len..whitespace_len + name_len]; + if name != name_ { + panic!("#define name mismatch. {:?} vs. {:?}", name, name_); + } + + match current { + Some('(') => state = State::DefineArgs, + Some(c) => { + if c.is_whitespace() { + state = State::DefineValue; + } else { + panic!("Unexpected char after #define name: {:?}", c); + } + } + None => break, + } + } + + State::DefineArgs => { + let mut text = chars.as_str(); + let mut current = chars.next(); + loop { + let mut whitespace_len = 0; + while let Some(c) = current { + if !c.is_whitespace() { + break; + } + whitespace_len += 1; + current = chars.next(); + } + + let mut name_len = 0; + while let Some(c) = current { + if !crate::c::is_c_identifier_char(c) { + break; + } + current = chars.next(); + name_len += 1; + } + let name = &text[whitespace_len..whitespace_len + name_len]; + parameters.push(String::from(name)); + + current = consume_whitespace(&mut chars, current); + match current { + Some(',') => { + text = chars.as_str(); + current = chars.next(); + } + Some(')') => { + chars.next(); + break; + } + Some(c) => { + panic!("Unexpected character in #define argument list: {:?}", c) + } + None => { + panic!("End of text while in the middle of #define argument list.") + } + } + } + state = State::DefineExpression; + } + + State::DefineExpression => { + c_expr_.replace(String::from(chars.as_str().trim())); + break; + } + + State::DefineValue => { + let v = String::from(chars.as_str().trim()); + if !defref.is_empty() { + c_expr_.replace(v); + } else { + value_.replace(v); + } + break; + } + } + } + } + + if replace { + c_expr_.replace(code); + } + let value = if let Some(expression) = c_expr_ { + if parameters.is_empty() { + TypeDefineValue::Expression(expression) + } else { + TypeDefineValue::Function { + params: parameters, + expression, + } + } + } else if let Some(value) = value_ { + TypeDefineValue::Value(value) + } else { + TypeDefineValue::Empty + }; + TypeDefine { + name: name_, + comment: comment_, + defref, + is_disabled, + replace, + value, + } } fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> TypesChild { @@ -851,8 +1110,10 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> None }; + let has_members = matches!(category.as_deref(), Some("struct" | "union")); + match_elements_combine_text! {ctx, attributes, code, - "member" => { + "member" if has_members => { let mut len = None; let mut altlen = None; let mut externsync = None; @@ -905,7 +1166,7 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> })) } }, - "comment" => members.push(TypeMember::Comment(parse_text_element(ctx))), + "comment" if has_members => members.push(TypeMember::Comment(parse_text_element(ctx))), "name" => { let text = parse_text_element(ctx); code.push_str(&text); @@ -923,12 +1184,112 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> } } + let spec = match category.as_deref() { + Some("include") => TypeSpec::Include { + name: markup.iter().find_map(|m| match m { + TypeCodeMarkup::Name(name) => Some(name.clone()), + _ => None, + }), + quoted: !code.contains("<"), + }, + Some("define") => { + let name_ = name + .clone() + .or_else(|| { + markup.iter().find_map(|m| match m { + TypeCodeMarkup::Name(name) => Some(name.clone()), + _ => None, + }) + }) + .unwrap(); + let defref = markup + .iter() + .filter_map(|m| match m { + TypeCodeMarkup::Type(ty) => Some(ty.clone()), + _ => None, + }) + .collect(); + TypeSpec::Define(process_define_code(code, name_, defref)) + } + Some("basetype") => TypeSpec::Typedef { + name: markup + .iter() + .find_map(|m| match m { + TypeCodeMarkup::Name(name) => Some(name.clone()), + _ => None, + }) + .unwrap(), + basetype: markup.iter().find_map(|m| match m { + TypeCodeMarkup::Type(ty) => Some(ty.clone()), + _ => None, + }), + }, + Some("bitmask") => { + if name.is_some() || alias.is_some() { + TypeSpec::Bitmask(None) + } else { + TypeSpec::Bitmask(Some(NameWithType { + name: markup + .iter() + .find_map(|m| match m { + TypeCodeMarkup::Name(name) => Some(name.clone()), + _ => None, + }) + .unwrap(), + type_name: markup + .iter() + .find_map(|m| match m { + TypeCodeMarkup::Type(ty) => Some(ty.clone()), + _ => None, + }) + .unwrap(), + pointer_kind: None, + is_struct: false, + bitfield_size: None, + array_shape: None, + })) + } + } + Some("handle") => { + if name.is_some() || alias.is_some() { + TypeSpec::None + } else { + TypeSpec::Handle(TypeHandle { + name: markup + .iter() + .find_map(|m| match m { + TypeCodeMarkup::Name(name) => Some(name.clone()), + _ => None, + }) + .unwrap(), + handle_type: match markup + .iter() + .find_map(|m| match m { + TypeCodeMarkup::Type(val) => Some(val.as_str()), + _ => None, + }) + .unwrap() + { + "VK_DEFINE_HANDLE" => HandleType::Dispatch, + "VK_DEFINE_NON_DISPATCHABLE_HANDLE" => HandleType::NoDispatch, + h => unreachable!("Unexpected handle type {:?}", h), + }, + }) + } + } + Some("enum") => TypeSpec::Enumeration, + Some("funcpointer") => TypeSpec::FunctionPointer(fn_ptr_spec.unwrap()), + Some("struct") => TypeSpec::Struct(members), + Some("union") => TypeSpec::Union(members), + None => TypeSpec::None, + Some(c) => unreachable!("Unexpected category of type {:?}", c), + }; + TypesChild::Type(Type { api, alias, requires, name, - category, parent, returnedonly, structextends, @@ -936,15 +1297,7 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> objtypeenum, bitvalues, comment, - spec: if let Some((defn, params)) = fn_ptr_spec { - TypeSpec::FunctionPointer(defn, params) - } else if members.len() > 0 { - TypeSpec::Members(members) - } else if code.len() > 0 { - TypeSpec::Code(TypeCode { code, markup }) - } else { - TypeSpec::None - }, + spec, }) } diff --git a/vk-parse/src/types.rs b/vk-parse/src/types.rs index af9427c..f55e370 100644 --- a/vk-parse/src/types.rs +++ b/vk-parse/src/types.rs @@ -217,12 +217,6 @@ pub struct Type { )] pub requires: Option, - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub category: Option, - #[cfg_attr( feature = "serialize", serde(default, skip_serializing_if = "is_default") @@ -278,9 +272,22 @@ pub struct Type { #[non_exhaustive] pub enum TypeSpec { None, - Code(TypeCode), - Members(Vec), - FunctionPointer(NameWithType, Vec), + Include { + name: Option, + quoted: bool, + }, + Define(TypeDefine), + Typedef { + name: String, + // FIXME doesn't include all of the necessary information to recreate + basetype: Option, + }, + Bitmask(Option), + Handle(TypeHandle), + Enumeration, + FunctionPointer(TypeFunctionPointer), + Struct(Vec), + Union(Vec), } impl Default for TypeSpec { @@ -289,23 +296,10 @@ impl Default for TypeSpec { } } -#[derive(Debug, Clone, PartialEq, Eq, Default)] -#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] -#[non_exhaustive] -pub struct TypeCode { - pub code: String, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub markup: Vec, -} - #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] -pub enum TypeCodeMarkup { +pub(crate) enum TypeCodeMarkup { Name(String), Type(String), ApiEntry(String), @@ -420,6 +414,61 @@ pub enum TypeMemberMarkup { Comment(String), } +#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub struct TypeFunctionPointer { + pub proto: NameWithType, + + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub params: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub struct TypeHandle { + pub name: String, + + pub handle_type: HandleType, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum HandleType { + Dispatch, + NoDispatch, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub struct TypeDefine { + pub name: String, + pub comment: Option, + pub defref: Vec, + pub is_disabled: bool, + pub replace: bool, + pub value: TypeDefineValue, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum TypeDefineValue { + Empty, + Value(String), + Expression(String), + Function { + params: Vec, + expression: String, + }, +} + #[derive(Debug, Clone, PartialEq, Eq, Default)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] From 1b7b97ddcab9301964000a42143e3ff757f2b369 Mon Sep 17 00:00:00 2001 From: Julia Tatz Date: Mon, 22 Aug 2022 01:09:02 -0400 Subject: [PATCH 03/17] Strong typing of field-like attributes FIXME finish error messages/handling TODO strongly type more stringly typed attributes TODO Add full c-expr/c-type/c-preproc parsing with nom? --- vk-parse/src/convert.rs | 161 ++++++++++++++++++++++------------------ vk-parse/src/parse.rs | 159 +++++++++++++++++++++++++++++++++++---- vk-parse/src/types.rs | 154 +++++++++++++++++++------------------- 3 files changed, 306 insertions(+), 168 deletions(-) diff --git a/vk-parse/src/convert.rs b/vk-parse/src/convert.rs index 3605e12..8f297ab 100644 --- a/vk-parse/src/convert.rs +++ b/vk-parse/src/convert.rs @@ -347,28 +347,7 @@ impl From for vkxml::StructElement { TypeMember::Comment(comment) => vkxml::StructElement::Notation(comment), TypeMember::Definition(def) => { let mut field: vkxml::Field = def.definition.into(); - field.c_size = def.altlen; - field.sync = def.externsync; - field.optional = def.optional; field.type_enums = def.values; - match def.len { - Some(mut value) => { - let null_terminated_part = ",null-terminated"; - if value.as_str().ends_with(null_terminated_part) { - field.null_terminate = true; - let start = value.len() - null_terminated_part.len(); - value.drain(start..); - } - - if value.as_str() == "null-terminated" { - field.null_terminate = true; - } else { - field.size = Some(value); - } - field.array = Some(vkxml::ArrayType::Dynamic); - } - None => (), - } for tag in def.markup { match tag { TypeMemberMarkup::Comment(comment) => field.notation = Some(comment), @@ -454,42 +433,94 @@ impl From for vkxml::Field { bitfield_size: _, array_shape, name, + dynamic_shape, + externsync, + optional, + noautovalidity, + objecttype: _, } = nt; - let mut r = new_field(); - r.name = Some(name); - r.is_const = matches!( - pointer_kind, - Some( - PointerKind::Single { is_const: true } | PointerKind::Double { is_const: true, .. } - ) - ); - r.is_struct = is_struct; - r.basetype = type_name; - r.reference = match pointer_kind { - Some(PointerKind::Single { .. }) => Some(vkxml::ReferenceType::Pointer), - Some(PointerKind::Double { - inner_is_const: true, - .. - }) => Some(vkxml::ReferenceType::PointerToConstPointer), - Some(PointerKind::Double { - inner_is_const: false, - .. - }) => Some(vkxml::ReferenceType::PointerToPointer), - None => None, - }; - if let Some(shape) = array_shape.as_deref() { - r.array = Some(vkxml::ArrayType::Static); - match shape { - [ArrayLength::Static(n), ..] => { - r.size = Some(n.to_string()); - } - [ArrayLength::Constant(c), ..] => { - r.size_enumref = Some(c.clone()); - } - [] => unreachable!(), - }; + vkxml::Field { + array: if dynamic_shape.is_some() { + Some(vkxml::ArrayType::Dynamic) + } else if array_shape.is_some() { + Some(vkxml::ArrayType::Static) + } else { + None + }, + auto_validity: noautovalidity.is_none(), + basetype: type_name, + c_size: match &dynamic_shape { + Some(DynamicShapeKind::Expression { + latex_expr: _, + c_expr, + }) => Some(c_expr.to_string()), + _ => None, + }, + errorcodes: None, + is_const: matches!( + pointer_kind, + Some( + PointerKind::Single { is_const: true } + | PointerKind::Double { is_const: true, .. } + ) + ), + is_struct, + sync: externsync.map(|es| match es { + ExternSyncKind::Value => "true".to_string(), + ExternSyncKind::Fields(fs) if dynamic_shape.is_some() => fs + .iter() + .map(|s| format!("{}[].{}", name, s)) + .collect::>() + .join(","), + ExternSyncKind::Fields(fs) => fs + .iter() + .map(|s| format!("{}->{}", name, s)) + .collect::>() + .join(","), + }), + name: Some(name), + notation: None, + null_terminate: matches!( + dynamic_shape, + Some( + DynamicShapeKind::Single(DynamicLength::NullTerminated) + | DynamicShapeKind::Double(DynamicLength::NullTerminated, _) + | DynamicShapeKind::Double(_, DynamicLength::NullTerminated) + ) + ), + optional: optional.map(|opt| match opt { + OptionalKind::Single(outer) => outer.to_string(), + OptionalKind::Double(outer, inner) => format!("{},{}", outer, inner), + }), + reference: pointer_kind.map(|kind| match kind { + PointerKind::Single { .. } => vkxml::ReferenceType::Pointer, + PointerKind::Double { + inner_is_const: true, + .. + } => vkxml::ReferenceType::PointerToConstPointer, + PointerKind::Double { + inner_is_const: false, + .. + } => vkxml::ReferenceType::PointerToPointer, + }), + size: match (&dynamic_shape, array_shape.as_deref()) { + ( + Some( + DynamicShapeKind::Single(DynamicLength::Parameterized(p)) + | DynamicShapeKind::Double(DynamicLength::Parameterized(p), _), + ), + _, + ) => Some(p.to_string()), + (None, Some([ArrayLength::Static(n), ..])) => Some(n.to_string()), + _ => None, + }, + size_enumref: match array_shape.as_deref() { + Some([ArrayLength::Constant(c), ..]) => Some(c.to_string()), + _ => None, + }, + successcodes: None, + type_enums: None, } - r } } @@ -885,25 +916,7 @@ impl From for Option { r.param.reserve(def.params.len()); for param in def.params { - let mut p: vkxml::Field = param.definition.into(); - p.optional = param.optional; - p.sync = param.externsync; - if let Some(mut value) = param.len { - let null_terminated_part = ",null-terminated"; - if value.as_str().ends_with(null_terminated_part) { - p.null_terminate = true; - let start = value.len() - null_terminated_part.len(); - value.drain(start..); - } - - if value.as_str() == "null-terminated" { - p.null_terminate = true; - } else { - p.size = Some(value); - } - p.array = Some(vkxml::ArrayType::Dynamic); - } - r.param.push(p); + r.param.push(param.definition.into()); } Some(r) diff --git a/vk-parse/src/parse.rs b/vk-parse/src/parse.rs index cc71f07..ac9e101 100644 --- a/vk-parse/src/parse.rs +++ b/vk-parse/src/parse.rs @@ -509,8 +509,90 @@ fn parse_name_with_type< >( ctx: &mut ParseCtx, code: &mut String, + len: Option, + altlen: Option, + externsync: Option, + optional: Option, + noautovalidity: Option, + objecttype: Option, mut handle_extra: F, ) -> Option { + let dynamic_shape = if let Some(latex_expr) = + len.as_deref().and_then(|l| l.strip_prefix("latexmath:")) + { + // altlen was only added in version 61, and we support down to version 33 + // let c_expr = altlen.expect("The `altlen` attribute is required when the `len` attribute is a latex expression"); + + if let Some(c_expr) = altlen { + Some(DynamicShapeKind::Expression { + latex_expr: Some(latex_expr.to_string()), + c_expr, + }) + } else { + None + } + } else if let Some(c_expr) = altlen { + // only required/fixed in version >= 1.2.188(?) + // unreachable!("only expecting the `altlen` attribute when the `len` attribute is a latex expression"); + Some(DynamicShapeKind::Expression { + latex_expr: None, + c_expr, + }) + } else if let Some(len) = len { + let mut it = len.split(',').map(|v| { + if v == "null-terminated" { + DynamicLength::NullTerminated + } else if let Ok(n) = v.parse() { + DynamicLength::Static(n) + } else if let Some((parameter, field)) = v.split_once("->") { + DynamicLength::ParameterizedField { + parameter: parameter.to_string(), + field: field.to_string(), + } + } else { + DynamicLength::Parameterized(v.to_string()) + } + }); + let outer = it.next().expect("The `len` attribute must not be empty"); + Some(match it.next() { + Some(inner) => { + let n = it.count(); + if n != 0 { + panic!( + "Expected only 1 or 2 comma-seperated values in the `len` attribute, found {} values", + 2 + n + ); + } + DynamicShapeKind::Double(outer, inner) + } + None => DynamicShapeKind::Single(outer), + }) + } else { + None + }; + let optional = optional.as_deref().map(|v| { + let mut it = v.split(',').map(|v| match v.parse::() { + Err(_) => panic!("The comma-seperated values of the `optional` attribute must be either \"true\" | \"false\" not {:?}", v), + Ok(b) => b, + }); + let outer = it.next().expect("The `optional` attribute must not be empty"); + match it.next() { + Some(inner) => { + // broken in versions 1.2.162,1.2.163, & 1.2.164, see https://github.com/KhronosGroup/Vulkan-Docs/issues/1405 + // assert_eq!(it.count(), 0, "Expected only 1 or 2 comma-seperated values in the `optional` attribute"); + OptionalKind::Double(outer, inner) + }, + None => OptionalKind::Single(outer) + } + }); + let noautovalidity = match noautovalidity.as_deref() { + Some("true") => Some(()), + None => None, + Some(v) => panic!( + "The `noautovalidity` attribute must only be \"true\" and not {:?}", + v + ), + }; let mut event = ctx.events.next(); if let Some(Ok(XmlEvent::Whitespace(text))) = event { code.push_str(&text); @@ -680,6 +762,29 @@ fn parse_name_with_type< event = ctx.events.next(); } + let externsync = externsync.map(|v| { + if v == "true" { + ExternSyncKind::Value + } else { + let it = v.split(',').map(|value| { + let v = value.strip_prefix(name.as_str()).expect(""); + if let Some(field) = v.strip_prefix("->") { + field + } else if let Some(field) = v.strip_prefix("[].") { + field + } else if let Some(field) = v.strip_prefix(".").or_else(|| v.strip_prefix("::")) { + // these field seperators were phased out / fixed by version 138, and replaced with '->' + // https://github.com/KhronosGroup/Vulkan-Docs/pull/1222 + field + } else { + unreachable!("unspported field sperator found {:?}", value) + } + .to_string() + }); + ExternSyncKind::Fields(it.collect()) + } + }); + Some(NameWithType { type_name, pointer_kind, @@ -687,6 +792,11 @@ fn parse_name_with_type< bitfield_size, name, array_shape, + dynamic_shape, + externsync, + optional, + noautovalidity, + objecttype, }) } @@ -740,6 +850,11 @@ fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option(ctx: &mut ParseCtx) -> Option(ctx: &mut ParseCtx, attributes: Vec) -> "limittype" => limittype = Some(a.value), "objecttype" => objecttype = Some(a.value) } - if let Some(definition) = parse_name_with_type(ctx, &mut code, |ctx, _code, local_name, _| match local_name { + if let Some(definition) = parse_name_with_type( + ctx, &mut code, + len, + altlen, + externsync, + optional, + noautovalidity, + objecttype, + |ctx, _code, local_name, _| match local_name { "comment" => { let text = parse_text_element(ctx); markup.push(TypeMemberMarkup::Comment(text)); @@ -1149,17 +1277,11 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> _ => ControlFlow::Break(()) }) { members.push(TypeMember::Definition(TypeMemberDefinition { - len, - altlen, - externsync, - optional, selector, selection, - noautovalidity, validextensionstructs, values, limittype, - objecttype, code, definition, markup, @@ -1247,6 +1369,11 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> is_struct: false, bitfield_size: None, array_shape: None, + dynamic_shape: None, + externsync: None, + optional: None, + noautovalidity: None, + objecttype: None, })) } } @@ -1339,7 +1466,7 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) match_elements! {ctx, attributes, "proto" => { - proto = parse_name_with_type(ctx, &mut code, |_, _, _, _| ControlFlow::Break(())); + proto = parse_name_with_type(ctx, &mut code, None, None, None, None, None, None, |_, _, _, _| ControlFlow::Break(())); code.push('('); }, @@ -1370,14 +1497,16 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) if !params.is_empty() { code.push_str(", "); } - if let Some(definition) = parse_name_with_type(ctx, &mut code, |_, _, _, _| ControlFlow::Break(())) { + if let Some(definition) = parse_name_with_type(ctx, &mut code, + len, + altlen, + externsync, + optional, + noautovalidity, + objecttype, + + |_, _, _, _| ControlFlow::Break(())) { params.push(CommandParam { - len, - altlen, - externsync, - optional, - noautovalidity, - objecttype, definition, validstructs, }); diff --git a/vk-parse/src/types.rs b/vk-parse/src/types.rs index f55e370..caf9caf 100644 --- a/vk-parse/src/types.rs +++ b/vk-parse/src/types.rs @@ -321,30 +321,6 @@ pub enum TypeMember { #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] pub struct TypeMemberDefinition { - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub len: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub altlen: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub externsync: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub optional: Option, - #[cfg_attr( feature = "serialize", serde(default, skip_serializing_if = "is_default") @@ -357,12 +333,6 @@ pub struct TypeMemberDefinition { )] pub selection: Option, - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub noautovalidity: Option, - #[cfg_attr( feature = "serialize", serde(default, skip_serializing_if = "is_default") @@ -381,12 +351,6 @@ pub struct TypeMemberDefinition { )] pub limittype: Option, - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub objecttype: Option, - #[cfg_attr( feature = "serialize", serde(default, skip_serializing_if = "is_default") @@ -787,47 +751,6 @@ pub struct CommandDefinition { #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] pub struct CommandParam { - /// The expression which indicates the length of this array. - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub len: Option, - - /// Alternate description of the length of this parameter. - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub altlen: Option, - - /// Whether this parameter must be externally synchronised by the app. - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub externsync: Option, - - /// Whether this parameter must have a non-null value. - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub optional: Option, - - /// Disables automatic validity language being generated for this item. - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub noautovalidity: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub objecttype: Option, - /// The definition of this parameter. #[cfg_attr( feature = "serialize", @@ -1199,11 +1122,50 @@ pub enum PointerKind { #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] pub enum ArrayLength { - // TODO could probably make this smaller and/or NonZero - Static(usize), + Static(core::num::NonZeroUsize), Constant(String), } +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum DynamicLength { + NullTerminated, + // FIXME only found in VkAccelerationStructureBuildGeometryInfoKHR->ppGeometries, is this a mistake? + Static(core::num::NonZeroUsize), + Parameterized(String), + ParameterizedField { parameter: String, field: String }, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum DynamicShapeKind { + Expression { + latex_expr: Option, + c_expr: String, + }, + Single(DynamicLength), + Double(DynamicLength, DynamicLength), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum OptionalKind { + Single(bool), + Double(bool, bool), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum ExternSyncKind { + /// externsync="true" + Value, + Fields(Vec), +} + #[derive(Debug, Clone, PartialEq, Eq, Default)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] @@ -1243,6 +1205,40 @@ pub struct NameWithType { serde(default, skip_serializing_if = "is_default") )] pub name: String, + + /// combination of `len` and `altlen` attributes + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub dynamic_shape: Option, + + /// Whether this parameter must be externally synchronised by the app. + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub externsync: Option, + + /// Whether this parameter must have a non-null value. + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub optional: Option, + + /// Disables automatic validity language being generated for this item. + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub noautovalidity: Option<()>, + + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub objecttype: Option, } #[derive(Debug, Clone, PartialEq, Eq, Default)] From 1d058f9bf282eb4ecea45da0b720985d5444990a Mon Sep 17 00:00:00 2001 From: Julia Tatz Date: Mon, 22 Aug 2022 01:10:12 -0400 Subject: [PATCH 04/17] Add parsing of video.xml to tests --- ci/tests/test.rs | 24 ++++++++++++++++++++++++ vk-parse/src/parse.rs | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/ci/tests/test.rs b/ci/tests/test.rs index 4e2f1bc..b1e6248 100644 --- a/ci/tests/test.rs +++ b/ci/tests/test.rs @@ -9,6 +9,8 @@ extern crate xml; const URL_REPO: &str = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs"; const URL_MAIN: &str = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/main/xml/vk.xml"; +const URL_MAIN_VIDEO: &str = + "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/main/xml/video.xml"; fn download(dst: &mut T, url: &str) { let resp = minreq::get(url) @@ -83,6 +85,28 @@ fn test_main() { } } +#[test] +fn test_main_video() { + use std::io::Cursor; + let mut buf = Cursor::new(vec![0; 15]); + download(&mut buf, URL_MAIN_VIDEO); + buf.set_position(0); + + match vk_parse::parse_stream(buf.clone()) { + Ok((_reg, errors)) => { + if !errors.is_empty() { + panic!("{:?}", errors); + } + } + Err(fatal_error) => panic!("{:?}", fatal_error), + } + + match vk_parse::parse_stream_as_vkxml(buf) { + Ok(_) => (), + Err(fatal_error) => panic!("{:?}", fatal_error), + } +} + test_version! {test_v1_0_33, 1, 0, 33, "-core/src/spec"} test_version! {test_v1_0_34, 1, 0, 34, "-core/src/spec"} test_version! {test_v1_0_35, 1, 0, 35, "-core/src/spec"} diff --git a/vk-parse/src/parse.rs b/vk-parse/src/parse.rs index ac9e101..9512422 100644 --- a/vk-parse/src/parse.rs +++ b/vk-parse/src/parse.rs @@ -674,7 +674,8 @@ fn parse_name_with_type< if let Some(rest) = trimmed_text.strip_prefix(':') { ( Some( - rest.parse() + rest.trim_start() + .parse() .expect("after bitfield's ':' only a non-zero integer is expected"), ), None, From b3f9143aad48e4b27c323adbafa2326a27108e97 Mon Sep 17 00:00:00 2001 From: Julia Tatz Date: Wed, 31 Aug 2022 23:09:59 -0400 Subject: [PATCH 05/17] Parse field-like comment into parse_name_with_type Remove parse_name_with_type's handle_extra param Which was a leftover of my old method, but unused now --- vk-parse/src/convert.rs | 8 ++---- vk-parse/src/parse.rs | 55 +++++++++++++++++------------------------ vk-parse/src/types.rs | 19 +++++--------- 3 files changed, 31 insertions(+), 51 deletions(-) diff --git a/vk-parse/src/convert.rs b/vk-parse/src/convert.rs index 8f297ab..76f7d49 100644 --- a/vk-parse/src/convert.rs +++ b/vk-parse/src/convert.rs @@ -348,11 +348,6 @@ impl From for vkxml::StructElement { TypeMember::Definition(def) => { let mut field: vkxml::Field = def.definition.into(); field.type_enums = def.values; - for tag in def.markup { - match tag { - TypeMemberMarkup::Comment(comment) => field.notation = Some(comment), - } - } vkxml::StructElement::Member(field) } @@ -438,6 +433,7 @@ impl From for vkxml::Field { optional, noautovalidity, objecttype: _, + comment, } = nt; vkxml::Field { array: if dynamic_shape.is_some() { @@ -479,7 +475,7 @@ impl From for vkxml::Field { .join(","), }), name: Some(name), - notation: None, + notation: comment, null_terminate: matches!( dynamic_shape, Some( diff --git a/vk-parse/src/parse.rs b/vk-parse/src/parse.rs index 9512422..5d2e542 100644 --- a/vk-parse/src/parse.rs +++ b/vk-parse/src/parse.rs @@ -2,9 +2,7 @@ extern crate xml; use std; use std::io::Read; -use std::ops::ControlFlow; use std::str::FromStr; -use xml::attribute::OwnedAttribute; use xml::name::OwnedName; use xml::reader::XmlEvent; @@ -503,10 +501,7 @@ fn parse_array_shape_text(text: &str, shape_vec: &mut Vec) -> bool } } -fn parse_name_with_type< - R: Read, - F: FnMut(&mut ParseCtx, &mut String, &str, &[OwnedAttribute]) -> ControlFlow<()>, ->( +fn parse_name_with_type( ctx: &mut ParseCtx, code: &mut String, len: Option, @@ -515,7 +510,6 @@ fn parse_name_with_type< optional: Option, noautovalidity: Option, objecttype: Option, - mut handle_extra: F, ) -> Option { let dynamic_shape = if let Some(latex_expr) = len.as_deref().and_then(|l| l.strip_prefix("latexmath:")) @@ -733,25 +727,28 @@ fn parse_name_with_type< }; } + let mut comment = None; while let Some(Ok(e)) = event { match e { XmlEvent::Whitespace(text) => code.push_str(&text), XmlEvent::Characters(text) => code.push_str(&text), XmlEvent::StartElement { - name: elem_name, - attributes, - .. + name: elem_name, .. } => { let elem_name = elem_name.local_name.as_str(); ctx.push_element(elem_name); - if let ControlFlow::Break(()) = - handle_extra(ctx, code, elem_name, attributes.as_ref()) - { - ctx.errors.push(Error::UnexpectedElement { - xpath: ctx.xpath.clone(), - name: String::from(elem_name), - }); - consume_current_element(ctx); + match elem_name { + "comment" => { + let text = parse_text_element(ctx); + comment.replace(text); + } + _ => { + ctx.errors.push(Error::UnexpectedElement { + xpath: ctx.xpath.clone(), + name: String::from(elem_name), + }); + consume_current_element(ctx); + } } } XmlEvent::EndElement { .. } => { @@ -798,6 +795,7 @@ fn parse_name_with_type< optional, noautovalidity, objecttype, + comment, }) } @@ -856,6 +854,7 @@ fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option(ctx: &mut ParseCtx) -> Option(ctx: &mut ParseCtx, attributes: Vec) -> let mut limittype = None; let mut objecttype = None; let mut code = String::new(); - let mut markup = Vec::new(); match_attributes!{ctx, a in attributes, "len" => len = Some(a.value), "altlen" => altlen = Some(a.value), @@ -1268,15 +1267,8 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> externsync, optional, noautovalidity, - objecttype, - |ctx, _code, local_name, _| match local_name { - "comment" => { - let text = parse_text_element(ctx); - markup.push(TypeMemberMarkup::Comment(text)); - ControlFlow::Continue(()) - }, - _ => ControlFlow::Break(()) - }) { + objecttype + ) { members.push(TypeMember::Definition(TypeMemberDefinition { selector, selection, @@ -1285,7 +1277,6 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> limittype, code, definition, - markup, })) } }, @@ -1375,6 +1366,7 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> optional: None, noautovalidity: None, objecttype: None, + comment: None, })) } } @@ -1467,7 +1459,7 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) match_elements! {ctx, attributes, "proto" => { - proto = parse_name_with_type(ctx, &mut code, None, None, None, None, None, None, |_, _, _, _| ControlFlow::Break(())); + proto = parse_name_with_type(ctx, &mut code, None, None, None, None, None, None); code.push('('); }, @@ -1505,8 +1497,7 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) optional, noautovalidity, objecttype, - - |_, _, _, _| ControlFlow::Break(())) { + ) { params.push(CommandParam { definition, validstructs, diff --git a/vk-parse/src/types.rs b/vk-parse/src/types.rs index caf9caf..548a286 100644 --- a/vk-parse/src/types.rs +++ b/vk-parse/src/types.rs @@ -363,19 +363,6 @@ pub struct TypeMemberDefinition { serde(default, skip_serializing_if = "is_default") )] pub definition: NameWithType, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub markup: Vec, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] -#[non_exhaustive] -pub enum TypeMemberMarkup { - Comment(String), } #[derive(Debug, Clone, PartialEq, Eq, Default)] @@ -1239,6 +1226,12 @@ pub struct NameWithType { serde(default, skip_serializing_if = "is_default") )] pub objecttype: Option, + + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub comment: Option, } #[derive(Debug, Clone, PartialEq, Eq, Default)] From 3874ac3ddce67c62ac0462473105c20cf45cb1b8 Mon Sep 17 00:00:00 2001 From: Julia Tatz Date: Wed, 31 Aug 2022 23:58:53 -0400 Subject: [PATCH 06/17] Make clippy happy & use char 'is_*' methods --- ci/tests/test.rs | 2 +- vk-parse/src/c.rs | 52 ++++++---------------- vk-parse/src/convert.rs | 96 +++++++++++++++++++---------------------- vk-parse/src/parse.rs | 59 +++++++++++-------------- vk-parse/src/types.rs | 6 +-- 5 files changed, 89 insertions(+), 126 deletions(-) diff --git a/ci/tests/test.rs b/ci/tests/test.rs index b1e6248..c615fb9 100644 --- a/ci/tests/test.rs +++ b/ci/tests/test.rs @@ -15,7 +15,7 @@ const URL_MAIN_VIDEO: &str = fn download(dst: &mut T, url: &str) { let resp = minreq::get(url) .send() - .expect(&format!("Failed to GET resource: {:?}", url)); + .unwrap_or_else(|_| panic!("Failed to GET resource: {:?}", url)); let is_success = 200 <= resp.status_code && resp.status_code < 300; if !is_success { diff --git a/vk-parse/src/c.rs b/vk-parse/src/c.rs index 848ed09..5b6a717 100644 --- a/vk-parse/src/c.rs +++ b/vk-parse/src/c.rs @@ -18,11 +18,7 @@ impl<'a> Iterator for IterPhase1<'a> { fn next(&mut self) -> Option { let mut iter = self.src.chars(); - let c = if let Some(c) = iter.next() { - c - } else { - return None; - }; + let c = iter.next()?; match c { // TODO: trigraphs and digraphs (probably not important) @@ -95,7 +91,7 @@ impl<'a> IterPhase3a<'a> { } fn is_merged_whitespace(c: char) -> bool { - c == '\t' || ('\u{000B}' <= c && c <= '\u{000D}') || c == ' ' + c == '\t' || ('\u{000B}'..='\u{000D}').contains(&c) || c == ' ' } } @@ -103,11 +99,7 @@ impl<'a> Iterator for IterPhase3a<'a> { type Item = char; fn next(&mut self) -> Option { loop { - let c = if let Some(c) = self.peek { - c - } else { - return None; - }; + let c = self.peek?; if c == '\n' { self.peek = self.src.next(); @@ -125,7 +117,7 @@ impl<'a> Iterator for IterPhase3a<'a> { if c == '/' { self.peek = None; - while let Some(c) = self.src.next() { + for c in self.src.by_ref() { if c == '\n' { self.peek = Some(c); break; @@ -154,7 +146,7 @@ impl<'a> Iterator for IterPhase3a<'a> { } } else if IterPhase3a::is_merged_whitespace(c) { self.peek = None; - while let Some(c) = self.src.next() { + for c in self.src.by_ref() { if !IterPhase3a::is_merged_whitespace(c) { self.peek = Some(c); break; @@ -262,11 +254,7 @@ impl<'src> IterTokenInner<'src> { fn next(&mut self) -> Option { self.buf.clear(); loop { - let c = if let Some(c) = self.peek { - c - } else { - return None; - }; + let c = self.peek?; match (self.line, c) { (LineState::Start, ' ') => self.peek = self.src.next(), (LineState::Start, '\n') => self.peek = self.src.next(), @@ -337,12 +325,12 @@ impl<'src> IterTokenInner<'src> { (LineState::Normal, '\n') => { self.peek = self.src.next(); self.line = LineState::Start; - if self.buf.len() > 0 { + if !self.buf.is_empty() { return Some(Token::Identifier(&self.buf)); } } (LineState::Normal, ';') => { - if self.buf.len() > 0 { + if !self.buf.is_empty() { return Some(Token::Identifier(&self.buf)); } else { self.peek = self.src.next(); @@ -359,7 +347,7 @@ impl<'src> IterTokenInner<'src> { } fn is_number_start(c: char) -> bool { - '0' <= c && c <= '9' + c.is_ascii_digit() } fn is_number_part(c: char) -> bool { @@ -368,17 +356,15 @@ fn is_number_part(c: char) -> bool { || c == '-' || c == 'x' || c == 'X' - || ('0' <= c && c <= '9') - || ('a' <= c && c <= 'f') - || ('A' <= c && c <= 'F') + || c.is_ascii_hexdigit() } fn is_identifier_start(c: char) -> bool { - c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') + c == '_' || c.is_ascii_alphabetic() } fn is_identifier_part(c: char) -> bool { - c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') + c == '_' || c.is_ascii_alphanumeric() } //-------------------------------------------------------------------------------------------------- @@ -421,17 +407,7 @@ impl<'a> Iterator for TokenIter<'a> { } pub fn is_c_identifier_char(c: char) -> bool { - if '0' <= c && c <= '9' { - true - } else if 'a' <= c && c <= 'z' { - true - } else if 'A' <= c && c <= 'Z' { - true - } else if c == '_' { - true - } else { - false - } + c.is_ascii_alphanumeric() || c == '_' } pub fn is_c_identifier(s: &str) -> bool { @@ -450,7 +426,7 @@ mod test { #[test] fn test() { - println!(""); + println!(); { let code = "typedef void // some comment \n /* some other comment */ (VKAPI_PTR *PFN_vkInternalAllocationNotification)(\\\r\n void* pUserData = M_PI/4,\r\n size_t size,\\\n VkInternalAllocationType allocationType, \n VkSystemAllocationScope allocationScope);"; diff --git a/vk-parse/src/convert.rs b/vk-parse/src/convert.rs index 76f7d49..3408ddd 100644 --- a/vk-parse/src/convert.rs +++ b/vk-parse/src/convert.rs @@ -109,7 +109,7 @@ impl From for vkxml::Registry { } Some(kind) => { let enumeration = vkxml::Enumeration { - name: e.name.unwrap_or(String::new()), + name: e.name.unwrap_or_default(), notation: e.comment, purpose: if kind.as_str() == "bitmask" { Some(vkxml::EnumerationPurpose::Bitmask) @@ -180,15 +180,15 @@ impl From for Option { TypesChild::Type(t) => { match t.spec { TypeSpec::None => { - let name = t.name.unwrap_or(String::new()); - return Some(vkxml::DefinitionsElement::Reference(vkxml::Reference { + let name = t.name.unwrap_or_default(); + Some(vkxml::DefinitionsElement::Reference(vkxml::Reference { name, notation: t.comment, include: t.requires, - })); + })) } TypeSpec::Include { name, quoted } => { - let name = t.name.or(name).unwrap_or(String::new()); + let name = t.name.or(name).unwrap_or_default(); let need_ext = !name.ends_with(".h"); let include = vkxml::Include { name, @@ -200,7 +200,7 @@ impl From for Option { need_ext, }; - return Some(vkxml::DefinitionsElement::Include(include)); + Some(vkxml::DefinitionsElement::Include(include)) } TypeSpec::Define(TypeDefine { @@ -212,7 +212,7 @@ impl From for Option { replace, }) => { let mut define = vkxml::Define { - name: name, + name, notation: t.comment, is_disabled, comment, @@ -235,7 +235,7 @@ impl From for Option { define.c_expression = Some(expression); } } - return Some(vkxml::DefinitionsElement::Define(define)); + Some(vkxml::DefinitionsElement::Define(define)) } TypeSpec::Typedef { name, basetype } => { @@ -244,11 +244,11 @@ impl From for Option { notation: t.comment, basetype: basetype.unwrap_or_default(), }; - return Some(vkxml::DefinitionsElement::Typedef(typedef)); + Some(vkxml::DefinitionsElement::Typedef(typedef)) } TypeSpec::Bitmask(None) => { - return None; + None } TypeSpec::Bitmask(Some(name_val)) => { let NameWithType { @@ -260,7 +260,7 @@ impl From for Option { basetype: type_name, enumref: t.requires, }; - return Some(vkxml::DefinitionsElement::Bitmask(bitmask)); + Some(vkxml::DefinitionsElement::Bitmask(bitmask)) } TypeSpec::Handle(TypeHandle { name, handle_type }) => { @@ -273,31 +273,31 @@ impl From for Option { HandleType::NoDispatch => vkxml::HandleType::NoDispatch, }, }; - return Some(vkxml::DefinitionsElement::Handle(handle)); + Some(vkxml::DefinitionsElement::Handle(handle)) } TypeSpec::Enumeration => { // if alias.is_some() { // return None; // } - return Some(vkxml::DefinitionsElement::Enumeration( + Some(vkxml::DefinitionsElement::Enumeration( vkxml::EnumerationDeclaration { - name: t.name.unwrap_or(String::new()), + name: t.name.unwrap_or_default(), notation: t.comment, }, - )); + )) } TypeSpec::FunctionPointer(TypeFunctionPointer { proto: defn, params, }) => { - return Some(vkxml::DefinitionsElement::FuncPtr(vkxml::FunctionPointer { - name: defn.name.clone().into(), + Some(vkxml::DefinitionsElement::FuncPtr(vkxml::FunctionPointer { + name: defn.name.clone(), notation: t.comment, return_type: defn.into(), param: params.into_iter().map(|p| p.into()).collect(), - })); + })) } TypeSpec::Struct(members) => { @@ -305,9 +305,9 @@ impl From for Option { return None; } let mut s = vkxml::Struct { - name: t.name.unwrap_or(String::new()), + name: t.name.unwrap_or_default(), notation: t.comment, - is_return: t.returnedonly.unwrap_or(String::new()).as_str() == "true", + is_return: t.returnedonly.unwrap_or_default().as_str() == "true", extends: t.structextends, elements: Vec::new(), }; @@ -315,12 +315,12 @@ impl From for Option { s.elements.push(member.into()); } - return Some(vkxml::DefinitionsElement::Struct(s)); + Some(vkxml::DefinitionsElement::Struct(s)) } TypeSpec::Union(members) => { let mut u = vkxml::Union { - name: t.name.unwrap_or(String::new()), + name: t.name.unwrap_or_default(), notation: t.comment, elements: Vec::new(), }; @@ -333,7 +333,7 @@ impl From for Option { } } - return Some(vkxml::DefinitionsElement::Union(u)); + Some(vkxml::DefinitionsElement::Union(u)) } } } @@ -368,11 +368,7 @@ impl From for Option { fn from(orig: EnumsChild) -> Self { match orig { EnumsChild::Enum(e) => { - if let Some(constant) = e.into() { - Some(vkxml::EnumerationElement::Enum(constant)) - } else { - None - } + Option::::from(e).map(vkxml::EnumerationElement::Enum) } EnumsChild::Unused(unused) => { Some(vkxml::EnumerationElement::UnusedRange(vkxml::Range { @@ -405,10 +401,10 @@ impl From for Option { bitpos: None, c_expression: None, }; - if let Ok(value) = i32::from_str_radix(&value, 10) { + if let Ok(value) = value.parse::() { r.number = Some(value); - } else if value.starts_with("0x") { - r.hex = Some(String::from(value.split_at(2).1)) + } else if let Some(value) = value.strip_prefix("0x") { + r.hex = Some(String::from(value)) } else { r.c_expression = Some(value) } @@ -595,36 +591,34 @@ impl From for vkxml::Extensions { } impl From for vkxml::Extension { - fn from(mut orig: Extension) -> Self { + fn from(orig: Extension) -> Self { + let Extension { name, comment, number, protect, platform: _, author, contact, ext_type, requires, requires_core: _, supported, deprecatedby: _, promotedto: _, obsoletedby: _, provisional: _, specialuse: _, sortorder: _, children } = orig; let mut disabled = false; let mut match_api = None; - let supported = orig.supported.take(); - match supported { - Some(text) => { - if text == "disabled" { - disabled = true; - } else { - match_api = Some(text); - } + + if let Some(text) = supported{ + if text == "disabled" { + disabled = true; + } else { + match_api = Some(text); } - None => (), } let mut elements = Vec::new(); - for item in orig.children { + for item in children { elements.push(item.into()); } vkxml::Extension { - name: orig.name, - notation: orig.comment, - number: match orig.number { + name, + notation: comment, + number: match number { Some(val) => val as i32, None => 0, }, disabled, match_api, - ty: match orig.ext_type { + ty: match ext_type { Some(text) => match text.as_str() { "instance" => Some(vkxml::ExtensionType::Instance), "device" => Some(vkxml::ExtensionType::Device), @@ -635,10 +629,10 @@ impl From for vkxml::Extension { }, None => None, }, - define: orig.protect, - requires: orig.requires, - author: orig.author, - contact: orig.contact, + define: protect, + requires, + author, + contact, elements, } } @@ -788,7 +782,7 @@ impl From for Option { let mut text = None; let mut number = None; let mut enumref = None; - if let Ok(val) = i32::from_str_radix(&value, 10) { + if let Ok(val) = value.parse::() { number = Some(val); } else if value.starts_with('"') && value.ends_with('"') { let end = value.len() - 1; diff --git a/vk-parse/src/parse.rs b/vk-parse/src/parse.rs index 5d2e542..498ac39 100644 --- a/vk-parse/src/parse.rs +++ b/vk-parse/src/parse.rs @@ -333,10 +333,7 @@ fn parse_vendorid( "name" => name = Some(a.value), "comment" => comment = Some(a.value), "id" => { - let mut v = None; - if a.value.starts_with("0x") { - v = u32::from_str_radix(&a.value.split_at(2).1, 16).ok(); - } + let v = a.value.strip_prefix("0x").and_then(|v| u32::from_str_radix(v, 16).ok()); if let Some(v) = v { id = Some(v); @@ -517,14 +514,10 @@ fn parse_name_with_type( // altlen was only added in version 61, and we support down to version 33 // let c_expr = altlen.expect("The `altlen` attribute is required when the `len` attribute is a latex expression"); - if let Some(c_expr) = altlen { - Some(DynamicShapeKind::Expression { - latex_expr: Some(latex_expr.to_string()), - c_expr, - }) - } else { - None - } + altlen.map(|c_expr| DynamicShapeKind::Expression { + latex_expr: Some(latex_expr.to_string()), + c_expr, + }) } else if let Some(c_expr) = altlen { // only required/fixed in version >= 1.2.188(?) // unreachable!("only expecting the `altlen` attribute when the `len` attribute is a latex expression"); @@ -716,7 +709,7 @@ fn parse_name_with_type( let trimmed_text = text .trim() - .strip_prefix("]") + .strip_prefix(']') .expect("Expected a ']' to denote the end of an element of a shape"); hungry = parse_array_shape_text(trimmed_text.trim_start(), array_shape_vec); } else { @@ -770,7 +763,7 @@ fn parse_name_with_type( field } else if let Some(field) = v.strip_prefix("[].") { field - } else if let Some(field) = v.strip_prefix(".").or_else(|| v.strip_prefix("::")) { + } else if let Some(field) = v.strip_prefix('.').or_else(|| v.strip_prefix("::")) { // these field seperators were phased out / fixed by version 138, and replaced with '->' // https://github.com/KhronosGroup/Vulkan-Docs/pull/1222 field @@ -829,9 +822,9 @@ fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option { ctx.push_element(&local_name); - let text = parse_text_element(ctx); + - text + parse_text_element(ctx) } _ => { ctx.errors.push(Error::MissingElement { @@ -886,9 +879,9 @@ fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option { ctx.push_element(&local_name); - let text = parse_text_element(ctx); + - text + parse_text_element(ctx) } _ => { ctx.errors.push(Error::MissingElement { @@ -989,11 +982,11 @@ fn process_define_code(code: String, name_: String, defref: Vec) -> Type Some('#') => { let text = chars.as_str(); let mut directive_len = 0; - while let Some(c) = chars.next() { + for c in chars.by_ref() { if c.is_whitespace() { break; } - if 'a' <= c && c <= 'z' { + if ('a'..='z').contains(&c) { directive_len += 1; } else { panic!("Unexpected symbol in preprocessor directive: {:?}", c); @@ -1269,7 +1262,7 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> noautovalidity, objecttype ) { - members.push(TypeMember::Definition(TypeMemberDefinition { + members.push(TypeMember::Definition(Box::new(TypeMemberDefinition { selector, selection, validextensionstructs, @@ -1277,7 +1270,7 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> limittype, code, definition, - })) + }))) } }, "comment" if has_members => members.push(TypeMember::Comment(parse_text_element(ctx))), @@ -1304,7 +1297,7 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> TypeCodeMarkup::Name(name) => Some(name.clone()), _ => None, }), - quoted: !code.contains("<"), + quoted: !code.contains('<'), }, Some("define") => { let name_ = name @@ -1405,7 +1398,7 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> Some(c) => unreachable!("Unexpected category of type {:?}", c), }; - TypesChild::Type(Type { + TypesChild::Type(Box::new(Type { api, alias, requires, @@ -1418,7 +1411,7 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> bitvalues, comment, spec, - }) + })) } fn parse_command(ctx: &mut ParseCtx, attributes: Vec) -> Option { @@ -1531,7 +1524,7 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) return None; }; - Some(Command::Definition(CommandDefinition { + Some(Command::Definition(Box::new(CommandDefinition { queues, successcodes, errorcodes, @@ -1546,7 +1539,7 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) description, implicitexternsyncparams, code, - })) + }))) } } @@ -1963,7 +1956,7 @@ fn parse_interface_item( consume_current_element(ctx); Some(InterfaceItem::Type { name, comment }) } - "enum" => parse_enum(ctx, attributes).map(|v| InterfaceItem::Enum(v)), + "enum" => parse_enum(ctx, attributes).map(InterfaceItem::Enum), "command" => { let mut name = None; let mut comment = None; @@ -1980,7 +1973,7 @@ fn parse_interface_item( xpath: ctx.xpath.clone(), name: String::from(name), }); - return None; + None } } } @@ -2311,10 +2304,10 @@ fn parse_enable(ctx: &mut ParseCtx, attributes: Vec) - } fn parse_integer(ctx: &mut ParseCtx, text: &str) -> Option { - let parse_res = if text.starts_with("0x") { - i64::from_str_radix(text.split_at(2).1, 16) + let parse_res = if let Some(hex) = text.strip_prefix("0x") { + i64::from_str_radix(hex, 16) } else { - i64::from_str_radix(text, 10) + text.parse::() }; if let Ok(v) = parse_res { @@ -2338,7 +2331,7 @@ fn parse_int_attribute, R: Read>( Err(e) => { ctx.errors.push(Error::ParseIntError { xpath: xpath_attribute(&ctx.xpath, attribute_name), - text: text, + text, error: e, }); None diff --git a/vk-parse/src/types.rs b/vk-parse/src/types.rs index 548a286..af6895c 100644 --- a/vk-parse/src/types.rs +++ b/vk-parse/src/types.rs @@ -185,7 +185,7 @@ pub type Types = CommentedChildren; #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] pub enum TypesChild { - Type(Type), + Type(Box), Comment(String), } @@ -314,7 +314,7 @@ pub enum TypeMember { Comment(String), /// A structure field definition. - Definition(TypeMemberDefinition), + Definition(Box), } #[derive(Debug, Clone, PartialEq, Eq, Default)] @@ -641,7 +641,7 @@ pub enum Command { Alias { name: String, alias: String }, /// Defines a new Vulkan function. - Definition(CommandDefinition), + Definition(Box), } #[derive(Debug, Clone, PartialEq, Eq, Default)] From d356587a2e2d80206d8f87e39965992d503ed9ae Mon Sep 17 00:00:00 2001 From: Julia Tatz Date: Thu, 1 Sep 2022 00:19:55 -0400 Subject: [PATCH 07/17] Add missing tests & use paste! for test_name I only added paste b/c only after copying & updating the patch version, did I realize I forgot the test name. But at least it makes maintenance easier --- ci/Cargo.toml | 1 + ci/tests/test.rs | 374 +++++++++++++++++++++++++---------------------- 2 files changed, 200 insertions(+), 175 deletions(-) diff --git a/ci/Cargo.toml b/ci/Cargo.toml index 087f6c4..9e3f2e8 100644 --- a/ci/Cargo.toml +++ b/ci/Cargo.toml @@ -13,3 +13,4 @@ serde_derive = "^1.0.75" vk-parse = { path = "../vk-parse", features = ["serialize", "vkxml-convert"] } vkxml = "^0.3" xml-rs = "^0.8" +paste = "1.0" diff --git a/ci/tests/test.rs b/ci/tests/test.rs index c615fb9..c75b0d4 100644 --- a/ci/tests/test.rs +++ b/ci/tests/test.rs @@ -55,10 +55,12 @@ fn parsing_test(major: u32, minor: u32, patch: u32, url_suffix: &str) { } macro_rules! test_version { - ($test_name:ident, $major:expr, $minor:expr, $patch:expr, $url_suffix:expr) => { - #[test] - fn $test_name() { - parsing_test($major, $minor, $patch, $url_suffix); + ($major:expr, $minor:expr, $patch:expr, $url_suffix:expr) => { + paste::paste! { + #[test] + fn [< test_v $major _ $minor _ $patch >] () { + parsing_test($major, $minor, $patch, $url_suffix); + } } }; } @@ -107,174 +109,196 @@ fn test_main_video() { } } -test_version! {test_v1_0_33, 1, 0, 33, "-core/src/spec"} -test_version! {test_v1_0_34, 1, 0, 34, "-core/src/spec"} -test_version! {test_v1_0_35, 1, 0, 35, "-core/src/spec"} -test_version! {test_v1_0_36, 1, 0, 36, "-core/src/spec"} -// test_version!{test_v1_1_37, 1, 0, 37, "-core/src/spec"} // no tag for v1.0.37 -test_version! {test_v1_0_38, 1, 0, 38, "-core/src/spec"} -test_version! {test_v1_0_39, 1, 0, 39, "-core/src/spec"} -test_version! {test_v1_0_40, 1, 0, 40, "-core/src/spec"} -test_version! {test_v1_0_41, 1, 0, 41, "-core/src/spec"} -test_version! {test_v1_0_42, 1, 0, 42, "-core/src/spec"} -test_version! {test_v1_0_43, 1, 0, 43, "-core/src/spec"} -test_version! {test_v1_0_44, 1, 0, 44, "-core/src/spec"} -test_version! {test_v1_0_45, 1, 0, 45, "-core/src/spec"} -test_version! {test_v1_0_46, 1, 0, 46, "-core/src/spec"} -test_version! {test_v1_0_47, 1, 0, 47, "-core/src/spec"} -test_version! {test_v1_0_48, 1, 0, 48, "-core/src/spec"} -test_version! {test_v1_0_49, 1, 0, 49, "-core/src/spec"} -test_version! {test_v1_0_50, 1, 0, 50, "-core/src/spec"} -test_version! {test_v1_0_51, 1, 0, 51, "-core/src/spec"} -// test_version!{test_v1_0_52, 1, 0, 52, "-core/src/spec"} // no tag for v1.0.52 -test_version! {test_v1_0_53, 1, 0, 53, "-core/src/spec"} -test_version! {test_v1_0_54, 1, 0, 54, "-core/src/spec"} -test_version! {test_v1_0_55, 1, 0, 55, "-core/src/spec"} -test_version! {test_v1_0_56, 1, 0, 56, "-core/src/spec"} -test_version! {test_v1_0_57, 1, 0, 57, "-core/src/spec"} -test_version! {test_v1_0_58, 1, 0, 58, "-core/src/spec"} -test_version! {test_v1_0_59, 1, 0, 59, "-core/src/spec"} -test_version! {test_v1_0_60, 1, 0, 60, "-core/src/spec"} -test_version! {test_v1_0_61, 1, 0, 61, "-core/src/spec"} -test_version! {test_v1_0_62, 1, 0, 62, "-core/src/spec"} -test_version! {test_v1_0_63, 1, 0, 63, "-core/src/spec"} -test_version! {test_v1_0_64, 1, 0, 64, "-core/src/spec"} -test_version! {test_v1_0_65, 1, 0, 65, "-core/src/spec"} -test_version! {test_v1_0_66, 1, 0, 66, "-core/src/spec"} -test_version! {test_v1_0_67, 1, 0, 67, "-core/src/spec"} -test_version! {test_v1_0_68, 1, 0, 68, "-core/src/spec"} -test_version! {test_v1_0_69, 1, 0, 69, "-core/src/spec"} -test_version! {test_v1_1_70, 1, 1, 70, "/src/spec"} -test_version! {test_v1_1_71, 1, 1, 71, "/src/spec"} -test_version! {test_v1_1_72, 1, 1, 72, "/xml"} -test_version! {test_v1_1_73, 1, 1, 73, "/xml"} -test_version! {test_v1_1_74, 1, 1, 74, "/xml"} -test_version! {test_v1_1_75, 1, 1, 75, "/xml"} -test_version! {test_v1_1_76, 1, 1, 76, "/xml"} -test_version! {test_v1_1_77, 1, 1, 77, "/xml"} -test_version! {test_v1_1_78, 1, 1, 78, "/xml"} -test_version! {test_v1_1_79, 1, 1, 79, "/xml"} -test_version! {test_v1_1_80, 1, 1, 80, "/xml"} -test_version! {test_v1_1_81, 1, 1, 81, "/xml"} -test_version! {test_v1_1_82, 1, 1, 82, "/xml"} -test_version! {test_v1_1_83, 1, 1, 83, "/xml"} -test_version! {test_v1_1_84, 1, 1, 84, "/xml"} -test_version! {test_v1_1_85, 1, 1, 85, "/xml"} -test_version! {test_v1_1_86, 1, 1, 86, "/xml"} -test_version! {test_v1_1_87, 1, 1, 87, "/xml"} -test_version! {test_v1_1_88, 1, 1, 88, "/xml"} -test_version! {test_v1_1_89, 1, 1, 89, "/xml"} -test_version! {test_v1_1_90, 1, 1, 90, "/xml"} -test_version! {test_v1_1_91, 1, 1, 91, "/xml"} -test_version! {test_v1_1_92, 1, 1, 92, "/xml"} -test_version! {test_v1_1_93, 1, 1, 93, "/xml"} -test_version! {test_v1_1_94, 1, 1, 94, "/xml"} -test_version! {test_v1_1_95, 1, 1, 95, "/xml"} -test_version! {test_v1_1_96, 1, 1, 96, "/xml"} -test_version! {test_v1_1_97, 1, 1, 97, "/xml"} -test_version! {test_v1_1_98, 1, 1, 98, "/xml"} -test_version! {test_v1_1_99, 1, 1, 99, "/xml"} -test_version! {test_v1_1_100, 1, 1, 100, "/xml"} -test_version! {test_v1_1_101, 1, 1, 101, "/xml"} -test_version! {test_v1_1_102, 1, 1, 102, "/xml"} -test_version! {test_v1_1_103, 1, 1, 103, "/xml"} -test_version! {test_v1_1_104, 1, 1, 104, "/xml"} -test_version! {test_v1_1_105, 1, 1, 105, "/xml"} -test_version! {test_v1_1_106, 1, 1, 106, "/xml"} -test_version! {test_v1_1_107, 1, 1, 107, "/xml"} -test_version! {test_v1_1_108, 1, 1, 108, "/xml"} -test_version! {test_v1_1_109, 1, 1, 109, "/xml"} -test_version! {test_v1_1_110, 1, 1, 110, "/xml"} -test_version! {test_v1_1_111, 1, 1, 111, "/xml"} -test_version! {test_v1_1_112, 1, 1, 112, "/xml"} -test_version! {test_v1_1_113, 1, 1, 113, "/xml"} -test_version! {test_v1_1_114, 1, 1, 114, "/xml"} -test_version! {test_v1_1_115, 1, 1, 115, "/xml"} -test_version! {test_v1_1_116, 1, 1, 116, "/xml"} -test_version! {test_v1_1_117, 1, 1, 117, "/xml"} -test_version! {test_v1_1_118, 1, 1, 118, "/xml"} -test_version! {test_v1_1_119, 1, 1, 119, "/xml"} -test_version! {test_v1_1_120, 1, 1, 120, "/xml"} -test_version! {test_v1_1_121, 1, 1, 121, "/xml"} -test_version! {test_v1_1_122, 1, 1, 122, "/xml"} -test_version! {test_v1_1_123, 1, 1, 123, "/xml"} -test_version! {test_v1_1_124, 1, 1, 124, "/xml"} -test_version! {test_v1_1_125, 1, 1, 125, "/xml"} -test_version! {test_v1_1_126, 1, 1, 126, "/xml"} -test_version! {test_v1_1_127, 1, 1, 127, "/xml"} -test_version! {test_v1_1_128, 1, 1, 128, "/xml"} -test_version! {test_v1_1_129, 1, 1, 129, "/xml"} -test_version! {test_v1_1_130, 1, 1, 130, "/xml"} -test_version! {test_v1_2_131, 1, 2, 131, "/xml"} -test_version! {test_v1_2_132, 1, 2, 132, "/xml"} -test_version! {test_v1_2_133, 1, 2, 133, "/xml"} -test_version! {test_v1_2_134, 1, 2, 134, "/xml"} -test_version! {test_v1_2_135, 1, 2, 135, "/xml"} -test_version! {test_v1_2_136, 1, 2, 136, "/xml"} -test_version! {test_v1_2_137, 1, 2, 137, "/xml"} -test_version! {test_v1_2_138, 1, 2, 138, "/xml"} -test_version! {test_v1_2_139, 1, 2, 139, "/xml"} -test_version! {test_v1_2_140, 1, 2, 140, "/xml"} -test_version! {test_v1_2_141, 1, 2, 141, "/xml"} -test_version! {test_v1_2_142, 1, 2, 142, "/xml"} -test_version! {test_v1_2_143, 1, 2, 143, "/xml"} -test_version! {test_v1_2_144, 1, 2, 144, "/xml"} -test_version! {test_v1_2_145, 1, 2, 145, "/xml"} -test_version! {test_v1_2_146, 1, 2, 146, "/xml"} -test_version! {test_v1_2_147, 1, 2, 147, "/xml"} -test_version! {test_v1_2_148, 1, 2, 148, "/xml"} -test_version! {test_v1_2_149, 1, 2, 149, "/xml"} -test_version! {test_v1_2_150, 1, 2, 150, "/xml"} -test_version! {test_v1_2_151, 1, 2, 151, "/xml"} -test_version! {test_v1_2_152, 1, 2, 152, "/xml"} -test_version! {test_v1_2_153, 1, 2, 153, "/xml"} -test_version! {test_v1_2_154, 1, 2, 154, "/xml"} -test_version! {test_v1_2_155, 1, 2, 155, "/xml"} -test_version! {test_v1_2_156, 1, 2, 156, "/xml"} -test_version! {test_v1_2_157, 1, 2, 157, "/xml"} -test_version! {test_v1_2_158, 1, 2, 158, "/xml"} -test_version! {test_v1_2_159, 1, 2, 159, "/xml"} -test_version! {test_v1_2_160, 1, 2, 160, "/xml"} -test_version! {test_v1_2_161, 1, 2, 161, "/xml"} -test_version! {test_v1_2_162, 1, 2, 162, "/xml"} -test_version! {test_v1_2_163, 1, 2, 163, "/xml"} -test_version! {test_v1_2_164, 1, 2, 164, "/xml"} -test_version! {test_v1_2_165, 1, 2, 165, "/xml"} -test_version! {test_v1_2_166, 1, 2, 166, "/xml"} -test_version! {test_v1_2_167, 1, 2, 167, "/xml"} -test_version! {test_v1_2_168, 1, 2, 168, "/xml"} -test_version! {test_v1_2_169, 1, 2, 169, "/xml"} -test_version! {test_v1_2_170, 1, 2, 170, "/xml"} -test_version! {test_v1_2_171, 1, 2, 171, "/xml"} -test_version! {test_v1_2_172, 1, 2, 172, "/xml"} -test_version! {test_v1_2_173, 1, 2, 173, "/xml"} -test_version! {test_v1_2_174, 1, 2, 174, "/xml"} -test_version! {test_v1_2_175, 1, 2, 175, "/xml"} -test_version! {test_v1_2_176, 1, 2, 176, "/xml"} -test_version! {test_v1_2_177, 1, 2, 177, "/xml"} -test_version! {test_v1_2_178, 1, 2, 178, "/xml"} -test_version! {test_v1_2_179, 1, 2, 179, "/xml"} -test_version! {test_v1_2_180, 1, 2, 180, "/xml"} -test_version! {test_v1_2_181, 1, 2, 181, "/xml"} -test_version! {test_v1_2_182, 1, 2, 182, "/xml"} -test_version! {test_v1_2_183, 1, 2, 183, "/xml"} -test_version! {test_v1_2_184, 1, 2, 184, "/xml"} -test_version! {test_v1_2_185, 1, 2, 185, "/xml"} -test_version! {test_v1_2_186, 1, 2, 186, "/xml"} -test_version! {test_v1_2_187, 1, 2, 187, "/xml"} -test_version! {test_v1_2_188, 1, 2, 188, "/xml"} -test_version! {test_v1_2_189, 1, 2, 189, "/xml"} -test_version! {test_v1_2_190, 1, 2, 190, "/xml"} -test_version! {test_v1_2_191, 1, 2, 191, "/xml"} -test_version! {test_v1_2_192, 1, 2, 192, "/xml"} -test_version! {test_v1_2_193, 1, 2, 193, "/xml"} -test_version! {test_v1_2_194, 1, 2, 194, "/xml"} -test_version! {test_v1_2_195, 1, 2, 195, "/xml"} -test_version! {test_v1_2_196, 1, 2, 196, "/xml"} -test_version! {test_v1_2_197, 1, 2, 197, "/xml"} -test_version! {test_v1_2_198, 1, 2, 198, "/xml"} -test_version! {test_v1_2_199, 1, 2, 199, "/xml"} -test_version! {test_v1_2_200, 1, 2, 200, "/xml"} -test_version! {test_v1_2_201, 1, 2, 201, "/xml"} -test_version! {test_v1_2_202, 1, 2, 202, "/xml"} -test_version! {test_v1_2_203, 1, 2, 203, "/xml"} +test_version! {1, 0, 33, "-core/src/spec"} +test_version! {1, 0, 34, "-core/src/spec"} +test_version! {1, 0, 35, "-core/src/spec"} +test_version! {1, 0, 36, "-core/src/spec"} +// test_version!{1, 0, 37, "-core/src/spec"} // no tag for v1.0.37 +test_version! {1, 0, 38, "-core/src/spec"} +test_version! {1, 0, 39, "-core/src/spec"} +test_version! {1, 0, 40, "-core/src/spec"} +test_version! {1, 0, 41, "-core/src/spec"} +test_version! {1, 0, 42, "-core/src/spec"} +test_version! {1, 0, 43, "-core/src/spec"} +test_version! {1, 0, 44, "-core/src/spec"} +test_version! {1, 0, 45, "-core/src/spec"} +test_version! {1, 0, 46, "-core/src/spec"} +test_version! {1, 0, 47, "-core/src/spec"} +test_version! {1, 0, 48, "-core/src/spec"} +test_version! {1, 0, 49, "-core/src/spec"} +test_version! {1, 0, 50, "-core/src/spec"} +test_version! {1, 0, 51, "-core/src/spec"} +// test_version!{1, 0, 52, "-core/src/spec"} // no tag for v1.0.52 +test_version! {1, 0, 53, "-core/src/spec"} +test_version! {1, 0, 54, "-core/src/spec"} +test_version! {1, 0, 55, "-core/src/spec"} +test_version! {1, 0, 56, "-core/src/spec"} +test_version! {1, 0, 57, "-core/src/spec"} +test_version! {1, 0, 58, "-core/src/spec"} +test_version! {1, 0, 59, "-core/src/spec"} +test_version! {1, 0, 60, "-core/src/spec"} +test_version! {1, 0, 61, "-core/src/spec"} +test_version! {1, 0, 62, "-core/src/spec"} +test_version! {1, 0, 63, "-core/src/spec"} +test_version! {1, 0, 64, "-core/src/spec"} +test_version! {1, 0, 65, "-core/src/spec"} +test_version! {1, 0, 66, "-core/src/spec"} +test_version! {1, 0, 67, "-core/src/spec"} +test_version! {1, 0, 68, "-core/src/spec"} +test_version! {1, 0, 69, "-core/src/spec"} +test_version! {1, 1, 70, "/src/spec"} +test_version! {1, 1, 71, "/src/spec"} +test_version! {1, 1, 72, "/xml"} +test_version! {1, 1, 73, "/xml"} +test_version! {1, 1, 74, "/xml"} +test_version! {1, 1, 75, "/xml"} +test_version! {1, 1, 76, "/xml"} +test_version! {1, 1, 77, "/xml"} +test_version! {1, 1, 78, "/xml"} +test_version! {1, 1, 79, "/xml"} +test_version! {1, 1, 80, "/xml"} +test_version! {1, 1, 81, "/xml"} +test_version! {1, 1, 82, "/xml"} +test_version! {1, 1, 83, "/xml"} +test_version! {1, 1, 84, "/xml"} +test_version! {1, 1, 85, "/xml"} +test_version! {1, 1, 86, "/xml"} +test_version! {1, 1, 87, "/xml"} +test_version! {1, 1, 88, "/xml"} +test_version! {1, 1, 89, "/xml"} +test_version! {1, 1, 90, "/xml"} +test_version! {1, 1, 91, "/xml"} +test_version! {1, 1, 92, "/xml"} +test_version! {1, 1, 93, "/xml"} +test_version! {1, 1, 94, "/xml"} +test_version! {1, 1, 95, "/xml"} +test_version! {1, 1, 96, "/xml"} +test_version! {1, 1, 97, "/xml"} +test_version! {1, 1, 98, "/xml"} +test_version! {1, 1, 99, "/xml"} +test_version! {1, 1, 100, "/xml"} +test_version! {1, 1, 101, "/xml"} +test_version! {1, 1, 102, "/xml"} +test_version! {1, 1, 103, "/xml"} +test_version! {1, 1, 104, "/xml"} +test_version! {1, 1, 105, "/xml"} +test_version! {1, 1, 106, "/xml"} +test_version! {1, 1, 107, "/xml"} +test_version! {1, 1, 108, "/xml"} +test_version! {1, 1, 109, "/xml"} +test_version! {1, 1, 110, "/xml"} +test_version! {1, 1, 111, "/xml"} +test_version! {1, 1, 112, "/xml"} +test_version! {1, 1, 113, "/xml"} +test_version! {1, 1, 114, "/xml"} +test_version! {1, 1, 115, "/xml"} +test_version! {1, 1, 116, "/xml"} +test_version! {1, 1, 117, "/xml"} +test_version! {1, 1, 118, "/xml"} +test_version! {1, 1, 119, "/xml"} +test_version! {1, 1, 120, "/xml"} +test_version! {1, 1, 121, "/xml"} +test_version! {1, 1, 122, "/xml"} +test_version! {1, 1, 123, "/xml"} +test_version! {1, 1, 124, "/xml"} +test_version! {1, 1, 125, "/xml"} +test_version! {1, 1, 126, "/xml"} +test_version! {1, 1, 127, "/xml"} +test_version! {1, 1, 128, "/xml"} +test_version! {1, 1, 129, "/xml"} +test_version! {1, 1, 130, "/xml"} +test_version! {1, 2, 131, "/xml"} +test_version! {1, 2, 132, "/xml"} +test_version! {1, 2, 133, "/xml"} +test_version! {1, 2, 134, "/xml"} +test_version! {1, 2, 135, "/xml"} +test_version! {1, 2, 136, "/xml"} +test_version! {1, 2, 137, "/xml"} +test_version! {1, 2, 138, "/xml"} +test_version! {1, 2, 139, "/xml"} +test_version! {1, 2, 140, "/xml"} +test_version! {1, 2, 141, "/xml"} +test_version! {1, 2, 142, "/xml"} +test_version! {1, 2, 143, "/xml"} +test_version! {1, 2, 144, "/xml"} +test_version! {1, 2, 145, "/xml"} +test_version! {1, 2, 146, "/xml"} +test_version! {1, 2, 147, "/xml"} +test_version! {1, 2, 148, "/xml"} +test_version! {1, 2, 149, "/xml"} +test_version! {1, 2, 150, "/xml"} +test_version! {1, 2, 151, "/xml"} +test_version! {1, 2, 152, "/xml"} +test_version! {1, 2, 153, "/xml"} +test_version! {1, 2, 154, "/xml"} +test_version! {1, 2, 155, "/xml"} +test_version! {1, 2, 156, "/xml"} +test_version! {1, 2, 157, "/xml"} +test_version! {1, 2, 158, "/xml"} +test_version! {1, 2, 159, "/xml"} +test_version! {1, 2, 160, "/xml"} +test_version! {1, 2, 161, "/xml"} +test_version! {1, 2, 162, "/xml"} +test_version! {1, 2, 163, "/xml"} +test_version! {1, 2, 164, "/xml"} +test_version! {1, 2, 165, "/xml"} +test_version! {1, 2, 166, "/xml"} +test_version! {1, 2, 167, "/xml"} +test_version! {1, 2, 168, "/xml"} +test_version! {1, 2, 169, "/xml"} +test_version! {1, 2, 170, "/xml"} +test_version! {1, 2, 171, "/xml"} +test_version! {1, 2, 172, "/xml"} +test_version! {1, 2, 173, "/xml"} +test_version! {1, 2, 174, "/xml"} +test_version! {1, 2, 175, "/xml"} +test_version! {1, 2, 176, "/xml"} +test_version! {1, 2, 177, "/xml"} +test_version! {1, 2, 178, "/xml"} +test_version! {1, 2, 179, "/xml"} +test_version! {1, 2, 180, "/xml"} +test_version! {1, 2, 181, "/xml"} +test_version! {1, 2, 182, "/xml"} +test_version! {1, 2, 183, "/xml"} +test_version! {1, 2, 184, "/xml"} +test_version! {1, 2, 185, "/xml"} +test_version! {1, 2, 186, "/xml"} +test_version! {1, 2, 187, "/xml"} +test_version! {1, 2, 188, "/xml"} +test_version! {1, 2, 189, "/xml"} +test_version! {1, 2, 190, "/xml"} +test_version! {1, 2, 191, "/xml"} +test_version! {1, 2, 192, "/xml"} +test_version! {1, 2, 193, "/xml"} +test_version! {1, 2, 194, "/xml"} +test_version! {1, 2, 195, "/xml"} +test_version! {1, 2, 196, "/xml"} +test_version! {1, 2, 197, "/xml"} +test_version! {1, 2, 198, "/xml"} +test_version! {1, 2, 199, "/xml"} +test_version! {1, 2, 200, "/xml"} +test_version! {1, 2, 201, "/xml"} +test_version! {1, 2, 202, "/xml"} +test_version! {1, 2, 203, "/xml"} +test_version! {1, 3, 204, "/xml"} +test_version! {1, 3, 205, "/xml"} +test_version! {1, 3, 206, "/xml"} +test_version! {1, 3, 207, "/xml"} +test_version! {1, 3, 208, "/xml"} +test_version! {1, 3, 209, "/xml"} +test_version! {1, 3, 210, "/xml"} +test_version! {1, 3, 211, "/xml"} +test_version! {1, 3, 212, "/xml"} +test_version! {1, 3, 213, "/xml"} +test_version! {1, 3, 214, "/xml"} +test_version! {1, 3, 215, "/xml"} +test_version! {1, 3, 216, "/xml"} +test_version! {1, 3, 217, "/xml"} +test_version! {1, 3, 218, "/xml"} +test_version! {1, 3, 219, "/xml"} +test_version! {1, 3, 220, "/xml"} +test_version! {1, 3, 221, "/xml"} +test_version! {1, 3, 222, "/xml"} +test_version! {1, 3, 223, "/xml"} +test_version! {1, 3, 224, "/xml"} +test_version! {1, 3, 225, "/xml"} From 0d3b1cf9139f522289c817b2b4965587ab753e48 Mon Sep 17 00:00:00 2001 From: Julia Tatz Date: Sat, 3 Sep 2022 02:39:06 -0400 Subject: [PATCH 08/17] is a catch-all tag, determine valid subsets Preparing to break it into a smaller enum without any unused fields Also stop blindly generating the code string with invalid C, it didn't handle the const ptr switch that offical python generator does --- vk-parse/src/convert.rs | 7 +- vk-parse/src/parse.rs | 275 +++++++++++++++++++--------------------- vk-parse/src/types.rs | 18 +-- 3 files changed, 136 insertions(+), 164 deletions(-) diff --git a/vk-parse/src/convert.rs b/vk-parse/src/convert.rs index 3408ddd..7499d13 100644 --- a/vk-parse/src/convert.rs +++ b/vk-parse/src/convert.rs @@ -188,14 +188,13 @@ impl From for Option { })) } TypeSpec::Include { name, quoted } => { - let name = t.name.or(name).unwrap_or_default(); let need_ext = !name.ends_with(".h"); let include = vkxml::Include { name, notation: t.comment, style: match quoted { - true => vkxml::IncludeStyle::Quote, - false => vkxml::IncludeStyle::Bracket, + Some(true) | None => vkxml::IncludeStyle::Quote, + Some(false) => vkxml::IncludeStyle::Bracket, }, need_ext, }; @@ -217,7 +216,7 @@ impl From for Option { is_disabled, comment, replace, - defref, + defref: defref.into_iter().collect(), parameters: Vec::new(), c_expression: None, value: None, diff --git a/vk-parse/src/parse.rs b/vk-parse/src/parse.rs index 498ac39..2d63556 100644 --- a/vk-parse/src/parse.rs +++ b/vk-parse/src/parse.rs @@ -135,7 +135,7 @@ macro_rules! match_elements { } macro_rules! match_elements_combine_text { - ( $ctx:expr, $attributes:ident, $buffer:ident, $($p:pat $(if $g:expr)? => $e:expr),+) => { + ( $ctx:expr, $attributes:ident, $buffer:ident, $($p:pat $(if $g:expr)? => $e:expr),+ $(,)?) => { while let Some(Ok(e)) = $ctx.events.next() { match e { XmlEvent::Characters(text) => $buffer.push_str(&text), @@ -165,7 +165,7 @@ macro_rules! match_elements_combine_text { } }; - ( $ctx:expr, $buffer:ident, $($p:pat => $e:expr),+) => { + ( $ctx:expr, $buffer:ident, $($p:pat => $e:expr),+ $(,)?) => { match_elements_combine_text!{ $ctx, _attributes, $buffer, $($p => $e),+ } }; } @@ -500,7 +500,6 @@ fn parse_array_shape_text(text: &str, shape_vec: &mut Vec) -> bool fn parse_name_with_type( ctx: &mut ParseCtx, - code: &mut String, len: Option, altlen: Option, externsync: Option, @@ -581,12 +580,10 @@ fn parse_name_with_type( ), }; let mut event = ctx.events.next(); - if let Some(Ok(XmlEvent::Whitespace(text))) = event { - code.push_str(&text); + if let Some(Ok(XmlEvent::Whitespace(_))) = event { event = ctx.events.next(); } let parsed_pre = if let Some(Ok(XmlEvent::Characters(text))) = event { - code.push_str(&text); event = ctx.events.next(); parse_pre_type_tag_text(&text) @@ -601,7 +598,6 @@ fn parse_name_with_type( })) if local_name == "type" => { ctx.push_element(&local_name); let text = parse_text_element(ctx); - code.push_str(&text); event = ctx.events.next(); text @@ -615,12 +611,10 @@ fn parse_name_with_type( } }; - if let Some(Ok(XmlEvent::Whitespace(text))) = event { - code.push_str(&text); + if let Some(Ok(XmlEvent::Whitespace(_))) = event { event = ctx.events.next(); } let pointer_kind = if let Some(Ok(XmlEvent::Characters(text))) = event { - code.push_str(&text); event = ctx.events.next(); let (kind, rest) = parse_post_type_tag_text(&text, parsed_pre); @@ -637,7 +631,6 @@ fn parse_name_with_type( })) if local_name == "name" => { ctx.push_element(&local_name); let text = parse_text_element(ctx); - code.push_str(&text); event = ctx.events.next(); text @@ -653,7 +646,6 @@ fn parse_name_with_type( let (bitfield_size, mut array_shape, mut hungry) = if let Some(Ok(XmlEvent::Characters(text))) = event { - code.push_str(&text); event = ctx.events.next(); let trimmed_text = text.trim(); @@ -689,7 +681,6 @@ fn parse_name_with_type( })) if local_name == "enum" => { ctx.push_element(&local_name); let text = parse_text_element(ctx); - code.push_str(&text); event = ctx.events.next(); array_shape_vec.push(ArrayLength::Constant(text)); @@ -704,7 +695,6 @@ fn parse_name_with_type( }; // if let Some(Ok(XmlEvent::Characters(text))) = event { - code.push_str(&text); event = ctx.events.next(); let trimmed_text = text @@ -723,8 +713,12 @@ fn parse_name_with_type( let mut comment = None; while let Some(Ok(e)) = event { match e { - XmlEvent::Whitespace(text) => code.push_str(&text), - XmlEvent::Characters(text) => code.push_str(&text), + XmlEvent::Whitespace(_) => {}, + XmlEvent::Characters(text) => { + if !text.trim().is_empty() { + todo!("wasn't prepared for characters {}", text) + } + }, XmlEvent::StartElement { name: elem_name, .. } => { @@ -932,7 +926,7 @@ fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option) -> TypeDefine { +fn process_define_code(code: String, name_: String, defref: Option) -> TypeDefine { fn consume_whitespace(chars: &mut std::str::Chars, mut current: Option) -> Option { while let Some(c) = current { if !c.is_whitespace() { @@ -1147,7 +1141,7 @@ fn process_define_code(code: String, name_: String, defref: Vec) -> Type State::DefineValue => { let v = String::from(chars.as_str().trim()); - if !defref.is_empty() { + if defref.is_some() { c_expr_.replace(v); } else { value_.replace(v); @@ -1185,6 +1179,51 @@ fn process_define_code(code: String, name_: String, defref: Vec) -> Type } } +fn parse_type_member(ctx: &mut ParseCtx, attributes: Vec) -> Option> { + let mut len = None; + let mut altlen = None; + let mut externsync = None; + let mut optional = None; + let mut selector = None; + let mut selection = None; + let mut noautovalidity = None; + let mut validextensionstructs = None; + let mut values = None; + let mut limittype = None; + let mut objecttype = None; + match_attributes!{ctx, a in attributes, + "len" => len = Some(a.value), + "altlen" => altlen = Some(a.value), + "externsync" => externsync = Some(a.value), + "optional" => optional = Some(a.value), + "selector" => selector = Some(a.value), + "selection" => selection = Some(a.value), + "noautovalidity" => noautovalidity = Some(a.value), + "validextensionstructs" => validextensionstructs = Some(a.value), + "values" => values = Some(a.value), + "limittype" => limittype = Some(a.value), + "objecttype" => objecttype = Some(a.value) + } + parse_name_with_type( + ctx, + len, + altlen, + externsync, + optional, + noautovalidity, + objecttype + ).map(|definition| + Box::new(TypeMemberDefinition { + selector, + selection, + validextensionstructs, + values, + limittype, + definition, + }) + ) +} + fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> TypesChild { let mut api = None; let mut alias = None; @@ -1200,7 +1239,6 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> let mut comment = None; let mut code = String::new(); - let mut markup = Vec::new(); let mut members = Vec::new(); match_attributes! {ctx, a in attributes, @@ -1218,138 +1256,104 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> "comment" => comment = Some(a.value) } + assert!(api.is_none()); + if alias.is_some() { + assert!(matches!(category.as_deref(), None | Some("bitmask" | "handle" | "enum" | "struct")), "category was {:?}", category); + } + if requires.is_some() { + assert!(matches!(category.as_deref(), None | Some("define" | "funcpointer" | "bitmask")) || (matches!(category.as_deref(), Some("struct")) && name.as_deref() == Some("StdVideoDecodeH264PictureInfo")), "category was {:?}", category); + } + if name.is_some() { + assert!(matches!(category.as_deref(), None | Some("enum" | "struct" | "union" | "include" | "define")) || (matches!(category.as_deref(), Some("bitmask" | "handle")) && alias.is_some()), "category was {:?}; name was {:?}", category, name); + } + if parent.is_some() { + assert_eq!(category.as_deref(), Some("handle"), "category was {:?}", category); + } + if returnedonly.is_some() { + assert!(matches!(category.as_deref(), Some("struct" | "union")), "category was {:?}", category); + } + if structextends.is_some() { + assert!(matches!(category.as_deref(), Some("struct")), "category was {:?}", category); + } + if allowduplicate.is_some() { + assert!(matches!(category.as_deref(), Some("struct")), "category was {:?}", category); + } + if objtypeenum.is_some() { + assert!(matches!(category.as_deref(), Some("handle")), "category was {:?}", category); + } + if bitvalues.is_some() { + assert!(requires.is_none()); + assert!(matches!(category.as_deref(), Some("bitmask")), "category was {:?}", category); + } + let fn_ptr_spec = if let Some("funcpointer") = category.as_deref() { - parse_type_funcptr(ctx) + let fn_ptr_spec = parse_type_funcptr(ctx); + name = fn_ptr_spec.as_ref().map(|fp| fp.proto.name.clone()); + fn_ptr_spec } else { None }; + // support versions older than 1.0.70 where was found inside + const SUPPORT_OLD: bool = true; let has_members = matches!(category.as_deref(), Some("struct" | "union")); + let is_empty_tag = matches!(category.as_deref(), Some("enum")) || alias.is_some(); + let is_empty_or_txt_tag = is_empty_tag || (matches!(category.as_deref(), Some("include")) && !SUPPORT_OLD); + let has_inner_tags = !is_empty_or_txt_tag; + let mut name_tag = None; + let mut type_tag = None; match_elements_combine_text! {ctx, attributes, code, - "member" if has_members => { - let mut len = None; - let mut altlen = None; - let mut externsync = None; - let mut optional = None; - let mut selector = None; - let mut selection = None; - let mut noautovalidity = None; - let mut validextensionstructs = None; - let mut values = None; - let mut limittype = None; - let mut objecttype = None; - let mut code = String::new(); - match_attributes!{ctx, a in attributes, - "len" => len = Some(a.value), - "altlen" => altlen = Some(a.value), - "externsync" => externsync = Some(a.value), - "optional" => optional = Some(a.value), - "selector" => selector = Some(a.value), - "selection" => selection = Some(a.value), - "noautovalidity" => noautovalidity = Some(a.value), - "validextensionstructs" => validextensionstructs = Some(a.value), - "values" => values = Some(a.value), - "limittype" => limittype = Some(a.value), - "objecttype" => objecttype = Some(a.value) + "member" if has_members && has_inner_tags => { + if let Some(defn) = parse_type_member(ctx, attributes) { + members.push(TypeMember::Definition(defn)); } - if let Some(definition) = parse_name_with_type( - ctx, &mut code, - len, - altlen, - externsync, - optional, - noautovalidity, - objecttype - ) { - members.push(TypeMember::Definition(Box::new(TypeMemberDefinition { - selector, - selection, - validextensionstructs, - values, - limittype, - code, - definition, - }))) - } }, - "comment" if has_members => members.push(TypeMember::Comment(parse_text_element(ctx))), - "name" => { + "comment" if has_members && has_inner_tags => members.push(TypeMember::Comment(parse_text_element(ctx))), + "name" if !has_members && has_inner_tags => { let text = parse_text_element(ctx); code.push_str(&text); - markup.push(TypeCodeMarkup::Name(text)); + name_tag.replace(text); }, - "type" => { + "type" if !has_members && has_inner_tags => { let text = parse_text_element(ctx); code.push_str(&text); - markup.push(TypeCodeMarkup::Type(text)); + type_tag.replace(text); }, - "apientry" => { - let text = parse_text_element(ctx); - code.push_str(&text); - markup.push(TypeCodeMarkup::ApiEntry(text)); - } + "apientry" if has_inner_tags => consume_current_element(ctx), } + assert!(!(name.is_some() && name_tag.is_some())); + let name = name.or(name_tag); + assert!(name.is_some(), "category was {:?}", category); let spec = match category.as_deref() { Some("include") => TypeSpec::Include { - name: markup.iter().find_map(|m| match m { - TypeCodeMarkup::Name(name) => Some(name.clone()), - _ => None, - }), - quoted: !code.contains('<'), + name: name.clone().unwrap(), + quoted: if code.is_empty() { None } else { Some(!code.contains('<')) }, }, Some("define") => { - let name_ = name - .clone() - .or_else(|| { - markup.iter().find_map(|m| match m { - TypeCodeMarkup::Name(name) => Some(name.clone()), - _ => None, - }) - }) - .unwrap(); - let defref = markup - .iter() - .filter_map(|m| match m { - TypeCodeMarkup::Type(ty) => Some(ty.clone()), - _ => None, - }) - .collect(); + let name_ = name.clone().unwrap(); + let defref = type_tag; TypeSpec::Define(process_define_code(code, name_, defref)) } Some("basetype") => TypeSpec::Typedef { - name: markup - .iter() - .find_map(|m| match m { - TypeCodeMarkup::Name(name) => Some(name.clone()), - _ => None, - }) - .unwrap(), - basetype: markup.iter().find_map(|m| match m { - TypeCodeMarkup::Type(ty) => Some(ty.clone()), - _ => None, - }), + name: name.clone().unwrap(), + basetype: type_tag, }, Some("bitmask") => { - if name.is_some() || alias.is_some() { + if let Some(req) = requires.as_deref() { + assert_eq!(req.split_once("FlagBits"), name.as_deref().unwrap().split_once("Flags")) + }; + if let Some(vals) = bitvalues.as_deref() { + assert_eq!(vals.split_once("FlagBits"), name.as_deref().unwrap().split_once("Flags")) + }; + if alias.is_some() { + assert!(requires.is_none() && bitvalues.is_none()); TypeSpec::Bitmask(None) } else { TypeSpec::Bitmask(Some(NameWithType { - name: markup - .iter() - .find_map(|m| match m { - TypeCodeMarkup::Name(name) => Some(name.clone()), - _ => None, - }) - .unwrap(), - type_name: markup - .iter() - .find_map(|m| match m { - TypeCodeMarkup::Type(ty) => Some(ty.clone()), - _ => None, - }) - .unwrap(), + name: name.clone().unwrap(), + type_name: type_tag.unwrap(), pointer_kind: None, is_struct: false, bitfield_size: None, @@ -1364,24 +1368,12 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> } } Some("handle") => { - if name.is_some() || alias.is_some() { + if alias.is_some() { TypeSpec::None } else { TypeSpec::Handle(TypeHandle { - name: markup - .iter() - .find_map(|m| match m { - TypeCodeMarkup::Name(name) => Some(name.clone()), - _ => None, - }) - .unwrap(), - handle_type: match markup - .iter() - .find_map(|m| match m { - TypeCodeMarkup::Type(val) => Some(val.as_str()), - _ => None, - }) - .unwrap() + name: name.clone().unwrap(), + handle_type: match type_tag.as_deref().unwrap() { "VK_DEFINE_HANDLE" => HandleType::Dispatch, "VK_DEFINE_NON_DISPATCHABLE_HANDLE" => HandleType::NoDispatch, @@ -1444,7 +1436,6 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) consume_current_element(ctx); Some(Command::Alias { alias, name }) } else { - let mut code = String::new(); let mut proto = None; let mut params = Vec::new(); let mut description = None; @@ -1452,8 +1443,7 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) match_elements! {ctx, attributes, "proto" => { - proto = parse_name_with_type(ctx, &mut code, None, None, None, None, None, None); - code.push('('); + proto = parse_name_with_type(ctx, None, None, None, None, None, None); }, "param" => { @@ -1480,10 +1470,7 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) |structs| structs.split(',').map(|s| s.to_owned()).collect() ); - if !params.is_empty() { - code.push_str(", "); - } - if let Some(definition) = parse_name_with_type(ctx, &mut code, + if let Some(definition) = parse_name_with_type(ctx, len, altlen, externsync, @@ -1512,7 +1499,6 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) } } } - code.push_str(");"); let proto = if let Some(v) = proto { v @@ -1538,7 +1524,6 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) alias, description, implicitexternsyncparams, - code, }))) } } diff --git a/vk-parse/src/types.rs b/vk-parse/src/types.rs index af6895c..a82be87 100644 --- a/vk-parse/src/types.rs +++ b/vk-parse/src/types.rs @@ -273,8 +273,8 @@ pub struct Type { pub enum TypeSpec { None, Include { - name: Option, - quoted: bool, + name: String, + quoted: Option, }, Define(TypeDefine), Typedef { @@ -351,12 +351,6 @@ pub struct TypeMemberDefinition { )] pub limittype: Option, - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub code: String, - /// The definition of this member. #[cfg_attr( feature = "serialize", @@ -401,7 +395,7 @@ pub enum HandleType { pub struct TypeDefine { pub name: String, pub comment: Option, - pub defref: Vec, + pub defref: Option, pub is_disabled: bool, pub replace: bool, pub value: TypeDefineValue, @@ -725,12 +719,6 @@ pub struct CommandDefinition { serde(default, skip_serializing_if = "is_default") )] pub implicitexternsyncparams: Vec, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub code: String, } /// Parameter for this Vulkan function. From fa29866a475583387caff458b5b0f467358fb64a Mon Sep 17 00:00:00 2001 From: Julia Tatz Date: Sat, 3 Sep 2022 04:20:13 -0400 Subject: [PATCH 09/17] v1 of type refactoring --- vk-parse/src/convert.rs | 318 ++++++++++++++++++++++------------------ vk-parse/src/parse.rs | 300 ++++++++++++++++++++++++------------- vk-parse/src/types.rs | 207 ++++++++++++++------------ 3 files changed, 485 insertions(+), 340 deletions(-) diff --git a/vk-parse/src/convert.rs b/vk-parse/src/convert.rs index 7499d13..ecf5526 100644 --- a/vk-parse/src/convert.rs +++ b/vk-parse/src/convert.rs @@ -177,165 +177,178 @@ impl From for Option { fn from(orig: TypesChild) -> Self { match orig { TypesChild::Comment(text) => Some(vkxml::DefinitionsElement::Notation(text)), - TypesChild::Type(t) => { - match t.spec { - TypeSpec::None => { - let name = t.name.unwrap_or_default(); - Some(vkxml::DefinitionsElement::Reference(vkxml::Reference { - name, - notation: t.comment, - include: t.requires, - })) - } - TypeSpec::Include { name, quoted } => { - let need_ext = !name.ends_with(".h"); - let include = vkxml::Include { - name, - notation: t.comment, - style: match quoted { - Some(true) | None => vkxml::IncludeStyle::Quote, - Some(false) => vkxml::IncludeStyle::Bracket, - }, - need_ext, - }; + TypesChild::Type { + definition, + comment: notation, + } => match *definition { + TypeDefinition::None { + name, + requires: include, + } => Some(vkxml::DefinitionsElement::Reference(vkxml::Reference { + name, + notation, + include, + })), + TypeDefinition::Include { name, quoted } => { + let need_ext = !name.ends_with(".h"); + let include = vkxml::Include { + name, + notation, + style: match quoted { + Some(true) | None => vkxml::IncludeStyle::Quote, + Some(false) => vkxml::IncludeStyle::Bracket, + }, + need_ext, + }; - Some(vkxml::DefinitionsElement::Include(include)) - } + Some(vkxml::DefinitionsElement::Include(include)) + } - TypeSpec::Define(TypeDefine { + TypeDefinition::Define(TypeDefine { + name, + value, + comment, + defref, + is_disabled, + replace, + .. + }) => { + let mut define = vkxml::Define { name, - value, - comment, - defref, + notation, is_disabled, + comment, replace, - }) => { - let mut define = vkxml::Define { - name, - notation: t.comment, - is_disabled, - comment, - replace, - defref: defref.into_iter().collect(), - parameters: Vec::new(), - c_expression: None, - value: None, - }; - match value { - TypeDefineValue::Empty => {} - TypeDefineValue::Value(v) => { - define.value = Some(v); - } - TypeDefineValue::Expression(e) => { - define.c_expression = Some(e); - } - TypeDefineValue::Function { params, expression } => { - define.parameters = params; - define.c_expression = Some(expression); - } + defref: defref.into_iter().collect(), + parameters: Vec::new(), + c_expression: None, + value: None, + }; + match value { + TypeDefineValue::Empty => {} + TypeDefineValue::Value(v) => { + define.value = Some(v); + } + TypeDefineValue::Expression(e) => { + define.c_expression = Some(e); + } + TypeDefineValue::Function { params, expression } => { + define.parameters = params; + define.c_expression = Some(expression); } - Some(vkxml::DefinitionsElement::Define(define)) } + Some(vkxml::DefinitionsElement::Define(define)) + } - TypeSpec::Typedef { name, basetype } => { - let typedef = vkxml::Typedef { - name, - notation: t.comment, - basetype: basetype.unwrap_or_default(), - }; - Some(vkxml::DefinitionsElement::Typedef(typedef)) - } + TypeDefinition::Typedef { name, basetype } => { + let typedef = vkxml::Typedef { + name, + notation, + basetype: basetype.unwrap_or_default(), + }; + Some(vkxml::DefinitionsElement::Typedef(typedef)) + } - TypeSpec::Bitmask(None) => { + TypeDefinition::Bitmask(TypeBitmask::Alias { .. }) => None, + TypeDefinition::Bitmask(TypeBitmask::Definition { + definition, + has_bitvalues, + }) => { + let NameWithType { + type_name, name, .. + } = *definition; + let enumref = if has_bitvalues { + Some(name.replacen("Flags", "FlagBits", 1)) + } else { None - } - TypeSpec::Bitmask(Some(name_val)) => { - let NameWithType { - type_name, name, .. - } = name_val; - let bitmask = vkxml::Bitmask { - name, - notation: t.comment, - basetype: type_name, - enumref: t.requires, - }; - Some(vkxml::DefinitionsElement::Bitmask(bitmask)) - } - - TypeSpec::Handle(TypeHandle { name, handle_type }) => { - let handle = vkxml::Handle { - name, - notation: t.comment, - parent: t.parent, - ty: match handle_type { - HandleType::Dispatch => vkxml::HandleType::Dispatch, - HandleType::NoDispatch => vkxml::HandleType::NoDispatch, - }, - }; - Some(vkxml::DefinitionsElement::Handle(handle)) - } - - TypeSpec::Enumeration => { - // if alias.is_some() { - // return None; - // } - Some(vkxml::DefinitionsElement::Enumeration( - vkxml::EnumerationDeclaration { - name: t.name.unwrap_or_default(), - notation: t.comment, - }, - )) - } + }; + let bitmask = vkxml::Bitmask { + name, + notation, + basetype: type_name, + enumref, + }; + Some(vkxml::DefinitionsElement::Bitmask(bitmask)) + } + TypeDefinition::Handle(TypeHandle::Alias { .. }) => None, + TypeDefinition::Handle(TypeHandle::Definition { + name, + handle_type, + parent, + .. + }) => { + let handle = vkxml::Handle { + name, + notation, + parent, + ty: match handle_type { + HandleType::Dispatch => vkxml::HandleType::Dispatch, + HandleType::NoDispatch => vkxml::HandleType::NoDispatch, + }, + }; + Some(vkxml::DefinitionsElement::Handle(handle)) + } - TypeSpec::FunctionPointer(TypeFunctionPointer { - proto: defn, - params, - }) => { - Some(vkxml::DefinitionsElement::FuncPtr(vkxml::FunctionPointer { - name: defn.name.clone(), - notation: t.comment, - return_type: defn.into(), - param: params.into_iter().map(|p| p.into()).collect(), - })) + TypeDefinition::Enumeration { alias: Some(_), .. } => None, + TypeDefinition::Enumeration { name, alias: None } => { + Some(vkxml::DefinitionsElement::Enumeration( + vkxml::EnumerationDeclaration { name, notation }, + )) + } + TypeDefinition::FunctionPointer(TypeFunctionPointer { + proto: defn, + params, + .. + }) => Some(vkxml::DefinitionsElement::FuncPtr(vkxml::FunctionPointer { + name: defn.name.clone(), + notation, + return_type: defn.into(), + param: params.into_iter().map(|p| p.into()).collect(), + })), + TypeDefinition::Struct(TypeStruct::Alias { .. }) => None, + TypeDefinition::Struct(TypeStruct::Definition { + name, + members, + returned_only, + struct_extends, + .. + }) => { + let mut s = vkxml::Struct { + name, + notation, + is_return: returned_only.is_some(), + extends: if struct_extends.is_empty() { + None + } else { + Some(struct_extends.join(",")) + }, + elements: Vec::new(), + }; + for member in members { + s.elements.push(member.into()); } - TypeSpec::Struct(members) => { - if t.alias.is_some() { - return None; - } - let mut s = vkxml::Struct { - name: t.name.unwrap_or_default(), - notation: t.comment, - is_return: t.returnedonly.unwrap_or_default().as_str() == "true", - extends: t.structextends, - elements: Vec::new(), - }; - for member in members { - s.elements.push(member.into()); - } - - Some(vkxml::DefinitionsElement::Struct(s)) - } + Some(vkxml::DefinitionsElement::Struct(s)) + } - TypeSpec::Union(members) => { - let mut u = vkxml::Union { - name: t.name.unwrap_or_default(), - notation: t.comment, - elements: Vec::new(), - }; - for member in members { - match member { - TypeMember::Comment(..) => (), - TypeMember::Definition(def) => { - u.elements.push(def.definition.into()); - } + TypeDefinition::Union(TypeUnion { name, members, .. }) => { + let mut u = vkxml::Union { + name, + notation, + elements: Vec::new(), + }; + for member in members { + match member { + TypeMember::Comment(..) => (), + TypeMember::Definition(def) => { + u.elements.push(def.definition.into()); } } - - Some(vkxml::DefinitionsElement::Union(u)) } + + Some(vkxml::DefinitionsElement::Union(u)) } - } + }, } } } @@ -591,11 +604,30 @@ impl From for vkxml::Extensions { impl From for vkxml::Extension { fn from(orig: Extension) -> Self { - let Extension { name, comment, number, protect, platform: _, author, contact, ext_type, requires, requires_core: _, supported, deprecatedby: _, promotedto: _, obsoletedby: _, provisional: _, specialuse: _, sortorder: _, children } = orig; + let Extension { + name, + comment, + number, + protect, + platform: _, + author, + contact, + ext_type, + requires, + requires_core: _, + supported, + deprecatedby: _, + promotedto: _, + obsoletedby: _, + provisional: _, + specialuse: _, + sortorder: _, + children, + } = orig; let mut disabled = false; let mut match_api = None; - if let Some(text) = supported{ + if let Some(text) = supported { if text == "disabled" { disabled = true; } else { diff --git a/vk-parse/src/parse.rs b/vk-parse/src/parse.rs index 2d63556..cdc3b0e 100644 --- a/vk-parse/src/parse.rs +++ b/vk-parse/src/parse.rs @@ -713,12 +713,12 @@ fn parse_name_with_type( let mut comment = None; while let Some(Ok(e)) = event { match e { - XmlEvent::Whitespace(_) => {}, + XmlEvent::Whitespace(_) => {} XmlEvent::Characters(text) => { if !text.trim().is_empty() { todo!("wasn't prepared for characters {}", text) } - }, + } XmlEvent::StartElement { name: elem_name, .. } => { @@ -786,7 +786,10 @@ fn parse_name_with_type( }) } -fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option { +fn parse_type_funcptr( + ctx: &mut ParseCtx, + requires: Option, +) -> Option { let (type_name, pointer_kind) = if let Some(Ok(XmlEvent::Characters(text))) = ctx.events.next() { let trimmed_text = text.trim(); @@ -816,7 +819,6 @@ fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option { ctx.push_element(&local_name); - parse_text_element(ctx) } @@ -853,6 +855,7 @@ fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option(ctx: &mut ParseCtx) -> Option { ctx.push_element(&local_name); - parse_text_element(ctx) } @@ -923,10 +925,16 @@ fn parse_type_funcptr(ctx: &mut ParseCtx) -> Option) -> TypeDefine { +fn process_define_code( + code: String, + name_: String, + defref: Option, + requires: Option, +) -> TypeDefine { fn consume_whitespace(chars: &mut std::str::Chars, mut current: Option) -> Option { while let Some(c) = current { if !c.is_whitespace() { @@ -1173,13 +1181,17 @@ fn process_define_code(code: String, name_: String, defref: Option) -> T name: name_, comment: comment_, defref, + requires, is_disabled, replace, value, } } -fn parse_type_member(ctx: &mut ParseCtx, attributes: Vec) -> Option> { +fn parse_type_member( + ctx: &mut ParseCtx, + attributes: Vec, +) -> Option> { let mut len = None; let mut altlen = None; let mut externsync = None; @@ -1191,7 +1203,7 @@ fn parse_type_member(ctx: &mut ParseCtx, attributes: Vec len = Some(a.value), "altlen" => altlen = Some(a.value), "externsync" => externsync = Some(a.value), @@ -1211,8 +1223,9 @@ fn parse_type_member(ctx: &mut ParseCtx, attributes: Vec(ctx: &mut ParseCtx, attributes: Vec(ctx: &mut ParseCtx, attributes: Vec) -> TypesChild { @@ -1258,47 +1271,114 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> assert!(api.is_none()); if alias.is_some() { - assert!(matches!(category.as_deref(), None | Some("bitmask" | "handle" | "enum" | "struct")), "category was {:?}", category); + assert!( + matches!( + category.as_deref(), + Some("bitmask" | "handle" | "enum" | "struct") + ), + "category was {:?}", + category + ); } if requires.is_some() { - assert!(matches!(category.as_deref(), None | Some("define" | "funcpointer" | "bitmask")) || (matches!(category.as_deref(), Some("struct")) && name.as_deref() == Some("StdVideoDecodeH264PictureInfo")), "category was {:?}", category); + assert!( + matches!( + category.as_deref(), + None | Some("define" | "funcpointer" | "bitmask") + ) || (matches!(category.as_deref(), Some("struct")) + && name.as_deref() == Some("StdVideoDecodeH264PictureInfo")), + "category was {:?}", + category + ); } if name.is_some() { - assert!(matches!(category.as_deref(), None | Some("enum" | "struct" | "union" | "include" | "define")) || (matches!(category.as_deref(), Some("bitmask" | "handle")) && alias.is_some()), "category was {:?}; name was {:?}", category, name); + assert!( + matches!( + category.as_deref(), + None | Some("enum" | "struct" | "union" | "include" | "define") + ) || (matches!(category.as_deref(), Some("bitmask" | "handle")) && alias.is_some()), + "category was {:?}; name was {:?}", + category, + name + ); } if parent.is_some() { - assert_eq!(category.as_deref(), Some("handle"), "category was {:?}", category); + assert_eq!( + category.as_deref(), + Some("handle"), + "category was {:?}", + category + ); } if returnedonly.is_some() { - assert!(matches!(category.as_deref(), Some("struct" | "union")), "category was {:?}", category); + assert!( + matches!(category.as_deref(), Some("struct" | "union")), + "category was {:?}", + category + ); } if structextends.is_some() { - assert!(matches!(category.as_deref(), Some("struct")), "category was {:?}", category); + assert!( + matches!(category.as_deref(), Some("struct")), + "category was {:?}", + category + ); } if allowduplicate.is_some() { - assert!(matches!(category.as_deref(), Some("struct")), "category was {:?}", category); + assert!( + matches!(category.as_deref(), Some("struct")), + "category was {:?}", + category + ); } if objtypeenum.is_some() { - assert!(matches!(category.as_deref(), Some("handle")), "category was {:?}", category); + assert!( + matches!(category.as_deref(), Some("handle")), + "category was {:?}", + category + ); } if bitvalues.is_some() { assert!(requires.is_none()); - assert!(matches!(category.as_deref(), Some("bitmask")), "category was {:?}", category); - } + assert!( + matches!(category.as_deref(), Some("bitmask")), + "category was {:?}", + category + ); + } + // returned_only + let returned_only = returnedonly.as_deref().map(|s| match s { + "true" => (), + _ => unreachable!( + "Expected the `returnedonly` attribute to either be missing or always `true`" + ), + }); + let struct_extends = + structextends.map(|s| s.split(',').map(|s| s.to_string()).collect::>()); + let allow_duplicate = allowduplicate.as_deref().map(|s| match s { + "true" => true, + "false" => false, + _ => unreachable!( + "Expected the `allowduplicate` attribute to either be missing, `true`, or `false`" + ), + }); - let fn_ptr_spec = if let Some("funcpointer") = category.as_deref() { - let fn_ptr_spec = parse_type_funcptr(ctx); - name = fn_ptr_spec.as_ref().map(|fp| fp.proto.name.clone()); - fn_ptr_spec - } else { - None - }; + if let Some("funcpointer") = category.as_deref() { + let fn_ptr_spec = parse_type_funcptr(ctx, requires).unwrap(); + // might want to ensure the next event is the tag end + consume_current_element(ctx); + return TypesChild::Type { + definition: Box::new(TypeDefinition::FunctionPointer(fn_ptr_spec)), + comment, + }; + } // support versions older than 1.0.70 where was found inside const SUPPORT_OLD: bool = true; let has_members = matches!(category.as_deref(), Some("struct" | "union")); let is_empty_tag = matches!(category.as_deref(), Some("enum")) || alias.is_some(); - let is_empty_or_txt_tag = is_empty_tag || (matches!(category.as_deref(), Some("include")) && !SUPPORT_OLD); + let is_empty_or_txt_tag = + is_empty_tag || (matches!(category.as_deref(), Some("include")) && !SUPPORT_OLD); let has_inner_tags = !is_empty_or_txt_tag; let mut name_tag = None; let mut type_tag = None; @@ -1325,85 +1405,95 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> assert!(!(name.is_some() && name_tag.is_some())); let name = name.or(name_tag); assert!(name.is_some(), "category was {:?}", category); - - let spec = match category.as_deref() { - Some("include") => TypeSpec::Include { - name: name.clone().unwrap(), - quoted: if code.is_empty() { None } else { Some(!code.contains('<')) }, - }, - Some("define") => { - let name_ = name.clone().unwrap(); - let defref = type_tag; - TypeSpec::Define(process_define_code(code, name_, defref)) - } - Some("basetype") => TypeSpec::Typedef { - name: name.clone().unwrap(), - basetype: type_tag, - }, - Some("bitmask") => { - if let Some(req) = requires.as_deref() { - assert_eq!(req.split_once("FlagBits"), name.as_deref().unwrap().split_once("Flags")) - }; - if let Some(vals) = bitvalues.as_deref() { - assert_eq!(vals.split_once("FlagBits"), name.as_deref().unwrap().split_once("Flags")) - }; - if alias.is_some() { - assert!(requires.is_none() && bitvalues.is_none()); - TypeSpec::Bitmask(None) - } else { - TypeSpec::Bitmask(Some(NameWithType { - name: name.clone().unwrap(), - type_name: type_tag.unwrap(), - pointer_kind: None, - is_struct: false, - bitfield_size: None, - array_shape: None, - dynamic_shape: None, - externsync: None, - optional: None, - noautovalidity: None, - objecttype: None, - comment: None, - })) + let name = name.unwrap(); + + TypesChild::Type { + definition: Box::new(match category.as_deref() { + Some("include") => TypeDefinition::Include { + name, + quoted: if code.is_empty() { + None + } else { + Some(!code.contains('<')) + }, + }, + Some("define") => { + let defref = type_tag; + TypeDefinition::Define(process_define_code(code, name, defref, requires)) } - } - Some("handle") => { - if alias.is_some() { - TypeSpec::None - } else { - TypeSpec::Handle(TypeHandle { - name: name.clone().unwrap(), - handle_type: match type_tag.as_deref().unwrap() - { - "VK_DEFINE_HANDLE" => HandleType::Dispatch, - "VK_DEFINE_NON_DISPATCHABLE_HANDLE" => HandleType::NoDispatch, - h => unreachable!("Unexpected handle type {:?}", h), - }, - }) + Some("basetype") => TypeDefinition::Typedef { + name, + basetype: type_tag, + }, + Some("bitmask") => { + if let Some(req) = requires.as_deref() { + assert_eq!(req.split_once("FlagBits"), name.split_once("Flags")) + }; + if let Some(vals) = bitvalues.as_deref() { + assert_eq!(vals.split_once("FlagBits"), name.split_once("Flags")) + }; + if let Some(alias) = alias { + assert!(requires.is_none() && bitvalues.is_none()); + TypeDefinition::Bitmask(TypeBitmask::Alias { name, alias }) + } else { + TypeDefinition::Bitmask(TypeBitmask::Definition { + definition: Box::new(NameWithType { + name, + type_name: type_tag.unwrap(), + pointer_kind: None, + is_struct: false, + bitfield_size: None, + array_shape: None, + dynamic_shape: None, + externsync: None, + optional: None, + noautovalidity: None, + objecttype: None, + comment: None, + }), + has_bitvalues: requires.is_some() || bitvalues.is_some(), + }) + } } - } - Some("enum") => TypeSpec::Enumeration, - Some("funcpointer") => TypeSpec::FunctionPointer(fn_ptr_spec.unwrap()), - Some("struct") => TypeSpec::Struct(members), - Some("union") => TypeSpec::Union(members), - None => TypeSpec::None, - Some(c) => unreachable!("Unexpected category of type {:?}", c), - }; - - TypesChild::Type(Box::new(Type { - api, - alias, - requires, - name, - parent, - returnedonly, - structextends, - allowduplicate, - objtypeenum, - bitvalues, + Some("handle") => { + if let Some(alias) = alias { + TypeDefinition::Handle(TypeHandle::Alias { name, alias }) + } else { + TypeDefinition::Handle(TypeHandle::Definition { + name, + handle_type: match type_tag.as_deref().unwrap() { + "VK_DEFINE_HANDLE" => HandleType::Dispatch, + "VK_DEFINE_NON_DISPATCHABLE_HANDLE" => HandleType::NoDispatch, + h => unreachable!("Unexpected handle type {:?}", h), + }, + // only required starting in version 164 + objtypeenum: objtypeenum.unwrap_or_default(), + parent, + }) + } + } + Some("enum") => TypeDefinition::Enumeration { name, alias }, + Some("struct") => TypeDefinition::Struct(if let Some(alias) = alias { + TypeStruct::Alias { name, alias } + } else { + TypeStruct::Definition { + name, + members, + returned_only, + struct_extends: struct_extends.unwrap_or_default(), + allow_duplicate, + } + }), + Some("union") => TypeDefinition::Union(TypeUnion { + name, + members, + returned_only, + }), + None => TypeDefinition::None { name, requires }, + Some(c) => unreachable!("Unexpected category of type {:?}", c), + }), comment, - spec, - })) + } } fn parse_command(ctx: &mut ParseCtx, attributes: Vec) -> Option { diff --git a/vk-parse/src/types.rs b/vk-parse/src/types.rs index a82be87..1c0c124 100644 --- a/vk-parse/src/types.rs +++ b/vk-parse/src/types.rs @@ -185,93 +185,26 @@ pub type Types = CommentedChildren; #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] pub enum TypesChild { - Type(Box), + Type { + definition: Box, + comment: Option, + }, Comment(String), } -#[derive(Debug, Clone, PartialEq, Eq, Default)] -#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] -#[non_exhaustive] -pub struct Type { - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub name: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub alias: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub api: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub requires: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub comment: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub parent: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub returnedonly: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub structextends: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub allowduplicate: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub objtypeenum: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub bitvalues: Option, - - #[cfg_attr( - feature = "serialize", - serde(default, skip_serializing_if = "is_default") - )] - pub spec: TypeSpec, -} - -/// The contents of a type definition. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] -pub enum TypeSpec { - None, +pub enum TypeDefinition { + /// When the `category` attribute is missing + None { + name: String, + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + requires: Option, + }, Include { name: String, quoted: Option, @@ -282,18 +215,34 @@ pub enum TypeSpec { // FIXME doesn't include all of the necessary information to recreate basetype: Option, }, - Bitmask(Option), + Bitmask(TypeBitmask), Handle(TypeHandle), - Enumeration, + Enumeration { + name: String, + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + alias: Option, + }, FunctionPointer(TypeFunctionPointer), - Struct(Vec), - Union(Vec), + Struct(TypeStruct), + Union(TypeUnion), } -impl Default for TypeSpec { - fn default() -> Self { - TypeSpec::None - } +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum TypeBitmask { + Alias { + name: String, + alias: String, + }, + + Definition { + definition: Box, + has_bitvalues: bool, + }, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -370,15 +319,33 @@ pub struct TypeFunctionPointer { serde(default, skip_serializing_if = "is_default") )] pub params: Vec, + + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub requires: Option, } #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] -pub struct TypeHandle { - pub name: String, +pub enum TypeHandle { + Alias { + name: String, + alias: String, + }, - pub handle_type: HandleType, + Definition { + name: String, + handle_type: HandleType, + objtypeenum: String, + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + parent: Option, + }, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -394,8 +361,21 @@ pub enum HandleType { #[non_exhaustive] pub struct TypeDefine { pub name: String, + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] pub comment: Option, + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] pub defref: Option, + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub requires: Option, pub is_disabled: bool, pub replace: bool, pub value: TypeDefineValue, @@ -414,6 +394,49 @@ pub enum TypeDefineValue { }, } +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum TypeStruct { + Alias { + name: String, + alias: String, + }, + + Definition { + name: String, + members: Vec, + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + returned_only: Option<()>, + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + struct_extends: Vec, + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + allow_duplicate: Option, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub struct TypeUnion { + pub name: String, + pub members: Vec, + #[cfg_attr( + feature = "serialize", + serde(default, skip_serializing_if = "is_default") + )] + pub returned_only: Option<()>, +} + #[derive(Debug, Clone, PartialEq, Eq, Default)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[non_exhaustive] From 538b518eae4f969af76fc71fe0bcd2d9c28fa2c5 Mon Sep 17 00:00:00 2001 From: Julia Tatz Date: Mon, 5 Sep 2022 00:55:45 -0400 Subject: [PATCH 10/17] Strongly-type CommandDefinition & removed pipeline The `queues` attribute should always be used instead per offical specs See the vulkan 1.2.180 changelog --- ci/tests/test.rs | 2 +- vk-parse/Cargo.toml | 1 + vk-parse/src/c.rs | 7 +-- vk-parse/src/convert.rs | 42 ++++++++------ vk-parse/src/parse.rs | 81 ++++++++++++++++++++++++++- vk-parse/src/types.rs | 120 ++++++++++++++++++++++++++++++++++++---- 6 files changed, 214 insertions(+), 39 deletions(-) diff --git a/ci/tests/test.rs b/ci/tests/test.rs index c75b0d4..40b8154 100644 --- a/ci/tests/test.rs +++ b/ci/tests/test.rs @@ -60,7 +60,7 @@ macro_rules! test_version { #[test] fn [< test_v $major _ $minor _ $patch >] () { parsing_test($major, $minor, $patch, $url_suffix); - } + } } }; } diff --git a/vk-parse/Cargo.toml b/vk-parse/Cargo.toml index 5062695..462231d 100644 --- a/vk-parse/Cargo.toml +++ b/vk-parse/Cargo.toml @@ -16,6 +16,7 @@ vkxml-convert = ["vkxml"] [dependencies] xml-rs = "^0.8" +bitflags = "^1.3" vkxml = { optional = true, version = "^0.3" } serde = { optional = true, version = "^1.0.75" } diff --git a/vk-parse/src/c.rs b/vk-parse/src/c.rs index 5b6a717..4e6662a 100644 --- a/vk-parse/src/c.rs +++ b/vk-parse/src/c.rs @@ -351,12 +351,7 @@ fn is_number_start(c: char) -> bool { } fn is_number_part(c: char) -> bool { - c == '.' - || c == '+' - || c == '-' - || c == 'x' - || c == 'X' - || c.is_ascii_hexdigit() + c == '.' || c == '+' || c == '-' || c == 'x' || c == 'X' || c.is_ascii_hexdigit() } fn is_identifier_start(c: char) -> bool { diff --git a/vk-parse/src/convert.rs b/vk-parse/src/convert.rs index ecf5526..6f4bd9c 100644 --- a/vk-parse/src/convert.rs +++ b/vk-parse/src/convert.rs @@ -907,33 +907,41 @@ impl From for Option { return_type: new_field(), param: Vec::new(), external_sync: None, - cmdbufferlevel: def.cmdbufferlevel, + cmdbufferlevel: def.cmdbufferlevel.map(|level| { + match level { + CommandBufferLevel::PrimaryOnly => "primary", + CommandBufferLevel::Both => "primary,secondary", + } + .to_string() + }), pipeline: None, - queues: def.queues, + queues: def.queues.map(|qs| qs.to_string()), renderpass: None, }; r.return_type.basetype = def.proto.type_name; - r.return_type.successcodes = def.successcodes; - r.return_type.errorcodes = def.errorcodes; + r.return_type.successcodes = def.successcodes.map(|sc| match sc { + CommandSuccessCodes::DefaultSuccess => "VK_SUCCESS".to_string(), + CommandSuccessCodes::Codes(cs) => cs.join(","), + }); + r.return_type.errorcodes = def.errorcodes.map(|ec| ec.join(",")); for text in def.implicitexternsyncparams { r.external_sync = Some(vkxml::ExternalSync { sync: text }) } if let Some(renderpass) = def.renderpass { - r.renderpass = match renderpass.as_str() { - "both" => Some(vkxml::Renderpass::Both), - "inside" => Some(vkxml::Renderpass::Inside), - "outside" => Some(vkxml::Renderpass::Outside), - _ => panic!("Unexpected renderpass value {:?}", renderpass), - }; - } - if let Some(pipeline) = def.pipeline { - r.pipeline = match pipeline.as_str() { - "graphics" => Some(vkxml::Pipeline::Graphics), - "compute" => Some(vkxml::Pipeline::Compute), - "transfer" => Some(vkxml::Pipeline::Transfer), - _ => panic!("Unexpected pipeline value {:?}", pipeline), + r.renderpass = match renderpass { + CommandRenderpass::Both => Some(vkxml::Renderpass::Both), + CommandRenderpass::Inside => Some(vkxml::Renderpass::Inside), + CommandRenderpass::Outside => Some(vkxml::Renderpass::Outside), }; } + // if let Some(pipeline) = def.pipeline { + // r.pipeline = match pipeline.as_str() { + // "graphics" => Some(vkxml::Pipeline::Graphics), + // "compute" => Some(vkxml::Pipeline::Compute), + // "transfer" => Some(vkxml::Pipeline::Transfer), + // _ => panic!("Unexpected pipeline value {:?}", pipeline), + // }; + // } r.param.reserve(def.params.len()); for param in def.params { diff --git a/vk-parse/src/parse.rs b/vk-parse/src/parse.rs index cdc3b0e..9021912 100644 --- a/vk-parse/src/parse.rs +++ b/vk-parse/src/parse.rs @@ -1496,6 +1496,23 @@ fn parse_type(ctx: &mut ParseCtx, attributes: Vec) -> } } +impl FromStr for CommandQueue { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "graphics" => Ok(Self::GRAPHICS), + "compute" => Ok(Self::COMPUTE), + "transfer" => Ok(Self::TRANSFER), + "sparse_binding" => Ok(Self::SPARSE_BINDING), + // "protected" => Ok(Self::PROTECTED), + "decode" => Ok(Self::VIDEO_DECODE), + "encode" => Ok(Self::VIDEO_ENCODE), + _ => Err(()), + } + } +} + fn parse_command(ctx: &mut ParseCtx, attributes: Vec) -> Option { let mut name = None; let mut alias = None; @@ -1600,14 +1617,72 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) return None; }; + // support versions older than 1.2.180 where the `pipeline` attribute for was removed + // + // Remove the `pipeline` attribute from `vk.xml`, and the corresponding + // "`Pipeline Types`" column from the generated command properties tables. + // The `queues` attribute should be used instead (internal merge request + // 4594). + const SUPPORT_OLD: bool = true; + + assert!(SUPPORT_OLD || pipeline.is_none()); + // if let Some(pipeline) = pipeline.as_deref() { + // if let Some(queues) = queues.as_deref() { + // assert!(proto.name == "vkCmdBlitImage" || queues.split(',').any(|q| q == pipeline), "name {:?} w/ {:?} v {:?}", proto, pipeline, queues) + // } else { + // panic!("pipeline attr {:?} without queues attr", pipeline); + // } + // } + + let successcodes = successcodes.map(|s| { + if "VK_SUCCESS" == s { + CommandSuccessCodes::DefaultSuccess + } else { + CommandSuccessCodes::Codes(s.split(',').map(|c| c.to_string()).collect()) + } + }); + let renderpass = renderpass.map(|rp| match rp.as_str() { + "inside" => CommandRenderpass::Inside, + "outside" => CommandRenderpass::Outside, + "both" => CommandRenderpass::Both, + _ => unreachable!(), + }); + let videocoding = videocoding.map(|vc| match vc.as_str() { + "inside" => CommandVideoCoding::Inside, + "outside" => CommandVideoCoding::Outside, + "both" => CommandVideoCoding::Both, + _ => unreachable!(), + }); + let cmdbufferlevel = cmdbufferlevel + .map(|level| match level.as_str() { + "primary,secondary" => Ok(CommandBufferLevel::Both), + "primary" => Ok(CommandBufferLevel::PrimaryOnly), + "secondary" => { + assert!( + proto.name.contains("Reserve"), + "Invalid cmdbufferlevel for {:?}", + proto + ); + Err(()) + } + _ => unreachable!("level {:?} was not expected for command `{}`", level, proto.name), + }) + .transpose() + .ok()?; + // I'm so sorry about the 'Option' -> 'Result