chandeliers_san/codegen/
options.rs1use proc_macro2::TokenStream;
4use quote::{quote, quote_spanned, ToTokens};
5
6use crate::ast;
7use crate::ast::options::usage::Codegen as This;
8use crate::ast::options::{Allow, Const, ExtNode, Node, TraceFile, TraceFormat};
9use crate::sp::Sp;
10
11use super::{AsIdent, RawIdent, SanitizedIdent};
12
13impl ToTokens for Allow {
14 fn to_tokens(&self, toks: &mut TokenStream) {
15 toks.extend(match self {
16 Self::Rustc(id) => quote!( #[allow(#id)] ),
17 Self::Clippy(id) => quote!( #[allow(clippy::#id)] ),
18 });
19 }
20}
21
22macro_rules! generic_option {
51 (
52 $( #[$($doc:tt)*] )*
53 trait $trait:ident for { $($implementor:ty),* }
54 impl {
55 from $field:ident return $fetch:ty ;
56 $($func:tt)*
57 }
58 ) => {
59 $( #[$($doc)*] )*
60 pub trait $trait {
61 #[doc = "How to get the value for this option"]
62 fn fetch(&self) -> $fetch;
63
64 #[doc = "How to construct a TokenStream that implements the feature from this option"]
65 $($func)*
66 }
67
68 $(
69 impl $trait for $implementor {
70 fn fetch(&self) -> $fetch {
71 self.$field.fetch::<This>()
72 }
73 }
74 )*
75 };
76}
77
78generic_option! {
79 #[doc = "`#[trace]`: print debug information."]
80 trait Traces for { Node, ExtNode }
81 impl {
82 from trace return &Option<(TraceFile, (TraceFormat, TraceFormat))>;
83 fn traces(
84 &self,
85 prefix: &str,
86 name: &Sp<ast::decl::NodeName>,
87 inputs: Sp<&ast::Tuple<Sp<ast::decl::TyVar>>>,
88 locals: Sp<&ast::Tuple<Sp<ast::decl::TyVar>>>,
89 outputs: Sp<&ast::Tuple<Sp<ast::decl::TyVar>>>,
90 ) -> (TokenStream, TokenStream) {
91 if let Some((file, (ifmt, ofmt))) = self.fetch() {
92 let name = format!("{name}");
93 let input_var_fmt = inputs.fmt_strings();
94 let output_var_fmt = outputs.fmt_strings();
95 let inputs_self_assigned = inputs.self_assigned();
96 let locals_self_assigned = locals.self_assigned();
97 let outputs_self_assigned = outputs.self_assigned();
98 let input_fmt = match ifmt {
99 TraceFormat::Default => format!("{{_ext}}{{_this}} <- {input_var_fmt}\n"),
100 TraceFormat::Empty => String::new(),
101 TraceFormat::Str(s) => s.clone(),
102 };
103 let output_fmt = match ofmt {
104 TraceFormat::Default => format!("{{_ext}}{{_this}} -> {output_var_fmt}\n"),
105 TraceFormat::Empty => String::new(),
106 TraceFormat::Str(s) => s.clone(),
107 };
108 let printer = match file {
109 TraceFile::StdOut => quote!(print),
110 TraceFile::StdErr => quote!(eprint),
111 };
112 (
113 quote! {
114 #[allow(unused_variables)]
115 {
116 let _ext = #prefix;
117 let _this = #name;
118 let _clk = self.__clock;
119 #inputs_self_assigned
120 #printer!(#input_fmt);
121 }
122 },
123 quote! {
124 #[allow(unused_variables)]
125 {
126 let _ext = #prefix;
127 let _this = #name;
128 let _clk = self.__clock;
129 #inputs_self_assigned
130 #locals_self_assigned
131 #outputs_self_assigned
132 #printer!(#output_fmt);
133 }
134 },
135 )
136 } else {
137 (quote!(), quote!())
138 }
139 }
140 }
141}
142
143generic_option! {
144 #[doc = "`#[main(42)]`: build a `main` function that executes this node a set number of times."]
145 trait FnMain for { Node, ExtNode }
146 impl {
147 from main return &Option<usize>;
148 fn fn_main(&self, name: &Sp<ast::decl::NodeName>, rustc_allow: &Vec<Allow>) -> TokenStream {
149 if let Some(nb_iter) = self.fetch() {
150 let ext_name = name.as_ident(SanitizedIdent, None);
151
152 let doc = format!(
153 "Main function automatically generated from {name} (runs for {nb_iter} steps)"
154 );
155 quote_spanned! {name.span.unwrap()=>
156 #[doc = #doc]
157 #[allow(unused_imports)] #( #rustc_allow )*
159 pub fn main() {
160 use ::chandeliers_sem::traits::{Step, Embed, Trusted};
161 let mut sys = #ext_name::default();
162 if #nb_iter == 0 {
163 loop {
164 sys.step(().embed()).trusted();
165 }
166 } else {
167 for _ in 1..=#nb_iter {
168 sys.step(().embed()).trusted();
169 }
170 }
171 }
172 }
173 } else {
174 quote!()
175 }
176 }
177 }
178}
179
180generic_option! {
181 #[doc = "`#[test(42)]`: build a test function that executes this node a set number of times."]
182 trait FnTest for { Node }
183 impl {
184 from test return &Option<usize>;
185 fn fn_test(&self, name: &Sp<ast::decl::NodeName>, rustc_allow: &Vec<Allow>) -> TokenStream {
186 if let Some(nb_iter) = self.fetch() {
187 let priv_name = name.as_ident(SanitizedIdent, None);
188 let pub_name = name.as_ident(RawIdent, None);
189
190 let doc = format!(
191 "Test function automatically generated from {name} (runs for {nb_iter} steps)"
192 );
193 quote_spanned! {name.span.unwrap()=>
194 #[doc = #doc]
195 #[allow(unused_imports)] #( #rustc_allow )*
197 #[test]
198 pub fn #pub_name() {
199 use ::chandeliers_sem::traits::{Step, Embed, Trusted};
200 let mut sys = #priv_name::default();
201 if #nb_iter == 0 {
202 loop {
203 sys.step(().embed()).trusted();
204 }
205 } else {
206 for _ in 1..=#nb_iter {
207 sys.step(().embed()).trusted();
208 }
209 }
210 }
211 }
212 } else {
213 quote!()
214 }
215 }
216 }
217}
218
219generic_option! {
220 #[doc = "`#[pub]`: make this declaration public. Implies `#[export]`."]
221 trait PubQualifier for { Const, Node }
222 impl {
223 from public return &bool;
224 fn pub_qualifier(&self, implementing_trait: bool) -> TokenStream {
225 if *self.fetch() && !implementing_trait{
226 quote!(pub)
227 } else {
228 quote!()
229 }
230 }
231 }
232}
233
234generic_option! {
235 #[doc = "`#[doc(\"Message\")]`: insert documentation in the generated code."]
236 trait Docs for { Const, Node }
237 impl {
238 from doc return &Vec<Sp<String>>;
239 fn docs(&self) -> TokenStream {
240 let docs = self.fetch();
241 if docs.is_empty() {
242 quote! {
243 #[doc = "(No user documentation provided)"]
244 }
245 } else {
246 quote! {
247 #( #[doc = #docs] )*
248 }
249 }
250 }
251 }
252}
253
254generic_option! {
255 #[doc = "`#[generic[T, U, V]]`: declare type variables. Returns (1) the variable declarations, (2) the phantom data to use, and (3) the trait bounds."]
256 trait GenericParams for { Node, ExtNode }
257 impl {
258 from generics return &Vec<Sp<String>>;
259 fn generic_params(&self) -> (TokenStream, TokenStream, TokenStream) {
260 let generics = self.fetch().iter().map(|t| syn::Ident::new_raw(&t.t, t.span.unwrap())).collect::<Vec<_>>();
261 if generics.is_empty() {
262 (quote!(), quote!( () ), quote!())
263 } else {
264 (quote! {
265 < #( #generics ),* >
266 }, quote! {
267 ::std::marker::PhantomData<( #( #generics ),* )>
268 }, quote! {
269 where #(
270 #generics: ::chandeliers_sem::traits::Scalar,
271 )*
272 })
273 }
274 }
275 }
276}