@@ -52,7 +52,10 @@ namespace Sass {
52
52
ctx (exp.ctx),
53
53
force(false ),
54
54
is_in_comment(false )
55
- { }
55
+ {
56
+ bool_true = SASS_MEMORY_NEW (Boolean, " [NA]" , true );
57
+ bool_false = SASS_MEMORY_NEW (Boolean, " [NA]" , false );
58
+ }
56
59
Eval::~Eval () { }
57
60
58
61
Env* Eval::environment ()
@@ -535,9 +538,138 @@ namespace Sass {
535
538
Expression_Ptr Eval::operator ()(Binary_Expression_Ptr b_in)
536
539
{
537
540
538
- String_Schema_Obj ret_schema;
541
+ Expression_Obj lhs = b_in->left ();
542
+ Expression_Obj rhs = b_in->right ();
543
+ enum Sass_OP op_type = b_in->optype ();
544
+
545
+ if (op_type == Sass_OP::AND) {
546
+ // LOCAL_FLAG(force, true);
547
+ lhs = lhs->perform (this );
548
+ if (!*lhs) return lhs.detach ();
549
+ return rhs->perform (this );
550
+ }
551
+ else if (op_type == Sass_OP::OR) {
552
+ // LOCAL_FLAG(force, true);
553
+ lhs = lhs->perform (this );
554
+ if (*lhs) return lhs.detach ();
555
+ return rhs->perform (this );
556
+ }
557
+
558
+ // Evaluate variables as early o
559
+ while (Variable_Ptr l_v = Cast<Variable>(lhs)) {
560
+ lhs = operator ()(l_v);
561
+ }
562
+ while (Variable_Ptr r_v = Cast<Variable>(rhs)) {
563
+ rhs = operator ()(r_v);
564
+ }
565
+
539
566
Binary_Expression_Obj b = b_in;
540
- enum Sass_OP op_type = b->optype ();
567
+
568
+ // Evaluate sub-expressions early on
569
+ while (Binary_Expression_Ptr l_b = Cast<Binary_Expression>(lhs)) {
570
+ if (!force && l_b->is_delayed ()) break ;
571
+ lhs = operator ()(l_b);
572
+ }
573
+ while (Binary_Expression_Ptr r_b = Cast<Binary_Expression>(rhs)) {
574
+ if (!force && r_b->is_delayed ()) break ;
575
+ rhs = operator ()(r_b);
576
+ }
577
+
578
+ // don't eval delayed expressions (the '/' when used as a separator)
579
+ if (!force && op_type == Sass_OP::DIV && b->is_delayed ()) {
580
+ b->right (b->right ()->perform (this ));
581
+ b->left (b->left ()->perform (this ));
582
+ return b.detach ();
583
+ }
584
+
585
+ // specific types we know are final
586
+ // handle them early to avoid overhead
587
+ if (Number_Ptr l_n = Cast<Number>(lhs)) {
588
+ // lhs is number and rhs is number
589
+ if (Number_Ptr r_n = Cast<Number>(rhs)) {
590
+ try {
591
+ switch (op_type) {
592
+ case Sass_OP::EQ: return *l_n == *r_n ? bool_true : bool_false;
593
+ case Sass_OP::NEQ: return *l_n == *r_n ? bool_false : bool_true;
594
+ case Sass_OP::LT: return *l_n < *r_n ? bool_true : bool_false;
595
+ case Sass_OP::GTE: return *l_n < *r_n ? bool_false : bool_true;
596
+ case Sass_OP::LTE: return *l_n < *r_n || *l_n == *r_n ? bool_true : bool_false;
597
+ case Sass_OP::GT: return *l_n < *r_n || *l_n == *r_n ? bool_false : bool_true;
598
+ case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:
599
+ return op_numbers (op_type, *l_n, *r_n, ctx.c_options , b_in->pstate ());
600
+ default : break ;
601
+ }
602
+ }
603
+ catch (Exception::OperationError& err)
604
+ {
605
+ throw Exception::SassValueError (b_in->pstate (), err);
606
+ }
607
+ }
608
+ // lhs is number and rhs is color
609
+ else if (Color_Ptr r_c = Cast<Color>(rhs)) {
610
+ try {
611
+ switch (op_type) {
612
+ case Sass_OP::EQ: return *l_n == *r_c ? bool_true : bool_false;
613
+ case Sass_OP::NEQ: return *l_n == *r_c ? bool_false : bool_true;
614
+ case Sass_OP::LT: return *l_n < *r_c ? bool_true : bool_false;
615
+ case Sass_OP::GTE: return *l_n < *r_c ? bool_false : bool_true;
616
+ case Sass_OP::LTE: return *l_n < *r_c || *l_n == *r_c ? bool_true : bool_false;
617
+ case Sass_OP::GT: return *l_n < *r_c || *l_n == *r_c ? bool_false : bool_true;
618
+ case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:
619
+ return op_number_color (op_type, *l_n, *r_c, ctx.c_options , b_in->pstate ());
620
+ default : break ;
621
+ }
622
+ }
623
+ catch (Exception::OperationError& err)
624
+ {
625
+ throw Exception::SassValueError (b_in->pstate (), err);
626
+ }
627
+ }
628
+ }
629
+ else if (Color_Ptr l_c = Cast<Color>(lhs)) {
630
+ // lhs is color and rhs is color
631
+ if (Color_Ptr r_c = Cast<Color>(rhs)) {
632
+ try {
633
+ switch (op_type) {
634
+ case Sass_OP::EQ: return *l_c == *r_c ? bool_true : bool_false;
635
+ case Sass_OP::NEQ: return *l_c == *r_c ? bool_false : bool_true;
636
+ case Sass_OP::LT: return *l_c < *r_c ? bool_true : bool_false;
637
+ case Sass_OP::GTE: return *l_c < *r_c ? bool_false : bool_true;
638
+ case Sass_OP::LTE: return *l_c < *r_c || *l_c == *r_c ? bool_true : bool_false;
639
+ case Sass_OP::GT: return *l_c < *r_c || *l_c == *r_c ? bool_false : bool_true;
640
+ case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:
641
+ return op_colors (op_type, *l_c, *r_c, ctx.c_options , b_in->pstate ());
642
+ default : break ;
643
+ }
644
+ }
645
+ catch (Exception::OperationError& err)
646
+ {
647
+ throw Exception::SassValueError (b_in->pstate (), err);
648
+ }
649
+ }
650
+ // lhs is color and rhs is number
651
+ else if (Number_Ptr r_n = Cast<Number>(rhs)) {
652
+ try {
653
+ switch (op_type) {
654
+ case Sass_OP::EQ: return *l_c == *r_n ? bool_true : bool_false;
655
+ case Sass_OP::NEQ: return *l_c == *r_n ? bool_false : bool_true;
656
+ case Sass_OP::LT: return *l_c < *r_n ? bool_true : bool_false;
657
+ case Sass_OP::GTE: return *l_c < *r_n ? bool_false : bool_true;
658
+ case Sass_OP::LTE: return *l_c < *r_n || *l_c == *r_n ? bool_true : bool_false;
659
+ case Sass_OP::GT: return *l_c < *r_n || *l_c == *r_n ? bool_false : bool_true;
660
+ case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:
661
+ return op_color_number (op_type, *l_c, *r_n, ctx.c_options , b_in->pstate ());
662
+ default : break ;
663
+ }
664
+ }
665
+ catch (Exception::OperationError& err)
666
+ {
667
+ throw Exception::SassValueError (b_in->pstate (), err);
668
+ }
669
+ }
670
+ }
671
+
672
+ String_Schema_Obj ret_schema;
541
673
542
674
// only the last item will be used to eval the binary expression
543
675
if (String_Schema_Ptr s_l = Cast<String_Schema>(b->left ())) {
@@ -568,16 +700,6 @@ namespace Sass {
568
700
}
569
701
}
570
702
571
- // don't eval delayed expressions (the '/' when used as a separator)
572
- if (!force && op_type == Sass_OP::DIV && b->is_delayed ()) {
573
- b->right (b->right ()->perform (this ));
574
- b->left (b->left ()->perform (this ));
575
- return b.detach ();
576
- }
577
-
578
- Expression_Obj lhs = b->left ();
579
- Expression_Obj rhs = b->right ();
580
-
581
703
// fully evaluate their values
582
704
if (op_type == Sass_OP::EQ ||
583
705
op_type == Sass_OP::NEQ ||
@@ -598,19 +720,6 @@ namespace Sass {
598
720
lhs = lhs->perform (this );
599
721
}
600
722
601
- Binary_Expression_Obj u3 = b;
602
- switch (op_type) {
603
- case Sass_OP::AND: {
604
- return *lhs ? b->right ()->perform (this ) : lhs.detach ();
605
- }
606
-
607
- case Sass_OP::OR: {
608
- return *lhs ? lhs.detach () : b->right ()->perform (this );
609
- }
610
-
611
- default :
612
- break ;
613
- }
614
723
// not a logical connective, so go ahead and eval the rhs
615
724
rhs = rhs->perform (this );
616
725
AST_Node_Obj lu = lhs;
@@ -1385,6 +1494,23 @@ namespace Sass {
1385
1494
throw Exception::ZeroDivisionError (l, r);
1386
1495
}
1387
1496
1497
+ size_t l_n_units = l.numerator_units ().size ();
1498
+ size_t l_d_units = l.numerator_units ().size ();
1499
+ size_t r_n_units = r.denominator_units ().size ();
1500
+ size_t r_d_units = r.denominator_units ().size ();
1501
+ // optimize out the most common and simplest case
1502
+ if (l_n_units == r_n_units && l_d_units == r_d_units) {
1503
+ if (l_n_units + l_d_units <= 1 && r_n_units + r_d_units <= 1 ) {
1504
+ if (l.numerator_units () == r.numerator_units ()) {
1505
+ if (l.denominator_units () == r.denominator_units ()) {
1506
+ Number_Ptr v = SASS_MEMORY_COPY (&l);
1507
+ v->value (ops[op](lv, rv));
1508
+ return v;
1509
+ }
1510
+ }
1511
+ }
1512
+ }
1513
+
1388
1514
Number tmp (&r); // copy
1389
1515
bool strict = op != Sass_OP::MUL && op != Sass_OP::DIV;
1390
1516
tmp.normalize (l.find_convertible_unit (), strict);
0 commit comments