device_driver_generation/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]

#[cfg(feature = "dsl")]
mod dsl_hir;
mod lir;
#[cfg(feature = "manifest")]
mod manifest;
mod mir;

/// Transform the tokens of the DSL lang to the generated device driver (or a compile error).
///
/// The `driver_name` arg is used to name the root block of the driver.
/// It should be given in `PascalCase` form.
#[cfg(feature = "dsl")]
pub fn transform_dsl(
    input: proc_macro2::TokenStream,
    driver_name: &str,
) -> proc_macro2::TokenStream {
    let mir = match _private_transform_dsl_mir(input) {
        Ok(mir) => mir,
        Err(e) => return e.into_compile_error(),
    };

    transform_mir(mir, driver_name)
}

#[doc(hidden)]
#[cfg(feature = "dsl")]
pub fn _private_transform_dsl_mir(
    input: proc_macro2::TokenStream,
) -> Result<mir::Device, syn::Error> {
    // Construct the HIR
    let hir = syn::parse2::<dsl_hir::Device>(input)?;

    // Transform into MIR
    let mir = dsl_hir::mir_transform::transform(hir)?;

    Ok(mir)
}

/// Transform the json string to the generated device driver (or a compile error).
///
/// The `driver_name` arg is used to name the root block of the driver.
/// It should be given in `PascalCase` form.
#[cfg(feature = "json")]
pub fn transform_json(source: &str, driver_name: &str) -> proc_macro2::TokenStream {
    let mir = match _private_transform_json_mir(source) {
        Ok(mir) => mir,
        Err(e) => return anyhow_error_to_compile_error(e),
    };

    transform_mir(mir, driver_name)
}

#[doc(hidden)]
#[cfg(feature = "json")]
pub fn _private_transform_json_mir(source: &str) -> anyhow::Result<mir::Device> {
    let value = dd_manifest_tree::parse_manifest::<dd_manifest_tree::JsonValue>(source)?;
    let mir = manifest::transform(value)?;

    Ok(mir)
}

/// Transform the yaml string to the generated device driver (or a compile error).
///
/// The `driver_name` arg is used to name the root block of the driver.
/// It should be given in `PascalCase` form.
#[cfg(feature = "yaml")]
pub fn transform_yaml(source: &str, driver_name: &str) -> proc_macro2::TokenStream {
    let mir = match _private_transform_yaml_mir(source) {
        Ok(mir) => mir,
        Err(e) => return anyhow_error_to_compile_error(e),
    };

    transform_mir(mir, driver_name)
}

#[doc(hidden)]
#[cfg(feature = "yaml")]
pub fn _private_transform_yaml_mir(source: &str) -> anyhow::Result<mir::Device> {
    let value = dd_manifest_tree::parse_manifest::<dd_manifest_tree::YamlValue>(source)?;
    let mir = manifest::transform(value)?;

    Ok(mir)
}

/// Transform the toml string to the generated device driver (or a compile error).
///
/// The `driver_name` arg is used to name the root block of the driver.
/// It should be given in `PascalCase` form.
#[cfg(feature = "toml")]
pub fn transform_toml(source: &str, driver_name: &str) -> proc_macro2::TokenStream {
    let mir = match _private_transform_toml_mir(source) {
        Ok(mir) => mir,
        Err(e) => return anyhow_error_to_compile_error(e),
    };

    transform_mir(mir, driver_name)
}

#[doc(hidden)]
#[cfg(feature = "toml")]
pub fn _private_transform_toml_mir(source: &str) -> anyhow::Result<mir::Device> {
    let value = dd_manifest_tree::parse_manifest::<dd_manifest_tree::TomlValue>(source)?;
    let mir = manifest::transform(value)?;

    Ok(mir)
}

fn transform_mir(mut mir: mir::Device, driver_name: &str) -> proc_macro2::TokenStream {
    // Run the MIR passes
    match mir::passes::run_passes(&mut mir) {
        Ok(()) => {}
        Err(e) => return anyhow_error_to_compile_error(e),
    }

    // Transform into LIR
    let mut lir = match mir::lir_transform::transform(mir, driver_name) {
        Ok(lir) => lir,
        Err(e) => return anyhow_error_to_compile_error(e),
    };

    // Run the LIR passes
    match lir::passes::run_passes(&mut lir) {
        Ok(()) => {}
        Err(e) => return anyhow_error_to_compile_error(e),
    };

    // Transform into Rust source token output
    lir::token_transform::transform(lir)
}

fn anyhow_error_to_compile_error(error: anyhow::Error) -> proc_macro2::TokenStream {
    syn::Error::new(proc_macro2::Span::call_site(), format!("{error:#}")).into_compile_error()
}