working on spirv parser generator
authorJacob Lifshay <programmerjake@gmail.com>
Wed, 24 Oct 2018 11:01:17 +0000 (04:01 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Wed, 24 Oct 2018 11:01:17 +0000 (04:01 -0700)
spirv-parser-generator/Cargo.toml
spirv-parser-generator/src/ast.rs
spirv-parser-generator/src/generate.rs [new file with mode: 0644]
spirv-parser-generator/src/lib.rs
spirv-parser-generator/src/util.rs
spirv-parser/Cargo.toml
spirv-parser/build.rs [new file with mode: 0644]
spirv-parser/src/lib.rs

index 84e83cb7076678974aa2ab86fcdd22bb79ee1b0f..1b1d8cace5371ce2a74833c25449b7d403fe620a 100644 (file)
@@ -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"
index 54696ba22acad21045e116eca043f941e47ab915..6fdafc745f558847c551a04890996e23a893ff34 100644 (file)
@@ -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<T>(pub T);
+
+pub trait QuotedIntegerProperties: Sized {
+    const DIGIT_COUNT: usize;
+    fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError>;
 }
 
-impl fmt::Display for QuotedInteger {
+impl QuotedIntegerProperties for QuotedInteger<u16> {
+    const DIGIT_COUNT: usize = 4;
+    fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
+        Ok(QuotedInteger(u16::from_str_radix(src, radix)?))
+    }
+}
+
+impl QuotedIntegerProperties for QuotedInteger<u32> {
+    const DIGIT_COUNT: usize = 8;
+    fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
+        Ok(QuotedInteger(u32::from_str_radix(src, radix)?))
+    }
+}
+
+impl<T: ToTokens> ToTokens for QuotedInteger<T> {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        self.0.to_tokens(tokens)
+    }
+}
+
+impl fmt::Display for QuotedInteger<u16> {
     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<u32> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{:#010X}", self.0)
     }
 }
 
-impl fmt::Debug for QuotedInteger {
+impl<T> fmt::Debug for QuotedInteger<T>
+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<T>(QuotedInteger<T>);
+        impl<T> fmt::Debug for DisplayQuotedInteger<T>
+        where
+            QuotedInteger<T>: 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<T>
+where
+    Self: QuotedIntegerProperties,
+{
     fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
         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<String>,
-    quantifier: Option<Quantifier>,
+    pub kind: Kind,
+    pub name: Option<String>,
+    pub quantifier: Option<Quantifier>,
 }
 
 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<String> 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<str> 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<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        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<InstructionOperand>,
+    pub operands: Vec<InstructionOperand>,
     #[serde(default)]
-    capabilities: Vec<String>,
+    pub capabilities: Vec<String>,
     #[serde(default)]
-    extensions: Vec<String>,
+    pub extensions: Vec<String>,
     #[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<InstructionOperand>,
+    pub operands: Vec<InstructionOperand>,
     #[serde(default)]
-    capabilities: Vec<String>,
+    pub capabilities: Vec<String>,
 }
 
 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<String>,
+pub struct ValueEnumerantParameter {
+    pub kind: Kind,
+    pub name: Option<String>,
 }
 
