working on spirv parser generator
[kazan.git] / spirv-parser-generator / src / lib.rs
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // Copyright 2018 Jacob Lifshay
3
4 // allow unneeded_field_pattern to ensure fields aren't accidently missed
5 #![cfg_attr(feature = "cargo-clippy", allow(clippy::unneeded_field_pattern))]
6
7 #[macro_use]
8 extern crate quote;
9 extern crate proc_macro2;
10 #[macro_use]
11 extern crate serde_derive;
12 extern crate serde;
13 extern crate serde_json;
14 extern crate which;
15
16 use std::collections::HashMap;
17 use std::error;
18 use std::fmt;
19 use std::fs::File;
20 use std::io;
21 use std::path::Path;
22 use std::path::PathBuf;
23
24 mod ast;
25 mod generate;
26 mod util;
27
28 pub const SPIRV_CORE_GRAMMAR_JSON_FILE_NAME: &str = "spirv.core.grammar.json";
29
30 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
31 pub enum ExtensionInstructionSet {
32 GLSLStd450,
33 OpenCLStd,
34 }
35
36 impl ExtensionInstructionSet {
37 pub fn get_grammar_json_file_name(self) -> &'static str {
38 match self {
39 ExtensionInstructionSet::GLSLStd450 => "extinst.glsl.std.450.grammar.json",
40 ExtensionInstructionSet::OpenCLStd => "extinst.opencl.std.100.grammar.json",
41 }
42 }
43 }
44
45 #[derive(Debug)]
46 pub enum Error {
47 IOError(io::Error),
48 JSONError(serde_json::Error),
49 DeducingNameForInstructionOperandFailed,
50 DeducingNameForEnumerantParameterFailed,
51 }
52
53 impl From<io::Error> for Error {
54 fn from(v: io::Error) -> Error {
55 Error::IOError(v)
56 }
57 }
58
59 impl From<serde_json::Error> for Error {
60 fn from(v: serde_json::Error) -> Error {
61 if let serde_json::error::Category::Io = v.classify() {
62 Error::IOError(v.into())
63 } else {
64 Error::JSONError(v)
65 }
66 }
67 }
68
69 impl fmt::Display for Error {
70 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 match self {
72 Error::IOError(v) => fmt::Display::fmt(v, f),
73 Error::JSONError(v) => fmt::Display::fmt(v, f),
74 Error::DeducingNameForInstructionOperandFailed => {
75 write!(f, "deducing name for InstructionOperand failed")
76 }
77 Error::DeducingNameForEnumerantParameterFailed => {
78 write!(f, "deducing name for EnumerantParameter failed")
79 }
80 }
81 }
82 }
83
84 impl error::Error for Error {}
85
86 impl From<Error> for io::Error {
87 fn from(error: Error) -> Self {
88 match error {
89 Error::IOError(v) => v,
90 Error::JSONError(v) => v.into(),
91 error @ Error::DeducingNameForInstructionOperandFailed
92 | error @ Error::DeducingNameForEnumerantParameterFailed => {
93 io::Error::new(io::ErrorKind::Other, format!("{}", error))
94 }
95 }
96 }
97 }
98
99 pub struct Output {
100 text: String,
101 }
102
103 impl Output {
104 pub fn to_str(&self) -> &str {
105 &self.text
106 }
107 pub fn into_string(self) -> String {
108 self.text
109 }
110 pub fn write<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
111 write!(writer, "{}", self.text)
112 }
113 pub fn write_to_file<T: AsRef<Path>>(&self, path: T) -> Result<(), io::Error> {
114 self.write(File::create(path)?)
115 }
116 }
117
118 struct Options {
119 run_rustfmt: bool,
120 rustfmt_path: Option<PathBuf>,
121 }
122
123 impl Default for Options {
124 fn default() -> Self {
125 Self {
126 run_rustfmt: true,
127 rustfmt_path: None,
128 }
129 }
130 }
131
132 pub struct Input {
133 spirv_core_grammar_json_path: PathBuf,
134 extension_instruction_sets: HashMap<ExtensionInstructionSet, PathBuf>,
135 options: Options,
136 }
137
138 fn get_spirv_grammar_path<T: AsRef<Path>>(name: T) -> PathBuf {
139 Path::new(env!("CARGO_MANIFEST_DIR"))
140 .join("../external/SPIRV-Headers/include/spirv/unified1")
141 .join(name)
142 }
143
144 impl Input {
145 pub fn with_default_paths(extension_instruction_sets: &[ExtensionInstructionSet]) -> Input {
146 let mut retval = Self::new(get_spirv_grammar_path("spirv.core.grammar.json"));
147 for &extension_instruction_set in extension_instruction_sets {
148 retval = retval.add_extension_instruction_set(
149 extension_instruction_set,
150 get_spirv_grammar_path(extension_instruction_set.get_grammar_json_file_name()),
151 );
152 }
153 retval
154 }
155 pub fn new<T: AsRef<Path>>(spirv_core_grammar_json_path: T) -> Input {
156 Input {
157 spirv_core_grammar_json_path: spirv_core_grammar_json_path.as_ref().into(),
158 extension_instruction_sets: HashMap::new(),
159 options: Options::default(),
160 }
161 }
162
163 pub fn add_extension_instruction_set<T: AsRef<Path>>(
164 mut self,
165 extension_instruction_set: ExtensionInstructionSet,
166 path: T,
167 ) -> Self {
168 assert!(
169 self.extension_instruction_sets
170 .insert(extension_instruction_set, path.as_ref().into())
171 .is_none(),
172 "duplicate extension instruction set: {:?}",
173 extension_instruction_set
174 );
175 self
176 }
177 pub fn generate(self) -> Result<Output, Error> {
178 let Input {
179 spirv_core_grammar_json_path,
180 extension_instruction_sets,
181 options,
182 } = self;
183 let mut core_grammar: ast::CoreGrammar =
184 serde_json::from_reader(File::open(spirv_core_grammar_json_path)?)?;
185 core_grammar.fixup()?;
186 let mut parsed_extension_instruction_sets: HashMap<
187 ExtensionInstructionSet,
188 ast::ExtensionInstructionSet,
189 > = Default::default();
190 for (extension_instruction_set, path) in extension_instruction_sets {
191 let mut parsed_extension_instruction_set: ast::ExtensionInstructionSet =
192 serde_json::from_reader(File::open(path)?)?;
193 parsed_extension_instruction_set.fixup()?;
194 assert!(parsed_extension_instruction_sets
195 .insert(extension_instruction_set, parsed_extension_instruction_set)
196 .is_none());
197 }
198 Ok(Output {
199 text: generate::generate(core_grammar, parsed_extension_instruction_sets, &options)?,
200 })
201 }
202 }
203
204 #[cfg(test)]
205 mod tests {
206 use super::*;
207
208 #[test]
209 fn parse_core_grammar() -> Result<(), Error> {
210 Input::with_default_paths(&[]).generate()?;
211 Ok(())
212 }
213
214 #[test]
215 fn parse_core_grammar_with_opencl() -> Result<(), Error> {
216 Input::with_default_paths(&[ExtensionInstructionSet::OpenCLStd]).generate()?;
217 Ok(())
218 }
219
220 #[test]
221 fn parse_core_grammar_with_opencl_and_glsl() -> Result<(), Error> {
222 Input::with_default_paths(&[
223 ExtensionInstructionSet::OpenCLStd,
224 ExtensionInstructionSet::GLSLStd450,
225 ])
226 .generate()?;
227 Ok(())
228 }
229
230 #[test]
231 fn parse_core_grammar_with_glsl() -> Result<(), Error> {
232 Input::with_default_paths(&[ExtensionInstructionSet::GLSLStd450]).generate()?;
233 Ok(())
234 }
235 }