mlang_rs/lang/rustgen/
mod.rs

1//! `mlang` code generator for rust language.
2
3pub mod mapping;
4
5mod opcode;
6pub use opcode::*;
7mod serde;
8pub use serde::*;
9
10mod ext {
11    use std::{
12        io::{Error, ErrorKind, Result},
13        path::{Path, PathBuf},
14    };
15
16    use proc_macro2::TokenStream;
17    use quote::quote;
18
19    use crate::lang::{ir::Stat, rustgen::gen_opcode_mod};
20
21    use super::gen_serde_mod;
22
23    fn write_and_fmt_rs<C: AsRef<[u8]>, P: AsRef<Path>>(path: P, content: C) -> Result<()> {
24        println!("codegen({:?}):", path.as_ref());
25
26        std::fs::write(path.as_ref(), content).map_err(|err| {
27            Error::new(
28                ErrorKind::Other,
29                format!("write file {:?} error: {}", path.as_ref(), err),
30            )
31        })?;
32
33        println!("    write file ... ok");
34
35        std::process::Command::new("rustfmt")
36            .arg(path.as_ref())
37            .output()
38            .map_err(|err| {
39                Error::new(
40                    ErrorKind::Other,
41                    format!("run rustfmt for {:?} error: {}", path.as_ref(), err),
42                )
43            })?;
44
45        println!("    run rustfmt ... ok");
46
47        Ok(())
48    }
49
50    /// A builder to config and generate rust source code.
51    pub struct CodeGen {
52        with_serde: bool,
53        target: PathBuf,
54    }
55
56    impl Default for CodeGen {
57        fn default() -> Self {
58            Self {
59                with_serde: true,
60                target: Path::new("./").to_path_buf(),
61            }
62        }
63    }
64
65    impl CodeGen {
66        /// Reset `serde` module generation flag, the default value is true.
67        pub fn with_serde(mut self, on: bool) -> Self {
68            self.with_serde = on;
69            self
70        }
71
72        /// Reset the target path of the code generation, the default value is `current directory`.
73        pub fn target(mut self, path: impl AsRef<Path>) -> Self {
74            self.target = path.as_ref().to_path_buf();
75            self
76        }
77
78        /// invoke real rust code generation processing.
79        pub fn codegen(self, stats: impl AsRef<[Stat]>) -> Result<()> {
80            if !self.target.exists() {
81                std::fs::create_dir_all(&self.target)?;
82            }
83
84            let mut mods = vec![("opcode", gen_opcode_mod(stats.as_ref()))];
85
86            if self.with_serde {
87                mods.push(("serde", gen_serde_mod(stats.as_ref(), "super::opcode::")));
88            }
89
90            let mut impls = vec![];
91
92            for (name, codes) in mods {
93                let target_file = self.target.join(format!("{}.rs", name));
94
95                write_and_fmt_rs(target_file, codes.to_string())?;
96
97                let ident = name.parse::<TokenStream>().unwrap();
98
99                impls.push(quote! {
100                    pub mod #ident;
101                });
102            }
103
104            let codes = quote! {
105                //! This module is automatically generated by the ml compiler, do not modify it manually.
106
107                #(#impls)*
108            };
109
110            let target_file = self.target.join("mod.rs");
111
112            write_and_fmt_rs(target_file, codes.to_string())?;
113
114            Ok(())
115        }
116    }
117}
118
119pub use ext::*;