@@ -52,7 +52,10 @@ namespace Sass {
5252 ctx (exp.ctx),
5353 force(false ),
5454 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+ }
5659 Eval::~Eval () { }
5760
5861 Env* Eval::environment ()
@@ -535,9 +538,138 @@ namespace Sass {
535538 Expression_Ptr Eval::operator ()(Binary_Expression_Ptr b_in)
536539 {
537540
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+
539566 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;
541673
542674 // only the last item will be used to eval the binary expression
543675 if (String_Schema_Ptr s_l = Cast<String_Schema>(b->left ())) {
@@ -568,16 +700,6 @@ namespace Sass {
568700 }
569701 }
570702
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-
581703 // fully evaluate their values
582704 if (op_type == Sass_OP::EQ ||
583705 op_type == Sass_OP::NEQ ||
@@ -598,19 +720,6 @@ namespace Sass {
598720 lhs = lhs->perform (this );
599721 }
600722
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- }
614723 // not a logical connective, so go ahead and eval the rhs
615724 rhs = rhs->perform (this );
616725 AST_Node_Obj lu = lhs;
@@ -1385,6 +1494,23 @@ namespace Sass {
13851494 throw Exception::ZeroDivisionError (l, r);
13861495 }
13871496
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+
13881514 Number tmp (&r); // copy
13891515 bool strict = op != Sass_OP::MUL && op != Sass_OP::DIV;
13901516 tmp.normalize (l.find_convertible_unit (), strict);
0 commit comments