1use proc_macro::*;
2use std::collections::HashSet;
3use quote::*;
4use syn::ext::IdentExt;
5use syn::parse::ParseStream;
6use syn::Token;
7
8struct FluxParse {
9 kind_type: Option<syn::Type>,
10 value_expr: Option<syn::Expr>,
11 change_expr: Option<syn::Expr>,
12 crate_path: Option<syn::Path>,
13}
14
15impl syn::parse::Parse for FluxParse {
16 fn parse(input: ParseStream) -> syn::Result<Self> {
17 let mut kind_type = None;
18 let mut value_expr = None;
19 let mut change_expr = None;
20 let mut crate_path = None;
21
22 let mut prev_paths = HashSet::new();
23 while !input.is_empty() {
24 let field = syn::MetaNameValue::parse(input)?;
25 let ident = field.path.require_ident()?;
26 let value = field.value;
27
28 if prev_paths.contains(ident) {
30 panic!("must only define each field once");
31 }
32 prev_paths.insert(ident.clone());
33
34 match ident.to_string().as_str() {
35 "kind" => {
36 if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(s), attrs }) = value {
37 assert!(attrs.is_empty(), "kind type must have no attributes");
38 kind_type = Some(s.parse()?);
39 } else {
40 panic!("kind type must be a string");
41 }
42 },
43 "value" => {
44 let mut is_field = false;
45 if let syn::Expr::Path(syn::ExprPath { path, attrs, qself: None }) = &value {
46 assert!(attrs.is_empty(), "value must have no attributes");
47 if let Some(ident) = path.get_ident() {
48 if ident != "self" {
49 is_field = true;
50 }
51 }
52 }
53 if is_field {
54 value_expr = Some(value);
55 } else {
56 panic!("expected value to be an identifier for a field");
57 }
58 },
59 "change" => {
60 if let syn::Expr::Closure(_) = value {
61 change_expr = Some(value);
62 } else {
63 panic!("change expression must be a closure");
64 }
65 },
66 "crate" => {
67 if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(s), attrs }) = value {
68 assert!(attrs.is_empty(), "no crate path attributes");
69 crate_path = Some(s.parse()?);
70 } else {
71 panic!("crate must be a path");
72 }
73 },
74 ident => panic!("invalid identifier -> {}", ident)
75 }
76
77 if input.is_empty() {
78 break
79 } else {
80 input.parse::<Token![,]>()?;
81 }
82 }
83
84 Ok(FluxParse {
85 kind_type,
86 value_expr,
87 change_expr,
88 crate_path,
89 })
90 }
91}
92
93fn fetch_idents(
94 expr: &syn::Expr,
95 idents: &mut HashSet<proc_macro2::Ident>,
96 cond_idents: &mut HashSet<proc_macro2::Ident>,
97) {
98 match expr {
99 syn::Expr::Paren(syn::ExprParen { expr, .. }) => {
100 fetch_idents(expr, idents, cond_idents);
101 },
102 syn::Expr::Call(syn::ExprCall { func, args, .. }) => {
103 let mut sub_cond_idents = HashSet::new();
104 fetch_idents(func, cond_idents, &mut sub_cond_idents);
105 *cond_idents = cond_idents.union(&sub_cond_idents).map(|x| x.clone()).collect();
106 for arg in args {
107 fetch_idents(arg, idents, cond_idents);
108 }
109 },
110 syn::Expr::MethodCall(syn::ExprMethodCall { receiver, args, .. }) => {
111 fetch_idents(receiver, idents, cond_idents);
112 for arg in args {
113 fetch_idents(arg, idents, cond_idents);
114 }
115 },
116 syn::Expr::Field(syn::ExprField { base, .. }) => {
117 fetch_idents(base, idents, cond_idents);
118 },
119 syn::Expr::Lit(syn::ExprLit { .. }) => {},
120 syn::Expr::Array(syn::ExprArray { elems, .. }) => {
121 for elem in elems {
122 fetch_idents(elem, idents, cond_idents);
123 }
124 },
125 syn::Expr::Closure(syn::ExprClosure { body, .. }) => {
126 fetch_idents(body, idents, cond_idents);
127 },
128 syn::Expr::Repeat(syn::ExprRepeat { expr, len, .. }) => {
129 let mut sub_cond_idents = HashSet::new();
130 fetch_idents(len, cond_idents, &mut sub_cond_idents);
131 *cond_idents = cond_idents.union(&sub_cond_idents).map(|x| x.clone()).collect();
132 fetch_idents(expr, idents, cond_idents);
133 },
134 syn::Expr::Struct(syn::ExprStruct { fields, rest, .. }) => {
135 for field in fields {
136 fetch_idents(&field.expr, idents, cond_idents);
137 }
138 if let Some(rest) = rest {
139 fetch_idents(rest, idents, cond_idents);
140 }
141 },
142 syn::Expr::Tuple(syn::ExprTuple { elems, .. }) => {
143 for elem in elems {
144 fetch_idents(elem, idents, cond_idents);
145 }
146 },
147 syn::Expr::Binary(syn::ExprBinary { left, right, .. }) => {
148 fetch_idents(left, idents, cond_idents);
149 fetch_idents(right, idents, cond_idents);
150 },
151 syn::Expr::Index(syn::ExprIndex { expr, index, .. }) => {
152 let mut sub_cond_idents = HashSet::new();
153 fetch_idents(index, cond_idents, &mut sub_cond_idents);
154 *cond_idents = cond_idents.union(&sub_cond_idents).map(|x| x.clone()).collect();
155 fetch_idents(expr, idents, cond_idents);
156 },
157 syn::Expr::Range(syn::ExprRange { .. }) => {},
158 syn::Expr::Reference(syn::ExprReference { expr, .. }) |
159 syn::Expr::Unary(syn::ExprUnary { expr, .. }) |
160 syn::Expr::Cast(syn::ExprCast { expr, .. }) |
161 syn::Expr::Try(syn::ExprTry { expr, .. }) => {
162 fetch_idents(expr, idents, cond_idents);
163 },
164 syn::Expr::If(syn::ExprIf { cond, then_branch, else_branch, .. }) => {
165 let mut sub_cond_idents = HashSet::new();
166 fetch_idents(cond, cond_idents, &mut sub_cond_idents);
167 *cond_idents = cond_idents.union(&sub_cond_idents).map(|x| x.clone()).collect();
168 for stmt in &then_branch.stmts {
169 match stmt {
170 syn::Stmt::Expr(ref expr, None) => {
171 fetch_idents(expr, idents, cond_idents);
172 },
173 s => panic!(
174 "must only use expressions -> {}",
175 s.to_token_stream()
176 ),
177 }
178 }
179 if let Some((_, expr)) = else_branch {
180 fetch_idents(expr, idents, cond_idents);
181 }
182 },
183 syn::Expr::Match(syn::ExprMatch { expr, arms, .. }) => {
184 let mut sub_cond_idents = HashSet::new();
185 fetch_idents(expr, cond_idents, &mut sub_cond_idents);
186 *cond_idents = cond_idents.union(&sub_cond_idents).map(|x| x.clone()).collect();
187 for arm in arms {
188 fetch_idents(&arm.body, idents, cond_idents);
189 }
190 },
191 syn::Expr::Block(syn::ExprBlock { block, .. }) => {
192 for stmt in &block.stmts {
193 match stmt {
194 syn::Stmt::Expr(ref expr, None) => {
195 fetch_idents(expr, idents, cond_idents);
196 },
197 s => panic!(
198 "must only use expressions -> {}",
199 s.to_token_stream()
200 ),
201 }
202 }
203 },
204
205 syn::Expr::Path(syn::ExprPath { path, .. }) => {
207 if path.segments.len() == 1 && !path.is_ident("self") {
208 idents.insert(path.get_ident()
209 .expect("identifier must exist")
210 .unraw());
211 }
212 },
213
214 e => panic!(
215 "unexpected expression -> {}",
216 e.to_token_stream()
217 )
218 }
219}
220
221#[proc_macro_attribute]
228pub fn flux(arg_stream: TokenStream, item_stream: TokenStream) -> TokenStream {
229 let item: syn::ItemStruct = match syn::parse(item_stream) {
230 Ok(item) => item,
231 Err(e) => panic!("{}", e),
232 };
233
234 let (
236 kind_type,
237 value_block,
238 value_idents,
239 change_block,
240 change_idents,
241 flux,
242 ) = match syn::parse(arg_stream) {
243 Ok(FluxParse { kind_type, value_expr, change_expr, crate_path }) => {
244 let kind_type = kind_type.expect("must specify kind type (kind_type = Type)");
245 let value_expr = value_expr.expect("must specify value expression (value = expr)");
246 let change_expr = change_expr.unwrap_or_else(|| syn::parse_quote!{|x| x});
247 let crate_path = crate_path.unwrap_or_else(|| syn::parse_quote!{::chime});
248
249 let mut value_idents = HashSet::new();
250 let mut value_cond_idents = HashSet::new();
251 let mut change_idents = HashSet::new();
252 let mut change_cond_idents = HashSet::new();
253
254 fetch_idents(&value_expr, &mut value_idents, &mut value_cond_idents);
257 assert_eq!(value_idents.len(), 1, "must specify a single value identifier without `self.`");
258 fetch_idents(&change_expr, &mut change_idents, &mut change_cond_idents);
259
260 let mut value_block: syn::Block = syn::parse_quote!{{ #value_expr }};
261 let mut change_block: syn::Block = syn::parse_quote!{{ (#change_expr)(accum) }};
262
263 let has_ident = |ident: &&syn::Ident| {
265 item.fields.iter().any(|field|
266 **ident == field.ident.as_ref()
267 .expect("identifier must exist")
268 .unraw()
269 )
270 };
271 for ident in change_idents.iter().filter(has_ident) {
272 change_block.stmts.insert(0, syn::parse_quote!{let #ident = &self.#ident;});
273 }
274 for ident in change_cond_idents.iter().filter(has_ident) {
275 change_block.stmts.insert(0, syn::parse_quote!{let #ident = self.#ident;});
276 }
277 for ident in value_idents.iter().filter(has_ident) {
278 value_block.stmts.insert(0, syn::parse_quote!{let #ident = self.#ident;});
279 }
280 for ident in value_cond_idents.iter().filter(has_ident) {
281 value_block.stmts.insert(0, syn::parse_quote!{let #ident = self.#ident;});
282 }
283
284 (
285 kind_type,
286 value_block,
287 value_idents,
288 change_block,
289 change_idents,
290 crate_path.to_token_stream(),
291 )
292 },
293 Err(err) => panic!("{}", err),
294 };
295
296 let mut flux_item = item.clone();
298 let flux_ident = syn::Ident::new(
299 format!("{}Flux", item.ident).as_str(),
300 proc_macro2::Span::mixed_site()
301 );
302 flux_item.ident = flux_ident.clone();
303 let mut moment_value_type: syn::Type = syn::parse_quote!{
304 <<Self::Flux as #flux::Flux>::Kind as #flux::kind::FluxKind>::Value
305 };
306 let mut moment_fields = Default::default();
307 let mut flux_fields = Default::default();
308 for field in &mut flux_item.fields {
309 let ident = field.ident.as_ref()
310 .expect("identifier must exist");
311
312 if value_idents.contains(&ident.unraw()) {
314 let field_ty = syn::parse_quote!{
315 <<Self as #flux::_hidden::InnerFlux>::Kind
316 as #flux::kind::FluxKind>::Value
317 };
318 moment_fields = quote::quote!{
319 #moment_fields
320 #ident: <<Self::Moment as #flux::Moment>::Value
321 as #flux::linear::LinearIso::<#field_ty>>
322 ::from_linear(base_value),
323 };
324 flux_fields = quote::quote!{
325 #flux_fields
326 #ident: <Self::Value
327 as #flux::linear::LinearIso::<#moment_value_type>>
328 ::into_linear(self.#ident),
329 };
330 moment_value_type = std::mem::replace(&mut field.ty, field_ty);
331 }
332
333 else if change_idents.contains(&ident.unraw()) {
335 let field_ty = &field.ty;
336 field.ty = syn::parse_quote!{
337 <#field_ty as #flux::Moment>::Flux
338 };
339 moment_fields = quote::quote!{
340 #moment_fields
341 #ident: #flux::Flux::to_moment(self.#ident, time),
342 };
343 flux_fields = quote::quote!{
344 #flux_fields
345 #ident: #flux::Moment::to_flux(self.#ident, time),
346 };
347 }
348
349 else {
351 moment_fields = quote::quote!{
352 #moment_fields
353 #ident: self.#ident,
354 };
355 flux_fields = quote::quote!{
356 #flux_fields
357 #ident: self.#ident,
358 };
359 }
360 }
361
362 let ident = item.ident.clone();
364 let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
365 let flux_value_impl = quote::quote!{
366 #item
367
368 impl #impl_generics #flux::Moment for #ident #ty_generics #where_clause {
369 type Flux = #flux::FluxValue<#flux_ident #ty_generics>;
370 type Value = #moment_value_type;
371 fn to_flux(self, time: #flux::time::Time) -> Self::Flux {
372 #flux::FluxValue::new(#flux_ident { #flux_fields }, time)
373 }
374 }
375
376 #flux_item
381
382 impl #impl_generics #flux::_hidden::InnerFlux for #flux_ident #ty_generics #where_clause {
383 type Moment = #ident #ty_generics;
384 type Kind = #kind_type;
385
386 fn base_value(&self) -> <Self::Kind as #flux::kind::FluxKind>::Value
387 #value_block
388
389 fn change<'a>(&self, accum: <Self::Kind as #flux::kind::FluxKind>::Accum<'a>)
390 -> <Self::Kind as #flux::kind::FluxKind>::OutAccum<'a>
391 #change_block
392
393 fn to_moment(self, time: #flux::time::Time, base_value: <Self::Kind as #flux::kind::FluxKind>::Value) -> Self::Moment {
394 #ident { #moment_fields }
395 }
396 }
397 };
398
399 flux_value_impl.into()
400}