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 4e2f1bc..a8349e2 100644 --- a/ci/tests/test.rs +++ b/ci/tests/test.rs @@ -9,11 +9,13 @@ 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) .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 { @@ -53,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); + } } }; } @@ -83,174 +87,221 @@ fn test_main() { } } -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] +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! {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"} +test_version! {1, 3, 226, "/xml"} +test_version! {1, 3, 227, "/xml"} +test_version! {1, 3, 228, "/xml"} diff --git a/vk-parse/Cargo.toml b/vk-parse/Cargo.toml index 5062695..383d956 100644 --- a/vk-parse/Cargo.toml +++ b/vk-parse/Cargo.toml @@ -9,6 +9,7 @@ documentation = "https://docs.rs/vk-parse" repository = "https://github.com/krolli/vk-parse" keywords = ["vulkan", "parser"] categories = ["parser-implementations", "rendering::graphics-api"] +edition = "2021" [features] serialize = ["serde", "serde_derive"] @@ -16,6 +17,9 @@ vkxml-convert = ["vkxml"] [dependencies] xml-rs = "^0.8" +logos = "^0.12" +peg = "^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 deleted file mode 100644 index 848ed09..0000000 --- a/vk-parse/src/c.rs +++ /dev/null @@ -1,491 +0,0 @@ -#![allow(dead_code)] - -//-------------------------------------------------------------------------------------------------- -/// Performs Phase 1 of C compilation, specifically replaces platform-specific end-of-line indicators -/// with newlines characters and transforms trigraph sequences. -struct IterPhase1<'a> { - src: &'a str, -} - -impl<'a> IterPhase1<'a> { - fn new(src: &'a str) -> Self { - Self { src } - } -} - -impl<'a> Iterator for IterPhase1<'a> { - type Item = char; - fn next(&mut self) -> Option { - let mut iter = self.src.chars(); - - let c = if let Some(c) = iter.next() { - c - } else { - return None; - }; - - match c { - // TODO: trigraphs and digraphs (probably not important) - '\r' => { - if let Some('\n') = iter.next() { - self.src = self.src.split_at(2).1; - Some('\n') - } else { - self.src = self.src.split_at(1).1; - Some('\r') - } - } - c => { - self.src = self.src.split_at(1).1; - Some(c) - } - } - } -} - -//-------------------------------------------------------------------------------------------------- -struct IterPhase2<'a> { - src: IterPhase1<'a>, - c1: Option, - c2: Option, -} - -impl<'a> IterPhase2<'a> { - fn new(code: &'a str) -> Self { - let mut src = IterPhase1::new(code); - let c1 = src.next(); - let c2 = src.next(); - Self { src, c1, c2 } - } -} - -impl<'a> Iterator for IterPhase2<'a> { - type Item = char; - fn next(&mut self) -> Option { - while let (Some('\\'), Some('\n')) = (self.c1, self.c2) { - self.c1 = self.src.next(); - self.c2 = self.src.next(); - } - - let c = self.c1; - self.c1 = self.c2; - self.c2 = self.src.next(); - c - } -} - -//-------------------------------------------------------------------------------------------------- -/// Part of phase 3 that deals with comments and whitespace. Does not produce tokens, just filters -/// and transforms characters. -struct IterPhase3a<'a> { - src: IterPhase2<'a>, - peek: Option, - prev_space: bool, -} - -impl<'a> IterPhase3a<'a> { - fn new(code: &'a str) -> Self { - let mut src = IterPhase2::new(code); - let peek = src.next(); - Self { - src, - peek, - prev_space: false, - } - } - - fn is_merged_whitespace(c: char) -> bool { - c == '\t' || ('\u{000B}' <= c && c <= '\u{000D}') || c == ' ' - } -} - -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; - }; - - if c == '\n' { - self.peek = self.src.next(); - self.prev_space = false; - return Some('\n'); - } else if c == '/' { - // line comment, block comment or just char - self.peek = self.src.next(); - let c = if let Some(c) = self.peek { - c - } else { - self.prev_space = false; - return Some('/'); - }; - - if c == '/' { - self.peek = None; - while let Some(c) = self.src.next() { - if c == '\n' { - self.peek = Some(c); - break; - } - } - if !self.prev_space { - self.prev_space = true; - return Some(' '); - } - } else if c == '*' { - while let Some(c) = self.src.next() { - if c == '*' { - if let Some('/') = self.src.next() { - self.peek = self.src.next(); - break; - } - } - } - if !self.prev_space { - self.prev_space = true; - return Some(' '); - } - } else { - self.prev_space = false; - return Some('/'); - } - } else if IterPhase3a::is_merged_whitespace(c) { - self.peek = None; - while let Some(c) = self.src.next() { - if !IterPhase3a::is_merged_whitespace(c) { - self.peek = Some(c); - break; - } - } - if !self.prev_space { - self.prev_space = true; - return Some(' '); - } - } else { - self.peek = self.src.next(); - self.prev_space = false; - return Some(c); - } - } - } -} - -//-------------------------------------------------------------------------------------------------- -/// Simplified tokens for easier dealing with most C code we care about. Not precisely according to -/// C/C++ standards, as those are not as easy to deal with and make more sense when combined with -/// preprocessor. -#[derive(Clone, Eq, PartialEq, Debug)] -enum Token<'a> { - PpDirective(PpDirective), - Punctuation, - HeaderName(&'a str), - Identifier(&'a str), - StringLiteral(&'a str), - CharacterLiteral(char), - NewLine, -} - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -enum PpDirective { - Define, - Undef, - Include, - If, - Ifdef, - Ifndef, - Else, - Elif, - Endif, - Line, - Error, - Pragma, -} - -impl PpDirective { - fn from_str(s: &str) -> PpDirective { - match s { - "define" => PpDirective::Define, - "undef" => PpDirective::Undef, - "include" => PpDirective::Include, - "if" => PpDirective::If, - "ifdef" => PpDirective::Ifdef, - "ifndef" => PpDirective::Ifndef, - "else" => PpDirective::Else, - "elif" => PpDirective::Elif, - "endif" => PpDirective::Endif, - "line" => PpDirective::Line, - "error" => PpDirective::Error, - "pragma" => PpDirective::Pragma, - _ => panic!("Unrecognized postprocessor directive {:?}", s), - } - } -} - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -enum LineState { - /// Right after newline. - Start, - /// When first thing encountered on the line is '#' - PpStart, - /// First identifier after '#' - PpDirective, - /// Contents of postprocessor directive. - PpDirectiveBody, - /// Reached newline but not reported yet. - PpDirectiveEnd, - /// Normal line tokens. - Normal, -} - -struct IterTokenInner<'src> { - src: IterPhase3a<'src>, - peek: Option, - line: LineState, - buf: String, -} - -impl<'src> IterTokenInner<'src> { - fn new(src: &'src str) -> Self { - let mut src = IterPhase3a::new(src); - let peek = src.next(); - Self { - src, - peek, - line: LineState::Start, - buf: String::new(), - } - } - - fn next(&mut self) -> Option { - self.buf.clear(); - loop { - let c = if let Some(c) = self.peek { - c - } else { - return None; - }; - match (self.line, c) { - (LineState::Start, ' ') => self.peek = self.src.next(), - (LineState::Start, '\n') => self.peek = self.src.next(), - (LineState::Start, '#') => { - self.line = LineState::PpStart; - self.peek = self.src.next(); - } - (LineState::Start, c) => { - self.buf.push(c); - self.line = LineState::Normal; - self.peek = self.src.next(); - } - - (LineState::PpStart, ' ') => self.peek = self.src.next(), - (LineState::PpStart, '\n') => panic!( - "Unexpected combination ({:?}, {:?})", - LineState::PpStart, - '\n' - ), - (LineState::PpStart, c) => { - self.buf.push(c); - self.line = LineState::PpDirective; - self.peek = self.src.next(); - } - - (LineState::PpDirective, ' ') => { - self.line = LineState::PpDirectiveBody; - self.peek = self.src.next(); - return Some(Token::PpDirective(PpDirective::from_str(&self.buf))); - } - (LineState::PpDirective, '\n') => { - self.line = LineState::PpDirectiveEnd; - return Some(Token::PpDirective(PpDirective::from_str(&self.buf))); - } - (LineState::PpDirective, c) => { - self.buf.push(c); - self.peek = self.src.next(); - } - - (LineState::PpDirectiveBody, ' ') => { - self.peek = self.src.next(); - return Some(Token::Identifier(&self.buf)); - } - (LineState::PpDirectiveBody, '\n') => { - self.line = LineState::PpDirectiveEnd; - return Some(Token::Identifier(&self.buf)); - } - (LineState::PpDirectiveBody, c) => { - self.buf.push(c); - self.peek = self.src.next(); - } - - (LineState::PpDirectiveEnd, '\n') => { - self.peek = self.src.next(); - self.line = LineState::Start; - return Some(Token::NewLine); - } - (LineState::PpDirectiveEnd, c) => panic!( - "Unexpected combination ({:?}, {:?})", - LineState::PpDirectiveEnd, - c - ), - - (LineState::Normal, ' ') => { - self.peek = self.src.next(); - return Some(Token::Identifier(&self.buf)); - } - (LineState::Normal, '\n') => { - self.peek = self.src.next(); - self.line = LineState::Start; - if self.buf.len() > 0 { - return Some(Token::Identifier(&self.buf)); - } - } - (LineState::Normal, ';') => { - if self.buf.len() > 0 { - return Some(Token::Identifier(&self.buf)); - } else { - self.peek = self.src.next(); - return Some(Token::Punctuation); - } - } - (LineState::Normal, c) => { - self.buf.push(c); - self.peek = self.src.next(); - } - } - } - } -} - -fn is_number_start(c: char) -> bool { - '0' <= c && c <= '9' -} - -fn is_number_part(c: char) -> bool { - c == '.' - || c == '+' - || c == '-' - || c == 'x' - || c == 'X' - || ('0' <= c && c <= '9') - || ('a' <= c && c <= 'f') - || ('A' <= c && c <= 'F') -} - -fn is_identifier_start(c: char) -> bool { - c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') -} - -fn is_identifier_part(c: char) -> bool { - c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') -} - -//-------------------------------------------------------------------------------------------------- -pub struct TokenIter<'a> { - src: &'a str, -} - -impl<'a> TokenIter<'a> { - pub fn new(src: &'a str) -> Self { - Self { src } - } -} - -impl<'a> Iterator for TokenIter<'a> { - type Item = &'a str; - fn next(&mut self) -> Option<&'a str> { - let mut iter = self.src.char_indices(); - if let Some((_, c)) = iter.next() { - if is_c_identifier_char(c) { - for (end_idx, c) in iter { - if !is_c_identifier_char(c) { - let split = self.src.split_at(end_idx); - self.src = split.1; - return Some(split.0); - } - } - - let res = self.src; - self.src = ""; - return Some(res); - } else { - let split = self.src.split_at(1); - self.src = split.1; - return Some(split.0); - } - } - - None - } -} - -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 - } -} - -pub fn is_c_identifier(s: &str) -> bool { - for c in s.chars() { - if !is_c_identifier_char(c) { - return false; - } - } - true -} - -//-------------------------------------------------------------------------------------------------- -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test() { - 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);"; - - let post_phase_1: String = IterPhase1::new(code).collect(); - let post_phase_1_ref = "typedef void // some comment \n /* some other comment */ (VKAPI_PTR *PFN_vkInternalAllocationNotification)(\\\n void* pUserData = M_PI/4,\n size_t size,\\\n VkInternalAllocationType allocationType, \n VkSystemAllocationScope allocationScope);"; - assert_eq!(&post_phase_1, post_phase_1_ref); - - let post_phase_2: String = IterPhase2::new(code).collect(); - let post_phase_2_ref = "typedef void // some comment \n /* some other comment */ (VKAPI_PTR *PFN_vkInternalAllocationNotification)( void* pUserData = M_PI/4,\n size_t size, VkInternalAllocationType allocationType, \n VkSystemAllocationScope allocationScope);"; - assert_eq!(&post_phase_2, post_phase_2_ref); - - let post_phase_3: String = IterPhase3a::new(code).collect(); - let post_phase_3_ref = "typedef void \n (VKAPI_PTR *PFN_vkInternalAllocationNotification)( void* pUserData = M_PI/4,\n size_t size, VkInternalAllocationType allocationType, \n VkSystemAllocationScope allocationScope);"; - assert_eq!(&post_phase_3, post_phase_3_ref); - } - - { - let code = "// DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead.\n//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) // Patch version should always be set to 0"; - let post_phase_3: String = IterPhase3a::new(code).collect(); - assert_eq!(&post_phase_3, " \n "); - } - - { - let code = "#define x y\n //some comment here\n class Something;\n"; - let mut token_iter = IterTokenInner::new(code); - assert_eq!( - token_iter.next(), - Some(Token::PpDirective(PpDirective::Define)) - ); - assert_eq!(token_iter.next(), Some(Token::Identifier("x"))); - assert_eq!(token_iter.next(), Some(Token::Identifier("y"))); - assert_eq!(token_iter.next(), Some(Token::NewLine)); - assert_eq!(token_iter.next(), Some(Token::Identifier("class")),); - assert_eq!(token_iter.next(), Some(Token::Identifier("Something")),); - assert_eq!(token_iter.next(), Some(Token::Punctuation)); - } - } -} diff --git a/vk-parse/src/c_lexer.rs b/vk-parse/src/c_lexer.rs new file mode 100644 index 0000000..46d3d08 --- /dev/null +++ b/vk-parse/src/c_lexer.rs @@ -0,0 +1,554 @@ +use core::fmt; +use logos::{Lexer, Logos}; +use std::{borrow::Cow, str::FromStr}; + +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +pub enum Constant { + Char(u8), + Integer(u64), + Float(f64), +} + +// SAFETY: Floating point constants must be finite (NaN is a macro) +impl Eq for Constant {} + +impl Constant { + fn from_c_char<'a>(lex: &mut Lexer<'a, Token<'a>>) -> Result::Err> { + let s = lex.slice(); + let s = s.strip_prefix('L').unwrap_or(s); + let end = s.len() - 1; + Ok(Constant::Char(char::from_str(&s[1..end])? as _)) + } +} + +const IS: &[char] = &['u', 'U', 'l', 'L']; +const FS: &[char] = &['f', 'F', 'l', 'L']; + +fn fix_literal(lit: &str) -> Cow { + let lit = lit.strip_prefix('L').unwrap_or(lit); + let lit = lit.strip_prefix('"').unwrap().strip_suffix('"').unwrap(); + // TODO remove un-escaped newlines + Cow::Borrowed(lit) +} + +#[derive(Logos, Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +#[logos(subpattern decimal = r"[1-9][0-9]*")] +#[logos(subpattern hex = r"[0-9a-fA-F]+")] +#[logos(subpattern octal = r"[0-7]+")] +#[logos(subpattern exp = r"[Ee][+-]?[0-9]+")] +#[logos(subpattern float_suffix = r"[fFlL]")] +#[logos(subpattern int_suffix = r"[uUlL]*")] +pub enum Token<'a> { + // #[regex(r"//[^\n]*", logos::skip)] + // #[regex(r"[ \t\n\f]+", logos::skip)] + #[error] + Error, + #[token("auto")] + Auto, + #[token("break")] + Break, + #[token("case")] + Case, + #[token("char")] + Char, + #[token("const")] + Const, + #[token("continue")] + Continue, + #[token("default")] + Default, + #[token("do")] + Do, + #[token("double")] + Double, + #[token("else")] + Else, + #[token("enum")] + Enum, + #[token("extern")] + Extern, + #[token("float")] + Float, + #[token("for")] + For, + #[token("goto")] + Goto, + #[token("if")] + If, + #[token("inline")] + Inline, + #[token("int")] + Int, + #[token("long")] + Long, + #[token("register")] + Register, + #[token("restrict")] + Restrict, + #[token("return")] + Return, + #[token("short")] + Short, + #[token("signed")] + Signed, + #[token("sizeof")] + SizeOf, + #[token("static")] + Static, + #[token("struct")] + Struct, + #[token("switch")] + Switch, + #[token("typedef")] + TypeDef, + #[token("union")] + Union, + #[token("unsigned")] + UnSigned, + #[token("void")] + Void, + #[token("volatile")] + Volatile, + #[token("while")] + While, + #[token("_Bool")] + Bool, + #[token("_Complex")] + Complex, + #[token("_Imaginary")] + Imaginary, + #[token("[")] + LBrack, + #[token("]")] + RBrack, + #[token("(")] + LParen, + #[token(")")] + RParen, + #[token("{")] + LBrace, + #[token("}")] + RBrace, + #[token(".")] + Dot, + #[token("->")] + Point, + #[token("++")] + Increment, + #[token("--")] + Decrement, + #[token("&")] + Ampersand, + #[token("*")] + MulStar, + #[token("+")] + Plus, + #[token("-")] + Minus, + #[token("~")] + Tilde, + #[token("!")] + Exclamation, + #[token("/")] + Slash, + #[token("%")] + Percent, + #[token("<<")] + LShift, + #[token(">>")] + RShift, + #[token("<")] + LessThan, + #[token(">")] + GreaterThan, + #[token("<=")] + LessThanEqual, + #[token(">=")] + GreaterThanEqual, + #[token("==")] + Equal, + #[token("!=")] + NotEqual, + #[token("^")] + Caret, + #[token("|")] + VerticalBar, + #[token("&&")] + And, + #[token("||")] + Or, + #[token("?")] + Question, + #[token(":")] + Colon, + #[token(";")] + SemiColon, + #[token("...")] + Ellipsis, + #[token("=")] + Assign, + #[token("*=")] + MulAssign, + #[token("/=")] + DivAssign, + #[token("%=")] + RemAssign, + #[token("+=")] + AddAssign, + #[token("-=")] + SubAssign, + #[token("<<=")] + LShiftAssign, + #[token(">>=")] + RShiftAssign, + #[token("&=")] + AndAssign, + #[token("|=")] + OrAssign, + #[token("^=")] + XorAssign, + #[token(",")] + Comma, + + // + #[regex(r"\n", logos::skip)] + NewLine, + #[regex(r"[ \t\f]+", logos::skip)] + Whitespace, + + // pre-processing tokens + // FIXME + #[doc(hidden)] + #[token("//#define\\s*")] + _MalformedDefine, + #[regex(r"//[^\n]*", logos::skip)] + Comment, + #[token("#")] + Pound, + #[token("##")] + DoublePound, + #[token("\\", logos::skip)] + BackSlash, + #[token("#define")] + Define, + + #[regex(r"[_a-zA-Z][_a-zA-Z0-9]*", |lex| Cow::Borrowed(lex.slice()))] + Identifier(Cow<'a, str>), + #[regex("0(?&int_suffix)?", |_lex| Constant::Integer(0))] + #[regex("(?&decimal)(?&int_suffix)?", |lex| lex.slice().trim_end_matches(IS).parse().map(Constant::Integer))] + #[regex("0(?&octal)(?&int_suffix)?", |lex| u64::from_str_radix(lex.slice()[1..].trim_end_matches(IS), 8).map(Constant::Integer))] + #[regex("0[xX](?&hex)(?&int_suffix)?", |lex| u64::from_str_radix(lex.slice()[2..].trim_end_matches(IS), 16).map(Constant::Integer))] + #[regex(r"L?'(\\.|[^\\'])+'", |lex| Constant::from_c_char(lex))] + #[regex(r#"[+-]?((\d\.\d?(?&exp)?[fFdD]?)|(\.\d(?&exp)?[fFdD]?)|(\d(?&exp)[fFdD]?)|(\d(?&exp)?[fFdD]))"#, |lex| lex.slice().trim_end_matches(FS).parse().map(Constant::Float))] + Constant(Constant), + #[regex(r#"L?"([^"\\]|\\t|\\u|\\n|\\")*""#, |lex| fix_literal(lex.slice()))] + Literal(Cow<'a, str>), +} + +impl<'a> Token<'a> { + pub fn from_literal(s: &str) -> Option { + match s { + "auto" => Some(Token::Auto), + "break" => Some(Token::Break), + "case" => Some(Token::Case), + "char" => Some(Token::Char), + "const" => Some(Token::Const), + "continue" => Some(Token::Continue), + "default" => Some(Token::Default), + "do" => Some(Token::Do), + "double" => Some(Token::Double), + "else" => Some(Token::Else), + "enum" => Some(Token::Enum), + "extern" => Some(Token::Extern), + "float" => Some(Token::Float), + "for" => Some(Token::For), + "goto" => Some(Token::Goto), + "if" => Some(Token::If), + "inline" => Some(Token::Inline), + "int" => Some(Token::Int), + "long" => Some(Token::Long), + "register" => Some(Token::Register), + "restrict" => Some(Token::Restrict), + "return" => Some(Token::Return), + "short" => Some(Token::Short), + "signed" => Some(Token::Signed), + "sizeof" => Some(Token::SizeOf), + "static" => Some(Token::Static), + "struct" => Some(Token::Struct), + "switch" => Some(Token::Switch), + "typedef" => Some(Token::TypeDef), + "union" => Some(Token::Union), + "unsigned" => Some(Token::UnSigned), + "void" => Some(Token::Void), + "volatile" => Some(Token::Volatile), + "while" => Some(Token::While), + "_Bool" => Some(Token::Bool), + "_Complex" => Some(Token::Complex), + "_Imaginary" => Some(Token::Imaginary), + "[" => Some(Token::LBrack), + "]" => Some(Token::RBrack), + "(" => Some(Token::LParen), + ")" => Some(Token::RParen), + "{" => Some(Token::LBrace), + "}" => Some(Token::RBrace), + "." => Some(Token::Dot), + "->" => Some(Token::Point), + "++" => Some(Token::Increment), + "--" => Some(Token::Decrement), + "&" => Some(Token::Ampersand), + "*" => Some(Token::MulStar), + "+" => Some(Token::Plus), + "-" => Some(Token::Minus), + "~" => Some(Token::Tilde), + "!" => Some(Token::Exclamation), + "/" => Some(Token::Slash), + "%" => Some(Token::Percent), + "<<" => Some(Token::LShift), + ">>" => Some(Token::RShift), + "<" => Some(Token::LessThan), + ">" => Some(Token::GreaterThan), + "<=" => Some(Token::LessThanEqual), + ">=" => Some(Token::GreaterThanEqual), + "==" => Some(Token::Equal), + "!=" => Some(Token::NotEqual), + "^" => Some(Token::Caret), + "|" => Some(Token::VerticalBar), + "&&" => Some(Token::And), + "||" => Some(Token::Or), + "?" => Some(Token::Question), + ":" => Some(Token::Colon), + ";" => Some(Token::SemiColon), + "..." => Some(Token::Ellipsis), + "=" => Some(Token::Assign), + "*=" => Some(Token::MulAssign), + "/=" => Some(Token::DivAssign), + "%=" => Some(Token::RemAssign), + "+=" => Some(Token::AddAssign), + "-=" => Some(Token::SubAssign), + "<<=" => Some(Token::LShiftAssign), + ">>=" => Some(Token::RShiftAssign), + "&=" => Some(Token::AndAssign), + "|=" => Some(Token::OrAssign), + "^=" => Some(Token::XorAssign), + "," => Some(Token::Comma), + "#" => Some(Token::Pound), + "##" => Some(Token::DoublePound), + "\\" => Some(Token::BackSlash), + "#define" => Some(Token::Define), + "//#define" => Some(Token::_MalformedDefine), + "\n" => Some(Token::NewLine), + " " => Some(Token::Whitespace), + _ => None, + } + } + + pub fn into_owned(self) -> Token<'static> { + match self { + Token::Identifier(id) => Token::Identifier(Cow::Owned(id.into_owned())), + Token::Literal(lit) => Token::Literal(Cow::Owned(lit.into_owned())), + Token::Error => Token::Error, // todo!(), + Token::Constant(c) => Token::Constant(c), + + Token::Auto => Token::Auto, + Token::Break => Token::Break, + Token::Case => Token::Case, + Token::Char => Token::Char, + Token::Const => Token::Const, + Token::Continue => Token::Continue, + Token::Default => Token::Default, + Token::Do => Token::Do, + Token::Double => Token::Double, + Token::Else => Token::Else, + Token::Enum => Token::Enum, + Token::Extern => Token::Extern, + Token::Float => Token::Float, + Token::For => Token::For, + Token::Goto => Token::Goto, + Token::If => Token::If, + Token::Inline => Token::Inline, + Token::Int => Token::Int, + Token::Long => Token::Long, + Token::Register => Token::Register, + Token::Restrict => Token::Restrict, + Token::Return => Token::Return, + Token::Short => Token::Short, + Token::Signed => Token::Signed, + Token::SizeOf => Token::SizeOf, + Token::Static => Token::Static, + Token::Struct => Token::Struct, + Token::Switch => Token::Switch, + Token::TypeDef => Token::TypeDef, + Token::Union => Token::Union, + Token::UnSigned => Token::UnSigned, + Token::Void => Token::Void, + Token::Volatile => Token::Volatile, + Token::While => Token::While, + Token::Bool => Token::Bool, + Token::Complex => Token::Complex, + Token::Imaginary => Token::Imaginary, + Token::LBrack => Token::LBrack, + Token::RBrack => Token::RBrack, + Token::LParen => Token::LParen, + Token::RParen => Token::RParen, + Token::LBrace => Token::LBrace, + Token::RBrace => Token::RBrace, + Token::Dot => Token::Dot, + Token::Point => Token::Point, + Token::Increment => Token::Increment, + Token::Decrement => Token::Decrement, + Token::Ampersand => Token::Ampersand, + Token::MulStar => Token::MulStar, + Token::Plus => Token::Plus, + Token::Minus => Token::Minus, + Token::Tilde => Token::Tilde, + Token::Exclamation => Token::Exclamation, + Token::Slash => Token::Slash, + Token::Percent => Token::Percent, + Token::LShift => Token::LShift, + Token::RShift => Token::RShift, + Token::LessThan => Token::LessThan, + Token::GreaterThan => Token::GreaterThan, + Token::LessThanEqual => Token::LessThanEqual, + Token::GreaterThanEqual => Token::GreaterThanEqual, + Token::Equal => Token::Equal, + Token::NotEqual => Token::NotEqual, + Token::Caret => Token::Caret, + Token::VerticalBar => Token::VerticalBar, + Token::And => Token::And, + Token::Or => Token::Or, + Token::Question => Token::Question, + Token::Colon => Token::Colon, + Token::SemiColon => Token::SemiColon, + Token::Ellipsis => Token::Ellipsis, + Token::Assign => Token::Assign, + Token::MulAssign => Token::MulAssign, + Token::DivAssign => Token::DivAssign, + Token::RemAssign => Token::RemAssign, + Token::AddAssign => Token::AddAssign, + Token::SubAssign => Token::SubAssign, + Token::LShiftAssign => Token::LShiftAssign, + Token::RShiftAssign => Token::RShiftAssign, + Token::AndAssign => Token::AndAssign, + Token::OrAssign => Token::OrAssign, + Token::XorAssign => Token::XorAssign, + Token::Comma => Token::Comma, + Token::Pound => Token::Pound, + Token::DoublePound => Token::DoublePound, + Token::BackSlash => Token::BackSlash, + Token::_MalformedDefine => Token::_MalformedDefine, + Token::NewLine => Token::NewLine, + Token::Whitespace => Token::Whitespace, + Token::Comment => Token::Comment, + Token::Define => Token::Define, + } + } +} + +impl<'a> fmt::Display for Token<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Token::Auto => write!(f, "auto"), + Token::Break => write!(f, "break"), + Token::Case => write!(f, "case"), + Token::Char => write!(f, "char"), + Token::Const => write!(f, "const"), + Token::Continue => write!(f, "continue"), + Token::Default => write!(f, "default"), + Token::Do => write!(f, "do"), + Token::Double => write!(f, "double"), + Token::Else => write!(f, "else"), + Token::Enum => write!(f, "enum"), + Token::Extern => write!(f, "extern"), + Token::Float => write!(f, "float"), + Token::For => write!(f, "for"), + Token::Goto => write!(f, "goto"), + Token::If => write!(f, "if"), + Token::Inline => write!(f, "inline"), + Token::Int => write!(f, "int"), + Token::Long => write!(f, "long"), + Token::Register => write!(f, "register"), + Token::Restrict => write!(f, "restrict"), + Token::Return => write!(f, "return"), + Token::Short => write!(f, "short"), + Token::Signed => write!(f, "signed"), + Token::SizeOf => write!(f, "sizeof"), + Token::Static => write!(f, "static"), + Token::Struct => write!(f, "struct"), + Token::Switch => write!(f, "switch"), + Token::TypeDef => write!(f, "typedef"), + Token::Union => write!(f, "union"), + Token::UnSigned => write!(f, "unsigned"), + Token::Void => write!(f, "void"), + Token::Volatile => write!(f, "volatile"), + Token::While => write!(f, "while"), + Token::Bool => write!(f, "_Bool"), + Token::Complex => write!(f, "_Complex"), + Token::Imaginary => write!(f, "_Imaginary"), + Token::LBrack => write!(f, "["), + Token::RBrack => write!(f, "]"), + Token::LParen => write!(f, "("), + Token::RParen => write!(f, ")"), + Token::LBrace => write!(f, "{{"), + Token::RBrace => write!(f, "}}"), + Token::Dot => write!(f, "."), + Token::Point => write!(f, "->"), + Token::Increment => write!(f, "++"), + Token::Decrement => write!(f, "--"), + Token::Ampersand => write!(f, "&"), + Token::MulStar => write!(f, "*"), + Token::Plus => write!(f, "+"), + Token::Minus => write!(f, "-"), + Token::Tilde => write!(f, "~"), + Token::Exclamation => write!(f, "!"), + Token::Slash => write!(f, "/"), + Token::Percent => write!(f, "%"), + Token::LShift => write!(f, "<<"), + Token::RShift => write!(f, ">>"), + Token::LessThan => write!(f, "<"), + Token::GreaterThan => write!(f, ">"), + Token::LessThanEqual => write!(f, "<="), + Token::GreaterThanEqual => write!(f, ">="), + Token::Equal => write!(f, "=="), + Token::NotEqual => write!(f, "!="), + Token::Caret => write!(f, "^"), + Token::VerticalBar => write!(f, "|"), + Token::And => write!(f, "&&"), + Token::Or => write!(f, "||"), + Token::Question => write!(f, "?"), + Token::Colon => write!(f, ":"), + Token::SemiColon => write!(f, ";"), + Token::Ellipsis => write!(f, "..."), + Token::Assign => write!(f, "="), + Token::MulAssign => write!(f, "*="), + Token::DivAssign => write!(f, "/="), + Token::RemAssign => write!(f, "%="), + Token::AddAssign => write!(f, "+="), + Token::SubAssign => write!(f, "-="), + Token::LShiftAssign => write!(f, "<<="), + Token::RShiftAssign => write!(f, ">>="), + Token::AndAssign => write!(f, "&="), + Token::OrAssign => write!(f, "|="), + Token::XorAssign => write!(f, "^="), + Token::Comma => write!(f, ","), + Token::Pound => write!(f, "#"), + Token::DoublePound => write!(f, "##"), + Token::BackSlash => write!(f, "\\"), + Token::_MalformedDefine => write!(f, "//#define"), + Token::NewLine => write!(f, "\n"), + Token::Whitespace => write!(f, " "), + Token::Comment => write!(f, "//\n"), + Token::Identifier(id) => write!(f, "{}", id), + Token::Constant(c) => write!(f, "{}", c), + Token::Literal(lit) => { + let lit: &str = &*lit; + // FIXME + write!(f, "{:?}", lit) + } + Token::Error => unreachable!(), + Token::Define => write!(f, "#define"), + }?; + write!(f, " ") + } +} diff --git a/vk-parse/src/c_parser.rs b/vk-parse/src/c_parser.rs new file mode 100644 index 0000000..8f68e3e --- /dev/null +++ b/vk-parse/src/c_parser.rs @@ -0,0 +1,604 @@ +use core::num::{NonZeroU32, NonZeroU8}; +use std::fmt; +use std::{borrow::Cow, fmt::Display, ops::Deref}; +// Using the C grammer from https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf + +// C Type Decleration types +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] + +pub enum TypeQualifer { + Const, + Restrict, + Volatile, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] + +pub enum TypeIdentifier { + Plain(String), + Struct(String), + Union(String), + Enum(String), +} +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] + +pub enum TypeSpecifier { + Void, + Char, + Short, + Int, + Long, + Float, + Double, + Identifier(TypeIdentifier), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] + +pub enum Type { + Specifier(TypeSpecifier), + Pointer { + pointee_ty: Box, + }, + Array(Box, Option), + Function { + return_ty: Box, + name: String, + arg_tys: Vec, + }, + Qualified(TypeQualifer, Box), +} + +enum TypeSpecifierOrQual { + Specifier(TypeSpecifier), + Qualifier(TypeQualifer), +} + +// C Expression types + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +pub enum FixOrder { + Prefix, + Postfix, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] + +pub enum ComparisionOp { + LT, + LTE, + Eq, + NEq, + GTE, + GT, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] + +pub enum UnaryOp { + Address, + Indirection, + Positive, + Negative, + BitwiseNegation, + LogicalNegation, + Increment(FixOrder), + Decrement(FixOrder), + Cast(Type), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] + +pub enum BinaryOp { + Addition, + Subtraction, + Multiplication, + Division, + Remainder, + LeftShift, + RightShift, + BitwiseAnd, + BitwiseOr, + BitwiseXor, + LogicalAnd, + LogicalOr, +} + +use logos::Lexer; + +use crate::c_lexer::{Constant, Token}; + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +pub enum Expression { + Identifier(String), + Constant(Constant), + Literal(String), + SizeOf(Type), + Unary(UnaryOp, Box), + Binary(BinaryOp, Box, Box), + Comparision(ComparisionOp, Box, Box), + Assignment(Option, Box, Box), + TernaryIfElse(Box, Box, Box), + FunctionCall(Box, Box<[Expression]>), + Comma(Box<[Expression]>), + Member(Box, String), + PointMember(Box, String), + ArrayElement(Box, Box), +} + +fn wrap_unary((op, e): (UnaryOp, Expression)) -> Expression { + Expression::Unary(op, Box::new(e)) +} + +fn wrap_binary((el, op, er): (Expression, BinaryOp, Expression)) -> Expression { + Expression::Binary(op, Box::new(el), Box::new(er)) +} + +fn wrap_comparision((el, op, er): (Expression, ComparisionOp, Expression)) -> Expression { + Expression::Comparision(op, Box::new(el), Box::new(er)) +} + +fn comma_expr_or_single(mut v: Vec) -> Expression { + if v.len() == 1 { + v.pop().unwrap() + } else { + Expression::Comma(v.into_boxed_slice()) + } +} + +impl Display for TypeIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TypeIdentifier::Plain(ident) => write!(f, "{}", ident), + TypeIdentifier::Struct(ident) => write!(f, "struct {}", ident), + TypeIdentifier::Union(ident) => write!(f, "union {}", ident), + TypeIdentifier::Enum(ident) => write!(f, "enum {}", ident), + } + } +} + +impl Display for TypeSpecifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TypeSpecifier::Void => write!(f, "void"), + TypeSpecifier::Char => write!(f, "char"), + TypeSpecifier::Short => write!(f, "short"), + TypeSpecifier::Int => write!(f, "int"), + TypeSpecifier::Long => write!(f, "long"), + TypeSpecifier::Float => write!(f, "float"), + TypeSpecifier::Double => write!(f, "double"), + TypeSpecifier::Identifier(ident) => write!(f, "{}", ident), + } + } +} + +impl Display for Type { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +impl Display for UnaryOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + UnaryOp::Address => write!(f, "&"), + UnaryOp::Indirection => write!(f, "*"), + UnaryOp::Positive => write!(f, "+"), + UnaryOp::Negative => write!(f, "-"), + UnaryOp::BitwiseNegation => write!(f, "~"), + UnaryOp::LogicalNegation => write!(f, "!"), + UnaryOp::Increment(_) => write!(f, "++"), + UnaryOp::Decrement(_) => write!(f, "--"), + UnaryOp::Cast(ty) => write!(f, "({})", ty), + } + } +} + +impl Display for BinaryOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BinaryOp::Addition => write!(f, "+"), + BinaryOp::Subtraction => write!(f, "-"), + BinaryOp::Multiplication => write!(f, "*"), + BinaryOp::Division => write!(f, "/"), + BinaryOp::Remainder => write!(f, "%"), + BinaryOp::LeftShift => write!(f, "<<"), + BinaryOp::RightShift => write!(f, ">>"), + BinaryOp::BitwiseAnd => write!(f, "&"), + BinaryOp::BitwiseOr => write!(f, "|"), + BinaryOp::BitwiseXor => write!(f, "^"), + BinaryOp::LogicalAnd => write!(f, "&&"), + BinaryOp::LogicalOr => write!(f, "||"), + } + } +} + +impl Display for ComparisionOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ComparisionOp::LT => write!(f, "<"), + ComparisionOp::LTE => write!(f, "<="), + ComparisionOp::Eq => write!(f, "=="), + ComparisionOp::NEq => write!(f, "!="), + ComparisionOp::GTE => write!(f, ">="), + ComparisionOp::GT => write!(f, ">"), + } + } +} + +impl Display for Constant { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Constant::Char(c) => write!(f, "'{}'", (*c) as char), + Constant::Integer(i) => write!(f, "{}", i), + Constant::Float(n) => write!(f, "{}", n), + } + } +} + +impl Display for Expression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Expression::Identifier(id) => write!(f, "{}", id), + Expression::Constant(c) => write!(f, "{}", c), + Expression::Literal(lit) => write!(f, "{:?}", lit), + Expression::SizeOf(_) => todo!(), + Expression::Unary(UnaryOp::Increment(FixOrder::Postfix), e) => write!(f, "{}++", e), + Expression::Unary(UnaryOp::Decrement(FixOrder::Postfix), e) => write!(f, "{}--", e), + Expression::Unary(op, e) => write!(f, "{}{}", op, e), + Expression::Binary(op, l, r) => write!(f, "{} {} {}", l, op, r), + Expression::Comparision(op, l, r) => write!(f, "{} {} {}", l, op, r), + Expression::Assignment(_, _, _) => todo!(), + Expression::TernaryIfElse(cond, et, ef) => write!(f, "{} ? {} : {}", cond, et, ef), + Expression::FunctionCall(func, args) => { + write!(f, "{}(", func)?; + for arg in args.iter() { + write!(f, "{}, ", arg)?; + } + write!(f, ")") + } + Expression::Comma(v) => { + debug_assert!(v.len() > 1); + write!(f, "{}", &v[0])?; + for e in &v[1..] { + write!(f, ", {}", e)?; + } + Ok(()) + } + Expression::Member(s, member) => write!(f, "{}.{}", s, member), + Expression::PointMember(s, member) => write!(f, "{}->{}", s, member), + Expression::ArrayElement(e, i) => write!(f, "{}[{}]", e, i), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum VkXMLToken<'a> { + C(Token<'a>), + TextTag { + name: Cow<'a, str>, + text: Cow<'a, str>, + }, +} + +impl<'a> fmt::Display for VkXMLToken<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + VkXMLToken::C(token) => write!(f, "{}", token), + VkXMLToken::TextTag { name, text } => write!(f, "<{0}>{1}", name, text), + } + } +} + +#[derive(Debug, Clone)] +pub struct VkXMLTokens<'s, 'a>(pub Cow<'s, [VkXMLToken<'a>]>); + +impl<'s, 'a> Deref for VkXMLTokens<'s, 'a> { + type Target = [VkXMLToken<'a>]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a> FromIterator> for VkXMLTokens<'static, 'a> { + fn from_iter>>(iter: T) -> Self { + VkXMLTokens(Cow::Owned(iter.into_iter().collect())) + } +} + +impl<'s, 'a> peg::Parse for VkXMLTokens<'s, 'a> { + type PositionRepr = usize; + fn start(&self) -> usize { + 0 + } + + fn is_eof(&self, pos: usize) -> bool { + pos >= self.len() + } + + fn position_repr(&self, pos: usize) -> usize { + pos + } +} + +impl<'input: 's, 's, 'a> peg::ParseElem<'input> for VkXMLTokens<'s, 'a> { + type Element = &'s VkXMLToken<'a>; + + fn parse_elem(&'input self, pos: usize) -> peg::RuleResult { + match self[pos..].first() { + Some(c) => peg::RuleResult::Matched(pos + 1, c), + None => peg::RuleResult::Failed, + } + } +} + +impl<'s, 'a> peg::ParseLiteral for VkXMLTokens<'s, 'a> { + fn parse_string_literal(&self, pos: usize, literal: &str) -> peg::RuleResult<()> { + if let Some(VkXMLToken::C(tok)) = self.get(pos) { + let literal_token = + Token::from_literal(literal).unwrap_or_else(|| panic!("I'm dum {:?}", literal)); + if &literal_token == tok { + peg::RuleResult::Matched(pos + 1, ()) + } else { + peg::RuleResult::Failed + } + } else { + peg::RuleResult::Failed + } + } +} + +impl<'input, 's, 'a: 'input> peg::ParseSlice<'input> for VkXMLTokens<'s, 'a> { + type Slice = &'input [VkXMLToken<'a>]; + fn parse_slice(&'input self, p1: usize, p2: usize) -> Self::Slice { + &self[p1..p2] + } +} + +// C vk.xml text parsing helpers +use crate::ArrayLength; + +#[derive(Debug)] +enum BitFieldSizeOrArrayshape { + BitfieldSize(NonZeroU8), + ArrayShape(Vec), +} + +#[derive(Debug, Default, Clone, Copy)] +struct ParsedPreTypeTag { + pub is_const: bool, + pub is_struct: bool, +} + +peg::parser! { + pub grammar c_with_vk_ext<'s, 'a>() for VkXMLTokens<'s, 'a> { + use crate::PointerKind; + use crate::TypeDefine; + use crate::TypeDefineValue; + use crate::NameWithType; + use crate::TypeFunctionPointer; + + pub rule line_comment() = [VkXMLToken::C(Token::Comment)] "\n"? + + rule _() = [VkXMLToken::C(Token::Whitespace | Token::NewLine | Token::Comment)]* + + rule __() = [VkXMLToken::C(Token::Whitespace | Token::NewLine | Token::Comment)]* + + pub rule identifier() -> String + = i:[VkXMLToken::C(Token::Identifier(i))] { i.to_string() } + + rule type_identifier() -> TypeIdentifier + = "struct" i:identifier() { TypeIdentifier::Struct(i) } + / "union" i:identifier() { TypeIdentifier::Union(i) } + / "enum" i:identifier() { TypeIdentifier::Enum(i) } + / i:identifier() { TypeIdentifier::Plain(i) } + + rule type_specifier() -> TypeSpecifier + = i:type_identifier() { TypeSpecifier::Identifier(i) } + / "void" { TypeSpecifier::Void } + / "char" { TypeSpecifier::Char } + / "short" { TypeSpecifier::Short } + / "int" { TypeSpecifier::Int } + / "long" { TypeSpecifier::Long } + / "float" { TypeSpecifier::Float } + / "double" { TypeSpecifier::Double } + + pub rule type_name() -> Type + = ty:type_specifier() { Type::Specifier(ty) } + + + // C-expr rules + + pub rule constant() -> Constant + = c:[VkXMLToken::C(Token::Constant(c))] { c.clone() } + + rule literal() -> String + = l:[VkXMLToken::C(Token::Literal(l))] { l.to_string() } + + rule primary_expr() -> Expression + = i:identifier() { Expression::Identifier(i) } + / l:literal() { Expression::Literal(l) } + / c:constant() { Expression::Constant(c) } + / "(" _ e:expr() _ ")" { e } + + rule arithmetic() -> Expression = precedence!{ + x:@ _ "=" _ y:(@) { Expression::Assignment(None, Box::new(x), Box::new(y)) } + x:@ _ "+=" _ y:(@) { Expression::Assignment(Some(BinaryOp::Addition), Box::new(x), Box::new(y)) } + x:@ _ "-=" _ y:(@) { Expression::Assignment(Some(BinaryOp::Subtraction), Box::new(x), Box::new(y)) } + x:@ _ "*=" _ y:(@) { Expression::Assignment(Some(BinaryOp::Multiplication), Box::new(x), Box::new(y)) } + x:@ _ "/=" _ y:(@) { Expression::Assignment(Some(BinaryOp::Division), Box::new(x), Box::new(y)) } + x:@ _ "%=" _ y:(@) { Expression::Assignment(Some(BinaryOp::Remainder), Box::new(x), Box::new(y)) } + x:@ _ "<<=" _ y:(@) { Expression::Assignment(Some(BinaryOp::LeftShift), Box::new(x), Box::new(y)) } + x:@ _ ">>=" _ y:(@) { Expression::Assignment(Some(BinaryOp::RightShift), Box::new(x), Box::new(y)) } + x:@ _ "&=" _ y:(@) { Expression::Assignment(Some(BinaryOp::BitwiseAnd), Box::new(x), Box::new(y)) } + x:@ _ "|=" _ y:(@) { Expression::Assignment(Some(BinaryOp::BitwiseOr), Box::new(x), Box::new(y)) } + x:@ _ "^=" _ y:(@) { Expression::Assignment(Some(BinaryOp::BitwiseXor), Box::new(x), Box::new(y)) } + -- + x:@ _ "?" _ y:expr() _ ":" _ z:(@) { Expression::TernaryIfElse(Box::new(x), Box::new(y), Box::new(z)) } + -- + x:(@) _ "||" _ y:@ { wrap_binary((x, BinaryOp::LogicalOr, y)) } + -- + x:(@) _ "&&" _ y:@ { wrap_binary((x, BinaryOp::LogicalAnd, y)) } + -- + x:(@) _ "|" _ y:@ { wrap_binary((x, BinaryOp::BitwiseOr, y)) } + -- + x:(@) _ "^" _ y:@ { wrap_binary((x, BinaryOp::BitwiseXor, y)) } + -- + x:(@) _ "&" _ y:@ { wrap_binary((x, BinaryOp::BitwiseAnd, y)) } + -- + x:(@) _ "==" _ y:@ { wrap_comparision((x, ComparisionOp::Eq, y)) } + x:(@) _ "!=" _ y:@ { wrap_comparision((x, ComparisionOp::NEq, y)) } + -- + x:(@) _ "<=" _ y:@ { wrap_comparision((x, ComparisionOp::LTE, y)) } + x:(@) _ ">=" _ y:@ { wrap_comparision((x, ComparisionOp::GTE, y)) } + x:(@) _ "<" _ y:@ { wrap_comparision((x, ComparisionOp::LT, y)) } + x:(@) _ ">" _ y:@ { wrap_comparision((x, ComparisionOp::LT, y)) } + -- + x:(@) _ "<<" _ y:@ { wrap_binary((x, BinaryOp::LeftShift, y)) } + x:(@) _ ">>" _ y:@ { wrap_binary((x, BinaryOp::RightShift, y)) } + -- + x:(@) _ "+" _ y:@ { wrap_binary((x, BinaryOp::Addition, y)) } + x:(@) _ "-" _ y:@ { wrap_binary((x, BinaryOp::Subtraction, y)) } + -- + x:(@) _ "*" _ y:@ { wrap_binary((x, BinaryOp::Multiplication, y)) } + x:(@) _ "/" _ y:@ { wrap_binary((x, BinaryOp::Division, y)) } + x:(@) _ "%" _ y:@ { wrap_binary((x, BinaryOp::Remainder, y)) } + -- + "(" ty:type_name() ")" _ x:(@) { wrap_unary((UnaryOp::Cast(ty), x)) } + -- + "&" _ x:(@) { wrap_unary((UnaryOp::Address, x)) } + "*" _ x:(@) { wrap_unary((UnaryOp::Indirection, x)) } + "+" _ x:(@) { wrap_unary((UnaryOp::Positive, x)) } + "-" _ x:(@) { wrap_unary((UnaryOp::Negative, x)) } + "~" _ x:(@) { wrap_unary((UnaryOp::BitwiseNegation, x)) } + "!" _ x:(@) { wrap_unary((UnaryOp::LogicalNegation, x)) } + "++" _ x:(@) { wrap_unary((UnaryOp::Increment(FixOrder::Prefix), x)) } + "--" _ x:(@) { wrap_unary((UnaryOp::Decrement(FixOrder::Prefix), x)) } + -- + x:(@) "." y:identifier() { Expression::Member(Box::new(x), y) } + x:(@) "->" y:identifier() { Expression::PointMember(Box::new(x), y) } + x:(@) "[" _ y:expr() _ "]" { Expression::ArrayElement(Box::new(x), Box::new(y)) } + x:(@) "(" _ y:(arithmetic() ** (_ "," _)) _ ("," _)? ")" { Expression::FunctionCall(Box::new(x), y.into_boxed_slice()) } + x:(@) "++" { wrap_unary((UnaryOp::Increment(FixOrder::Postfix), x)) } + x:(@) "--" { wrap_unary((UnaryOp::Decrement(FixOrder::Postfix), x)) } + -- + p:primary_expr() { p } + } + + pub rule expr() -> Expression + = v:(arithmetic() ++ (_ "," _)) { comma_expr_or_single(v) } + + + // C vk.xml exts + rule define_macro_value() -> String + = l:$([^ VkXMLToken::C(Token::BackSlash | Token::NewLine)]*) ** ("\\" [VkXMLToken::C(Token::Whitespace)]* "\n") { l.into_iter().flat_map(|l| l.iter().map(|v| v.to_string())).collect() } + + /// handle const_ptr / struct info, can be 'const', 'struct', or 'const struct' + rule pre_type_tag_text() -> ParsedPreTypeTag + = _ c:"const"? _ s:"struct"? _ { ParsedPreTypeTag { is_const: c.is_some(), is_struct: s.is_some() } } + + // handle pointer info, can be '*' or '**' or '* const*' + rule post_type_tag_text() -> Option + = _ p:("*" _ b:(c:"const"? _ "*" { c })? { b })? { + match p { + Some(None) => Some(PointerKind::Single), + Some(Some(None)) => Some(PointerKind::Double { inner_is_const: false }), + Some(Some(Some(()))) => Some(PointerKind::Double { inner_is_const: true }), + None => None, + } + } + + rule typed_tag(name_text: rule) -> NameWithType + = pre:pre_type_tag_text() type_name:([VkXMLToken::TextTag { name, text } if name == "type"] { text }) pointer_kind:post_type_tag_text() name:name_text() { + NameWithType { + type_name: type_name.to_string(), + pointer_kind, + is_const: pre.is_const, + is_struct: pre.is_struct, + name: name.to_string(), + ..NameWithType::default() + } + } + + rule name_tag() -> Cow<'a, str> = [VkXMLToken::TextTag { name, text } if name == "name"] { text.to_owned() } + + /// parses the content of ... or ... or ... + pub rule name_with_type() -> NameWithType + = typed:typed_tag() bitOrArr:( + ":" bitfield_size:([VkXMLToken::C(Token::Constant(Constant::Integer(v)))] { NonZeroU8::new((*v).try_into().unwrap()).unwrap() }) { BitFieldSizeOrArrayshape::BitfieldSize(bitfield_size) } + / array_shape:("[" n:( + [VkXMLToken::C(Token::Constant(Constant::Integer(v)))] { ArrayLength::Static(NonZeroU32::new((*v).try_into().unwrap()).unwrap()) } + / [VkXMLToken::TextTag { name, text } if name == "enum"] { ArrayLength::Constant(name.to_string()) } + ) "]" { n })+ { BitFieldSizeOrArrayshape::ArrayShape(array_shape) } + )? comment:([VkXMLToken::TextTag { name, text } if name == "comment"] { text })? { + let (bitfield_size, array_shape) = match bitOrArr { + Some(BitFieldSizeOrArrayshape::BitfieldSize(size)) => (Some(size), None), + Some(BitFieldSizeOrArrayshape::ArrayShape(shape)) => (None, Some(shape)), + None => (None, None), + }; + NameWithType { + bitfield_size, + array_shape, + comment: comment.map(|s| s.to_string()), + ..typed + } + } + + /// ... + pub rule type_define(name_attr: Option<&str>, requires_attr: Option<&str>) -> TypeDefine + = _ is_disabled:("#define" {false} / "//#define"? {true}) __ name:([VkXMLToken::TextTag { name, text } if name == "name"] { text }) value:( + // / "(" _ params:(identifier() ** (_ "," _)) _ ("," _)? ")" [VkXMLToken::C(Token::Whitespace)]* e:define_macro_value() { + "(" params:(identifier() ** ",") ")" e:$([VkXMLToken::C(_)]+) { + TypeDefineValue::FunctionDefine { + params: params.into_boxed_slice(), + expression: e.iter().map(|v| v.to_string()).collect(), + } + } + / __ e:expr() _ { TypeDefineValue::Expression(e) } + / _ macro_name:([VkXMLToken::TextTag { name, text } if name == "type"] { text }) "(" _ args:(arithmetic() ** (_ "," _)) _ ("," _)? ")" _ { TypeDefineValue::MacroFunctionCall { + name: macro_name.to_string(), + args: args.into_boxed_slice(), + } } + ) { TypeDefine { + name: name.to_string(), + comment: None, + requires: requires_attr.map(|s| s.to_string()), + is_disabled, + value, + } } + / l:$([VkXMLToken::C(_)]+) { + name_attr.unwrap_or_else(|| panic!("{:?}", l)); + TypeDefine { + name: name_attr.map(|s| s.to_string()).expect("If no name is found inside the tag then it must be an attribute"), + comment: None, requires: requires_attr.map(|s| s.to_string()), is_disabled: false, value: TypeDefineValue::Code(l.iter().map(|v| v.to_string()).collect()) + } } + + + /// ... + pub rule type_funcptr(requires_attr: Option<&str>) -> TypeFunctionPointer + = _ "typedef" __ ty_name:type_specifier() _ ptr:"*"? _ "(" _ [VkXMLToken::C(Token::Identifier(id)) if id == "VKAPI_PTR"] _ "*" _ name:([VkXMLToken::TextTag { name, text } if name == "name"] { text }) ")" "(" _ params:( + "void" ")" ";" { Vec::new() } + / params:(typed_tag() ** (_ "," _)) ")" ";" { params } + ) { TypeFunctionPointer { proto: NameWithType { name: name.to_string(), type_name: ty_name.to_string(), pointer_kind: ptr.map(|()| PointerKind::Single), ..NameWithType::default() }, params, requires: requires_attr.map(|s| s.to_string()) } } + } +} + +// pub use c_with_vk_ext::expr as parse_cexpr; +// pub fn parse_cexpr(text: &str) -> Result { +pub fn parse_cexpr(text: &str) -> Option { + let tokens = Lexer::new(text) + .into_iter() + .map(|token: Token| VkXMLToken::<'static>::C(token.into_owned())) + .collect(); + c_with_vk_ext::expr(&tokens).ok() +} diff --git a/vk-parse/src/convert.rs b/vk-parse/src/convert.rs index d0f7c82..f7d8294 100644 --- a/vk-parse/src/convert.rs +++ b/vk-parse/src/convert.rs @@ -1,9 +1,5 @@ -extern crate vkxml; - -use c; -use parse::*; -use std; -use types::*; +use crate::parse::*; +use crate::types::*; //-------------------------------------------------------------------------------------------------- fn new_field() -> vkxml::Field { @@ -110,9 +106,9 @@ 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" { + purpose: if kind == EnumsKind::Bitmask { Some(vkxml::EnumerationPurpose::Bitmask) } else { None @@ -178,241 +174,185 @@ impl From for Option { fn from(orig: TypesChild) -> Self { match orig { TypesChild::Comment(text) => Some(vkxml::DefinitionsElement::Notation(text)), - TypesChild::Type(t) => { - let category = match t.category { - Some(c) => c, - None => { - let name = t.name.unwrap_or(String::new()); - return Some(vkxml::DefinitionsElement::Reference(vkxml::Reference { - name, - notation: t.comment, - include: t.requires, - })); - } - }; - - match category.as_str() { - "include" => { - let mut include = vkxml::Include { - name: t.name.unwrap_or(String::new()), - notation: t.comment, - style: vkxml::IncludeStyle::Quote, - need_ext: false, - }; - - 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" => { - let mut define = vkxml::Define { - name: t.name.unwrap_or(String::new()), - notation: t.comment, - is_disabled: true, - comment: None, - replace: false, - defref: Vec::new(), - 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); - } - _ => panic!("Unexpected contents of define {:?}", t.spec), - } - return Some(vkxml::DefinitionsElement::Define(define)); - } + 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, + }; - "basetype" => { - let mut typedef = vkxml::Typedef { - name: String::new(), - notation: t.comment, - basetype: String::new(), - }; - let markup = match t.spec { - TypeSpec::Code(TypeCode { markup, .. }) => markup, - _ => panic!("Unexpected contents of typedef {:?}", t.spec), - }; - 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)); - } + Some(vkxml::DefinitionsElement::Include(include)) + } - "bitmask" => { - if t.name.is_some() || t.alias.is_some() { - return None; + TypeDefinition::Define(TypeDefine { + name, + value, + comment, + is_disabled, + .. + }) => { + let mut define = vkxml::Define { + name, + notation, + is_disabled, + comment, + replace: false, + defref: Vec::new(), + parameters: Vec::new(), + c_expression: None, + value: None, + }; + match value { + TypeDefineValue::Expression(e) => { + define.value = Some(e.to_string()); } - let mut bitmask = vkxml::Bitmask { - name: vkxml::Identifier::new(), - notation: t.comment, - basetype: vkxml::Identifier::new(), - 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), - } + TypeDefineValue::FunctionDefine { params, expression } => { + define.parameters = params.to_vec(); + define.c_expression = Some(expression); } - return Some(vkxml::DefinitionsElement::Bitmask(bitmask)); - } - - "handle" => { - if t.name.is_some() || t.alias.is_some() { - return None; + TypeDefineValue::MacroFunctionCall { name, args } => { + define.defref = vec![name]; + define.c_expression = Some(format!( + "({})", + args.into_iter() + .map(|e| e.to_string()) + .collect::>() + .join(",") + )); } - let mut handle = vkxml::Handle { - name: String::new(), - 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), - }; - 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), - } + TypeDefineValue::Code(code) => { + define.replace = true; + define.c_expression = Some(code); } - return Some(vkxml::DefinitionsElement::Handle(handle)); - } - - "enum" => { - // if alias.is_some() { - // return None; - // } - return Some(vkxml::DefinitionsElement::Enumeration( - vkxml::EnumerationDeclaration { - name: t.name.unwrap_or(String::new()), - notation: t.comment, - }, - )); } + Some(vkxml::DefinitionsElement::Define(define)) + } - "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)); - } + TypeDefinition::Typedef { name, basetype } => { + let typedef = vkxml::Typedef { + name, + notation, + basetype: basetype.unwrap_or_default(), + }; + Some(vkxml::DefinitionsElement::Typedef(typedef)) + } - "struct" => { - if t.alias.is_some() { - return None; - } - let mut s = vkxml::Struct { - name: t.name.unwrap_or(String::new()), - notation: t.comment, - is_return: t.returnedonly.unwrap_or(String::new()).as_str() == "true", - 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), - } + TypeDefinition::Bitmask(TypeBitmask::Alias { .. }) => None, + TypeDefinition::Bitmask(TypeBitmask::Definition { + name, + is_64bit, + has_bitvalues, + }) => { + let basetype = (if is_64bit { "VkFlags64" } else { "VkFlags" }).to_string(); + let enumref = if has_bitvalues { + Some(name.replacen("Flags", "FlagBits", 1)) + } else { + None + }; + let bitmask = vkxml::Bitmask { + name, + notation, + basetype, + 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)) + } - return Some(vkxml::DefinitionsElement::Struct(s)); + 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()); } - "union" => { - 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) => { - let mut iter = def - .code - .split_whitespace() - .flat_map(|s| c::TokenIter::new(s)) - .peekable(); + Some(vkxml::DefinitionsElement::Struct(s)) + } - let field = parse_c_field(&mut iter).unwrap(); - u.elements.push(field); - } - } - } + 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()); } - _ => panic!("Unexpected contents of union {:?}: {:?}", u.name, t.spec), } - - return Some(vkxml::DefinitionsElement::Union(u)); } - _ => panic!("Unexpected category of type {:?}", category), + Some(vkxml::DefinitionsElement::Union(u)) } - } + }, } } } @@ -422,42 +362,8 @@ 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(); - field.c_size = def.altlen; - field.sync = def.externsync; - field.optional = def.optional; + let mut field: vkxml::Field = def.definition.into(); 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::Enum(value) => field.size_enumref = Some(value), - TypeMemberMarkup::Comment(comment) => field.notation = Some(comment), - _ => (), - } - } vkxml::StructElement::Member(field) } @@ -465,285 +371,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); - } -} - -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 { @@ -757,11 +384,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 { @@ -794,12 +417,14 @@ impl From for Option { bitpos: None, c_expression: None, }; - if let Ok(value) = i32::from_str_radix(&value, 10) { - r.number = Some(value); - } else if value.starts_with("0x") { - r.hex = Some(String::from(value.split_at(2).1)) - } else { - r.c_expression = Some(value) + match value { + EnumTypeValue::I32(v) => r.number = Some(v), + EnumTypeValue::U32(v) => r.hex = Some(format!("{:X}", v)), + EnumTypeValue::U64(v) => r.hex = Some(format!("{:X}", v)), + EnumTypeValue::F32(v) => r.c_expression = Some(v.to_string()), + EnumTypeValue::Text(_) => todo!(), + EnumTypeValue::Refrence(refr) => r.c_expression = Some(refr), + EnumTypeValue::Expression(e) => r.c_expression = Some(e.to_string()), } Some(r) } @@ -808,69 +433,100 @@ 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), - _ => (), +impl From for vkxml::Field { + fn from(nt: NameWithType) -> Self { + let NameWithType { + type_name, + pointer_kind, + is_struct, + bitfield_size: _, + array_shape, + name, + dynamic_shape, + externsync, + optional, + noautovalidity, + objecttype: _, + comment, + is_const, + } = nt; + vkxml::Field { + array: if dynamic_shape.is_some() { + Some(vkxml::ArrayType::Dynamic) + } else if array_shape.is_some() { + Some(vkxml::ArrayType::Static) + } else { + None }, - "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)) - } - } + 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, + 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: comment, + 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, } - iter.next().unwrap(); } - - Some(r) } //-------------------------------------------------------------------------------------------------- @@ -948,50 +604,60 @@ 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); - } + Some(ExtensionSupport::Vulkan) => match_api = Some("vulkan".to_string()), + Some(ExtensionSupport::Disabled) => { + disabled = true; } - None => (), - } + 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 { - Some(text) => match text.as_str() { - "instance" => Some(vkxml::ExtensionType::Instance), - "device" => Some(vkxml::ExtensionType::Device), - _ => panic!( - "Unexpected value of type attribute on extension: {:?}", - text - ), - }, - None => None, - }, - define: orig.protect, - requires: orig.requires, - author: orig.author, - contact: orig.contact, + ty: ext_type.map(|e| match e { + ExtensionType::Instance => vkxml::ExtensionType::Instance, + ExtensionType::Device => vkxml::ExtensionType::Device, + }), + define: protect, + requires, + author, + contact, elements, } } @@ -1137,19 +803,20 @@ impl From for Option { } } - EnumSpec::Value { mut value, extends } => { + EnumSpec::Value { value, extends } => { let mut text = None; let mut number = None; let mut enumref = None; - if let Ok(val) = i32::from_str_radix(&value, 10) { - number = Some(val); - } else if value.starts_with('"') && value.ends_with('"') { - let end = value.len() - 1; - value.remove(end); - value.remove(0); - text = Some(value); - } else { - enumref = Some(value); + match value { + EnumTypeValue::I32(v) => { + number = Some(v); + } + EnumTypeValue::U32(v) => enumref = Some(format!("{:X}", v)), + EnumTypeValue::U64(v) => enumref = Some(format!("{:X}", v)), + EnumTypeValue::F32(v) => enumref = Some(v.to_string()), + EnumTypeValue::Text(t) => text = Some(t), + EnumTypeValue::Refrence(_) => {} + EnumTypeValue::Expression(e) => enumref = Some(e.to_string()), } if let Some(extends) = extends { @@ -1235,113 +902,45 @@ 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, }; - match def.proto.type_name { - Some(type_name) => r.return_type.basetype = type_name, - None => (), - } - r.return_type.successcodes = def.successcodes; - r.return_type.errorcodes = def.errorcodes; + r.return_type.basetype = def.proto.type_name; + 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 { - let mut p = new_field(); - p.name = Some(param.definition.name); - if let Some(v) = param.definition.type_name { - p.basetype = v; - } - 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); - } - - 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); - } - } - } + r.param.push(param.definition.into()); } Some(r) @@ -1356,8 +955,11 @@ impl From for vkxml::Feature { Self { name: orig.name, notation: orig.comment, - api: orig.api, - version: f32::from_str(&orig.number).unwrap(), + api: match orig.api { + FeatureApi::Vulkan => "vulkan".to_string(), + }, + version: f32::from_str(&format!("{}.{}", orig.number.major, orig.number.minor)) + .unwrap(), define: orig.protect, elements: { let mut elements = Vec::with_capacity(orig.children.len()); diff --git a/vk-parse/src/lib.rs b/vk-parse/src/lib.rs index 3e1aefe..6fc81d5 100644 --- a/vk-parse/src/lib.rs +++ b/vk-parse/src/lib.rs @@ -4,24 +4,19 @@ //! return a `Registry` object. This object contains all the information contained //! in the Vulkan API registry. -extern crate xml; - #[cfg(feature = "serialize")] #[macro_use] extern crate serde_derive; -#[cfg(feature = "serialize")] -extern crate serde; - -#[cfg(feature = "vkxml-convert")] -extern crate vkxml; #[macro_use] mod parse; -mod c; #[cfg(feature = "vkxml-convert")] mod convert; mod types; +mod c_lexer; +mod c_parser; + #[cfg(feature = "vkxml-convert")] pub use convert::parse_file_as_vkxml; #[cfg(feature = "vkxml-convert")] diff --git a/vk-parse/src/parse.rs b/vk-parse/src/parse.rs index bac45b7..5dcf425 100644 --- a/vk-parse/src/parse.rs +++ b/vk-parse/src/parse.rs @@ -1,11 +1,12 @@ -extern crate xml; - -use std; +use logos::Lexer; +use std::borrow::Cow; use std::io::Read; use std::str::FromStr; use xml::reader::XmlEvent; -use types::*; +use crate::c_lexer::Token; +use crate::c_parser::{parse_cexpr, VkXMLToken, VkXMLTokens}; +use crate::types::*; type XmlEvents = xml::reader::Events; type XmlAttribute = xml::attribute::OwnedAttribute; @@ -42,6 +43,34 @@ fn xpath_attribute(xpath: &str, attribute_name: &str) -> String { xpath } +fn tokenize(ctx: &mut ParseCtx) -> VkXMLTokens<'static, 'static> { + let mut tokens = Vec::new(); + while let Some(Ok(e)) = ctx.events.next() { + match e { + XmlEvent::StartElement { name, .. } => { + let name = name.local_name.as_str(); + ctx.push_element(name); + let text = parse_text_element(ctx); + tokens.push(VkXMLToken::TextTag { + name: Cow::Owned(name.to_string()), + text: Cow::Owned(text), + }) + } + XmlEvent::Characters(text) => tokens.extend( + Lexer::new(text.as_str()) + .into_iter() + .map(|token: Token| VkXMLToken::<'static>::C(token.into_owned())), + ), + XmlEvent::EndElement { .. } => { + ctx.pop_element(); + break; + } + _ => {} + } + } + VkXMLTokens(Cow::Owned(tokens)) +} + //-------------------------------------------------------------------------------------------------- macro_rules! unwrap_attribute ( ($ctx:expr, $element:ident, $attribute:ident) => { @@ -134,18 +163,17 @@ macro_rules! match_elements { } macro_rules! match_elements_combine_text { - ( $ctx:expr, $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), XmlEvent::Whitespace(text) => $buffer.push_str(&text), - XmlEvent::StartElement { name, .. } => { - $buffer.push(' '); + XmlEvent::StartElement { name, attributes: $attributes, .. } => { let name = name.local_name.as_str(); $ctx.push_element(name); match name { $( - $p => $e, + $p $(if $g)? => $e, )+ _ => { $ctx.errors.push(Error::UnexpectedElement { @@ -157,43 +185,16 @@ macro_rules! match_elements_combine_text { } } 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, .. } => { - 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 { .. } => { - $ctx.pop_element(); - break; - } - _ => {} - } - } + ( $ctx:expr, $buffer:ident, $($p:pat => $e:expr),+ $(,)?) => { + match_elements_combine_text!{ $ctx, _attributes, $buffer, $($p => $e),+ } }; } @@ -300,6 +301,11 @@ fn parse_registry(ctx: &mut ParseCtx) -> Result EnumsKind::Enum, + "bitmask" => EnumsKind::Bitmask, + _ => unimplemented!("Unexpected `type` attribute value of {:?}", k), + }); registry.0.push(RegistryChild::Enums(Enums{ name, kind, start, end, vendor, comment, children, bitwidth })); }, @@ -360,10 +366,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); @@ -452,6 +455,224 @@ fn parse_tag(ctx: &mut ParseCtx, attributes: Vec) -> O }) } +fn parse_name_with_type( + ctx: &mut ParseCtx, + len: Option, + altlen: Option, + externsync: Option, + optional: Option, + noautovalidity: Option, + objecttype: Option, +) -> 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"); + + altlen.map(|c_expr| DynamicShapeKind::Expression { + latex_expr: Some(latex_expr.to_string()), + c_expr: parse_cexpr(c_expr.as_str()).unwrap(), + }) + } 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: parse_cexpr(c_expr.as_str()).unwrap(), + }) + } 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 + ), + }; + + use crate::c_parser::c_with_vk_ext::name_with_type; + let tokens = tokenize(ctx); + let base = + name_with_type(&tokens).unwrap_or_else(|e| panic!("{} with tokens: {:?}", e, &tokens)); + + let externsync = externsync.map(|v| { + if v == "true" { + ExternSyncKind::Value + } else { + let it = v.split(',').map(|value| { + let v = value.strip_prefix(base.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 { + dynamic_shape, + externsync, + optional, + noautovalidity, + objecttype, + ..base + }) +} + +fn parse_type_funcptr( + ctx: &mut ParseCtx, + requires: Option, +) -> Option { + use crate::c_parser::c_with_vk_ext::type_funcptr; + let tokens = tokenize(ctx); + Some( + type_funcptr(&tokens, requires.as_deref()) + .unwrap_or_else(|e| panic!("{} with tokens: {:?}", e, &tokens)), + ) +} + +fn parse_type_define( + ctx: &mut ParseCtx, + name: Option, + requires: Option, +) -> Option { + use crate::c_parser::c_with_vk_ext::type_define; + let tokens = tokenize(ctx); + // TODO should we parse any comments found inside or not? + Some( + type_define(&tokens, name.as_deref(), requires.as_deref()) + .unwrap_or_else(|e| panic!("{} with tokens: {:?}", e, &tokens)), + ) +} + +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) + } + + let selector = selector.map(|s| match s.as_str() { + "type" => TypeMemberSelector::Type, + "format" => TypeMemberSelector::Format, + "geometryType" => TypeMemberSelector::GeometryType, + _ => panic!("unrecongized selector {:?}", s), + }); + + let limittype = limittype.map(|lt| match lt.as_str() { + "min" => TypeMemberLimitType::Min, + "max" => TypeMemberLimitType::Max, + "exact" => TypeMemberLimitType::Exact, + "bits" => TypeMemberLimitType::Bits, + "range" => TypeMemberLimitType::Range, + "bitmask" => TypeMemberLimitType::Bitmask, + "struct" => TypeMemberLimitType::Struct, + "noauto" => TypeMemberLimitType::NoAuto, + "min,pot" => TypeMemberLimitType::MinPot, + "max,pot" => TypeMemberLimitType::MaxPot, + "min,mul" => TypeMemberLimitType::MinMul, + _ => panic!("unrecongized limittype {:?}", lt), + }); + + 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; @@ -467,7 +688,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, @@ -485,110 +705,255 @@ 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(), + 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 + ); + } + // 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`" + ), + }); + + if let Some("funcpointer") = category.as_deref() { + let fn_ptr_spec = parse_type_funcptr(ctx, requires).unwrap(); + return TypesChild::Type { + definition: Box::new(TypeDefinition::FunctionPointer(fn_ptr_spec)), + comment, + }; + } + if let Some("define") = category.as_deref() { + let ty_define = + parse_type_define(ctx, name, requires).unwrap_or_else(|| panic!("{:?}", ctx.errors)); + return TypesChild::Type { + definition: Box::new(TypeDefinition::Define(ty_define)), + 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 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" => { - 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(); - let mut markup = Vec::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)); } - 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)); - }, - "comment" => { - let text = parse_text_element(ctx); - markup.push(TypeMemberMarkup::Comment(text)); - } - } - members.push(TypeMember::Definition(TypeMemberDefinition { - len, - altlen, - externsync, - optional, - selector, - selection, - noautovalidity, - validextensionstructs, - values, - limittype, - objecttype, - code, - markup, - })) }, - "comment" => 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), } - - TypesChild::Type(Type { - api, - alias, - requires, - name, - category, - parent, - returnedonly, - structextends, - allowduplicate, - objtypeenum, - bitvalues, + assert!(!(name.is_some() && name_tag.is_some())); + let name = name.or(name_tag); + assert!(name.is_some(), "category was {:?}", category); + 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("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 { + assert!(matches!(type_tag.as_deref(), Some("VkFlags" | "VkFlags64"))); + TypeDefinition::Bitmask(TypeBitmask::Definition { + name, + is_64bit: matches!(type_tag.as_deref(), Some("VkFlags64")), + has_bitvalues: requires.is_some() || bitvalues.is_some(), + }) + } + } + 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: if members.len() > 0 { - TypeSpec::Members(members) - } else if code.len() > 0 { - TypeSpec::Code(TypeCode { code, markup }) - } else { - TypeSpec::None - }, - }) + } +} + +impl FromStr for CommandQueue { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(match s { + "graphics" => Self::GRAPHICS, + "compute" => Self::COMPUTE, + "transfer" => Self::TRANSFER, + "sparse_binding" => Self::SPARSE_BINDING, + // "protected" => Self::PROTECTED, + "decode" => Self::VIDEO_DECODE, + "encode" => Self::VIDEO_ENCODE, + "opticalflow" => Self::OPTICAL_FLOW, + _ => return Err(format!("Unknown value for attribute {:?} is not in the set {{graphics, compute, transfer, sparse_binding, decode, encode, opticalflow}}", s)), + }) + } +} + +impl FromStr for CommandTask { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "action" => CommandTask::ACTION, + "state" => CommandTask::STATE, + "synchronization" => CommandTask::SYNCHRONIZATION, + "indirection" => CommandTask::INDIRECTION, + _ => return Err(()), + }) + } } fn parse_command(ctx: &mut ParseCtx, attributes: Vec) -> Option { @@ -600,6 +965,7 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) let mut renderpass = None; let mut videocoding = None; let mut cmdbufferlevel = None; + let mut tasks = None; let mut pipeline = None; let mut comment = None; @@ -612,6 +978,7 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) "renderpass" => renderpass = Some(a.value), "videocoding" => videocoding = Some(a.value), "cmdbufferlevel" => cmdbufferlevel = Some(a.value), + "tasks" => tasks = Some(a.value), "pipeline" => pipeline = Some(a.value), "comment" => comment = Some(a.value) } @@ -621,47 +988,14 @@ 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; 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); - code.push('('); + proto = parse_name_with_type(ctx, None, None, None, None, None, None); }, "param" => { @@ -672,6 +1006,7 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) let mut noautovalidity = None; let mut objecttype = None; let mut validstructs = None; + let mut stride = None; match_attributes!{ctx, a in attributes, "len" => len = Some(a.value), @@ -681,6 +1016,7 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) "noautovalidity" => noautovalidity = Some(a.value), "objecttype" => objecttype = Some(a.value), "validstructs" => validstructs = Some(a.value), + "stride" => stride = Some(a.value), } let validstructs = validstructs.map_or( @@ -688,19 +1024,18 @@ 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, + optional, + noautovalidity, + objecttype, + ) { params.push(CommandParam { - len, - altlen, - externsync, - optional, - noautovalidity, - objecttype, definition, validstructs, + stride, }); } }, @@ -719,7 +1054,6 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) } } } - code.push_str(");"); let proto = if let Some(v) = proto { v @@ -731,22 +1065,135 @@ fn parse_command(ctx: &mut ParseCtx, attributes: Vec) return None; }; - Some(Command::Definition(CommandDefinition { - queues, + // 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; + + #[allow(clippy::assertions_on_constants)] + { + 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