From 6e04453c9f318f0aab1f74590be58f4ea23b0f71 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 24 Oct 2018 04:01:17 -0700 Subject: [PATCH] working on spirv parser generator --- spirv-parser-generator/Cargo.toml | 3 + spirv-parser-generator/src/ast.rs | 822 +++++++++++++++++++++---- spirv-parser-generator/src/generate.rs | 356 +++++++++++ spirv-parser-generator/src/lib.rs | 101 ++- spirv-parser-generator/src/util.rs | 56 +- spirv-parser/Cargo.toml | 3 + spirv-parser/build.rs | 17 + spirv-parser/src/lib.rs | 4 + 8 files changed, 1217 insertions(+), 145 deletions(-) create mode 100644 spirv-parser-generator/src/generate.rs create mode 100644 spirv-parser/build.rs diff --git a/spirv-parser-generator/Cargo.toml b/spirv-parser-generator/Cargo.toml index 84e83cb..1b1d8ca 100644 --- a/spirv-parser-generator/Cargo.toml +++ b/spirv-parser-generator/Cargo.toml @@ -13,3 +13,6 @@ crate-type = ["rlib"] serde_json = "1.0" serde = "1.0" serde_derive = "1.0" +proc-macro2 = "0.4" +quote = "0.6" +which = "2.0.0" diff --git a/spirv-parser-generator/src/ast.rs b/spirv-parser-generator/src/ast.rs index 54696ba..6fdafc7 100644 --- a/spirv-parser-generator/src/ast.rs +++ b/spirv-parser-generator/src/ast.rs @@ -1,41 +1,80 @@ // SPDX-License-Identifier: LGPL-2.1-or-later // Copyright 2018 Jacob Lifshay +use proc_macro2::TokenStream; +use quote::ToTokens; use serde::de::{self, Deserialize, Deserializer}; +use std::borrow::Cow; use std::fmt; +use std::mem; +use std::num::ParseIntError; use util::NameFormat::*; use util::WordIterator; #[derive(Copy, Clone)] -pub enum QuotedInteger { - U16Hex(u16), - U32Hex(u32), +pub struct QuotedInteger(pub T); + +pub trait QuotedIntegerProperties: Sized { + const DIGIT_COUNT: usize; + fn from_str_radix(src: &str, radix: u32) -> Result; } -impl fmt::Display for QuotedInteger { +impl QuotedIntegerProperties for QuotedInteger { + const DIGIT_COUNT: usize = 4; + fn from_str_radix(src: &str, radix: u32) -> Result { + Ok(QuotedInteger(u16::from_str_radix(src, radix)?)) + } +} + +impl QuotedIntegerProperties for QuotedInteger { + const DIGIT_COUNT: usize = 8; + fn from_str_radix(src: &str, radix: u32) -> Result { + Ok(QuotedInteger(u32::from_str_radix(src, radix)?)) + } +} + +impl ToTokens for QuotedInteger { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens) + } +} + +impl fmt::Display for QuotedInteger { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - QuotedInteger::U16Hex(v) => write!(f, "{:#06X}", v), - QuotedInteger::U32Hex(v) => write!(f, "{:#010X}", v), - } + write!(f, "{:#06X}", self.0) + } +} + +impl fmt::Display for QuotedInteger { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:#010X}", self.0) } } -impl fmt::Debug for QuotedInteger { +impl fmt::Debug for QuotedInteger +where + Self: fmt::Display + Copy, +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - struct DisplayQuotedInteger(self::QuotedInteger); - impl fmt::Debug for DisplayQuotedInteger { + struct DisplayQuotedInteger(QuotedInteger); + impl fmt::Debug for DisplayQuotedInteger + where + QuotedInteger: fmt::Display, + { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } - #[derive(Debug)] - struct QuotedInteger(DisplayQuotedInteger); - QuotedInteger(DisplayQuotedInteger(*self)).fmt(f) + f.debug_tuple("QuotedInteger") + .field(&DisplayQuotedInteger(*self)) + .finish() } } -impl<'de> Deserialize<'de> for QuotedInteger { +impl<'de, T> Deserialize<'de> for QuotedInteger +where + Self: QuotedIntegerProperties, +{ fn deserialize>(deserializer: D) -> Result { let s = String::deserialize(deserializer)?; let prefix = "0x"; @@ -52,16 +91,12 @@ impl<'de> Deserialize<'de> for QuotedInteger { "invalid quoted integer -- not a hexadecimal digit", )); } - let retval = match digits.len() { - 4 => QuotedInteger::U16Hex(u16::from_str_radix(digits, radix).unwrap()), - 8 => QuotedInteger::U32Hex(u32::from_str_radix(digits, radix).unwrap()), - _ => { - return Err(de::Error::custom( - "invalid quoted integer -- wrong number of hex digits", - )); - } - }; - Ok(retval) + if digits.len() != Self::DIGIT_COUNT { + return Err(de::Error::custom( + "invalid quoted integer -- wrong number of hex digits", + )); + } + Ok(Self::from_str_radix(digits, radix).unwrap()) } } @@ -121,46 +156,344 @@ pub enum Quantifier { Variadic, } -#[derive(Deserialize, Debug)] +#[derive(Clone, Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct InstructionOperand { - kind: String, - name: Option, - quantifier: Option, + pub kind: Kind, + pub name: Option, + pub quantifier: Option, } impl InstructionOperand { - pub fn guess_name(&mut self) -> Result<(), ::Error> { + pub fn fixup(&mut self) -> Result<(), ::Error> { if self.name.is_none() { self.name = Some( SnakeCase - .name_from_words(WordIterator::new(&self.kind)) + .name_from_words(WordIterator::new(self.kind.as_ref())) .ok_or(::Error::DeducingNameForInstructionOperandFailed)?, ); } + self.kind.set_bit_width(BitWidth::Bits32); Ok(()) } } -#[derive(Deserialize, Debug)] +#[derive(Clone, Eq, PartialEq, Hash, Debug)] +pub enum InstructionName { + OpSwitch, + OpSwitch32, + OpSwitch64, + OpConstant, + OpConstant32, + OpConstant64, + OpSpecConstant, + OpSpecConstant32, + OpSpecConstant64, + OpSpecConstantOp, + OpAccessChain, + OpBitcast, + OpBitwiseAnd, + OpBitwiseOr, + OpBitwiseXor, + OpCompositeExtract, + OpCompositeInsert, + OpConvertFToS, + OpConvertFToU, + OpConvertPtrToU, + OpConvertSToF, + OpConvertUToF, + OpConvertUToPtr, + OpFAdd, + OpFConvert, + OpFDiv, + OpFMod, + OpFMul, + OpFNegate, + OpFRem, + OpFSub, + OpGenericCastToPtr, + OpIAdd, + OpIEqual, + OpIMul, + OpINotEqual, + OpISub, + OpInBoundsAccessChain, + OpInBoundsPtrAccessChain, + OpLogicalAnd, + OpLogicalEqual, + OpLogicalNot, + OpLogicalNotEqual, + OpLogicalOr, + OpNot, + OpPtrAccessChain, + OpPtrCastToGeneric, + OpQuantizeToF16, + OpSConvert, + OpSDiv, + OpSGreaterThan, + OpSGreaterThanEqual, + OpSLessThan, + OpSLessThanEqual, + OpSMod, + OpSNegate, + OpSRem, + OpSelect, + OpShiftLeftLogical, + OpShiftRightArithmetic, + OpShiftRightLogical, + OpUConvert, + OpUDiv, + OpUGreaterThan, + OpUGreaterThanEqual, + OpULessThan, + OpULessThanEqual, + OpUMod, + OpVectorShuffle, + Other(String), +} + +pub const OP_SPEC_CONSTANT_OP_SUPPORTED_INSTRUCTIONS: &[InstructionName] = &[ + InstructionName::OpAccessChain, + InstructionName::OpBitcast, + InstructionName::OpBitwiseAnd, + InstructionName::OpBitwiseOr, + InstructionName::OpBitwiseXor, + InstructionName::OpCompositeExtract, + InstructionName::OpCompositeInsert, + InstructionName::OpConvertFToS, + InstructionName::OpConvertFToU, + InstructionName::OpConvertPtrToU, + InstructionName::OpConvertSToF, + InstructionName::OpConvertUToF, + InstructionName::OpConvertUToPtr, + InstructionName::OpFAdd, + InstructionName::OpFConvert, + InstructionName::OpFDiv, + InstructionName::OpFMod, + InstructionName::OpFMul, + InstructionName::OpFNegate, + InstructionName::OpFRem, + InstructionName::OpFSub, + InstructionName::OpGenericCastToPtr, + InstructionName::OpIAdd, + InstructionName::OpIEqual, + InstructionName::OpIMul, + InstructionName::OpINotEqual, + InstructionName::OpISub, + InstructionName::OpInBoundsAccessChain, + InstructionName::OpInBoundsPtrAccessChain, + InstructionName::OpLogicalAnd, + InstructionName::OpLogicalEqual, + InstructionName::OpLogicalNot, + InstructionName::OpLogicalNotEqual, + InstructionName::OpLogicalOr, + InstructionName::OpNot, + InstructionName::OpPtrAccessChain, + InstructionName::OpPtrCastToGeneric, + InstructionName::OpQuantizeToF16, + InstructionName::OpSConvert, + InstructionName::OpSDiv, + InstructionName::OpSGreaterThan, + InstructionName::OpSGreaterThanEqual, + InstructionName::OpSLessThan, + InstructionName::OpSLessThanEqual, + InstructionName::OpSMod, + InstructionName::OpSNegate, + InstructionName::OpSRem, + InstructionName::OpSelect, + InstructionName::OpShiftLeftLogical, + InstructionName::OpShiftRightArithmetic, + InstructionName::OpShiftRightLogical, + InstructionName::OpUConvert, + InstructionName::OpUDiv, + InstructionName::OpUGreaterThan, + InstructionName::OpUGreaterThanEqual, + InstructionName::OpULessThan, + InstructionName::OpULessThanEqual, + InstructionName::OpUMod, + InstructionName::OpVectorShuffle, +]; + +impl Default for InstructionName { + fn default() -> Self { + InstructionName::Other(String::new()) + } +} + +impl From for InstructionName { + fn from(v: String) -> Self { + match &*v { + "OpSwitch" => return InstructionName::OpSwitch, + "OpConstant" => return InstructionName::OpConstant, + "OpSpecConstant" => return InstructionName::OpSpecConstant, + "OpSpecConstantOp" => return InstructionName::OpSpecConstantOp, + "OpAccessChain" => return InstructionName::OpAccessChain, + "OpBitcast" => return InstructionName::OpBitcast, + "OpBitwiseAnd" => return InstructionName::OpBitwiseAnd, + "OpBitwiseOr" => return InstructionName::OpBitwiseOr, + "OpBitwiseXor" => return InstructionName::OpBitwiseXor, + "OpCompositeExtract" => return InstructionName::OpCompositeExtract, + "OpCompositeInsert" => return InstructionName::OpCompositeInsert, + "OpConvertFToS" => return InstructionName::OpConvertFToS, + "OpConvertFToU" => return InstructionName::OpConvertFToU, + "OpConvertPtrToU" => return InstructionName::OpConvertPtrToU, + "OpConvertSToF" => return InstructionName::OpConvertSToF, + "OpConvertUToF" => return InstructionName::OpConvertUToF, + "OpConvertUToPtr" => return InstructionName::OpConvertUToPtr, + "OpFAdd" => return InstructionName::OpFAdd, + "OpFConvert" => return InstructionName::OpFConvert, + "OpFDiv" => return InstructionName::OpFDiv, + "OpFMod" => return InstructionName::OpFMod, + "OpFMul" => return InstructionName::OpFMul, + "OpFNegate" => return InstructionName::OpFNegate, + "OpFRem" => return InstructionName::OpFRem, + "OpFSub" => return InstructionName::OpFSub, + "OpGenericCastToPtr" => return InstructionName::OpGenericCastToPtr, + "OpIAdd" => return InstructionName::OpIAdd, + "OpIEqual" => return InstructionName::OpIEqual, + "OpIMul" => return InstructionName::OpIMul, + "OpINotEqual" => return InstructionName::OpINotEqual, + "OpISub" => return InstructionName::OpISub, + "OpInBoundsAccessChain" => return InstructionName::OpInBoundsAccessChain, + "OpInBoundsPtrAccessChain" => return InstructionName::OpInBoundsPtrAccessChain, + "OpLogicalAnd" => return InstructionName::OpLogicalAnd, + "OpLogicalEqual" => return InstructionName::OpLogicalEqual, + "OpLogicalNot" => return InstructionName::OpLogicalNot, + "OpLogicalNotEqual" => return InstructionName::OpLogicalNotEqual, + "OpLogicalOr" => return InstructionName::OpLogicalOr, + "OpNot" => return InstructionName::OpNot, + "OpPtrAccessChain" => return InstructionName::OpPtrAccessChain, + "OpPtrCastToGeneric" => return InstructionName::OpPtrCastToGeneric, + "OpQuantizeToF16" => return InstructionName::OpQuantizeToF16, + "OpSConvert" => return InstructionName::OpSConvert, + "OpSDiv" => return InstructionName::OpSDiv, + "OpSGreaterThan" => return InstructionName::OpSGreaterThan, + "OpSGreaterThanEqual" => return InstructionName::OpSGreaterThanEqual, + "OpSLessThan" => return InstructionName::OpSLessThan, + "OpSLessThanEqual" => return InstructionName::OpSLessThanEqual, + "OpSMod" => return InstructionName::OpSMod, + "OpSNegate" => return InstructionName::OpSNegate, + "OpSRem" => return InstructionName::OpSRem, + "OpSelect" => return InstructionName::OpSelect, + "OpShiftLeftLogical" => return InstructionName::OpShiftLeftLogical, + "OpShiftRightArithmetic" => return InstructionName::OpShiftRightArithmetic, + "OpShiftRightLogical" => return InstructionName::OpShiftRightLogical, + "OpUConvert" => return InstructionName::OpUConvert, + "OpUDiv" => return InstructionName::OpUDiv, + "OpUGreaterThan" => return InstructionName::OpUGreaterThan, + "OpUGreaterThanEqual" => return InstructionName::OpUGreaterThanEqual, + "OpULessThan" => return InstructionName::OpULessThan, + "OpULessThanEqual" => return InstructionName::OpULessThanEqual, + "OpUMod" => return InstructionName::OpUMod, + "OpVectorShuffle" => return InstructionName::OpVectorShuffle, + _ => {} + } + InstructionName::Other(v) + } +} + +impl AsRef for InstructionName { + fn as_ref(&self) -> &str { + match self { + InstructionName::OpSwitch => "OpSwitch", + InstructionName::OpSwitch32 => "OpSwitch32", + InstructionName::OpSwitch64 => "OpSwitch64", + InstructionName::OpConstant => "OpConstant", + InstructionName::OpConstant32 => "OpConstant32", + InstructionName::OpConstant64 => "OpConstant64", + InstructionName::OpSpecConstant => "OpSpecConstant", + InstructionName::OpSpecConstant32 => "OpSpecConstant32", + InstructionName::OpSpecConstant64 => "OpSpecConstant64", + InstructionName::OpSpecConstantOp => "OpSpecConstantOp", + InstructionName::OpAccessChain => "OpAccessChain", + InstructionName::OpBitcast => "OpBitcast", + InstructionName::OpBitwiseAnd => "OpBitwiseAnd", + InstructionName::OpBitwiseOr => "OpBitwiseOr", + InstructionName::OpBitwiseXor => "OpBitwiseXor", + InstructionName::OpCompositeExtract => "OpCompositeExtract", + InstructionName::OpCompositeInsert => "OpCompositeInsert", + InstructionName::OpConvertFToS => "OpConvertFToS", + InstructionName::OpConvertFToU => "OpConvertFToU", + InstructionName::OpConvertPtrToU => "OpConvertPtrToU", + InstructionName::OpConvertSToF => "OpConvertSToF", + InstructionName::OpConvertUToF => "OpConvertUToF", + InstructionName::OpConvertUToPtr => "OpConvertUToPtr", + InstructionName::OpFAdd => "OpFAdd", + InstructionName::OpFConvert => "OpFConvert", + InstructionName::OpFDiv => "OpFDiv", + InstructionName::OpFMod => "OpFMod", + InstructionName::OpFMul => "OpFMul", + InstructionName::OpFNegate => "OpFNegate", + InstructionName::OpFRem => "OpFRem", + InstructionName::OpFSub => "OpFSub", + InstructionName::OpGenericCastToPtr => "OpGenericCastToPtr", + InstructionName::OpIAdd => "OpIAdd", + InstructionName::OpIEqual => "OpIEqual", + InstructionName::OpIMul => "OpIMul", + InstructionName::OpINotEqual => "OpINotEqual", + InstructionName::OpISub => "OpISub", + InstructionName::OpInBoundsAccessChain => "OpInBoundsAccessChain", + InstructionName::OpInBoundsPtrAccessChain => "OpInBoundsPtrAccessChain", + InstructionName::OpLogicalAnd => "OpLogicalAnd", + InstructionName::OpLogicalEqual => "OpLogicalEqual", + InstructionName::OpLogicalNot => "OpLogicalNot", + InstructionName::OpLogicalNotEqual => "OpLogicalNotEqual", + InstructionName::OpLogicalOr => "OpLogicalOr", + InstructionName::OpNot => "OpNot", + InstructionName::OpPtrAccessChain => "OpPtrAccessChain", + InstructionName::OpPtrCastToGeneric => "OpPtrCastToGeneric", + InstructionName::OpQuantizeToF16 => "OpQuantizeToF16", + InstructionName::OpSConvert => "OpSConvert", + InstructionName::OpSDiv => "OpSDiv", + InstructionName::OpSGreaterThan => "OpSGreaterThan", + InstructionName::OpSGreaterThanEqual => "OpSGreaterThanEqual", + InstructionName::OpSLessThan => "OpSLessThan", + InstructionName::OpSLessThanEqual => "OpSLessThanEqual", + InstructionName::OpSMod => "OpSMod", + InstructionName::OpSNegate => "OpSNegate", + InstructionName::OpSRem => "OpSRem", + InstructionName::OpSelect => "OpSelect", + InstructionName::OpShiftLeftLogical => "OpShiftLeftLogical", + InstructionName::OpShiftRightArithmetic => "OpShiftRightArithmetic", + InstructionName::OpShiftRightLogical => "OpShiftRightLogical", + InstructionName::OpUConvert => "OpUConvert", + InstructionName::OpUDiv => "OpUDiv", + InstructionName::OpUGreaterThan => "OpUGreaterThan", + InstructionName::OpUGreaterThanEqual => "OpUGreaterThanEqual", + InstructionName::OpULessThan => "OpULessThan", + InstructionName::OpULessThanEqual => "OpULessThanEqual", + InstructionName::OpUMod => "OpUMod", + InstructionName::OpVectorShuffle => "OpVectorShuffle", + InstructionName::Other(v) => v, + } + } +} + +impl<'de> Deserialize<'de> for InstructionName { + fn deserialize>(deserializer: D) -> Result { + Ok(Self::from(String::deserialize(deserializer)?)) + } +} + +#[derive(Clone, Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct Instruction { - opname: String, - opcode: u16, + pub opname: InstructionName, + pub opcode: u16, #[serde(default)] - operands: Vec, + pub operands: Vec, #[serde(default)] - capabilities: Vec, + pub capabilities: Vec, #[serde(default)] - extensions: Vec, + pub extensions: Vec, #[serde(default)] - version: SPIRVVersion, + pub version: SPIRVVersion, } impl Instruction { - pub fn guess_names(&mut self) -> Result<(), ::Error> { + pub fn fixup(&mut self) -> Result<(), ::Error> { for operand in self.operands.iter_mut() { - operand.guess_name()?; + operand.fixup()?; } Ok(()) } @@ -169,133 +502,402 @@ impl Instruction { #[derive(Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct ExtensionInstruction { - opname: String, - opcode: u16, + pub opname: String, + pub opcode: u16, #[serde(default)] - operands: Vec, + pub operands: Vec, #[serde(default)] - capabilities: Vec, + pub capabilities: Vec, } impl ExtensionInstruction { - pub fn guess_names(&mut self) -> Result<(), ::Error> { + pub fn fixup(&mut self) -> Result<(), ::Error> { for operand in self.operands.iter_mut() { - operand.guess_name()?; + operand.fixup()?; } Ok(()) } } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Default)] +#[serde(deny_unknown_fields)] +pub struct BitwiseEnumerantParameter { + pub kind: Kind, +} + +impl BitwiseEnumerantParameter { + pub fn fixup(&mut self) -> Result<(), ::Error> { + self.kind.set_bit_width(BitWidth::Bits32); + Ok(()) + } +} + +#[derive(Deserialize, Debug, Default)] #[serde(deny_unknown_fields)] -pub struct EnumerantParameter { - kind: String, - name: Option, +pub struct ValueEnumerantParameter { + pub kind: Kind, + pub name: Option, } -impl EnumerantParameter { - pub fn guess_name(&mut self) -> Result<(), ::Error> { +impl ValueEnumerantParameter { + pub fn fixup(&mut self) -> Result<(), ::Error> { if self.name.is_none() { self.name = Some( SnakeCase - .name_from_words(WordIterator::new(&self.kind)) + .name_from_words(WordIterator::new(self.kind.as_ref())) .ok_or(::Error::DeducingNameForEnumerantParameterFailed)?, ); } + self.kind.set_bit_width(BitWidth::Bits32); Ok(()) } } #[derive(Deserialize, Debug)] #[serde(deny_unknown_fields)] -pub struct Enumerant { - enumerant: String, - value: Value, +pub struct Enumerant { + pub enumerant: String, + pub value: Value, #[serde(default)] - capabilities: Vec, + pub capabilities: Vec, #[serde(default)] - parameters: Vec, + pub parameters: Vec, #[serde(default)] - extensions: Vec, + pub extensions: Vec, #[serde(default)] - version: SPIRVVersion, + pub version: SPIRVVersion, +} + +impl Enumerant { + pub fn fixup(&mut self) -> Result<(), ::Error> { + for parameter in self.parameters.iter_mut() { + parameter.fixup()?; + } + Ok(()) + } } -impl Enumerant { - pub fn guess_names(&mut self) -> Result<(), ::Error> { +impl Enumerant, BitwiseEnumerantParameter> { + pub fn fixup(&mut self) -> Result<(), ::Error> { for parameter in self.parameters.iter_mut() { - parameter.guess_name()?; + parameter.fixup()?; } Ok(()) } } +#[derive(Clone, Eq, PartialEq, Hash, Debug)] +pub enum Kind { + Literal(LiteralKind), + IdRef, + PairLiteralIntegerIdRef, + PairLiteralInteger32IdRef, + PairLiteralInteger64IdRef, + Other(String), +} + +impl Kind { + pub fn set_bit_width(&mut self, bit_width: BitWidth) { + match (self, bit_width) { + (Kind::Literal(literal), bit_width) => literal.set_bit_width(bit_width), + (this @ Kind::PairLiteralIntegerIdRef, BitWidth::Bits32) => { + *this = Kind::PairLiteralInteger32IdRef + } + (this @ Kind::PairLiteralIntegerIdRef, BitWidth::Bits64) => { + *this = Kind::PairLiteralInteger64IdRef + } + (Kind::IdRef, _) + | (Kind::PairLiteralInteger32IdRef, _) + | (Kind::PairLiteralInteger64IdRef, _) + | (Kind::Other(_), _) => {} + } + } +} + +impl Default for Kind { + fn default() -> Self { + Kind::Other(String::new()) + } +} + +impl<'a> From> for Kind { + fn from(v: Cow<'a, str>) -> Self { + if let Some(v) = LiteralKind::from_str(&v) { + Kind::Literal(v) + } else if v == "IdRef" { + Kind::IdRef + } else if v == "PairLiteralIntegerIdRef" { + Kind::PairLiteralIntegerIdRef + } else { + Kind::Other(v.into_owned()) + } + } +} + +impl<'a> From<&'a str> for Kind { + fn from(v: &'a str) -> Self { + Kind::from(Cow::Borrowed(v)) + } +} + +impl From for Kind { + fn from(v: String) -> Self { + Kind::from(Cow::Owned(v)) + } +} + +impl AsRef for Kind { + fn as_ref(&self) -> &str { + match self { + Kind::Literal(v) => v.as_ref(), + Kind::IdRef => "IdRef", + Kind::PairLiteralIntegerIdRef => "PairLiteralIntegerIdRef", + Kind::PairLiteralInteger32IdRef => "PairLiteralInteger32IdRef", + Kind::PairLiteralInteger64IdRef => "PairLiteralInteger64IdRef", + Kind::Other(v) => v, + } + } +} + +impl<'de> Deserialize<'de> for Kind { + fn deserialize>(deserializer: D) -> Result { + Ok(Self::from(String::deserialize(deserializer)?)) + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Deserialize)] +pub enum LiteralKind { + LiteralInteger, + #[serde(skip_deserializing)] + LiteralInteger32, + #[serde(skip_deserializing)] + LiteralInteger64, + LiteralString, + LiteralContextDependentNumber, + #[serde(skip_deserializing)] + LiteralContextDependentNumber32, + #[serde(skip_deserializing)] + LiteralContextDependentNumber64, + LiteralExtInstInteger, + LiteralSpecConstantOpInteger, +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub enum BitWidth { + Bits32, + Bits64, +} + +impl LiteralKind { + pub fn from_str>(v: T) -> Option { + match v.as_ref() { + "LiteralInteger" => Some(LiteralKind::LiteralInteger), + "LiteralString" => Some(LiteralKind::LiteralString), + "LiteralContextDependentNumber" => Some(LiteralKind::LiteralContextDependentNumber), + "LiteralExtInstInteger" => Some(LiteralKind::LiteralExtInstInteger), + "LiteralSpecConstantOpInteger" => Some(LiteralKind::LiteralSpecConstantOpInteger), + _ => None, + } + } + pub fn set_bit_width(&mut self, bit_width: BitWidth) { + *self = match (*self, bit_width) { + (LiteralKind::LiteralInteger, BitWidth::Bits32) => LiteralKind::LiteralInteger32, + (LiteralKind::LiteralInteger, BitWidth::Bits64) => LiteralKind::LiteralInteger64, + (LiteralKind::LiteralContextDependentNumber, BitWidth::Bits32) => { + LiteralKind::LiteralContextDependentNumber32 + } + (LiteralKind::LiteralContextDependentNumber, BitWidth::Bits64) => { + LiteralKind::LiteralContextDependentNumber64 + } + (LiteralKind::LiteralInteger32, _) + | (LiteralKind::LiteralInteger64, _) + | (LiteralKind::LiteralString, _) + | (LiteralKind::LiteralContextDependentNumber32, _) + | (LiteralKind::LiteralContextDependentNumber64, _) + | (LiteralKind::LiteralExtInstInteger, _) + | (LiteralKind::LiteralSpecConstantOpInteger, _) => return, + } + } +} + +impl AsRef for LiteralKind { + fn as_ref(&self) -> &str { + match self { + LiteralKind::LiteralInteger => "LiteralInteger", + LiteralKind::LiteralInteger32 => "LiteralInteger32", + LiteralKind::LiteralInteger64 => "LiteralInteger64", + LiteralKind::LiteralString => "LiteralString", + LiteralKind::LiteralContextDependentNumber => "LiteralContextDependentNumber", + LiteralKind::LiteralContextDependentNumber32 => "LiteralContextDependentNumber32", + LiteralKind::LiteralContextDependentNumber64 => "LiteralContextDependentNumber64", + LiteralKind::LiteralExtInstInteger => "LiteralExtInstInteger", + LiteralKind::LiteralSpecConstantOpInteger => "LiteralSpecConstantOpInteger", + } + } +} + #[derive(Deserialize, Debug)] #[serde(deny_unknown_fields)] #[serde(tag = "category")] pub enum OperandKind { BitEnum { - kind: String, - enumerants: Vec>, + kind: Kind, + enumerants: Vec, BitwiseEnumerantParameter>>, }, ValueEnum { - kind: String, - enumerants: Vec>, + kind: Kind, + enumerants: Vec>, }, Id { - kind: String, + kind: Kind, doc: Option, }, Literal { - kind: String, + kind: LiteralKind, doc: Option, }, Composite { - kind: String, - bases: Vec, + kind: Kind, + bases: Vec, }, } -impl OperandKind { - pub fn guess_names(&mut self) -> Result<(), ::Error> { - match self { - OperandKind::BitEnum { enumerants, .. } => { - for enumerant in enumerants.iter_mut() { - enumerant.guess_names()?; - } - } - OperandKind::ValueEnum { enumerants, .. } => { - for enumerant in enumerants.iter_mut() { - enumerant.guess_names()?; - } - } - OperandKind::Id { .. } - | OperandKind::Literal { .. } - | OperandKind::Composite { .. } => {} - } - Ok(()) - } -} - #[derive(Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct CoreGrammar { - copyright: Vec, - magic_number: QuotedInteger, - major_version: u16, - minor_version: u16, - revision: u32, - instructions: Vec, - operand_kinds: Vec, + pub copyright: Vec, + pub magic_number: QuotedInteger, + pub major_version: u32, + pub minor_version: u32, + pub revision: u32, + pub instructions: Vec, + pub operand_kinds: Vec, } impl CoreGrammar { - pub fn guess_names(&mut self) -> Result<(), ::Error> { - for instruction in self.instructions.iter_mut() { - instruction.guess_names()?; + pub fn fixup(&mut self) -> Result<(), ::Error> { + let instructions = mem::replace(&mut self.instructions, Vec::new()); + for mut instruction in instructions { + if instruction.version == SPIRVVersion::None { + continue; + } + let (opname_32, opname_64) = match instruction.opname { + InstructionName::OpSwitch => { + (InstructionName::OpSwitch32, InstructionName::OpSwitch64) + } + InstructionName::OpConstant => { + (InstructionName::OpConstant32, InstructionName::OpConstant64) + } + InstructionName::OpSpecConstant => ( + InstructionName::OpSpecConstant32, + InstructionName::OpSpecConstant64, + ), + opname => { + instruction.opname = opname; + instruction.fixup()?; + self.instructions.push(instruction); + continue; + } + }; + instruction.opname = InstructionName::default(); + let mut op_32 = Instruction { + opname: opname_32, + ..instruction.clone() + }; + for operand in op_32.operands.iter_mut() { + operand.kind.set_bit_width(BitWidth::Bits32); + } + op_32.fixup()?; + self.instructions.push(op_32); + let mut op_64 = Instruction { + opname: opname_64, + ..instruction + }; + for operand in op_64.operands.iter_mut() { + operand.kind.set_bit_width(BitWidth::Bits64); + } + op_64.fixup()?; + self.instructions.push(op_64); } - for operand_kind in self.operand_kinds.iter_mut() { - operand_kind.guess_names()?; + let operand_kinds = mem::replace(&mut self.operand_kinds, Vec::new()); + for operand_kind in operand_kinds { + match operand_kind { + OperandKind::BitEnum { + kind, + mut enumerants, + } => { + enumerants.retain(|enumerant| enumerant.version != SPIRVVersion::None); + for enumerant in enumerants.iter_mut() { + enumerant.fixup()?; + } + self.operand_kinds + .push(OperandKind::BitEnum { kind, enumerants }); + } + OperandKind::ValueEnum { + kind, + mut enumerants, + } => { + enumerants.retain(|enumerant| enumerant.version != SPIRVVersion::None); + for enumerant in enumerants.iter_mut() { + enumerant.fixup()?; + } + self.operand_kinds + .push(OperandKind::ValueEnum { kind, enumerants }); + } + OperandKind::Composite { kind, mut bases } => match kind { + Kind::PairLiteralIntegerIdRef => { + let mut bases_32 = bases.clone(); + let mut bases_64 = bases; + for base in bases_32.iter_mut() { + base.set_bit_width(BitWidth::Bits32); + } + for base in bases_64.iter_mut() { + base.set_bit_width(BitWidth::Bits64); + } + self.operand_kinds.push(OperandKind::Composite { + kind: Kind::PairLiteralInteger32IdRef, + bases: bases_32, + }); + self.operand_kinds.push(OperandKind::Composite { + kind: Kind::PairLiteralInteger64IdRef, + bases: bases_64, + }); + } + kind => { + for base in bases.iter_mut() { + base.set_bit_width(BitWidth::Bits32); + } + self.operand_kinds + .push(OperandKind::Composite { kind, bases }); + } + }, + OperandKind::Literal { kind, doc } => match kind { + LiteralKind::LiteralInteger => { + self.operand_kinds.push(OperandKind::Literal { + kind: LiteralKind::LiteralInteger32, + doc: doc.clone(), + }); + self.operand_kinds.push(OperandKind::Literal { + kind: LiteralKind::LiteralInteger64, + doc, + }); + } + LiteralKind::LiteralContextDependentNumber => { + self.operand_kinds.push(OperandKind::Literal { + kind: LiteralKind::LiteralContextDependentNumber32, + doc: doc.clone(), + }); + self.operand_kinds.push(OperandKind::Literal { + kind: LiteralKind::LiteralContextDependentNumber64, + doc, + }); + } + kind => self.operand_kinds.push(OperandKind::Literal { kind, doc }), + }, + OperandKind::Id { kind, doc } => { + self.operand_kinds.push(OperandKind::Id { kind, doc }) + } + } } Ok(()) } @@ -304,16 +906,16 @@ impl CoreGrammar { #[derive(Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct ExtensionInstructionSet { - copyright: Vec, - version: u32, - revision: u32, - instructions: Vec, + pub copyright: Vec, + pub version: u32, + pub revision: u32, + pub instructions: Vec, } impl ExtensionInstructionSet { - pub fn guess_names(&mut self) -> Result<(), ::Error> { + pub fn fixup(&mut self) -> Result<(), ::Error> { for instruction in self.instructions.iter_mut() { - instruction.guess_names()?; + instruction.fixup()?; } Ok(()) } diff --git a/spirv-parser-generator/src/generate.rs b/spirv-parser-generator/src/generate.rs new file mode 100644 index 0000000..b3d8884 --- /dev/null +++ b/spirv-parser-generator/src/generate.rs @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright 2018 Jacob Lifshay + +use ast; +use proc_macro2; +use std::borrow::Cow; +use std::collections::HashMap; +use std::fmt; +use std::io::{self, Read, Write}; +use std::process::{Child, Command, ExitStatus, Stdio}; +use std::thread; +use util::{self, NameFormat::*}; +use which; +use Error; +use Options; + +#[derive(Debug)] +enum FormatError { + IOError(io::Error), + WhichError(which::Error), + RustFmtFailed(ExitStatus), +} + +impl fmt::Display for FormatError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + FormatError::IOError(v) => fmt::Display::fmt(v, f), + FormatError::WhichError(v) => fmt::Display::fmt(v, f), + FormatError::RustFmtFailed(v) => write!(f, "rustfmt failed: {:?}", v), + } + } +} + +impl From for FormatError { + fn from(v: which::Error) -> Self { + FormatError::WhichError(v) + } +} + +impl From for FormatError { + fn from(v: io::Error) -> Self { + FormatError::IOError(v) + } +} + +fn format_source<'a>(options: &Options, source: &'a str) -> Result, FormatError> { + if !options.run_rustfmt { + return Ok(Cow::Borrowed(source)); + } + let rustfmt_path = match options.rustfmt_path.clone() { + Some(v) => v, + None => which::which("rustfmt")?, + }; + let mut command = Command::new(rustfmt_path) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn()?; + let stdin = command.stdin.take().unwrap(); + let reader_thread = thread::spawn(move || -> io::Result<(String, Child)> { + let mut output = String::new(); + command.stdout.take().unwrap().read_to_string(&mut output)?; + Ok((output, command)) + }); + { stdin }.write_all(source.as_bytes())?; + let (output, mut command) = reader_thread.join().unwrap()?; + let exit_status = command.wait()?; + if exit_status.success() { + Ok(Cow::Owned(output)) + } else { + Err(FormatError::RustFmtFailed(exit_status)) + } +} + +fn remove_initial_op(name: &str) -> &str { + const INITIAL_OP: &str = "Op"; + assert!(name.starts_with(INITIAL_OP)); + &name[INITIAL_OP.len()..] +} + +fn new_id>(name: T, name_format: util::NameFormat) -> proc_macro2::Ident { + proc_macro2::Ident::new( + &name_format + .name_from_words(util::WordIterator::new(name.as_ref())) + .unwrap(), + proc_macro2::Span::call_site(), + ) +} + +fn new_enumerant_id, T2: AsRef>( + enum_name: T1, + enumerant_name: T2, +) -> proc_macro2::Ident { + let enumerant_name_words = util::WordIterator::new(enumerant_name.as_ref()); + let enumerant_name_first_word = enumerant_name_words.clone().next(); + let name = if enumerant_name_first_word + .map(str::chars) + .as_mut() + .and_then(Iterator::next) + .filter(char::is_ascii_digit) + .is_some() + { + CamelCase + .name_from_words( + util::WordIterator::new(enum_name.as_ref()).chain(enumerant_name_words), + ) + .unwrap() + } else { + CamelCase.name_from_words(enumerant_name_words).unwrap() + }; + proc_macro2::Ident::new(&name, proc_macro2::Span::call_site()) +} + +fn new_combined_id(names: I, name_format: util::NameFormat) -> proc_macro2::Ident +where + I::Item: AsRef, +{ + let names: Vec = names.into_iter().collect(); + proc_macro2::Ident::new( + &name_format + .name_from_words( + names + .iter() + .map(AsRef::as_ref) + .flat_map(util::WordIterator::new), + ) + .unwrap(), + proc_macro2::Span::call_site(), + ) +} + +#[cfg_attr(feature = "cargo-clippy", allow(clippy::cyclomatic_complexity))] +pub(crate) fn generate( + core_grammar: ast::CoreGrammar, + parsed_extension_instruction_sets: HashMap< + super::ExtensionInstructionSet, + ast::ExtensionInstructionSet, + >, + options: &Options, +) -> Result { + let mut out = Vec::new(); + let ast::CoreGrammar { + copyright: core_grammar_copyright, + magic_number, + major_version, + minor_version, + revision: core_revision, + instructions: core_instructions, + operand_kinds, + } = core_grammar; + writeln!(&mut out, "// automatically generated file")?; + writeln!(&mut out, "//")?; + for i in &core_grammar_copyright { + assert_eq!(i.find('\r'), None); + assert_eq!(i.find('\n'), None); + if i == "" { + writeln!(&mut out, "//"); + } else { + writeln!(&mut out, "// {}", i); + } + } + writeln!( + &mut out, + "{}", + quote!{ + pub const MAGIC_NUMBER: u32 = #magic_number; + pub const MAJOR_VERSION: u32 = #major_version; + pub const MINOR_VERSION: u32 = #minor_version; + pub const REVISION: u32 = #core_revision; + } + )?; + for operand_kind in &operand_kinds { + match operand_kind { + ast::OperandKind::BitEnum { kind, enumerants } => { + let mut enumerant_members = Vec::new(); + let mut enumerant_member_names = Vec::new(); + let mut enumerant_items = Vec::new(); + for enumerant in enumerants { + if enumerant.value.0 == 0 { + continue; + } + let member_name = new_id(&enumerant.enumerant, SnakeCase); + enumerant_member_names.push(member_name.clone()); + let type_name = + new_combined_id(&[kind.as_ref(), &enumerant.enumerant], CamelCase); + if enumerant.parameters.is_empty() { + enumerant_items.push(quote!{ + #[derive(Clone, Debug, Default)] + pub struct #type_name; + }); + } else { + let parameters = enumerant.parameters.iter().map(|parameter| { + let kind = new_id(¶meter.kind, CamelCase); + quote!{ + pub #kind, + } + }); + enumerant_items.push(quote!{ + #[derive(Clone, Debug, Default)] + pub struct #type_name(#(#parameters)*); + }); + } + enumerant_members.push(quote!{ + pub #member_name: Option<#type_name> + }); + } + let kind_id = new_id(kind, CamelCase); + writeln!( + &mut out, + "{}", + quote!{ + #[derive(Clone, Debug, Default)] + pub struct #kind_id { + #(#enumerant_members),* + } + impl #kind_id { + pub fn new() -> Self { + Self { + #(#enumerant_member_names: None,)* + } + } + } + #(#enumerant_items)* + } + )?; + } + ast::OperandKind::ValueEnum { kind, enumerants } => { + let kind_id = new_id(&kind, CamelCase); + let mut generated_enumerants = Vec::new(); + for enumerant in enumerants { + let name = new_enumerant_id(&kind, &enumerant.enumerant); + if enumerant.parameters.is_empty() { + generated_enumerants.push(quote!{#name}); + continue; + } + } + writeln!( + &mut out, + "{}", + quote!{ + #[derive(Clone, Debug)] + pub enum #kind_id { + #(#generated_enumerants,)* + } + } + )?; + } + ast::OperandKind::Id { kind, doc: _ } => { + let base = if *kind == ast::Kind::IdRef { + quote!{u32} + } else { + quote!{IdRef} + }; + let kind_id = new_id(kind, CamelCase); + writeln!( + &mut out, + "{}", + quote!{ + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] + #[repr(transparent)] + pub struct #kind_id(pub #base); + } + )?; + } + ast::OperandKind::Literal { kind, doc: _ } => { + let kind_id = new_id(kind, CamelCase); + writeln!( + &mut out, + "{}", + match kind { + ast::LiteralKind::LiteralInteger + | ast::LiteralKind::LiteralContextDependentNumber => unreachable!(), + ast::LiteralKind::LiteralInteger32 + | ast::LiteralKind::LiteralContextDependentNumber32 => { + quote!{pub type #kind_id = u32;} + } + ast::LiteralKind::LiteralInteger64 + | ast::LiteralKind::LiteralContextDependentNumber64 => { + quote!{pub type #kind_id = u64;} + } + ast::LiteralKind::LiteralString => quote!{pub type #kind_id = String;}, + ast::LiteralKind::LiteralExtInstInteger => { + quote!{pub type #kind_id = u32;} + } + ast::LiteralKind::LiteralSpecConstantOpInteger => continue, + } + )?; + } + ast::OperandKind::Composite { kind, bases } => { + let kind = new_id(kind, CamelCase); + let bases = bases.into_iter().map(|base| new_id(base, CamelCase)); + writeln!(&mut out, "{}", quote!{pub type #kind = (#(#bases),*);})?; + } + } + } + { + let mut instruction_enumerants = Vec::new(); + let mut spec_constant_op_instruction_enumerants = Vec::new(); + for instruction in core_instructions.iter() { + let opname = new_id(remove_initial_op(instruction.opname.as_ref()), CamelCase); + let instruction_enumerant = + if instruction.opname == ast::InstructionName::OpSpecConstantOp { + quote!{ + #opname { + operation: OpSpecConstantOp, + } + } + } else if instruction.operands.is_empty() { + quote!{#opname} + } else { + let mut fields = Vec::new(); + for operand in instruction.operands.iter() { + let kind = new_id(&operand.kind, CamelCase); + let name = new_id(operand.name.as_ref().unwrap(), SnakeCase); + let kind = match &operand.quantifier { + None => quote!{#kind}, + Some(ast::Quantifier::Optional) => quote!{Option<#kind>}, + Some(ast::Quantifier::Variadic) => quote!{Vec<#kind>}, + }; + fields.push(quote!{#name: #kind}); + } + quote!{ + #opname { + #(#fields,)* + } + } + }; + if ast::OP_SPEC_CONSTANT_OP_SUPPORTED_INSTRUCTIONS.contains(&instruction.opname) { + spec_constant_op_instruction_enumerants.push(instruction_enumerant.clone()); + } + instruction_enumerants.push(instruction_enumerant); + } + writeln!( + &mut out, + "{}", + quote!{ + #[derive(Clone, Debug)] + pub enum OpSpecConstantOp { + #(#spec_constant_op_instruction_enumerants,)* + } + #[derive(Clone, Debug)] + pub enum Instruction { + #(#instruction_enumerants,)* + } + } + )?; + } + let source = String::from_utf8(out).unwrap(); + let source = match format_source(&options, &source) { + Ok(source) => source.into_owned(), + Err(error) => { + eprintln!("formatting source failed: {}", error); + source.clone() + } + }; + Ok(source) +} diff --git a/spirv-parser-generator/src/lib.rs b/spirv-parser-generator/src/lib.rs index d931a6a..03df5e5 100644 --- a/spirv-parser-generator/src/lib.rs +++ b/spirv-parser-generator/src/lib.rs @@ -1,10 +1,17 @@ // SPDX-License-Identifier: LGPL-2.1-or-later // Copyright 2018 Jacob Lifshay +// allow unneeded_field_pattern to ensure fields aren't accidently missed +#![cfg_attr(feature = "cargo-clippy", allow(clippy::unneeded_field_pattern))] + +#[macro_use] +extern crate quote; +extern crate proc_macro2; #[macro_use] extern crate serde_derive; extern crate serde; extern crate serde_json; +extern crate which; use std::collections::HashMap; use std::error; @@ -15,6 +22,7 @@ use std::path::Path; use std::path::PathBuf; mod ast; +mod generate; mod util; pub const SPIRV_CORE_GRAMMAR_JSON_FILE_NAME: &str = "spirv.core.grammar.json"; @@ -88,20 +96,70 @@ impl From for io::Error { } } -pub struct Output {} +pub struct Output { + text: String, +} + +impl Output { + pub fn to_str(&self) -> &str { + &self.text + } + pub fn into_string(self) -> String { + self.text + } + pub fn write(&self, mut writer: W) -> Result<(), io::Error> { + write!(writer, "{}", self.text) + } + pub fn write_to_file>(&self, path: T) -> Result<(), io::Error> { + self.write(File::create(path)?) + } +} + +struct Options { + run_rustfmt: bool, + rustfmt_path: Option, +} + +impl Default for Options { + fn default() -> Self { + Self { + run_rustfmt: true, + rustfmt_path: None, + } + } +} pub struct Input { spirv_core_grammar_json_path: PathBuf, extension_instruction_sets: HashMap, + options: Options, +} + +fn get_spirv_grammar_path>(name: T) -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../external/SPIRV-Headers/include/spirv/unified1") + .join(name) } impl Input { + pub fn with_default_paths(extension_instruction_sets: &[ExtensionInstructionSet]) -> Input { + let mut retval = Self::new(get_spirv_grammar_path("spirv.core.grammar.json")); + for &extension_instruction_set in extension_instruction_sets { + retval = retval.add_extension_instruction_set( + extension_instruction_set, + get_spirv_grammar_path(extension_instruction_set.get_grammar_json_file_name()), + ); + } + retval + } pub fn new>(spirv_core_grammar_json_path: T) -> Input { Input { spirv_core_grammar_json_path: spirv_core_grammar_json_path.as_ref().into(), extension_instruction_sets: HashMap::new(), + options: Options::default(), } } + pub fn add_extension_instruction_set>( mut self, extension_instruction_set: ExtensionInstructionSet, @@ -120,10 +178,11 @@ impl Input { let Input { spirv_core_grammar_json_path, extension_instruction_sets, + options, } = self; let mut core_grammar: ast::CoreGrammar = serde_json::from_reader(File::open(spirv_core_grammar_json_path)?)?; - core_grammar.guess_names()?; + core_grammar.fixup()?; let mut parsed_extension_instruction_sets: HashMap< ExtensionInstructionSet, ast::ExtensionInstructionSet, @@ -131,52 +190,36 @@ impl Input { for (extension_instruction_set, path) in extension_instruction_sets { let mut parsed_extension_instruction_set: ast::ExtensionInstructionSet = serde_json::from_reader(File::open(path)?)?; - parsed_extension_instruction_set.guess_names()?; - assert!( - parsed_extension_instruction_sets - .insert(extension_instruction_set, parsed_extension_instruction_set) - .is_none() - ); + parsed_extension_instruction_set.fixup()?; + assert!(parsed_extension_instruction_sets + .insert(extension_instruction_set, parsed_extension_instruction_set) + .is_none()); } - unimplemented!() + Ok(Output { + text: generate::generate(core_grammar, parsed_extension_instruction_sets, &options)?, + }) } } #[cfg(test)] mod tests { use super::*; - fn get_spirv_grammar_path>(name: T) -> PathBuf { - Path::new(env!("CARGO_MANIFEST_DIR")) - .join("../external/SPIRV-Headers/include/spirv/unified1") - .join(name) - } - - fn create_input(extension_instruction_sets: &[ExtensionInstructionSet]) -> Input { - let mut retval = Input::new(get_spirv_grammar_path("spirv.core.grammar.json")); - for &extension_instruction_set in extension_instruction_sets { - retval = retval.add_extension_instruction_set( - extension_instruction_set, - get_spirv_grammar_path(extension_instruction_set.get_grammar_json_file_name()), - ); - } - retval - } #[test] fn parse_core_grammar() -> Result<(), Error> { - create_input(&[]).generate()?; + Input::with_default_paths(&[]).generate()?; Ok(()) } #[test] fn parse_core_grammar_with_opencl() -> Result<(), Error> { - create_input(&[ExtensionInstructionSet::OpenCLStd]).generate()?; + Input::with_default_paths(&[ExtensionInstructionSet::OpenCLStd]).generate()?; Ok(()) } #[test] fn parse_core_grammar_with_opencl_and_glsl() -> Result<(), Error> { - create_input(&[ + Input::with_default_paths(&[ ExtensionInstructionSet::OpenCLStd, ExtensionInstructionSet::GLSLStd450, ]) @@ -186,7 +229,7 @@ mod tests { #[test] fn parse_core_grammar_with_glsl() -> Result<(), Error> { - create_input(&[ExtensionInstructionSet::GLSLStd450]).generate()?; + Input::with_default_paths(&[ExtensionInstructionSet::GLSLStd450]).generate()?; Ok(()) } } diff --git a/spirv-parser-generator/src/util.rs b/spirv-parser-generator/src/util.rs index e82265d..1b2b5ba 100644 --- a/spirv-parser-generator/src/util.rs +++ b/spirv-parser-generator/src/util.rs @@ -23,20 +23,60 @@ impl From for CharClass { } } +#[derive(Clone)] pub struct WordIterator<'a> { word: Option<&'a str>, words: &'a str, + custom_word: &'static [&'static str], } impl<'a> WordIterator<'a> { pub fn new(words: &'a str) -> Self { - WordIterator { word: None, words } + WordIterator { + word: None, + words, + custom_word: &[], + } } } +struct CustomWord { + input: &'static str, + output: &'static [&'static str], +} + +const CUSTOM_WORDS: &[CustomWord] = &[CustomWord { + input: "NaN", + output: &["NaN"], +}]; + impl<'a> Iterator for WordIterator<'a> { type Item = &'a str; fn next(&mut self) -> Option<&'a str> { + if let Some((first, rest)) = self.custom_word.split_first() { + self.custom_word = rest; + return Some(first); + } + self.words = self + .words + .trim_left_matches(|ch| CharClass::from(ch) == CharClass::WordSeparator); + for custom_word in CUSTOM_WORDS { + if !self.words.starts_with(custom_word.input) { + continue; + } + match self.words[custom_word.input.len()..] + .chars() + .next() + .map(CharClass::from) + { + Some(CharClass::WordSeparator) | Some(CharClass::Uppercase) | None => {} + Some(CharClass::Number) | Some(CharClass::OtherIdentifier) => continue, + } + let (first, rest) = custom_word.output.split_first().unwrap(); + self.custom_word = rest; + self.words = &self.words[custom_word.input.len()..]; + return Some(first); + } let mut word_start = None; let mut last_char_class = CharClass::WordSeparator; for (i, ch) in self.words.char_indices() { @@ -93,6 +133,7 @@ pub const RUST_RESERVED_WORDS: &[&str] = &[ pub enum CharacterCase { Upper, Lower, + Unchanged, } impl CharacterCase { @@ -101,6 +142,7 @@ impl CharacterCase { match self { CharacterCase::Upper => retval.make_ascii_uppercase(), CharacterCase::Lower => retval.make_ascii_lowercase(), + CharacterCase::Unchanged => {} } retval } @@ -110,6 +152,7 @@ impl CharacterCase { match self { CharacterCase::Upper => first.make_ascii_uppercase(), CharacterCase::Lower => first.make_ascii_lowercase(), + CharacterCase::Unchanged => {} } } retval @@ -119,27 +162,28 @@ impl CharacterCase { #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub enum NameFormat { SnakeCase, - ScreamingSnakeCase, + UppercaseSnakeCase, CamelCase, } impl NameFormat { pub fn word_separator(self) -> &'static str { match self { - NameFormat::SnakeCase | NameFormat::ScreamingSnakeCase => "_", + NameFormat::SnakeCase | NameFormat::UppercaseSnakeCase => "_", NameFormat::CamelCase => "", } } pub fn word_initial_char_case(self) -> CharacterCase { match self { - NameFormat::CamelCase | NameFormat::ScreamingSnakeCase => CharacterCase::Upper, + NameFormat::CamelCase | NameFormat::UppercaseSnakeCase => CharacterCase::Upper, NameFormat::SnakeCase => CharacterCase::Lower, } } pub fn word_char_case(self) -> CharacterCase { match self { - NameFormat::ScreamingSnakeCase => CharacterCase::Upper, - NameFormat::CamelCase | NameFormat::SnakeCase => CharacterCase::Lower, + NameFormat::UppercaseSnakeCase => CharacterCase::Upper, + NameFormat::CamelCase => CharacterCase::Unchanged, + NameFormat::SnakeCase => CharacterCase::Lower, } } pub fn name_from_words, I: Iterator>( diff --git a/spirv-parser/Cargo.toml b/spirv-parser/Cargo.toml index cd65d77..a821f2c 100644 --- a/spirv-parser/Cargo.toml +++ b/spirv-parser/Cargo.toml @@ -10,3 +10,6 @@ license = "LGPL-2.1-or-later" crate-type = ["rlib"] [dependencies] + +[build-dependencies] +spirv-parser-generator = {path = "../spirv-parser-generator"} diff --git a/spirv-parser/build.rs b/spirv-parser/build.rs new file mode 100644 index 0000000..3d819ca --- /dev/null +++ b/spirv-parser/build.rs @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright 2018 Jacob Lifshay +extern crate spirv_parser_generator; +use spirv_parser_generator::*; +use std::env; +use std::io; +use std::path::Path; + +fn main() -> Result<(), io::Error> { + Input::with_default_paths(&[ + ExtensionInstructionSet::OpenCLStd, + ExtensionInstructionSet::GLSLStd450, + ]) + .generate()? + .write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("generated_parser.rs"))?; + Ok(()) +} diff --git a/spirv-parser/src/lib.rs b/spirv-parser/src/lib.rs index 84e9d79..2d7ec67 100644 --- a/spirv-parser/src/lib.rs +++ b/spirv-parser/src/lib.rs @@ -1,2 +1,6 @@ // SPDX-License-Identifier: LGPL-2.1-or-later // Copyright 2018 Jacob Lifshay + +#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))] + +include!(concat!(env!("OUT_DIR"), "/generated_parser.rs")); -- 2.30.2