protoflow_syntax/
codegen.rs

1// This is free and unencumbered software released into the public domain.
2
3extern crate std;
4
5use crate::{
6    prelude::{fmt, String, Vec},
7    AnalysisError,
8};
9use error_stack::Report;
10use quote::{format_ident, quote, ToTokens};
11use sysml_parser::{ParsedBlock, ParsedImport, ParsedMember, ParsedModel};
12
13#[derive(Debug, Default)]
14pub struct Code(proc_macro2::TokenStream);
15
16impl Code {
17    pub fn unparse(&self) -> String {
18        let file = syn::parse2::<syn::File>(self.0.clone()).unwrap();
19        prettyplease::unparse(&file)
20    }
21}
22
23impl fmt::Display for Code {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        self.0.fmt(f)
26    }
27}
28
29impl ToTokens for Code {
30    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
31        self.0.to_tokens(tokens);
32    }
33}
34
35impl TryFrom<&ParsedModel> for Code {
36    type Error = Report<AnalysisError>;
37
38    fn try_from(model: &ParsedModel) -> Result<Self, Self::Error> {
39        let members = model
40            .members()
41            .into_iter()
42            .map(|member| Code::try_from(member))
43            .collect::<Result<Vec<_>, _>>()?;
44        Ok(Code(quote! {
45            fn main() {
46                #(#members)*
47            }
48        }))
49    }
50}
51
52impl TryFrom<&ParsedMember> for Code {
53    type Error = Report<AnalysisError>;
54
55    fn try_from(member: &ParsedMember) -> Result<Self, Self::Error> {
56        use ParsedMember::*;
57        match member {
58            Import(import) => Code::try_from(import),
59            Package(package) => {
60                let members = package
61                    .members()
62                    .into_iter()
63                    .map(|member| Code::try_from(member))
64                    .collect::<Result<Vec<_>, _>>()?;
65                Ok(Code(quote! {
66                    #(#members)*
67                }))
68            }
69            BlockUsage(usage) => Code::try_from(usage),
70            AttributeUsage(_usage) => Ok(Code::default()), // TODO
71            PortUsage(_usage) => Ok(Code::default()),      // TODO
72        }
73    }
74}
75
76impl TryFrom<&ParsedImport> for Code {
77    type Error = Report<AnalysisError>;
78
79    fn try_from(import: &ParsedImport) -> Result<Self, Self::Error> {
80        Ok(Self(match import.imported_name.to_tuple3() {
81            (Some("Protoflow"), Some("*") | Some("**"), None) => {
82                quote! {
83                    use protoflow::*;
84                }
85            }
86            (Some("Protoflow"), Some(unqualified_name), None) => {
87                let block_name = format_ident!("{}", unqualified_name);
88                quote! {
89                    use protoflow::blocks::#block_name;
90                }
91            }
92            _ => {
93                return Err(Report::new(AnalysisError::InvalidImport(
94                    import.imported_name.clone(),
95                )));
96            }
97        }))
98    }
99}
100
101impl TryFrom<&ParsedBlock> for Code {
102    type Error = Report<AnalysisError>;
103
104    fn try_from(usage: &ParsedBlock) -> Result<Self, Self::Error> {
105        let name = format_ident!(
106            "{}",
107            usage
108                .name
109                .as_ref()
110                .map(|s| s.as_str())
111                .unwrap_or_else(|| "block")
112        );
113        Ok(Self(quote! {
114            let #name = s.block();
115        }))
116    }
117}