device_driver_generation/
lib.rs

1#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
2
3#[cfg(feature = "dsl")]
4mod dsl_hir;
5mod lir;
6#[cfg(feature = "manifest")]
7mod manifest;
8mod mir;
9
10/// Transform the tokens of the DSL lang to the generated device driver (or a compile error).
11///
12/// The `driver_name` arg is used to name the root block of the driver.
13/// It should be given in `PascalCase` form.
14#[cfg(feature = "dsl")]
15pub fn transform_dsl(
16    input: proc_macro2::TokenStream,
17    driver_name: &str,
18) -> proc_macro2::TokenStream {
19    let mir = match _private_transform_dsl_mir(input) {
20        Ok(mir) => mir,
21        Err(e) => return e.into_compile_error(),
22    };
23
24    transform_mir(mir, driver_name)
25}
26
27#[doc(hidden)]
28#[cfg(feature = "dsl")]
29pub fn _private_transform_dsl_mir(
30    input: proc_macro2::TokenStream,
31) -> Result<mir::Device, syn::Error> {
32    // Construct the HIR
33    let hir = syn::parse2::<dsl_hir::Device>(input)?;
34
35    // Transform into MIR
36    let mir = dsl_hir::mir_transform::transform(hir)?;
37
38    Ok(mir)
39}
40
41/// Transform the json string to the generated device driver (or a compile error).
42///
43/// The `driver_name` arg is used to name the root block of the driver.
44/// It should be given in `PascalCase` form.
45#[cfg(feature = "json")]
46pub fn transform_json(source: &str, driver_name: &str) -> proc_macro2::TokenStream {
47    let mir = match _private_transform_json_mir(source) {
48        Ok(mir) => mir,
49        Err(e) => return anyhow_error_to_compile_error(e),
50    };
51
52    transform_mir(mir, driver_name)
53}
54
55#[doc(hidden)]
56#[cfg(feature = "json")]
57pub fn _private_transform_json_mir(source: &str) -> anyhow::Result<mir::Device> {
58    let value = dd_manifest_tree::parse_manifest::<dd_manifest_tree::JsonValue>(source)?;
59    let mir = manifest::transform(value)?;
60
61    Ok(mir)
62}
63
64/// Transform the yaml string to the generated device driver (or a compile error).
65///
66/// The `driver_name` arg is used to name the root block of the driver.
67/// It should be given in `PascalCase` form.
68#[cfg(feature = "yaml")]
69pub fn transform_yaml(source: &str, driver_name: &str) -> proc_macro2::TokenStream {
70    let mir = match _private_transform_yaml_mir(source) {
71        Ok(mir) => mir,
72        Err(e) => return anyhow_error_to_compile_error(e),
73    };
74
75    transform_mir(mir, driver_name)
76}
77
78#[doc(hidden)]
79#[cfg(feature = "yaml")]
80pub fn _private_transform_yaml_mir(source: &str) -> anyhow::Result<mir::Device> {
81    let value = dd_manifest_tree::parse_manifest::<dd_manifest_tree::YamlValue>(source)?;
82    let mir = manifest::transform(value)?;
83
84    Ok(mir)
85}
86
87/// Transform the toml string to the generated device driver (or a compile error).
88///
89/// The `driver_name` arg is used to name the root block of the driver.
90/// It should be given in `PascalCase` form.
91#[cfg(feature = "toml")]
92pub fn transform_toml(source: &str, driver_name: &str) -> proc_macro2::TokenStream {
93    let mir = match _private_transform_toml_mir(source) {
94        Ok(mir) => mir,
95        Err(e) => return anyhow_error_to_compile_error(e),
96    };
97
98    transform_mir(mir, driver_name)
99}
100
101#[doc(hidden)]
102#[cfg(feature = "toml")]
103pub fn _private_transform_toml_mir(source: &str) -> anyhow::Result<mir::Device> {
104    let value = dd_manifest_tree::parse_manifest::<dd_manifest_tree::TomlValue>(source)?;
105    let mir = manifest::transform(value)?;
106
107    Ok(mir)
108}
109
110fn transform_mir(mut mir: mir::Device, driver_name: &str) -> proc_macro2::TokenStream {
111    // Run the MIR passes
112    match mir::passes::run_passes(&mut mir) {
113        Ok(()) => {}
114        Err(e) => return anyhow_error_to_compile_error(e),
115    }
116
117    // Transform into LIR
118    let mut lir = match mir::lir_transform::transform(mir, driver_name) {
119        Ok(lir) => lir,
120        Err(e) => return anyhow_error_to_compile_error(e),
121    };
122
123    // Run the LIR passes
124    match lir::passes::run_passes(&mut lir) {
125        Ok(()) => {}
126        Err(e) => return anyhow_error_to_compile_error(e),
127    };
128
129    // Transform into Rust source token output
130    lir::token_transform::transform(lir)
131}
132
133fn anyhow_error_to_compile_error(error: anyhow::Error) -> proc_macro2::TokenStream {
134    syn::Error::new(proc_macro2::Span::call_site(), format!("{error:#}")).into_compile_error()
135}