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 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 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, "a" => BlendSuffix::Alpha, _ => 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 (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 (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 (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 (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) => 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 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 (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, 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, 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 (Term::LinearTerm(a_a, a_op, a_b), op, b) => if b > *a_a {
927 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 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 linear_term(b, op, Term::LinearTerm(a_a, a_op, a_b))
962 },
963
964 (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 (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 (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, 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}