feo3boy_microcode_generator/lib.rs
1//! This crate provides the `#[define_microcode]` attribute macro which automatically
2//! generates the `Microcode` type from a module containing the microcode's operations as
3//! pure functions.
4use proc_macro2::{Ident, Span, TokenStream};
5use proc_macro_crate::FoundCrate;
6use quote::quote;
7use syn::{parse_macro_input, ItemMod};
8
9use crate::extractor::extract_defs;
10use crate::generator::generate_microcode_type;
11use crate::validation::validate_defs;
12
13/// Provides structures which represent the various parts of the Microcode definition that
14/// are needed for code generation.
15mod defs;
16
17/// Extracts various parts of the definition of the Microcode type from the microcode
18/// definition module.
19mod extractor;
20
21/// Generates the resulting Microcode type from the definition data.
22mod generator;
23
24/// Utilities for specialty parsing.
25mod parsing;
26
27/// Provides validation for the defs before generating output.
28mod validation;
29
30/// Generates the `Microcode` type from a Rust module. The module that this attribute
31/// macro is placed on must be defined in-line not in a separate file. The attribute macro
32/// takes a single argument, which is the name to use for the `Microcode` type.
33///
34/// Doc comments applies to the module will be copied to the `Microcode` type. The
35/// visibility modifier of the `Microcode` type is taken from the module this attribute is
36/// applied to.
37#[proc_macro_attribute]
38pub fn define_microcode(
39 attr: proc_macro::TokenStream,
40 item: proc_macro::TokenStream,
41) -> proc_macro::TokenStream {
42 // Name to use for the `Microcode` type.
43 let name = parse_macro_input!(attr as Ident);
44
45 // Contents of the type-definition module.
46 let module = parse_macro_input!(item as ItemMod);
47
48 let defs = match extract_defs(name, module) {
49 Ok(defs) => defs,
50 Err(e) => return e.into_compile_error().into(),
51 };
52 if let Err(e) = validate_defs(&defs) {
53 return e.into_compile_error().into();
54 }
55 generate_microcode_type(&defs).into()
56}
57
58/// Get a representation of either `crate` or `::<name>` to use for the specified crate.
59fn get_crate(name: &str) -> TokenStream {
60 match proc_macro_crate::crate_name(name) {
61 Ok(FoundCrate::Itself) => quote! { crate },
62 Ok(FoundCrate::Name(name)) => {
63 let name = Ident::new(&name, Span::call_site());
64 quote! { ::#name }
65 }
66 Err(e) => panic!(
67 "Unable to find the {} crate: {}, required by this macro.",
68 name, e
69 ),
70 }
71}