sassc 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -28,14 +28,22 @@ namespace Sass {
28
28
  size_t ip = 0, LP = ps->length();
29
29
  size_t ia = 0, LA = as->length();
30
30
  while (ia < LA) {
31
+ Argument* a = (*as)[ia];
31
32
  if (ip >= LP) {
33
+ // skip empty rest arguments
34
+ if (a->is_rest_argument()) {
35
+ if (List* l = dynamic_cast<List*>(a->value())) {
36
+ if (l->length() == 0) {
37
+ ++ ia; continue;
38
+ }
39
+ }
40
+ }
32
41
  stringstream msg;
33
42
  msg << callee << " only takes " << LP << " arguments; "
34
43
  << "given " << LA;
35
44
  error(msg.str(), as->pstate());
36
45
  }
37
46
  Parameter* p = (*ps)[ip];
38
- Argument* a = (*as)[ia];
39
47
 
40
48
  // If the current parameter is the rest parameter, process and break the loop
41
49
  if (p->is_rest_parameter()) {
@@ -48,6 +56,20 @@ namespace Sass {
48
56
  else {
49
57
  env->local_frame()[p->name()] = a->value();
50
58
  }
59
+ } else if (a->is_keyword_argument()) {
60
+
61
+ // expand keyword arguments into their parameters
62
+ List* arglist = new (ctx.mem) List(p->pstate(), 0, List::COMMA, true);
63
+ env->local_frame()[p->name()] = arglist;
64
+ Map* argmap = static_cast<Map*>(a->value());
65
+ for (auto key : argmap->keys()) {
66
+ string name = unquote(static_cast<String_Constant*>(key)->value());
67
+ (*arglist) << new (ctx.mem) Argument(key->pstate(),
68
+ argmap->at(key),
69
+ name,
70
+ false);
71
+ }
72
+
51
73
  } else {
52
74
 
53
75
  // copy all remaining arguments into the rest parameter, preserving names
@@ -3,6 +3,8 @@
3
3
  namespace Sass {
4
4
  namespace Constants {
5
5
 
6
+ extern const unsigned long MaxCallStack = 1024;
7
+
6
8
  // https://github.com/sass/libsass/issues/592
7
9
  // https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
8
10
  // https://github.com/sass/sass/issues/1495#issuecomment-61189114
@@ -138,7 +140,7 @@ namespace Sass {
138
140
  extern const char arglist_name[] = "arglist";
139
141
 
140
142
  // constants for uri parsing (RFC 3986 Appendix A.)
141
- extern const char uri_chars[] = ":;/?!$%&#@[]{}'\"*+-.,_=";
143
+ extern const char uri_chars[] = ":;/?!$%&#@|[]{}'`^\"*+-.,_=~";
142
144
 
143
145
  // some specific constant character classes
144
146
  // they must be static to be useable by lexer
@@ -4,6 +4,9 @@
4
4
  namespace Sass {
5
5
  namespace Constants {
6
6
 
7
+ // The maximum call stack that can be created
8
+ extern const unsigned long MaxCallStack;
9
+
7
10
  // https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
8
11
  // The following list of selectors is by increasing specificity:
9
12
  extern const unsigned long Specificity_Star;
@@ -512,7 +512,6 @@ namespace Sass {
512
512
  register_function(ctx, index_sig, index, env);
513
513
  register_function(ctx, join_sig, join, env);
514
514
  register_function(ctx, append_sig, append, env);
515
- register_function(ctx, compact_sig, compact, env);
516
515
  register_function(ctx, zip_sig, zip, env);
517
516
  register_function(ctx, list_separator_sig, list_separator, env);
518
517
  // Map Functions
@@ -290,6 +290,7 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
290
290
  cerr << ind << "Import " << block;
291
291
  cerr << " (" << pstate_source_position(node) << ")";
292
292
  cerr << " " << block->tabs() << endl;
293
+ debug_ast(block->media_queries(), ind + " @ ");
293
294
  // vector<string> files_;
294
295
  for (auto imp : block->urls()) debug_ast(imp, "@ ", env);
295
296
  } else if (dynamic_cast<Assignment*>(node)) {
@@ -342,7 +343,13 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
342
343
  Definition* block = dynamic_cast<Definition*>(node);
343
344
  cerr << ind << "Definition " << block;
344
345
  cerr << " (" << pstate_source_position(node) << ")";
346
+ cerr << " [name: " << block->name() << "] ";
347
+ cerr << " [type: " << (block->type() == Sass::Definition::Type::MIXIN ? "Mixin " : "Function ") << "] ";
348
+ // this seems to lead to segfaults some times?
349
+ // cerr << " [signature: " << block->signature() << "] ";
350
+ cerr << " [native: " << block->native_function() << "] ";
345
351
  cerr << " " << block->tabs() << endl;
352
+ debug_ast(block->parameters(), ind + " params: ", env);
346
353
  if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }
347
354
  } else if (dynamic_cast<Mixin_Call*>(node)) {
348
355
  Mixin_Call* block = dynamic_cast<Mixin_Call*>(node);
@@ -368,9 +375,14 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
368
375
  else if (expression->type() == Textual::PERCENTAGE) cerr << " [PERCENTAGE]";
369
376
  else if (expression->type() == Textual::DIMENSION) cerr << " [DIMENSION]";
370
377
  else if (expression->type() == Textual::HEX) cerr << " [HEX]";
371
- cerr << expression << " [" << expression->value() << "]" << endl;
378
+ cerr << expression << " [" << expression->value() << "]";
379
+ if (expression->is_delayed()) cerr << " [delayed]";
380
+ cerr << endl;
372
381
  } else if (dynamic_cast<Variable*>(node)) {
373
382
  Variable* expression = dynamic_cast<Variable*>(node);
383
+ cerr << ind << "Variable " << expression << " [" << expression->name() << "]";
384
+ if (expression->is_delayed()) cerr << " [delayed]";
385
+ cerr << endl;
374
386
  cerr << ind << "Variable " << expression;
375
387
  cerr << " (" << pstate_source_position(node) << ")";
376
388
  cerr << " [" << expression->name() << "]" << endl;
@@ -378,6 +390,9 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
378
390
  if (env && env->has(name)) debug_ast(static_cast<Expression*>((*env)[name]), ind + " -> ", env);
379
391
  } else if (dynamic_cast<Function_Call_Schema*>(node)) {
380
392
  Function_Call_Schema* expression = dynamic_cast<Function_Call_Schema*>(node);
393
+ cerr << ind << "Function_Call_Schema " << expression << "]";
394
+ if (expression->is_delayed()) cerr << " [delayed]";
395
+ cerr << endl;
381
396
  cerr << ind << "Function_Call_Schema " << expression;
382
397
  cerr << " (" << pstate_source_position(node) << ")";
383
398
  cerr << "" << endl;
@@ -385,30 +400,65 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
385
400
  debug_ast(expression->arguments(), ind + " args: ", env);
386
401
  } else if (dynamic_cast<Function_Call*>(node)) {
387
402
  Function_Call* expression = dynamic_cast<Function_Call*>(node);
403
+ cerr << ind << "Function_Call " << expression << " [" << expression->name() << "]";
404
+ if (expression->is_delayed()) cerr << " [delayed]";
405
+ cerr << endl;
388
406
  cerr << ind << "Function_Call " << expression;
389
407
  cerr << " (" << pstate_source_position(node) << ")";
390
408
  cerr << " [" << expression->name() << "]" << endl;
391
409
  debug_ast(expression->arguments(), ind + " args: ", env);
392
410
  } else if (dynamic_cast<Arguments*>(node)) {
393
411
  Arguments* expression = dynamic_cast<Arguments*>(node);
412
+ cerr << ind << "Arguments " << expression << "]";
413
+ if (expression->is_delayed()) cerr << " [delayed]";
414
+ cerr << endl;
394
415
  cerr << ind << "Arguments " << expression;
395
416
  cerr << " (" << pstate_source_position(node) << ")";
396
417
  cerr << endl;
397
418
  for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }
398
419
  } else if (dynamic_cast<Argument*>(node)) {
399
420
  Argument* expression = dynamic_cast<Argument*>(node);
421
+ cerr << ind << "Argument " << expression << " [" << expression->value() << "]";
422
+ if (expression->is_delayed()) cerr << " [delayed]";
423
+ if (expression->is_rest_argument()) cerr << " [is_rest_argument]";
424
+ if (expression->is_keyword_argument()) cerr << " [is_keyword_argument]";
425
+ cerr << endl;
400
426
  cerr << ind << "Argument " << expression;
401
427
  cerr << " (" << pstate_source_position(node) << ")";
402
- cerr << " [" << expression->value() << "]" << endl;
428
+ cerr << " [" << expression->value() << "]";
429
+ cerr << " [name: " << expression->name() << "] ";
430
+ cerr << " [rest: " << expression->is_rest_argument() << "] ";
431
+ cerr << " [keyword: " << expression->is_keyword_argument() << "] " << endl;
403
432
  debug_ast(expression->value(), ind + " value: ", env);
433
+ } else if (dynamic_cast<Parameters*>(node)) {
434
+ Parameters* expression = dynamic_cast<Parameters*>(node);
435
+ cerr << ind << "Parameters " << expression;
436
+ cerr << " (" << pstate_source_position(node) << ")";
437
+ cerr << " [has_optional: " << expression->has_optional_parameters() << "] ";
438
+ cerr << " [has_rest: " << expression->has_rest_parameter() << "] ";
439
+ cerr << endl;
440
+ for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }
441
+ } else if (dynamic_cast<Parameter*>(node)) {
442
+ Parameter* expression = dynamic_cast<Parameter*>(node);
443
+ cerr << ind << "Parameter " << expression;
444
+ cerr << " (" << pstate_source_position(node) << ")";
445
+ cerr << " [name: " << expression->name() << "] ";
446
+ cerr << " [default: " << expression->default_value() << "] ";
447
+ cerr << " [rest: " << expression->is_rest_parameter() << "] " << endl;
404
448
  } else if (dynamic_cast<Unary_Expression*>(node)) {
405
449
  Unary_Expression* expression = dynamic_cast<Unary_Expression*>(node);
450
+ cerr << ind << "Unary_Expression " << expression << " [" << expression->type_name() << "]";
451
+ if (expression->is_delayed()) cerr << " [delayed]";
452
+ cerr << endl;
406
453
  cerr << ind << "Unary_Expression " << expression;
407
454
  cerr << " (" << pstate_source_position(node) << ")";
408
455
  cerr << " [" << expression->type() << "]" << endl;
409
456
  debug_ast(expression->operand(), ind + " operand: ", env);
410
457
  } else if (dynamic_cast<Binary_Expression*>(node)) {
411
458
  Binary_Expression* expression = dynamic_cast<Binary_Expression*>(node);
459
+ cerr << ind << "Binary_Expression " << expression << " [" << expression->type_name() << "]";
460
+ if (expression->is_delayed()) cerr << " [delayed]";
461
+ cerr << endl;
412
462
  cerr << ind << "Binary_Expression " << expression;
413
463
  cerr << " (" << pstate_source_position(node) << ")";
414
464
  cerr << " [" << expression->type() << "]" << endl;
@@ -426,7 +476,11 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
426
476
  cerr << " (" << expression->length() << ") " <<
427
477
  (expression->separator() == Sass::List::Separator::COMMA ? "Comma " : "Space ") <<
428
478
  " [delayed: " << expression->is_delayed() << "] " <<
479
+ " [interpolant: " << expression->is_interpolant() << "]";
480
+ if (expression->is_arglist()) cerr << " [is_arglist]";
481
+ cerr << endl;
429
482
  " [interpolant: " << expression->is_interpolant() << "] " <<
483
+ " [arglist: " << expression->is_arglist() << "] " <<
430
484
  endl;
431
485
  for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }
432
486
  } else if (dynamic_cast<Content*>(node)) {
@@ -436,21 +490,36 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
436
490
  cerr << " [Statement]" << endl;
437
491
  } else if (dynamic_cast<Boolean*>(node)) {
438
492
  Boolean* expression = dynamic_cast<Boolean*>(node);
493
+ cerr << ind << "Boolean " << expression << " [" << expression->value() << "]";
494
+ if (expression->is_delayed()) cerr << " [delayed]";
495
+ cerr << endl;
439
496
  cerr << ind << "Boolean " << expression;
440
497
  cerr << " (" << pstate_source_position(node) << ")";
441
498
  cerr << " [" << expression->value() << "]" << endl;
442
499
  } else if (dynamic_cast<Color*>(node)) {
443
500
  Color* expression = dynamic_cast<Color*>(node);
501
+ cerr << ind << "Color " << expression << " [" << expression->r() << ":" << expression->g() << ":" << expression->b() << "@" << expression->a() << "]";
502
+ if (expression->is_delayed()) cerr << " [delayed]";
503
+ cerr << endl;
444
504
  cerr << ind << "Color " << expression;
445
505
  cerr << " (" << pstate_source_position(node) << ")";
446
506
  cerr << " [" << expression->r() << ":" << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << endl;
447
507
  } else if (dynamic_cast<Number*>(node)) {
448
508
  Number* expression = dynamic_cast<Number*>(node);
509
+ cerr << ind << "Number " << expression << " [" << expression->value() << expression->unit() << "]";
510
+ if (expression->is_delayed()) cerr << " [delayed]";
511
+ cerr << endl;
449
512
  cerr << ind << "Number " << expression;
450
513
  cerr << " (" << pstate_source_position(node) << ")";
451
514
  cerr << " [" << expression->value() << expression->unit() << "]" << endl;
452
515
  } else if (dynamic_cast<String_Quoted*>(node)) {
453
516
  String_Quoted* expression = dynamic_cast<String_Quoted*>(node);
517
+ cerr << ind << "String_Quoted : " << expression << " [";
518
+ cerr << prettyprint(expression->value()) << "]";
519
+ if (expression->is_delayed()) cerr << " [delayed]";
520
+ if (expression->sass_fix_1291()) cerr << " [sass_fix_1291]";
521
+ if (expression->quote_mark()) cerr << " [quote_mark]";
522
+ cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << endl;
454
523
  cerr << ind << "String_Quoted : " << expression;
455
524
  cerr << " (" << pstate_source_position(node) << ")";
456
525
  cerr << " [" << prettyprint(expression->value()) << "]" <<
@@ -461,13 +530,23 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
461
530
  } else if (dynamic_cast<String_Constant*>(node)) {
462
531
  String_Constant* expression = dynamic_cast<String_Constant*>(node);
463
532
  cerr << ind << "String_Constant : " << expression;
533
+ cerr << " [" << prettyprint(expression->value()) << "]";
534
+ if (expression->is_delayed()) cerr << " [delayed]";
535
+ if (expression->sass_fix_1291()) cerr << " [sass_fix_1291]";
536
+ cerr " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << endl;
537
+ cerr << ind << "String_Constant : " << expression;
464
538
  cerr << " (" << pstate_source_position(node) << ")";
465
539
  cerr << " [" << prettyprint(expression->value()) << "]" <<
466
540
  (expression->is_delayed() ? " {delayed}" : "") <<
467
541
  (expression->sass_fix_1291() ? " {sass_fix_1291}" : "") <<
542
+ (expression->quote_mark() != 0 ? " {qm:" + string(1, expression->quote_mark()) + "}" : "") <<
468
543
  " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << endl;
469
544
  } else if (dynamic_cast<String_Schema*>(node)) {
470
545
  String_Schema* expression = dynamic_cast<String_Schema*>(node);
546
+ cerr << ind << "String_Schema " << expression << " [" << expression->concrete_type() << "]";
547
+ if (expression->is_delayed()) cerr << " [delayed]";
548
+ if (expression->has_interpolants()) cerr << " [has_interpolants]";
549
+ cerr " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << endl;
471
550
  cerr << ind << "String_Schema " << expression;
472
551
  cerr << " (" << pstate_source_position(node) << ")";
473
552
  cerr << " " << expression->concrete_type() <<
@@ -476,6 +555,9 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
476
555
  for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }
477
556
  } else if (dynamic_cast<String*>(node)) {
478
557
  String* expression = dynamic_cast<String*>(node);
558
+ cerr << ind << "String " << expression << expression->concrete_type();
559
+ if (expression->sass_fix_1291()) cerr << " [sass_fix_1291]";
560
+ cerr " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << endl;
479
561
  cerr << ind << "String " << expression;
480
562
  cerr << " (" << pstate_source_position(node) << ")";
481
563
  cerr << expression->concrete_type() <<
@@ -8,18 +8,28 @@ namespace Sass {
8
8
  : type(type), pstate(pstate), message(message)
9
9
  { }
10
10
 
11
- void error(string msg, ParserState pstate)
11
+ void warn(string msg, ParserState pstate)
12
12
  {
13
- throw Sass_Error(Sass_Error::syntax, pstate, msg);
13
+ cerr << "Warning: " << msg<< endl;
14
14
  }
15
15
 
16
- void error(string msg, ParserState pstate, Backtrace* bt)
16
+ void warn(string msg, ParserState pstate, Backtrace* bt)
17
17
  {
18
-
19
18
  Backtrace top(bt, pstate, "");
20
19
  msg += top.to_string();
20
+ warn(msg, pstate);
21
+ }
21
22
 
23
+ void error(string msg, ParserState pstate)
24
+ {
22
25
  throw Sass_Error(Sass_Error::syntax, pstate, msg);
23
26
  }
24
27
 
28
+ void error(string msg, ParserState pstate, Backtrace* bt)
29
+ {
30
+ Backtrace top(bt, pstate, "");
31
+ msg += "\n" + top.to_string();
32
+ error(msg, pstate);
33
+ }
34
+
25
35
  }
@@ -21,6 +21,9 @@ namespace Sass {
21
21
 
22
22
  };
23
23
 
24
+ void warn(string msg, ParserState pstate);
25
+ void warn(string msg, ParserState pstate, Backtrace* bt);
26
+
24
27
  void error(string msg, ParserState pstate);
25
28
  void error(string msg, ParserState pstate, Backtrace* bt);
26
29
 
@@ -435,8 +435,23 @@ namespace Sass {
435
435
  }
436
436
  // not a logical connective, so go ahead and eval the rhs
437
437
  Expression* rhs = b->right()->perform(this);
438
- rhs->is_delayed(false);
439
- while (typeid(*rhs) == typeid(Binary_Expression)) rhs = rhs->perform(this);
438
+ // maybe fully evaluate structure
439
+ if (op_type == Binary_Expression::EQ ||
440
+ op_type == Binary_Expression::NEQ ||
441
+ op_type == Binary_Expression::GT ||
442
+ op_type == Binary_Expression::GTE ||
443
+ op_type == Binary_Expression::LT ||
444
+ op_type == Binary_Expression::LTE)
445
+ {
446
+ rhs->is_expanded(false);
447
+ rhs->set_delayed(false);
448
+ rhs = rhs->perform(this);
449
+ }
450
+ else
451
+ {
452
+ rhs->is_delayed(false);
453
+ rhs = rhs->perform(this);
454
+ }
440
455
 
441
456
  // see if it's a relational expression
442
457
  switch(op_type) {
@@ -512,6 +527,9 @@ namespace Sass {
512
527
 
513
528
  Expression* Eval::operator()(Function_Call* c)
514
529
  {
530
+ if (backtrace->parent != NULL && backtrace->depth() > Constants::MaxCallStack) {
531
+ error("Stack depth exceeded max of " + to_string(Constants::MaxCallStack), c->pstate(), backtrace);
532
+ }
515
533
  string name(Util::normalize_underscores(c->name()));
516
534
  string full_name(name + "[f]");
517
535
  Arguments* args = c->arguments();
@@ -700,7 +718,11 @@ namespace Sass {
700
718
  if (auto str = dynamic_cast<String_Quoted*>(value)) {
701
719
  value = new (ctx.mem) String_Quoted(*str);
702
720
  } else if (auto str = dynamic_cast<String_Constant*>(value)) {
703
- value = new (ctx.mem) String_Constant(*str);
721
+ if (str->quote_mark()) {
722
+ value = new (ctx.mem) String_Quoted(str->pstate(), str->perform(&to_string));
723
+ } else {
724
+ value = new (ctx.mem) String_Constant(str->pstate(), unquote(str->value()));
725
+ }
704
726
  }
705
727
  }
706
728
  else if (value->concrete_type() == Expression::LIST) {
@@ -785,6 +807,7 @@ namespace Sass {
785
807
 
786
808
  Expression* Eval::operator()(Number* n)
787
809
  {
810
+ n->normalize();
788
811
  // behave according to as ruby sass (add leading zero)
789
812
  return new (ctx.mem) Number(n->pstate(),
790
813
  n->value(),
@@ -812,12 +835,18 @@ namespace Sass {
812
835
  string Eval::interpolation(Expression* s) {
813
836
  if (String_Quoted* str_quoted = dynamic_cast<String_Quoted*>(s)) {
814
837
  if (str_quoted->quote_mark()) {
815
- return string_escape(str_quoted->value());
838
+ if (str_quoted->quote_mark() == '*' || str_quoted->is_delayed()) {
839
+ return interpolation(new (ctx.mem) String_Constant(*str_quoted));
840
+ } else {
841
+ return string_escape(quote(str_quoted->value(), str_quoted->quote_mark()));
842
+ }
816
843
  } else {
817
844
  return evacuate_escapes(str_quoted->value());
818
845
  }
819
846
  } else if (String_Constant* str_constant = dynamic_cast<String_Constant*>(s)) {
820
- return evacuate_escapes(str_constant->value());
847
+ string str = str_constant->value();
848
+ if (!str_constant->quote_mark()) str = unquote(str);
849
+ return evacuate_escapes(str);
821
850
  } else if (String_Schema* str_schema = dynamic_cast<String_Schema*>(s)) {
822
851
  string res = "";
823
852
  for(auto i : str_schema->elements())
@@ -872,6 +901,9 @@ namespace Sass {
872
901
  {
873
902
  string acc;
874
903
  for (size_t i = 0, L = s->length(); i < L; ++i) {
904
+ if (String_Quoted* str_quoted = dynamic_cast<String_Quoted*>((*s)[i])) {
905
+ if (!str_quoted->is_delayed()) str_quoted->value(string_eval_escapes(str_quoted->value()));
906
+ }
875
907
  acc += interpolation((*s)[i]);
876
908
  }
877
909
  String_Quoted* str = new (ctx.mem) String_Quoted(s->pstate(), acc);
@@ -1042,13 +1074,8 @@ namespace Sass {
1042
1074
  } break;
1043
1075
 
1044
1076
  case Expression::NUMBER: {
1045
- Number* l = static_cast<Number*>(lhs);
1046
- Number* r = static_cast<Number*>(rhs);
1047
- Number tmp_r(*r);
1048
- tmp_r.normalize(l->find_convertible_unit());
1049
- return l->unit() == tmp_r.unit() && l->value() == tmp_r.value()
1050
- ? true
1051
- : false;
1077
+ return *static_cast<Number*>(lhs) ==
1078
+ *static_cast<Number*>(rhs);
1052
1079
  } break;
1053
1080
 
1054
1081
  case Expression::COLOR: {
@@ -1139,8 +1166,8 @@ namespace Sass {
1139
1166
  v->denominator_units() = r->denominator_units();
1140
1167
  }
1141
1168
 
1142
- v->value(ops[op](lv, tmp.value()));
1143
1169
  if (op == Binary_Expression::MUL) {
1170
+ v->value(ops[op](lv, rv));
1144
1171
  for (size_t i = 0, S = r->numerator_units().size(); i < S; ++i) {
1145
1172
  v->numerator_units().push_back(r->numerator_units()[i]);
1146
1173
  }
@@ -1149,14 +1176,17 @@ namespace Sass {
1149
1176
  }
1150
1177
  }
1151
1178
  else if (op == Binary_Expression::DIV) {
1179
+ v->value(ops[op](lv, rv));
1152
1180
  for (size_t i = 0, S = r->numerator_units().size(); i < S; ++i) {
1153
1181
  v->denominator_units().push_back(r->numerator_units()[i]);
1154
1182
  }
1155
1183
  for (size_t i = 0, S = r->denominator_units().size(); i < S; ++i) {
1156
1184
  v->numerator_units().push_back(r->denominator_units()[i]);
1157
1185
  }
1186
+ } else {
1187
+ v->value(ops[op](lv, tmp.value()));
1158
1188
  }
1159
- v->normalize();
1189
+ // v->normalize();
1160
1190
  return v;
1161
1191
  }
1162
1192