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}