blend_formula_proc_macro/
lib.rs

1use proc_macro::{TokenStream, TokenTree};
2use std::iter::Peekable;
3
4use std::cmp::{PartialOrd, Ordering};
5use std::error::Error;
6use std::fmt::{Display, Formatter};
7use std::vec::IntoIter;
8
9type TokenIter = Peekable<IntoIter<TokenTree>>;
10
11#[proc_macro]
12pub fn blend_equation(token_stream: TokenStream) -> TokenStream {
13	let token_iter = token_stream.into_iter();
14	
15	let mut color_token_list = vec![];
16	let mut alpha_token_list = vec![];
17	
18	let token_group_list = [&mut color_token_list, &mut alpha_token_list];
19	let mut token_group_index = 0;
20	
21	for token in token_iter {
22		match token {
23			TokenTree::Punct(punct) if punct == ',' => {
24				token_group_index += 1;
25				if token_group_index > 1 {
26					panic!("too many comma-separated formulae");
27				}
28			},
29			token => token_group_list[token_group_index].push(token),
30		}
31	}
32	
33	let (color_formula, alpha_formula) = if token_group_index == 0 {
34		alpha_token_list = color_token_list.clone();
35		match parse_blend_equation(
36			&mut color_token_list.into_iter().peekable(),
37			BlendSuffix::Full
38		) {
39			Ok(("SaturatedSrcAlpha", _, _)) |
40			Ok((_, "SaturatedSrcAlpha", _)) => {
41				alpha_token_list = vec![
42					TokenTree::Group(proc_macro::Group::new(
43						proc_macro::Delimiter::Parenthesis,
44						TokenStream::from_iter(alpha_token_list.into_iter())
45					)),
46					TokenTree::Punct(proc_macro::Punct::new(
47						'.',
48						proc_macro::Spacing::Alone
49					)),
50					TokenTree::Ident(proc_macro::Ident::new(
51						"a",
52						proc_macro::Span::call_site()
53					)),
54				];
55				match parse_blend_equation(
56					&mut alpha_token_list.into_iter().peekable(),
57					BlendSuffix::Alpha,
58				) {
59					Ok(formula) => (formula.clone(), formula),
60					Err(ParseError::InvalidFormula(_)) => {
61						panic!("{}", ParseError::InvalidFormula(BlendSuffix::Full))
62					},
63					Err(error) => panic!("{}", error),
64				}
65			},
66			Ok(formula) => (formula.clone(), formula),
67			Err(error) => panic!("{}", error),
68		}
69	} else {
70		match (
71			parse_blend_equation(
72				&mut color_token_list.into_iter().peekable(),
73				BlendSuffix::Color
74			),
75			parse_blend_equation(
76				&mut alpha_token_list.into_iter().peekable(),
77				BlendSuffix::Alpha
78			)
79		) {
80			(Ok(color), Ok((alpha_src_factor, alpha_dst_factor, alpha_op))) => {
81				fn alp(s: &str) -> &str {
82					match s {
83						"Src" => "SrcAlpha",
84						"Dst" => "DstAlpha",
85						"OneMinusSrc" => "OneMinusSrcAlpha",
86						"OneMinusDst" => "OneMinusDstAlpha",
87						other => other
88					}
89				}
90				(color, (alp(alpha_src_factor), alp(alpha_dst_factor), alpha_op))
91			},
92			(Err(error), _) |
93			(_, Err(error)) => panic!("{}", error)
94		}
95	};
96	
97	let text = format!("\
98		blend_formula::BlendEquation {{\n\
99			color: {},\n\
100			alpha: {},\n\
101		}}",
102		format_blend_formula(color_formula),
103		format_blend_formula(alpha_formula),
104	);
105	
106	text.parse().unwrap()
107}
108
109#[proc_macro]
110pub fn blend_formula(token_stream: TokenStream) -> TokenStream {
111	let mut token_iter = token_stream
112		.into_iter()
113		.collect::<Vec<TokenTree>>()
114		.into_iter()
115		.peekable();
116	
117	match parse_blend_equation(&mut token_iter, BlendSuffix::Full) {
118		Ok(formula) => format_blend_formula(formula).parse().unwrap(),
119		Err(error) => panic!("{}", error),
120	}
121}
122
123fn parse_blend_equation(token_iter: &mut TokenIter, dimension: BlendSuffix)
124	-> Result<(&'static str, &'static str, &'static str), ParseError>
125{
126	match parse_formula(token_iter, false) {
127		Ok(t) => {
128			let t_dimension = t.dimension().unwrap();
129			if t_dimension != dimension && t_dimension != BlendSuffix::Alpha {
130				return Err(ParseError::ExpectedDimension(dimension, t_dimension))
131			}
132			term_formula(simplify_term(t.clear_dimension(dimension)))
133				.ok_or(ParseError::InvalidFormula(dimension))
134		},
135		
136		 // Operation Shortcuts:
137		Err((None, ParseError::ExpectedTerm(TokenTree::Punct(punct))))
138		if token_iter.peek().is_none() => match punct.as_char() {
139			'*' => Ok(("Zero", "Src", "Add")),
140			'+' => Ok(("One", "One", "Add")),
141			'<' => Ok(("One", "One", "Min")),
142			'>' => Ok(("One", "One", "Max")),
143			_ => Err(ParseError::ExpectedTerm(TokenTree::Punct(punct)))
144		},
145		Err((Some(Term::Zero), ParseError::ExpectedTerm(TokenTree::Punct(punct))))
146		if token_iter.peek().is_none() && punct == '-' => {
147			Ok(("One", "One", "Sub"))
148		},
149		
150		Err((_, err)) => Err(err)
151	}
152}
153
154fn format_blend_formula(formula: (&str, &str, &str)) -> String {
155	let (src_factor_name, dst_factor_name, op_name) = formula;
156	format!("\
157		blend_formula::BlendFormula {{\
158			src_factor: blend_formula::BlendFactor::{},\
159			dst_factor: blend_formula::BlendFactor::{},\
160			operation: blend_formula::BlendOperation::{},\
161		}}",
162		src_factor_name,
163		dst_factor_name,
164		op_name,
165	)
166}
167
168fn term_formula(term: Term)
169	-> Option<(&'static str, &'static str, &'static str)>
170{
171	let term = &*term.to_string();
172	Some(include!("formula_map.in"))
173}
174
175#[test]
176fn gen_formula_map() {
177	use std::collections::HashSet;
178	use std::fs::File;
179	use std::io::Write;
180	
181	let operation_list = [
182		("Add",    linear_term(Term::Src, LinearOp::Plus,  Term::Dst)),
183		("Sub",    linear_term(Term::Src, LinearOp::Minus, Term::Dst)),
184		("RevSub", linear_term(Term::Dst, LinearOp::Minus, Term::Src)),
185		("Min",    comparison_term(Term::Src, ComparisonOp::Min, Term::Dst)),
186		("Max",    comparison_term(Term::Src, ComparisonOp::Max, Term::Dst)),
187	];
188	
189	let src_alpha = suffix_term(Term::Src, BlendSuffix::Alpha);
190	let dst_alpha = suffix_term(Term::Dst, BlendSuffix::Alpha);
191	let one_minus_dst_alpha = linear_term(Term::One, LinearOp::Minus, dst_alpha.clone());
192	
193	let factor_list = [
194		("Zero",              Term::Zero),
195		("One",               Term::One),
196		("Src",               Term::Src),
197		("SrcAlpha",          src_alpha.clone()),
198		("Dst",               Term::Dst),
199		("DstAlpha",          dst_alpha.clone()),
200		("Constant",          Term::Constant),
201		("OneMinusSrc",       linear_term(Term::One, LinearOp::Minus, Term::Src)),
202		("OneMinusSrcAlpha",  linear_term(Term::One, LinearOp::Minus, src_alpha.clone())),
203		("OneMinusDst",       linear_term(Term::One, LinearOp::Minus, Term::Dst)),
204		("OneMinusDstAlpha",  one_minus_dst_alpha.clone()),
205		("OneMinusConstant",  linear_term(Term::One, LinearOp::Minus, Term::Constant)),
206		("SaturatedSrcAlpha", comparison_term(src_alpha, ComparisonOp::Min, one_minus_dst_alpha)),
207	];
208	
209	let mut formula_set = HashSet::new();
210	let mut match_text = String::from("match term {\n");
211	
212	for (op_name, term) in &operation_list {
213	for (a_factor_name, a_factor) in &factor_list {
214	for (b_factor_name, b_factor) in &factor_list {
215		let term = match term {
216			Term::LinearTerm(a, op, b) => simplify_linear_term(
217				factor_term(*a.clone(), a_factor.clone()),
218				op.clone(),
219				factor_term(*b.clone(), b_factor.clone())
220			),
221			Term::Comparison(a, op, b) => simplify_comparison_term(
222				factor_term(*a.clone(), a_factor.clone()),
223				op.clone(),
224				factor_term(*b.clone(), b_factor.clone())
225			),
226			_ => unreachable!()
227		};
228		if !formula_set.contains(&term) {
229			match_text.push_str(&*format!(
230				"\"{}\" => (\"{}\", \"{}\", \"{}\"),\n",
231				term,
232				if *op_name == "RevSub" { b_factor_name } else { a_factor_name },
233				if *op_name == "RevSub" { a_factor_name } else { b_factor_name },
234				op_name
235			));
236			formula_set.insert(term);
237		}
238	}}}
239	
240	match_text.push_str("_ => return None }");
241	
242	File::create("src/formula_map.in").unwrap()
243		.write(match_text.as_bytes())
244		.expect("failed to write");
245}
246
247#[derive(Debug, Clone, PartialEq, Eq, Hash)]
248enum Term {
249	Zero,
250	One,
251	Src,
252	Dst,
253	Constant,
254	
255	SuffixTerm(Box<Term>, BlendSuffix),
256	FactorTerm(Box<Term>, Box<Term>),
257	LinearTerm(Box<Term>, LinearOp, Box<Term>),
258	Comparison(Box<Term>, ComparisonOp, Box<Term>),
259}
260
261impl Term {
262	const fn order(&self) -> u8 {
263		match self {
264			Term::Zero           => 0,
265			Term::One            => 1,
266			Term::Src            => 2,
267			Term::Dst            => 3,
268			Term::Constant       => 4,
269			Term::SuffixTerm(..) => 5,
270			Term::FactorTerm(..) => 6,
271			Term::LinearTerm(..) => 7,
272			Term::Comparison(..) => 8,
273		}
274	}
275	
276	fn dimension(&self) -> Option<BlendSuffix> {
277		match self {
278			Term::Zero     => Some(BlendSuffix::Alpha),
279			Term::One      => Some(BlendSuffix::Alpha),
280			Term::Src      => Some(BlendSuffix::Full),
281			Term::Dst      => Some(BlendSuffix::Full),
282			Term::Constant => Some(BlendSuffix::Full),
283			
284			Term::SuffixTerm(term, suffix) => if term.dimension()? >= *suffix {
285				Some(*suffix)
286			} else {
287				None
288			},
289			
290			Term::FactorTerm(a,    b) |
291			Term::LinearTerm(a, _, b) => match (a.dimension()?, b.dimension()?) {
292				(d, BlendSuffix::Alpha) |
293				(BlendSuffix::Alpha, d) => Some(d),
294				(a, b) if a == b => Some(a),
295				_ => None
296			},
297			
298			Term::Comparison(a, _, b) => match (a.dimension()?, b.dimension()?) {
299				(a, b) if a == b => Some(a),
300				_ => None
301			},
302		}
303	}
304	
305	fn clear_dimension(self, dimension: BlendSuffix) -> Self {
306		match self {
307			Term::SuffixTerm(term, suffix) if suffix == dimension => {
308				term.clear_dimension(dimension)
309			},
310			Term::FactorTerm(a, b) => factor_term(
311				a.clear_dimension(dimension),
312				b.clear_dimension(dimension)
313			),
314			Term::LinearTerm(a, op, b) => linear_term(
315				a.clear_dimension(dimension),
316				op,
317				b.clear_dimension(dimension)
318			),
319			Term::Comparison(a, op, b) => comparison_term(
320				a.clear_dimension(dimension),
321				op,
322				b.clear_dimension(dimension)
323			),
324			term => term
325		}
326	}
327}
328
329impl PartialOrd for Term {
330	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
331		Some(self.cmp(other))
332	}
333}
334
335impl Ord for Term {
336	fn cmp(&self, other: &Self) -> Ordering {
337		match (self, other) {
338			(Term::SuffixTerm(a, a_s), Term::SuffixTerm(b, b_s)) => if a == b {
339				(*a_s).cmp(b_s)
340			} else {
341				(*a).cmp(b)
342			},
343			(Term::FactorTerm(a, a_f), Term::FactorTerm(b, b_f)) => if a == b {
344				(*a_f).cmp(b_f)
345			} else {
346				(*a).cmp(b)
347			},
348			(Term::LinearTerm(a, a_op, a_b), Term::LinearTerm(b, b_op, b_b)) => {
349				if a == b {
350					if a_b == b_b {
351						(*a_op).cmp(b_op)
352					} else {
353						(*a_b).cmp(b_b)
354					}
355				} else {
356					(*a).cmp(b)
357				}
358			},
359			(Term::Comparison(a, a_op, a_b), Term::Comparison(b, b_op, b_b)) => {
360				if a == b {
361					if a_b == b_b {
362						(*a_op).cmp(b_op)
363					} else {
364						(*a_b).cmp(b_b)
365					}
366				} else {
367					(*a).cmp(b)
368				}
369			},
370			(a, b) => a.order().cmp(&b.order())
371		}
372	}
373}
374
375impl Display for Term {
376	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
377		fn par(outer: &Term, inner: &Term) -> String {
378			match Ord::cmp(&inner.order(), &outer.order()) {
379				Ordering::Greater => format!("({})", inner),
380				Ordering::Less    => inner.to_string(),
381				Ordering::Equal   => match outer {
382					Term::SuffixTerm(..) => inner.to_string(),
383					_ => format!("({})", inner)
384				},
385			}
386		}
387		match self {
388			Term::Zero     => write!(f, "0"),
389			Term::One      => write!(f, "1"),
390			Term::Src      => write!(f, "src"),
391			Term::Dst      => write!(f, "dst"),
392			Term::Constant => write!(f, "c"),
393			
394			Term::SuffixTerm(term, suf) => write!(f,
395				"{}.{}",
396				par(self, term),
397				suf
398			),
399			Term::FactorTerm(a, b) => write!(f,
400				"{}*{}",
401				par(self, a),
402				par(self, b)
403			),
404			Term::LinearTerm(a, op, b) => write!(f,
405				"{}{op}{}",
406				par(self, a),
407				par(self, b)
408			),
409			Term::Comparison(a, op, b) => write!(f,
410				"{}{op}{}",
411				par(self, a),
412				par(self, b)
413			),
414		}
415	}
416}
417
418#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
419enum BlendSuffix {
420	Alpha,
421	Color,
422	Full,
423}
424
425impl Display for BlendSuffix {
426	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
427		match *self {
428			BlendSuffix::Full  => write!(f, "rgba"),
429			BlendSuffix::Color => write!(f, "rgb"),
430			BlendSuffix::Alpha => write!(f, "a"),
431		}
432	}
433}
434
435#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
436enum LinearOp {
437	Plus,
438	Minus,
439}
440
441impl LinearOp {
442	fn inverse(self) -> Self {
443		match self {
444			Self::Plus  => Self::Minus,
445			Self::Minus => Self::Plus,
446		}
447	}
448}
449
450impl Display for LinearOp {
451	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
452		match *self {
453			Self::Plus  => write!(f, "+"),
454			Self::Minus => write!(f, "-"),
455		}
456	}
457}
458
459#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
460enum ComparisonOp {
461	Min,
462	Max,
463}
464
465impl ComparisonOp {
466	fn inverse(self) -> Self {
467		match self {
468			Self::Min => Self::Max,
469			Self::Max => Self::Min,
470		}
471	}
472}
473
474impl Display for ComparisonOp {
475	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
476		match *self {
477			Self::Min => write!(f, "<"),
478			Self::Max => write!(f, ">"),
479		}
480	}
481}
482
483fn suffix_term(term: Term, suf: BlendSuffix) -> Term {
484	Term::SuffixTerm(Box::new(term), suf)
485}
486fn factor_term(a: Term, b: Term) -> Term {
487	Term::FactorTerm(Box::new(a), Box::new(b))
488}
489fn linear_term(a: Term, op: LinearOp, b: Term) -> Term {
490	Term::LinearTerm(Box::new(a), op, Box::new(b))
491}
492fn comparison_term(a: Term, op: ComparisonOp, b: Term) -> Term {
493	Term::Comparison(Box::new(a), op, Box::new(b))
494}
495
496#[derive(Debug)]
497enum ParseError {
498	ExpectedTerm(TokenTree),
499	ExpectedTermGotEnd,
500	ExpectedOp(TokenTree),
501	ExpectedSuffix(TokenTree),
502	ExpectedSuffixGotEnd,
503	AmbiguousComparison,
504	NestedSuffix(Vec<BlendSuffix>),
505	IncompatibleDimension(Term),
506	ExpectedDimension(BlendSuffix, BlendSuffix),
507	InvalidFormula(BlendSuffix),
508}
509
510impl Display for ParseError {
511	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
512		const TERM_TEXT: &str = "'0', '1', 'src', 'dst', 'c'";
513		const OP_TEXT: &str = "'.', '*', '+', '-', '<', '>'";
514		const SUFFIX_TEXT: &str = "'rgb', 'a'";
515		match self {
516			Self::ExpectedTerm(token) => write!(f,
517				"expected term ({}); got '{}'",
518				TERM_TEXT,
519				token
520			),
521			Self::ExpectedTermGotEnd => write!(f,
522				"expected term ({}); got end",
523				TERM_TEXT
524			),
525			Self::ExpectedOp(token) => write!(f,
526				"expected operator ({}); got '{}'",
527				OP_TEXT,
528				token
529			),
530			Self::ExpectedSuffix(token) => write!(f,
531				"expected accessor ({}); got '{}'",
532				SUFFIX_TEXT,
533				token
534			),
535			Self::ExpectedSuffixGotEnd => write!(f,
536				"expected accessor ({}); got end",
537				SUFFIX_TEXT
538			),
539			Self::AmbiguousComparison => write!(f,
540				"chained comparison operators require parentheses"
541			),
542			Self::NestedSuffix(suf_list) => write!(f,
543				"terms can only have one accessor each; got '{}'",
544				{
545					let mut text = String::new();
546					for suf in suf_list {
547						text.push('.');
548						text.push_str(&suf.to_string());
549					}
550					text
551				}
552			),
553			Self::IncompatibleDimension(term) => match term {
554				Term::SuffixTerm(a, suf) => write!(f,
555					"can't apply the accessor '{}' to a {} term",
556					suf,
557					match a.dimension().unwrap() {
558						BlendSuffix::Full  => "'vec4'",
559						BlendSuffix::Color => "'vec3'",
560						BlendSuffix::Alpha => "scalar",
561					},
562				),
563				Term::FactorTerm(a, b) => write!(f,
564					"multiplying terms with incompatible dimensions ({} * {})",
565					a.dimension().unwrap(),
566					b.dimension().unwrap(),
567				),
568				Term::LinearTerm(a, op, b) => write!(f,
569					"combining terms with incompatible dimensions ({} {op} {})",
570					a.dimension().unwrap(),
571					b.dimension().unwrap(),
572				),
573				Term::Comparison(a, op, b) => write!(f,
574					"comparing terms with incompatible dimensions ({} {op} {})",
575					a.dimension().unwrap(),
576					b.dimension().unwrap(),
577				),
578				term => write!(f, "this error shouldn't occur: {:?}", term),
579			},
580			Self::ExpectedDimension(dimension, got_dimension) => write!(f,
581				"expected a formula for '{}'; got a formula for '{}'",
582				dimension,
583				got_dimension
584			),
585			Self::InvalidFormula(dimension) => write!(f,
586				"{0} formula must evaluate to 'Term{1}*Factor Op Term{1}*Factor', where:\
587				\n- each Term is either 'src' or 'dst' (mutually exclusive)\
588				\n- each Factor is: '0', '1', 'Term{1}', '1-Term{1}'{2}, 'c{1}', or '1-c{1}'\
589				\n- Op is: '+', '-', '<', or '>'",
590				match dimension {
591					BlendSuffix::Full  => "blend",
592					BlendSuffix::Color => "color",
593					BlendSuffix::Alpha => "alpha",
594				},
595				match dimension {
596					BlendSuffix::Full  => "",
597					BlendSuffix::Color => ".rgb",
598					BlendSuffix::Alpha => ".a",
599				},
600				match dimension {
601					BlendSuffix::Full  => ", 'Term.a', '1-Term.a'",
602					BlendSuffix::Color => ", 'Term.a', '1-Term.a', 'src.a<(1-dst.a)'",
603					BlendSuffix::Alpha => "",
604				},
605			),
606		}
607	}
608}
609
610impl Error for ParseError {}
611
612fn parse_formula(token_iter: &mut TokenIter, is_comparison: bool)
613	-> Result<Term, (Option<Term>, ParseError)>
614{
615	let mut term = parse_term(token_iter)?;
616	while let Some(token) = token_iter.next() {
617		if let TokenTree::Punct(punct) = token {
618			let prev = std::mem::replace(&mut term, Term::Zero);
619			term = match punct.as_char() {
620				'+' | '-' => {
621					let op = if punct == '+' {
622						LinearOp::Plus
623					} else {
624						LinearOp::Minus
625					};
626					match parse_term(token_iter) {
627						Ok(next) => linear_term(prev, op, next),
628						Err((None,       err)) => return Err((Some(prev), err)),
629						Err((Some(next), err)) => return Err((
630							Some(linear_term(prev, op, next)),
631							err
632						)),
633					}
634				},
635				'<' | '>' => {
636					if is_comparison {
637						return Err((Some(prev), ParseError::AmbiguousComparison));
638					}
639					let op = if punct == '<' {
640						ComparisonOp::Min
641					} else {
642						ComparisonOp::Max
643					};
644					match parse_formula(token_iter, true) {
645						Ok(next) => comparison_term(prev, op, next),
646						Err((None,       err)) => return Err((Some(prev), err)),
647						Err((Some(next), err)) => return Err((
648							Some(comparison_term(prev, op, next)),
649							err
650						)),
651					}
652				},
653				_ => return Err((
654					Some(prev),
655					ParseError::ExpectedOp(TokenTree::Punct(punct))
656				))
657			};
658			
659			 // Incompatible Terms:
660			if term.dimension().is_none() {
661				return Err((
662					Some(term.clone()),
663					ParseError::IncompatibleDimension(term)
664				))
665			}
666		} else {
667			return Err((Some(term), ParseError::ExpectedOp(token)))
668		}
669	}
670	Ok(term)
671}
672
673fn parse_term(token_iter: &mut TokenIter)
674	-> Result<Term, (Option<Term>, ParseError)>
675{
676	let mut term = match token_iter.next() {
677		Some(TokenTree::Ident(ident)) => match ident.to_string().as_str() {
678			"src" => Term::Src,
679			"dst" => Term::Dst,
680			"c"   => Term::Constant,
681			_ => return Err((
682				None,
683				ParseError::ExpectedTerm(TokenTree::Ident(ident))
684			))
685		},
686		Some(TokenTree::Literal(liter)) => match liter.to_string().as_str() {
687			"0" => Term::Zero,
688			"1" => Term::One,
689			_ => return Err((
690				None,
691				ParseError::ExpectedTerm(TokenTree::Literal(liter))
692			))
693		},
694		Some(TokenTree::Group(group)) => parse_formula(
695			&mut group.stream()
696				.into_iter()
697				.collect::<Vec<TokenTree>>()
698				.into_iter()
699				.peekable(),
700			false
701		)?,
702		Some(TokenTree::Punct(punct)) => match punct.as_char() {
703			'-' => match parse_term(token_iter) {
704				Ok(t) => linear_term(Term::Zero, LinearOp::Minus, t),
705				Err((None, ParseError::ExpectedTermGotEnd)) => return Err((
706					Some(Term::Zero),
707					ParseError::ExpectedTerm(TokenTree::Punct(punct))
708				)),
709				Err((None,    err)) => return Err((Some(Term::Zero), err)),
710				Err((Some(t), err)) => return Err((
711					Some(linear_term(Term::Zero, LinearOp::Minus, t)),
712					err
713				)),
714			},
715			_ => return Err((
716				None,
717				ParseError::ExpectedTerm(TokenTree::Punct(punct))
718			))
719		},
720		None => return Err((None, ParseError::ExpectedTermGotEnd))
721	};
722	
723	match parse_term_suffix(token_iter) {
724		Ok(None) => {},
725		Ok(Some(suffix)) => {
726			term = suffix_term(term, suffix);
727			if term.dimension().is_none() {
728				return Err((
729					Some(term.clone()),
730					ParseError::IncompatibleDimension(term)
731				))
732			}
733		},
734		Err(error) => return Err((Some(term), error)),
735	};
736	
737	match parse_term_factor(token_iter) {
738		Ok(None) => {},
739		Ok(Some(factor)) => {
740			term = factor_term(term, factor);
741			if term.dimension().is_none() {
742				return Err((
743					Some(term.clone()),
744					ParseError::IncompatibleDimension(term)
745				))
746			}
747		},
748		Err((_, error)) => return Err((Some(term), error)),
749	};
750	
751	Ok(term)
752}
753
754fn parse_term_suffix(token_iter: &mut TokenIter)
755	-> Result<Option<BlendSuffix>, ParseError>
756{
757	match token_iter.peek() {
758		Some(TokenTree::Punct(p)) => if *p == '.' {
759			token_iter.next();
760			if let Some(token) = token_iter.next() {
761				if let TokenTree::Ident(suf) = token {
762					let suf = match suf.to_string().as_str() {
763						"rgb" => BlendSuffix::Color, // ??? .xyz
764						"a"   => BlendSuffix::Alpha, // ??? .w
765						_ => return Err(ParseError::ExpectedSuffix(TokenTree::Ident(suf)))
766					};
767					match parse_term_suffix(token_iter) {
768						Ok(None) => Ok(Some(suf)),
769						Ok(Some(next_suf)) => {
770							Err(ParseError::NestedSuffix(vec![suf, next_suf]))
771						},
772						Err(ParseError::NestedSuffix(mut nested_suf_list)) => {
773							nested_suf_list.insert(0, suf);
774							Err(ParseError::NestedSuffix(nested_suf_list))
775						}
776						error => error
777					}
778				} else {
779					Err(ParseError::ExpectedSuffix(token))
780				}
781			} else {
782				Err(ParseError::ExpectedSuffixGotEnd)
783			}
784		} else {
785			Ok(None)
786		},
787		None => Ok(None),
788		_ => Err(ParseError::ExpectedOp(token_iter.next().unwrap())),
789	}
790}
791
792fn parse_term_factor(token_iter: &mut TokenIter) -> Result<Option<Term>, (Option<Term>, ParseError)> {
793	match token_iter.peek() {
794		Some(TokenTree::Punct(p)) => if *p == '*' {
795			token_iter.next();
796			Ok(Some(parse_term(token_iter)?))
797		} else {
798			Ok(None)
799		},
800		None => Ok(None),
801		_ => Err((
802			None,
803			ParseError::ExpectedOp(token_iter.next().unwrap())
804		)),
805	}
806}
807
808fn simplify_term(term: Term) -> Term {
809	match term {
810		Term::Comparison(a, op, b) => simplify_comparison_term(*a, op, *b),
811		Term::LinearTerm(a, op, b) => simplify_linear_term(*a, op, *b),
812		Term::FactorTerm(a, b) => simplify_factor_term(*a, *b),
813		Term::SuffixTerm(term, suffix) => simplify_suffix_term(*term, suffix),
814		term => term
815	}
816}
817
818fn simplify_suffix_term(term: Term, suf: BlendSuffix) -> Term {
819	match simplify_term(term) {
820		Term::Comparison(sub_a, sub_op, sub_b) => comparison_term(
821			simplify_suffix_term(*sub_a, suf),
822			sub_op,
823			simplify_suffix_term(*sub_b, suf)
824		),
825		Term::LinearTerm(sub_a, sub_op, sub_b) => linear_term(
826			simplify_suffix_term(*sub_a, suf),
827			sub_op,
828			simplify_suffix_term(*sub_b, suf)
829		),
830		Term::FactorTerm(sub_a, sub_b) => factor_term(
831			simplify_suffix_term(*sub_a, suf),
832			simplify_suffix_term(*sub_b, suf)
833		),
834		Term::SuffixTerm(term, sub_suf) => Term::SuffixTerm(term, sub_suf),
835		term => suffix_term(term, suf)
836	}
837}
838
839fn simplify_factor_term(a: Term, b: Term) -> Term {
840	match (simplify_term(a), simplify_term(b)) {
841		(Term::Zero, _) |
842		(_, Term::Zero) => Term::Zero,
843		
844		(Term::One, term) |
845		(term, Term::One) => term,
846		
847		 // `(a_a > a_b) * b` => `(a_a * b) > (a_b * b)`:
848		(Term::Comparison(a_a, a_op, a_b), b) |
849		(b, Term::Comparison(a_a, a_op, a_b)) => comparison_term(
850			simplify_factor_term(*a_a, b.clone()),
851			a_op,
852			simplify_factor_term(*a_b, b)
853		),
854		
855		 // `(a_a ± a_b) * b` => `(a_a * b) ± (a_b * b)`:
856		(Term::LinearTerm(a_a, a_op, a_b), b) |
857		(b, Term::LinearTerm(a_a, a_op, a_b)) => linear_term(
858			simplify_factor_term(*a_a, b.clone()),
859			a_op,
860			simplify_factor_term(*a_b, b)
861		),
862		
863		 // `(a_a * a_b) * (b_a * b_b)` => `a_a * (a_b * (b_b * b_a))`:
864		(Term::FactorTerm(a_a, a_b), Term::FactorTerm(b_a, b_b)) => simplify_factor_term(
865			*a_a,
866			factor_term(*a_b, Term::FactorTerm(b_a, b_b))
867		),
868		 
869		 // `(a_a * a_b) * b`:
870		(Term::FactorTerm(a_a, a_b), b) |
871		(b, Term::FactorTerm(a_a, a_b)) => if b > *a_a {
872			factor_term(*a_a, simplify_factor_term(*a_b, b))
873		} else {
874			factor_term(b, Term::FactorTerm(a_a, a_b))
875		},
876		
877		 // `a * b`:
878		(a, b) => if a < b {
879			factor_term(a, b)
880		} else {
881			factor_term(b, a)
882		}
883	}
884}
885
886fn simplify_linear_term(a: Term, op: LinearOp, b: Term) -> Term {
887	// 0 - (1 + (1 + (c + (c + (src + (src + (src + (dst + (dst + dst)))))))))
888	// trace::trace!("? [{a}] {op} [{b}]");
889	match (simplify_term(a), op, simplify_term(b)) {
890		(Term::Zero, LinearOp::Plus, term)  |
891		(term, LinearOp::Plus,  Term::Zero) |
892		(term, LinearOp::Minus, Term::Zero) => term,
893		
894		 // `min(a_a, a_b) ± b` => `min(a_a ± b, a_b ± b)`:
895		(Term::Comparison(a_a, a_op, a_b), op, b) => simplify_comparison_term(
896			linear_term(*a_a, op, b.clone()),
897			a_op,
898			linear_term(*a_b, op, b),
899		),
900		
901		 // `a - min(b_a, b_b)` => `max(a - b_a, a - b_b)`:
902		(a, op, Term::Comparison(b_a, b_op, b_b)) => simplify_comparison_term(
903			linear_term(a.clone(), op, *b_a),
904			match op {
905				LinearOp::Plus  => b_op,
906				LinearOp::Minus => b_op.inverse(),
907			},
908			linear_term(a, op, *b_b),
909		),
910		
911		 // `a ± (b_a ± b_b)` => `(a ± b_b) ± b_a`:
912		(a, op, Term::LinearTerm(b_a, b_op, b_b)) => simplify_linear_term(
913			linear_term(
914				a,
915				match op {
916					LinearOp::Plus  => b_op,
917					LinearOp::Minus => b_op.inverse(),
918				},
919				*b_b
920			),
921			op,
922			*b_a
923		),
924		
925		 // `(a_a ± a_b) ± b`:
926		(Term::LinearTerm(a_a, a_op, a_b), op, b) => if b > *a_a {
927			 // => `a_a ± (a_b ± b)`:
928			match simplify_linear_term(
929				*a_b,
930				match a_op {
931					LinearOp::Plus  => op,
932					LinearOp::Minus => op.inverse(),
933				},
934				b
935			) {
936				Term::Zero => *a_a,
937				Term::LinearTerm(term_a, LinearOp::Minus, term_b)
938				if *term_a == Term::Zero => if *a_a == Term::Zero {
939					*term_b
940				} else {
941					Term::LinearTerm(a_a, a_op.inverse(), term_b)
942				},
943				term => linear_term(*a_a, a_op, term),
944			}
945		} else if op == LinearOp::Minus {
946			 // => `0 - (b - (a_a ± a_b))`:
947			if b == *a_a {
948				match a_op {
949					LinearOp::Plus  => *a_b,
950					LinearOp::Minus => linear_term(Term::Zero, a_op, *a_b),
951				}
952			} else {
953				linear_term(
954					Term::Zero,
955					op,
956					linear_term(b, op, Term::LinearTerm(a_a, a_op, a_b))
957				)
958			}
959		} else {
960			 // `(a_a ± a_b) + b` => `b + (a_a ± a_b)`:
961			linear_term(b, op, Term::LinearTerm(a_a, a_op, a_b))
962		},
963		
964		 // `a ± b`:
965		(a, op, b) => match b.cmp(&a) {
966			Ordering::Less => match op {
967				LinearOp::Plus  => linear_term(b, op, a),
968				LinearOp::Minus => linear_term(Term::Zero, op, linear_term(b, op, a)),
969			},
970			Ordering::Equal => match op {
971				LinearOp::Plus  => linear_term(a, op, b),
972				LinearOp::Minus => Term::Zero,
973			},
974			Ordering::Greater => linear_term(a, op, b),
975		}
976	}
977}
978
979fn simplify_comparison_term(a: Term, op: ComparisonOp, b: Term) -> Term {
980	match (simplify_term(a), op, simplify_term(b)) {
981		//  // `min(0, b)` => `0`:
982		// (Term::Zero, ComparisonOp::Min, Term::Zero)     |
983		// (Term::Zero, ComparisonOp::Min, Term::One)      |
984		// (Term::Zero, ComparisonOp::Min, Term::Src)      |
985		// (Term::Zero, ComparisonOp::Min, Term::Dst)      |
986		// (Term::Zero, ComparisonOp::Min, Term::Constant) => Term::Zero,
987		
988		//  // `max(0, b)` => `b`:
989		// (Term::Zero, ComparisonOp::Max, Term::Zero)     => Term::Zero,
990		// (Term::Zero, ComparisonOp::Max, Term::One)      => Term::One,
991		// (Term::Zero, ComparisonOp::Max, Term::Src)      => Term::Src,
992		// (Term::Zero, ComparisonOp::Max, Term::Dst)      => Term::Dst,
993		// (Term::Zero, ComparisonOp::Max, Term::Constant) => Term::Constant,
994		
995		 // `(a_a < a_b) < (b_a < b_b)`:
996		(Term::Comparison(a_a, a_op, a_b), op, Term::Comparison(b_a, b_op, b_b)) => {
997			if op == a_op {
998				simplify_comparison_term(*a_a, a_op, comparison_term(*a_b, op, Term::Comparison(b_a, b_op, b_b)))
999			} else if op == b_op {
1000				simplify_comparison_term(*b_a, b_op, comparison_term(*b_b, op, Term::Comparison(a_a, a_op, a_b)))
1001			} else if a_a == b_a {
1002				simplify_comparison_term(*a_a, a_op, Term::Comparison(a_b, op, b_b)) 
1003			} else if a_a == b_b {
1004				simplify_comparison_term(*a_a, a_op, Term::Comparison(b_a, op, a_b))
1005			} else if a_b == b_a {
1006				simplify_comparison_term(*a_b, a_op, Term::Comparison(a_a, op, b_b))
1007			} else if a_b == b_b {
1008				simplify_comparison_term(*a_b, a_op, Term::Comparison(a_a, op, b_a))
1009			} else {
1010				let a = Term::Comparison(a_a, a_op, a_b);
1011				let b = Term::Comparison(b_a, b_op, b_b);
1012				match b.cmp(&a) {
1013					Ordering::Less => comparison_term(b, op, a),
1014					Ordering::Equal => a,
1015					Ordering::Greater => comparison_term(a, op, b),
1016				}
1017			}
1018		},
1019		
1020		 // `(a_a < a_b) < b`:
1021		(Term::Comparison(a_a, a_op, a_b), op, b) |
1022		(b, op, Term::Comparison(a_a, a_op, a_b)) => if op == a_op {
1023			match b.cmp(&a_a) {
1024				Ordering::Less => comparison_term(b, op, Term::Comparison(a_a, a_op, a_b)),
1025				Ordering::Equal => Term::Comparison(a_a, a_op, a_b),
1026				Ordering::Greater => comparison_term(*a_a, a_op, simplify_comparison_term(*a_b, op, b)),
1027			}
1028		} else if b == *a_a || b == *a_b {
1029			b
1030		} else {
1031			comparison_term(b, op, Term::Comparison(a_a, a_op, a_b))
1032		},
1033		
1034		 // `a < b`:
1035		(a, op, b) => match b.cmp(&a) {
1036			Ordering::Less => comparison_term(b, op, a),
1037			Ordering::Equal => a,
1038			Ordering::Greater => comparison_term(a, op, b),
1039		}
1040	}
1041}