-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<Value> {
-    enumerant: String,
-    value: Value,
+pub struct Enumerant<Value, EnumerantParameter> {
+    pub enumerant: String,
+    pub value: Value,
     #[serde(default)]
-    capabilities: Vec<String>,
+    pub capabilities: Vec<String>,
     #[serde(default)]
-    parameters: Vec<EnumerantParameter>,
+    pub parameters: Vec<EnumerantParameter>,
     #[serde(default)]
-    extensions: Vec<String>,
+    pub extensions: Vec<String>,
     #[serde(default)]
-    version: SPIRVVersion,
+    pub version: SPIRVVersion,
+}
+
+impl Enumerant<u32, ValueEnumerantParameter> {
+    pub fn fixup(&mut self) -> Result<(), ::Error> {
+        for parameter in self.parameters.iter_mut() {
+            parameter.fixup()?;
+        }
+        Ok(())
+    }
 }
 
-impl<Value> Enumerant<Value> {
-    pub fn guess_names(&mut self) -> Result<(), ::Error> {
+impl Enumerant<QuotedInteger<u16>, 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<Cow<'a, str>> 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<String> for Kind {
+    fn from(v: String) -> Self {
+        Kind::from(Cow::Owned(v))
+    }
+}
+
+impl AsRef<str> 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<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        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<T: AsRef<str>>(v: T) -> Option<Self> {
+        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<str> 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<Enumerant<QuotedInteger>>,
+        kind: Kind,
+        enumerants: Vec<Enumerant<QuotedInteger<u16>, BitwiseEnumerantParameter>>,
     },
     ValueEnum {
-        kind: String,
-        enumerants: Vec<Enumerant<u32>>,
+        kind: Kind,
+        enumerants: Vec<Enumerant<u32, ValueEnumerantParameter>>,
     },
     Id {
-        kind: String,
+        kind: Kind,
         doc: Option<String>,
     },
     Literal {
-        kind: String,
+        kind: LiteralKind,
         doc: Option<String>,
     },
     Composite {
-        kind: String,
-        bases: Vec<String>,
+        kind: Kind,
+        bases: Vec<Kind>,
     },
 }
 
-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<String>,
-    magic_number: QuotedInteger,
-    major_version: u16,
-    minor_version: u16,
-    revision: u32,
-    instructions: Vec<Instruction>,
-    operand_kinds: Vec<OperandKind>,
+    pub copyright: Vec<String>,
+    pub magic_number: QuotedInteger<u32>,
+    pub major_version: u32,
+    pub minor_version: u32,
+    pub revision: u32,
+    pub instructions: Vec<Instruction>,
+    pub operand_kinds: Vec<OperandKind>,
 }
 
 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<String>,
-    version: u32,
-    revision: u32,
-    instructions: Vec<ExtensionInstruction>,
+    pub copyright: Vec<String>,
+    pub version: u32,
+    pub revision: u32,
+    pub instructions: Vec<ExtensionInstruction>,
 }
 
 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 (file)
index 0000000..b3d8884
--- /dev/null
@@ -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<which::Error> for FormatError {
+    fn from(v: which::Error) -> Self {
+        FormatError::WhichError(v)
+    }
+}
+
+impl From<io::Error> for FormatError {
+    fn from(v: io::Error) -> Self {
+        FormatError::IOError(v)
+    }
+}
+
+fn format_source<'a>(options: &Options, source: &'a str) -> Result<Cow<'a, str>, 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<T: AsRef<str>>(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<T1: AsRef<str>, T2: AsRef<str>>(
+    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<I: IntoIterator>(names: I, name_format: util::NameFormat) -> proc_macro2::Ident
+where
+    I::Item: AsRef<str>,
+{
+    let names: Vec<I::Item> = 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<String, Error> {
+    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(&parameter.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)
+}
index d931a6a782c4be5cb0717f4996978214e6e754fc..03df5e5220a5e70b59ee9ba25d57ef5dd0e0fee8 100644 (file)
@@ -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<Error> 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<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
+        write!(writer, "{}", self.text)
+    }
+    pub fn write_to_file<T: AsRef<Path>>(&self, path: T) -> Result<(), io::Error> {
+        self.write(File::create(path)?)
+    }
+}
+
+struct Options {
+    run_rustfmt: bool,
+    rustfmt_path: Option<PathBuf>,
+}
+
+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<ExtensionInstructionSet, PathBuf>,
+    options: Options,
+}
+
+fn get_spirv_grammar_path<T: AsRef<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<T: AsRef<Path>>(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<T: AsRef<Path>>(
         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<T: AsRef<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(())
     }
 }
index e82265d95c7c3bad02e24a8092e23fbbe82464db..1b2b5ba851023b694d3116be6b4b493e40e0a44a 100644 (file)
@@ -23,20 +23,60 @@ impl From<char> 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<T: Borrow<str>, I: Iterator<Item = T>>(
index cd65d7716bb9ea36d4412aa15a6dfedacf5160ec..a821f2c0c49fe5c913e07039738773b7bfac4f69 100644 (file)
@@ -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 (file)
index 0000000..3d819ca
--- /dev/null
@@ -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(())
+}
index 84e9d795808d9eedf7e4232c6249e678704c8520..2d7ec67848f2d32cab890cc9d085cc0812f93769 100644 (file)
@@ -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"));