1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // Copyright 2018 Jacob Lifshay
7 use std::collections::HashMap;
9 use std::io::{self, Read, Write};
10 use std::process::{Child, Command, ExitStatus, Stdio};
12 use util::{self, NameFormat::*};
20 WhichError(which::Error),
21 RustFmtFailed(ExitStatus),
24 impl fmt::Display for FormatError {
25 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27 FormatError::IOError(v) => fmt::Display::fmt(v, f),
28 FormatError::WhichError(v) => fmt::Display::fmt(v, f),
29 FormatError::RustFmtFailed(v) => write!(f, "rustfmt failed: {:?}", v),
34 impl From<which::Error> for FormatError {
35 fn from(v: which::Error) -> Self {
36 FormatError::WhichError(v)
40 impl From<io::Error> for FormatError {
41 fn from(v: io::Error) -> Self {
42 FormatError::IOError(v)
46 fn format_source<'a>(options: &Options, source: &'a str) -> Result<Cow<'a, str>, FormatError> {
47 if !options.run_rustfmt {
48 return Ok(Cow::Borrowed(source));
50 let rustfmt_path = match options.rustfmt_path.clone() {
52 None => which::which("rustfmt")?,
54 let mut command = Command::new(rustfmt_path)
55 .stdin(Stdio::piped())
56 .stdout(Stdio::piped())
58 let stdin = command.stdin.take().unwrap();
59 let reader_thread = thread::spawn(move || -> io::Result<(String, Child)> {
60 let mut output = String::new();
61 command.stdout.take().unwrap().read_to_string(&mut output)?;
64 { stdin }.write_all(source.as_bytes())?;
65 let (output, mut command) = reader_thread.join().unwrap()?;
66 let exit_status = command.wait()?;
67 if exit_status.success() {
68 Ok(Cow::Owned(output))
70 Err(FormatError::RustFmtFailed(exit_status))
74 fn remove_initial_op(name: &str) -> &str {
75 const INITIAL_OP: &str = "Op";
76 assert!(name.starts_with(INITIAL_OP));
77 &name[INITIAL_OP.len()..]
80 fn new_id<T: AsRef<str>>(name: T, name_format: util::NameFormat) -> proc_macro2::Ident {
81 proc_macro2::Ident::new(
83 .name_from_words(util::WordIterator::new(name.as_ref()))
85 proc_macro2::Span::call_site(),
89 fn new_enumerant_id<T1: AsRef<str>, T2: AsRef<str>>(
92 ) -> proc_macro2::Ident {
93 let enumerant_name_words = util::WordIterator::new(enumerant_name.as_ref());
94 let enumerant_name_first_word = enumerant_name_words.clone().next();
95 let name = if enumerant_name_first_word
98 .and_then(Iterator::next)
99 .filter(char::is_ascii_digit)
104 util::WordIterator::new(enum_name.as_ref()).chain(enumerant_name_words),
108 CamelCase.name_from_words(enumerant_name_words).unwrap()
110 proc_macro2::Ident::new(&name, proc_macro2::Span::call_site())
113 fn new_combined_id<I: IntoIterator>(names: I, name_format: util::NameFormat) -> proc_macro2::Ident
117 let names: Vec<I::Item> = names.into_iter().collect();
118 proc_macro2::Ident::new(
124 .flat_map(util::WordIterator::new),
127 proc_macro2::Span::call_site(),
131 #[cfg_attr(feature = "cargo-clippy", allow(clippy::cyclomatic_complexity))]
132 pub(crate) fn generate(
133 core_grammar: ast::CoreGrammar,
134 parsed_extension_instruction_sets: HashMap<
135 super::ExtensionInstructionSet,
136 ast::ExtensionInstructionSet,
139 ) -> Result<String, Error> {
140 let mut out = Vec::new();
141 let ast::CoreGrammar {
142 copyright: core_grammar_copyright,
146 revision: core_revision,
147 instructions: core_instructions,
150 writeln!(&mut out, "// automatically generated file")?;
151 writeln!(&mut out, "//")?;
152 for i in &core_grammar_copyright {
153 assert_eq!(i.find('\r'), None);
154 assert_eq!(i.find('\n'), None);
156 writeln!(&mut out, "//");
158 writeln!(&mut out, "// {}", i);
165 pub const MAGIC_NUMBER: u32 = #magic_number;
166 pub const MAJOR_VERSION: u32 = #major_version;
167 pub const MINOR_VERSION: u32 = #minor_version;
168 pub const REVISION: u32 = #core_revision;
171 for operand_kind in &operand_kinds {
173 ast::OperandKind::BitEnum { kind, enumerants } => {
174 let mut enumerant_members = Vec::new();
175 let mut enumerant_member_names = Vec::new();
176 let mut enumerant_items = Vec::new();
177 for enumerant in enumerants {
178 if enumerant.value.0 == 0 {
181 let member_name = new_id(&enumerant.enumerant, SnakeCase);
182 enumerant_member_names.push(member_name.clone());
184 new_combined_id(&[kind.as_ref(), &enumerant.enumerant], CamelCase);
185 if enumerant.parameters.is_empty() {
186 enumerant_items.push(quote!{
187 #[derive(Clone, Debug, Default)]
188 pub struct #type_name;
191 let parameters = enumerant.parameters.iter().map(|parameter| {
192 let kind = new_id(¶meter.kind, CamelCase);
197 enumerant_items.push(quote!{
198 #[derive(Clone, Debug, Default)]
199 pub struct #type_name(#(#parameters)*);
202 enumerant_members.push(quote!{
203 pub #member_name: Option<#type_name>
206 let kind_id = new_id(kind, CamelCase);
211 #[derive(Clone, Debug, Default)]
212 pub struct #kind_id {
213 #(#enumerant_members),*
216 pub fn new() -> Self {
218 #(#enumerant_member_names: None,)*
226 ast::OperandKind::ValueEnum { kind, enumerants } => {
227 let kind_id = new_id(&kind, CamelCase);
228 let mut generated_enumerants = Vec::new();
229 for enumerant in enumerants {
230 let name = new_enumerant_id(&kind, &enumerant.enumerant);
231 if enumerant.parameters.is_empty() {
232 generated_enumerants.push(quote!{#name});
240 #[derive(Clone, Debug)]
242 #(#generated_enumerants,)*
247 ast::OperandKind::Id { kind, doc: _ } => {
248 let base = if *kind == ast::Kind::IdRef {
253 let kind_id = new_id(kind, CamelCase);
258 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
260 pub struct #kind_id(pub #base);
264 ast::OperandKind::Literal { kind, doc: _ } => {
265 let kind_id = new_id(kind, CamelCase);
270 ast::LiteralKind::LiteralInteger
271 | ast::LiteralKind::LiteralContextDependentNumber => unreachable!(),
272 ast::LiteralKind::LiteralInteger32
273 | ast::LiteralKind::LiteralContextDependentNumber32 => {
274 quote!{pub type #kind_id = u32;}
276 ast::LiteralKind::LiteralInteger64
277 | ast::LiteralKind::LiteralContextDependentNumber64 => {
278 quote!{pub type #kind_id = u64;}
280 ast::LiteralKind::LiteralString => quote!{pub type #kind_id = String;},
281 ast::LiteralKind::LiteralExtInstInteger => {
282 quote!{pub type #kind_id = u32;}
284 ast::LiteralKind::LiteralSpecConstantOpInteger => continue,
288 ast::OperandKind::Composite { kind, bases } => {
289 let kind = new_id(kind, CamelCase);
290 let bases = bases.into_iter().map(|base| new_id(base, CamelCase));
291 writeln!(&mut out, "{}", quote!{pub type #kind = (#(#bases),*);})?;
296 let mut instruction_enumerants = Vec::new();
297 let mut spec_constant_op_instruction_enumerants = Vec::new();
298 for instruction in core_instructions.iter() {
299 let opname = new_id(remove_initial_op(instruction.opname.as_ref()), CamelCase);
300 let instruction_enumerant =
301 if instruction.opname == ast::InstructionName::OpSpecConstantOp {
304 operation: OpSpecConstantOp,
307 } else if instruction.operands.is_empty() {
310 let mut fields = Vec::new();
311 for operand in instruction.operands.iter() {
312 let kind = new_id(&operand.kind, CamelCase);
313 let name = new_id(operand.name.as_ref().unwrap(), SnakeCase);
314 let kind = match &operand.quantifier {
315 None => quote!{#kind},
316 Some(ast::Quantifier::Optional) => quote!{Option<#kind>},
317 Some(ast::Quantifier::Variadic) => quote!{Vec<#kind>},
319 fields.push(quote!{#name: #kind});
327 if ast::OP_SPEC_CONSTANT_OP_SUPPORTED_INSTRUCTIONS.contains(&instruction.opname) {
328 spec_constant_op_instruction_enumerants.push(instruction_enumerant.clone());
330 instruction_enumerants.push(instruction_enumerant);
336 #[derive(Clone, Debug)]
337 pub enum OpSpecConstantOp {
338 #(#spec_constant_op_instruction_enumerants,)*
340 #[derive(Clone, Debug)]
341 pub enum Instruction {
342 #(#instruction_enumerants,)*
347 let source = String::from_utf8(out).unwrap();
348 let source = match format_source(&options, &source) {
349 Ok(source) => source.into_owned(),
351 eprintln!("formatting source failed: {}", error);