Skip to main content

chime_flux_proc_macro/
lib.rs

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			 // No Repeats:
29			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		 // Fetch Identifier:
206		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/// Args: `kind_type="Type"`, `value=expr`, `change=closure`, `crate="path"`.
222/// - `kind_type`: Any type that implements `FluxKind`.
223/// - `value_expr`: An expression that describes the initial value.
224/// - `change_expr`: A closure that applies change over time to an accumulator
225///   and returns the result. Defaults to an identity function.
226/// - `crate`: The path to the `chime` crate. Defaults to `"::chime"`.
227#[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	 // Parse Attribute Arguments:
235	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			// !!! Assert no overlap between `value_idents` and `change_idents`.
255			
256			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			 // Convenient Identifiers:
264			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	 // Generate `Flux` Variant of Struct:
297	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		 // Constant/Linear Types:
313		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		 // Flux/Moment Types:
334		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		 // Copy Types:
350		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	 // Generate Implementation:
363	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		// ??? Unsure if `item.attrs` and `item.vis` should be inherited by
377		// `flux_item` as they currently are.
378		// ??? Unsure if `flux_item` should be placed in a private module, so
379		// it's only accessible through `flux::Moment::Flux`.
380		#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}