14 #include "inspect.hpp"
15 #include "environment.hpp"
16 #include "position.hpp"
17 #include "sass/values.h"
18 #include "to_value.hpp"
20 #include "context.hpp"
21 #include "backtrace.hpp"
23 #include "prelexer.hpp"
26 #include "color_maps.hpp"
27 #include "sass_functions.hpp"
31 inline double add(double x, double y) { return x + y; }
32 inline double sub(double x, double y) { return x - y; }
33 inline double mul(double x, double y) { return x * y; }
34 inline double div(double x, double y) { return x / y; } // x/0 checked by caller
35 inline double mod(double x, double y) { // x/0 checked by caller
36 if ((x > 0 && y < 0) || (x < 0 && y > 0)) {
37 double ret = std::fmod(x, y);
38 return ret ? ret + y : ret;
40 return std::fmod(x, y);
43 typedef double (*bop)(double, double);
44 bop ops[Sass_OP::NUM_OPS] = {
46 0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte
47 add, sub, mul, div, mod
50 Eval::Eval(Expand& exp)
58 Env* Eval::environment()
60 return exp.environment();
63 Selector_List_Obj Eval::selector()
65 return exp.selector();
68 Backtrace* Eval::backtrace()
70 return exp.backtrace();
73 Expression_Ptr Eval::operator()(Block_Ptr b)
75 Expression_Ptr val = 0;
76 for (size_t i = 0, L = b->length(); i < L; ++i) {
77 val = b->at(i)->perform(this);
83 Expression_Ptr Eval::operator()(Assignment_Ptr a)
85 Env* env = exp.environment();
86 std::string var(a->variable());
88 if (a->is_default()) {
89 if (env->has_global(var)) {
90 Expression_Ptr e = Cast<Expression>(env->get_global(var));
91 if (!e || e->concrete_type() == Expression::NULL_VAL) {
92 env->set_global(var, a->value()->perform(this));
96 env->set_global(var, a->value()->perform(this));
100 env->set_global(var, a->value()->perform(this));
103 else if (a->is_default()) {
104 if (env->has_lexical(var)) {
106 while (cur && cur->is_lexical()) {
107 if (cur->has_local(var)) {
108 if (AST_Node_Obj node = cur->get_local(var)) {
109 Expression_Ptr e = Cast<Expression>(node);
110 if (!e || e->concrete_type() == Expression::NULL_VAL) {
111 cur->set_local(var, a->value()->perform(this));
115 throw std::runtime_error("Env not in sync");
121 throw std::runtime_error("Env not in sync");
123 else if (env->has_global(var)) {
124 if (AST_Node_Obj node = env->get_global(var)) {
125 Expression_Ptr e = Cast<Expression>(node);
126 if (!e || e->concrete_type() == Expression::NULL_VAL) {
127 env->set_global(var, a->value()->perform(this));
131 else if (env->is_lexical()) {
132 env->set_local(var, a->value()->perform(this));
135 env->set_local(var, a->value()->perform(this));
139 env->set_lexical(var, a->value()->perform(this));
144 Expression_Ptr Eval::operator()(If_Ptr i)
146 Expression_Obj rv = 0;
147 Env env(exp.environment());
148 exp.env_stack.push_back(&env);
149 Expression_Obj cond = i->predicate()->perform(this);
150 if (!cond->is_false()) {
151 rv = i->block()->perform(this);
154 Block_Obj alt = i->alternative();
155 if (alt) rv = alt->perform(this);
157 exp.env_stack.pop_back();
161 // For does not create a new env scope
162 // But iteration vars are reset afterwards
163 Expression_Ptr Eval::operator()(For_Ptr f)
165 std::string variable(f->variable());
166 Expression_Obj low = f->lower_bound()->perform(this);
167 if (low->concrete_type() != Expression::NUMBER) {
168 throw Exception::TypeMismatch(*low, "integer");
170 Expression_Obj high = f->upper_bound()->perform(this);
171 if (high->concrete_type() != Expression::NUMBER) {
172 throw Exception::TypeMismatch(*high, "integer");
174 Number_Obj sass_start = Cast<Number>(low);
175 Number_Obj sass_end = Cast<Number>(high);
176 // check if units are valid for sequence
177 if (sass_start->unit() != sass_end->unit()) {
178 std::stringstream msg; msg << "Incompatible units: '"
179 << sass_end->unit() << "' and '"
180 << sass_start->unit() << "'.";
181 error(msg.str(), low->pstate(), backtrace());
183 double start = sass_start->value();
184 double end = sass_end->value();
185 // only create iterator once in this environment
186 Env env(environment(), true);
187 exp.env_stack.push_back(&env);
188 Number_Ptr it = SASS_MEMORY_NEW(Number, low->pstate(), start, sass_end->unit());
189 env.set_local(variable, it);
190 Block_Obj body = f->block();
191 Expression_Ptr val = 0;
193 if (f->is_inclusive()) ++end;
194 for (double i = start;
198 env.set_local(variable, it);
199 val = body->perform(this);
203 if (f->is_inclusive()) --end;
204 for (double i = start;
208 env.set_local(variable, it);
209 val = body->perform(this);
213 exp.env_stack.pop_back();
217 // Eval does not create a new env scope
218 // But iteration vars are reset afterwards
219 Expression_Ptr Eval::operator()(Each_Ptr e)
221 std::vector<std::string> variables(e->variables());
222 Expression_Obj expr = e->list()->perform(this);
223 Env env(environment(), true);
224 exp.env_stack.push_back(&env);
227 if (expr->concrete_type() == Expression::MAP) {
228 map = Cast<Map>(expr);
230 else if (Selector_List_Ptr ls = Cast<Selector_List>(expr)) {
232 Expression_Obj rv = ls->perform(&listize);
233 list = Cast<List>(rv);
235 else if (expr->concrete_type() != Expression::LIST) {
236 list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA);
240 list = Cast<List>(expr);
243 Block_Obj body = e->block();
244 Expression_Obj val = 0;
247 for (Expression_Obj key : map->keys()) {
248 Expression_Obj value = map->at(key);
250 if (variables.size() == 1) {
251 List_Ptr variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE);
252 variable->append(key);
253 variable->append(value);
254 env.set_local(variables[0], variable);
256 env.set_local(variables[0], key);
257 env.set_local(variables[1], value);
260 val = body->perform(this);
265 if (list->length() == 1 && Cast<Selector_List>(list)) {
266 list = Cast<List>(list);
268 for (size_t i = 0, L = list->length(); i < L; ++i) {
269 Expression_Ptr e = list->at(i);
270 // unwrap value if the expression is an argument
271 if (Argument_Ptr arg = Cast<Argument>(e)) e = arg->value();
272 // check if we got passed a list of args (investigate)
273 if (List_Ptr scalars = Cast<List>(e)) {
274 if (variables.size() == 1) {
275 Expression_Ptr var = scalars;
276 env.set_local(variables[0], var);
278 // XXX: this is never hit via spec tests
279 for (size_t j = 0, K = variables.size(); j < K; ++j) {
280 Expression_Ptr res = j >= scalars->length()
281 ? SASS_MEMORY_NEW(Null, expr->pstate())
283 env.set_local(variables[j], res);
287 if (variables.size() > 0) {
288 env.set_local(variables.at(0), e);
289 for (size_t j = 1, K = variables.size(); j < K; ++j) {
290 // XXX: this is never hit via spec tests
291 Expression_Ptr res = SASS_MEMORY_NEW(Null, expr->pstate());
292 env.set_local(variables[j], res);
296 val = body->perform(this);
300 exp.env_stack.pop_back();
304 Expression_Ptr Eval::operator()(While_Ptr w)
306 Expression_Obj pred = w->predicate();
307 Block_Obj body = w->block();
308 Env env(environment(), true);
309 exp.env_stack.push_back(&env);
310 Expression_Obj cond = pred->perform(this);
311 while (!cond->is_false()) {
312 Expression_Obj val = body->perform(this);
314 exp.env_stack.pop_back();
317 cond = pred->perform(this);
319 exp.env_stack.pop_back();
323 Expression_Ptr Eval::operator()(Return_Ptr r)
325 return r->value()->perform(this);
328 Expression_Ptr Eval::operator()(Warning_Ptr w)
330 Sass_Output_Style outstyle = ctx.c_options.output_style;
331 ctx.c_options.output_style = NESTED;
332 Expression_Obj message = w->message()->perform(this);
333 Env* env = exp.environment();
335 // try to use generic function
336 if (env->has("@warn[f]")) {
338 // add call stack entry
339 ctx.callee_stack.push_back({
342 w->pstate().line + 1,
343 w->pstate().column + 1,
344 SASS_CALLEE_FUNCTION,
348 Definition_Ptr def = Cast<Definition>((*env)["@warn[f]"]);
349 // Block_Obj body = def->block();
350 // Native_Function func = def->native_function();
351 Sass_Function_Entry c_function = def->c_function();
352 Sass_Function_Fn c_func = sass_function_get_function(c_function);
355 union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false);
356 sass_list_set_value(c_args, 0, message->perform(&to_c));
357 union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);
358 ctx.c_options.output_style = outstyle;
359 ctx.callee_stack.pop_back();
360 sass_delete_value(c_args);
361 sass_delete_value(c_val);
366 std::string result(unquote(message->to_sass()));
367 Backtrace top(backtrace(), w->pstate(), "");
368 std::cerr << "WARNING: " << result;
369 std::cerr << top.to_string();
370 std::cerr << std::endl << std::endl;
371 ctx.c_options.output_style = outstyle;
375 Expression_Ptr Eval::operator()(Error_Ptr e)
377 Sass_Output_Style outstyle = ctx.c_options.output_style;
378 ctx.c_options.output_style = NESTED;
379 Expression_Obj message = e->message()->perform(this);
380 Env* env = exp.environment();
382 // try to use generic function
383 if (env->has("@error[f]")) {
385 // add call stack entry
386 ctx.callee_stack.push_back({
389 e->pstate().line + 1,
390 e->pstate().column + 1,
391 SASS_CALLEE_FUNCTION,
395 Definition_Ptr def = Cast<Definition>((*env)["@error[f]"]);
396 // Block_Obj body = def->block();
397 // Native_Function func = def->native_function();
398 Sass_Function_Entry c_function = def->c_function();
399 Sass_Function_Fn c_func = sass_function_get_function(c_function);
402 union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false);
403 sass_list_set_value(c_args, 0, message->perform(&to_c));
404 union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);
405 ctx.c_options.output_style = outstyle;
406 ctx.callee_stack.pop_back();
407 sass_delete_value(c_args);
408 sass_delete_value(c_val);
413 std::string result(unquote(message->to_sass()));
414 ctx.c_options.output_style = outstyle;
415 error(result, e->pstate());
419 Expression_Ptr Eval::operator()(Debug_Ptr d)
421 Sass_Output_Style outstyle = ctx.c_options.output_style;
422 ctx.c_options.output_style = NESTED;
423 Expression_Obj message = d->value()->perform(this);
424 Env* env = exp.environment();
426 // try to use generic function
427 if (env->has("@debug[f]")) {
429 // add call stack entry
430 ctx.callee_stack.push_back({
433 d->pstate().line + 1,
434 d->pstate().column + 1,
435 SASS_CALLEE_FUNCTION,
439 Definition_Ptr def = Cast<Definition>((*env)["@debug[f]"]);
440 // Block_Obj body = def->block();
441 // Native_Function func = def->native_function();
442 Sass_Function_Entry c_function = def->c_function();
443 Sass_Function_Fn c_func = sass_function_get_function(c_function);
446 union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false);
447 sass_list_set_value(c_args, 0, message->perform(&to_c));
448 union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);
449 ctx.c_options.output_style = outstyle;
450 ctx.callee_stack.pop_back();
451 sass_delete_value(c_args);
452 sass_delete_value(c_val);
457 std::string cwd(ctx.cwd());
458 std::string result(unquote(message->to_sass()));
459 std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd, cwd));
460 std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd, cwd));
461 std::string output_path(Sass::File::path_for_console(rel_path, abs_path, d->pstate().path));
462 ctx.c_options.output_style = outstyle;
464 std::cerr << output_path << ":" << d->pstate().line+1 << " DEBUG: " << result;
465 std::cerr << std::endl;
469 Expression_Ptr Eval::operator()(List_Ptr l)
471 // special case for unevaluated map
472 if (l->separator() == SASS_HASH) {
473 Map_Obj lm = SASS_MEMORY_NEW(Map,
476 for (size_t i = 0, L = l->length(); i < L; i += 2)
478 Expression_Obj key = (*l)[i+0]->perform(this);
479 Expression_Obj val = (*l)[i+1]->perform(this);
480 // make sure the color key never displays its real name
481 key->is_delayed(true); // verified
482 *lm << std::make_pair(key, val);
484 if (lm->has_duplicate_key()) {
485 throw Exception::DuplicateKeyError(*lm, *l);
488 lm->is_interpolant(l->is_interpolant());
489 return lm->perform(this);
491 // check if we should expand it
492 if (l->is_expanded()) return l;
493 // regular case for unevaluated lists
494 List_Obj ll = SASS_MEMORY_NEW(List,
500 for (size_t i = 0, L = l->length(); i < L; ++i) {
501 ll->append((*l)[i]->perform(this));
503 ll->is_interpolant(l->is_interpolant());
504 ll->from_selector(l->from_selector());
505 ll->is_expanded(true);
509 Expression_Ptr Eval::operator()(Map_Ptr m)
511 if (m->is_expanded()) return m;
513 // make sure we're not starting with duplicate keys.
514 // the duplicate key state will have been set in the parser phase.
515 if (m->has_duplicate_key()) {
516 throw Exception::DuplicateKeyError(*m, *m);
519 Map_Obj mm = SASS_MEMORY_NEW(Map,
522 for (auto key : m->keys()) {
523 Expression_Ptr ex_key = key->perform(this);
524 Expression_Ptr ex_val = m->at(key)->perform(this);
525 *mm << std::make_pair(ex_key, ex_val);
528 // check the evaluated keys aren't duplicates.
529 if (mm->has_duplicate_key()) {
530 throw Exception::DuplicateKeyError(*mm, *m);
533 mm->is_expanded(true);
537 Expression_Ptr Eval::operator()(Binary_Expression_Ptr b_in)
540 String_Schema_Obj ret_schema;
541 Binary_Expression_Obj b = b_in;
542 enum Sass_OP op_type = b->optype();
544 // only the last item will be used to eval the binary expression
545 if (String_Schema_Ptr s_l = Cast<String_Schema>(b->left())) {
546 if (!s_l->has_interpolant() && (!s_l->is_right_interpolant())) {
547 ret_schema = SASS_MEMORY_NEW(String_Schema, b->pstate());
548 Binary_Expression_Obj bin_ex = SASS_MEMORY_NEW(Binary_Expression, b->pstate(),
549 b->op(), s_l->last(), b->right());
550 bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // unverified
551 for (size_t i = 0; i < s_l->length() - 1; ++i) {
552 ret_schema->append(s_l->at(i)->perform(this));
554 ret_schema->append(bin_ex->perform(this));
555 return ret_schema->perform(this);
558 if (String_Schema_Ptr s_r = Cast<String_Schema>(b->right())) {
560 if (!s_r->has_interpolant() && (!s_r->is_left_interpolant() || op_type == Sass_OP::DIV)) {
561 ret_schema = SASS_MEMORY_NEW(String_Schema, b->pstate());
562 Binary_Expression_Obj bin_ex = SASS_MEMORY_NEW(Binary_Expression, b->pstate(),
563 b->op(), b->left(), s_r->first());
564 bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // verified
565 ret_schema->append(bin_ex->perform(this));
566 for (size_t i = 1; i < s_r->length(); ++i) {
567 ret_schema->append(s_r->at(i)->perform(this));
569 return ret_schema->perform(this);
573 // don't eval delayed expressions (the '/' when used as a separator)
574 if (!force && op_type == Sass_OP::DIV && b->is_delayed()) {
575 b->right(b->right()->perform(this));
576 b->left(b->left()->perform(this));
580 Expression_Obj lhs = b->left();
581 Expression_Obj rhs = b->right();
583 // fully evaluate their values
584 if (op_type == Sass_OP::EQ ||
585 op_type == Sass_OP::NEQ ||
586 op_type == Sass_OP::GT ||
587 op_type == Sass_OP::GTE ||
588 op_type == Sass_OP::LT ||
589 op_type == Sass_OP::LTE)
591 LOCAL_FLAG(force, true);
592 lhs->is_expanded(false);
593 lhs->set_delayed(false);
594 lhs = lhs->perform(this);
595 rhs->is_expanded(false);
596 rhs->set_delayed(false);
597 rhs = rhs->perform(this);
600 lhs = lhs->perform(this);
603 Binary_Expression_Obj u3 = b;
606 return *lhs ? b->right()->perform(this) : lhs.detach();
610 return *lhs ? lhs.detach() : b->right()->perform(this);
616 // not a logical connective, so go ahead and eval the rhs
617 rhs = rhs->perform(this);
618 AST_Node_Obj lu = lhs;
619 AST_Node_Obj ru = rhs;
621 Expression::Concrete_Type l_type = lhs->concrete_type();
622 Expression::Concrete_Type r_type = rhs->concrete_type();
624 // Is one of the operands an interpolant?
625 String_Schema_Obj s1 = Cast<String_Schema>(b->left());
626 String_Schema_Obj s2 = Cast<String_Schema>(b->right());
627 Binary_Expression_Obj b1 = Cast<Binary_Expression>(b->left());
628 Binary_Expression_Obj b2 = Cast<Binary_Expression>(b->right());
630 bool schema_op = false;
632 bool force_delay = (s2 && s2->is_left_interpolant()) ||
633 (s1 && s1->is_right_interpolant()) ||
634 (b1 && b1->is_right_interpolant()) ||
635 (b2 && b2->is_left_interpolant());
637 if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants()) || force_delay)
639 if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::MOD || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB ||
640 op_type == Sass_OP::EQ) {
641 // If possible upgrade LHS to a number (for number to string compare)
642 if (String_Constant_Ptr str = Cast<String_Constant>(lhs)) {
643 std::string value(str->value());
644 const char* start = value.c_str();
645 if (Prelexer::sequence < Prelexer::dimension, Prelexer::end_of_file >(start) != 0) {
646 Textual_Obj l = SASS_MEMORY_NEW(Textual, b->pstate(), Textual::DIMENSION, str->value());
647 lhs = l->perform(this);
650 // If possible upgrade RHS to a number (for string to number compare)
651 if (String_Constant_Ptr str = Cast<String_Constant>(rhs)) {
652 std::string value(str->value());
653 const char* start = value.c_str();
654 if (Prelexer::sequence < Prelexer::dimension, Prelexer::number >(start) != 0) {
655 Textual_Obj r = SASS_MEMORY_NEW(Textual, b->pstate(), Textual::DIMENSION, str->value());
656 rhs = r->perform(this);
661 To_Value to_value(ctx);
662 Value_Obj v_l = Cast<Value>(lhs->perform(&to_value));
663 Value_Obj v_r = Cast<Value>(rhs->perform(&to_value));
664 l_type = lhs->concrete_type();
665 r_type = rhs->concrete_type();
667 if (s2 && s2->has_interpolants() && s2->length()) {
668 Textual_Obj front = Cast<Textual>(s2->elements().front());
669 if (front && !front->is_interpolant())
671 // XXX: this is never hit via spec tests
673 rhs = front->perform(this);
679 str += v_l->to_string(ctx.c_options);
680 if (b->op().ws_before) str += " ";
681 str += b->separator();
682 if (b->op().ws_after) str += " ";
683 str += v_r->to_string(ctx.c_options);
684 String_Constant_Ptr val = SASS_MEMORY_NEW(String_Constant, b->pstate(), str);
685 val->is_interpolant(b->left()->has_interpolant());
690 // see if it's a relational expression
693 case Sass_OP::EQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), eq(lhs, rhs));
694 case Sass_OP::NEQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), !eq(lhs, rhs));
695 case Sass_OP::GT: return SASS_MEMORY_NEW(Boolean, b->pstate(), !lt(lhs, rhs, "gt") && !eq(lhs, rhs));
696 case Sass_OP::GTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), !lt(lhs, rhs, "gte"));
697 case Sass_OP::LT: return SASS_MEMORY_NEW(Boolean, b->pstate(), lt(lhs, rhs, "lt"));
698 case Sass_OP::LTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), lt(lhs, rhs, "lte") || eq(lhs, rhs));
702 catch (Exception::OperationError& err)
704 // throw Exception::Base(b->pstate(), err.what());
705 throw Exception::SassValueError(b->pstate(), err);
708 l_type = lhs->concrete_type();
709 r_type = rhs->concrete_type();
711 // ToDo: throw error in op functions
712 // ToDo: then catch and re-throw them
713 Expression_Obj rv = 0;
715 ParserState pstate(b->pstate());
716 if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {
717 Number_Ptr l_n = Cast<Number>(lhs);
718 Number_Ptr r_n = Cast<Number>(rhs);
719 rv = op_numbers(op_type, *l_n, *r_n, ctx.c_options, &pstate);
721 else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {
722 Number_Ptr l_n = Cast<Number>(lhs);
723 Color_Ptr r_c = Cast<Color>(rhs);
724 rv = op_number_color(op_type, *l_n, *r_c, ctx.c_options, &pstate);
726 else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {
727 Color_Ptr l_c = Cast<Color>(lhs);
728 Number_Ptr r_n = Cast<Number>(rhs);
729 rv = op_color_number(op_type, *l_c, *r_n, ctx.c_options, &pstate);
731 else if (l_type == Expression::COLOR && r_type == Expression::COLOR) {
732 Color_Ptr l_c = Cast<Color>(lhs);
733 Color_Ptr r_c = Cast<Color>(rhs);
734 rv = op_colors(op_type, *l_c, *r_c, ctx.c_options, &pstate);
737 To_Value to_value(ctx);
738 // this will leak if perform does not return a value!
739 Value_Obj v_l = Cast<Value>(lhs->perform(&to_value));
740 Value_Obj v_r = Cast<Value>(rhs->perform(&to_value));
741 bool interpolant = b->is_right_interpolant() ||
742 b->is_left_interpolant() ||
744 if (op_type == Sass_OP::SUB) interpolant = false;
745 // if (op_type == Sass_OP::DIV) interpolant = true;
746 // check for type violations
747 if (l_type == Expression::MAP) {
748 throw Exception::InvalidValue(*v_l);
750 if (r_type == Expression::MAP) {
751 throw Exception::InvalidValue(*v_r);
753 Value_Ptr ex = op_strings(b->op(), *v_l, *v_r, ctx.c_options, &pstate, !interpolant); // pass true to compress
754 if (String_Constant_Ptr str = Cast<String_Constant>(ex))
756 if (str->concrete_type() == Expression::STRING)
758 String_Constant_Ptr lstr = Cast<String_Constant>(lhs);
759 String_Constant_Ptr rstr = Cast<String_Constant>(rhs);
760 if (op_type != Sass_OP::SUB) {
761 if (String_Constant_Ptr org = lstr ? lstr : rstr)
762 { str->quote_mark(org->quote_mark()); }
766 ex->is_interpolant(b->is_interpolant());
770 catch (Exception::OperationError& err)
772 // throw Exception::Base(b->pstate(), err.what());
773 throw Exception::SassValueError(b->pstate(), err);
778 // XXX: this is never hit via spec tests
780 rv = s2->perform(this);
788 Expression_Ptr Eval::operator()(Unary_Expression_Ptr u)
790 Expression_Obj operand = u->operand()->perform(this);
791 if (u->optype() == Unary_Expression::NOT) {
792 Boolean_Ptr result = SASS_MEMORY_NEW(Boolean, u->pstate(), (bool)*operand);
793 result->value(!result->value());
796 else if (Number_Obj nr = Cast<Number>(operand)) {
797 // negate value for minus unary expression
798 if (u->optype() == Unary_Expression::MINUS) {
799 Number_Obj cpy = SASS_MEMORY_COPY(nr);
800 cpy->value( - cpy->value() ); // negate value
801 return cpy.detach(); // return the copy
803 // nothing for positive
807 // Special cases: +/- variables which evaluate to null ouput just +/-,
808 // but +/- null itself outputs the string
809 if (operand->concrete_type() == Expression::NULL_VAL && Cast<Variable>(u->operand())) {
810 u->operand(SASS_MEMORY_NEW(String_Quoted, u->pstate(), ""));
812 // Never apply unary opertions on colors @see #2140
813 else if (Color_Ptr color = Cast<Color>(operand)) {
814 // Use the color name if this was eval with one
815 if (color->disp().length() > 0) {
816 operand = SASS_MEMORY_NEW(String_Constant, operand->pstate(), color->disp());
824 return SASS_MEMORY_NEW(String_Quoted,
832 Expression_Ptr Eval::operator()(Function_Call_Ptr c)
834 if (backtrace()->parent != NULL && backtrace()->depth() > Constants::MaxCallStack) {
835 // XXX: this is never hit via spec tests
836 std::ostringstream stm;
837 stm << "Stack depth exceeded max of " << Constants::MaxCallStack;
838 error(stm.str(), c->pstate(), backtrace());
840 std::string name(Util::normalize_underscores(c->name()));
841 std::string full_name(name + "[f]");
842 // we make a clone here, need to implement that further
843 Arguments_Obj args = c->arguments();
845 Env* env = environment();
846 if (!env->has(full_name) || (!c->via_call() && Prelexer::re_special_fun(name.c_str()))) {
847 if (!env->has("*[f]")) {
848 for (Argument_Obj arg : args->elements()) {
849 if (List_Obj ls = Cast<List>(arg->value())) {
850 if (ls->size() == 0) error("() isn't a valid CSS value.", c->pstate());
853 args = Cast<Arguments>(args->perform(this));
854 Function_Call_Obj lit = SASS_MEMORY_NEW(Function_Call,
858 if (args->has_named_arguments()) {
859 error("Function " + c->name() + " doesn't support keyword arguments", c->pstate());
861 String_Quoted_Ptr str = SASS_MEMORY_NEW(String_Quoted,
863 lit->to_string(ctx.c_options));
864 str->is_interpolant(c->is_interpolant());
867 // call generic function
872 // further delay for calls
873 if (full_name != "call[f]") {
874 args->set_delayed(false); // verified
876 if (full_name != "if[f]") {
877 args = Cast<Arguments>(args->perform(this));
879 Definition_Ptr def = Cast<Definition>((*env)[full_name]);
881 if (def->is_overload_stub()) {
882 std::stringstream ss;
883 size_t L = args->length();
884 // account for rest arguments
885 if (args->has_rest_argument() && args->length() > 0) {
886 // get the rest arguments list
887 List_Ptr rest = Cast<List>(args->last()->value());
888 // arguments before rest argument plus rest
889 if (rest) L += rest->length() - 1;
891 ss << full_name << L;
892 full_name = ss.str();
893 std::string resolved_name(full_name);
894 if (!env->has(resolved_name)) error("overloaded function `" + std::string(c->name()) + "` given wrong number of arguments", c->pstate());
895 def = Cast<Definition>((*env)[resolved_name]);
898 Expression_Obj result = c;
899 Block_Obj body = def->block();
900 Native_Function func = def->native_function();
901 Sass_Function_Entry c_function = def->c_function();
903 Parameters_Obj params = def->parameters();
904 Env fn_env(def->environment());
905 exp.env_stack.push_back(&fn_env);
908 bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);
909 Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");
910 exp.backtrace_stack.push_back(&here);
911 ctx.callee_stack.push_back({
914 c->pstate().line + 1,
915 c->pstate().column + 1,
916 SASS_CALLEE_FUNCTION,
920 // eval the body if user-defined or special, invoke underlying CPP function if native
921 if (body /* && !Prelexer::re_special_fun(name.c_str()) */) {
922 result = body->perform(this);
925 result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace(), exp.selector_stack);
928 error(std::string("Function ") + c->name() + " did not return a value", c->pstate());
930 exp.backtrace_stack.pop_back();
931 ctx.callee_stack.pop_back();
934 // else if it's a user-defined c function
935 // convert call into C-API compatible form
936 else if (c_function) {
937 Sass_Function_Fn c_func = sass_function_get_function(c_function);
938 if (full_name == "*[f]") {
939 String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, c->pstate(), c->name());
940 Arguments_Obj new_args = SASS_MEMORY_NEW(Arguments, c->pstate());
941 new_args->append(SASS_MEMORY_NEW(Argument, c->pstate(), str));
942 new_args->concat(args);
946 // populates env with default values for params
947 std::string ff(c->name());
948 bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);
950 Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");
951 exp.backtrace_stack.push_back(&here);
952 ctx.callee_stack.push_back({
955 c->pstate().line + 1,
956 c->pstate().column + 1,
957 SASS_CALLEE_C_FUNCTION,
962 union Sass_Value* c_args = sass_make_list(params->length(), SASS_COMMA, false);
963 for(size_t i = 0; i < params->length(); i++) {
964 Parameter_Obj param = params->at(i);
965 std::string key = param->name();
966 AST_Node_Obj node = fn_env.get_local(key);
967 Expression_Obj arg = Cast<Expression>(node);
968 sass_list_set_value(c_args, i, arg->perform(&to_c));
970 union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);
971 if (sass_value_get_tag(c_val) == SASS_ERROR) {
972 error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), backtrace());
973 } else if (sass_value_get_tag(c_val) == SASS_WARNING) {
974 error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), backtrace());
976 result = cval_to_astnode(c_val, backtrace(), c->pstate());
978 exp.backtrace_stack.pop_back();
979 ctx.callee_stack.pop_back();
980 sass_delete_value(c_args);
982 sass_delete_value(c_val);
985 // link back to function definition
986 // only do this for custom functions
987 if (result->pstate().file == std::string::npos)
988 result->pstate(c->pstate());
990 result = result->perform(this);
991 result->is_interpolant(c->is_interpolant());
992 exp.env_stack.pop_back();
993 return result.detach();
996 Expression_Ptr Eval::operator()(Function_Call_Schema_Ptr s)
998 Expression_Ptr evaluated_name = s->name()->perform(this);
999 Expression_Ptr evaluated_args = s->arguments()->perform(this);
1000 String_Schema_Obj ss = SASS_MEMORY_NEW(String_Schema, s->pstate(), 2);
1001 ss->append(evaluated_name);
1002 ss->append(evaluated_args);
1003 return ss->perform(this);
1006 Expression_Ptr Eval::operator()(Variable_Ptr v)
1008 std::string name(v->name());
1009 Expression_Obj value = 0;
1010 Env* env = environment();
1011 if (env->has(name)) {
1012 value = Cast<Expression>((*env)[name]);
1014 else error("Undefined variable: \"" + v->name() + "\".", v->pstate());
1015 if (Argument* arg = Cast<Argument>(value)) {
1016 value = arg->value();
1019 // behave according to as ruby sass (add leading zero)
1020 if (Number_Ptr nr = Cast<Number>(value)) {
1024 value->is_interpolant(v->is_interpolant());
1025 if (force) value->is_expanded(false);
1026 value->set_delayed(false); // verified
1027 value = value->perform(this);
1028 if(!force) (*env)[name] = value;
1029 return value.detach();
1032 Expression_Ptr Eval::operator()(Textual_Ptr t)
1034 using Prelexer::number;
1035 Expression_Obj result = 0;
1036 size_t L = t->value().length();
1037 bool zero = !( (L > 0 && t->value().substr(0, 1) == ".") ||
1038 (L > 1 && t->value().substr(0, 2) == "0.") ||
1039 (L > 1 && t->value().substr(0, 2) == "-.") ||
1040 (L > 2 && t->value().substr(0, 3) == "-0.")
1043 const std::string& text = t->value();
1044 size_t num_pos = text.find_first_not_of(" \n\r\t");
1045 if (num_pos == std::string::npos) num_pos = text.length();
1046 size_t unit_pos = text.find_first_not_of("-+0123456789.", num_pos);
1047 if (unit_pos == std::string::npos) unit_pos = text.length();
1048 const std::string& num = text.substr(num_pos, unit_pos - num_pos);
1050 switch (t->valtype())
1052 case Textual::NUMBER:
1053 result = SASS_MEMORY_NEW(Number,
1055 sass_atof(num.c_str()),
1059 case Textual::PERCENTAGE:
1060 result = SASS_MEMORY_NEW(Number,
1062 sass_atof(num.c_str()),
1066 case Textual::DIMENSION:
1067 result = SASS_MEMORY_NEW(Number,
1069 sass_atof(num.c_str()),
1070 Token(number(text.c_str())),
1073 case Textual::HEX: {
1074 if (t->value().substr(0, 1) != "#") {
1075 result = SASS_MEMORY_NEW(String_Quoted, t->pstate(), t->value());
1078 std::string hext(t->value().substr(1)); // chop off the '#'
1079 if (hext.length() == 6) {
1080 std::string r(hext.substr(0,2));
1081 std::string g(hext.substr(2,2));
1082 std::string b(hext.substr(4,2));
1083 result = SASS_MEMORY_NEW(Color,
1085 static_cast<double>(strtol(r.c_str(), NULL, 16)),
1086 static_cast<double>(strtol(g.c_str(), NULL, 16)),
1087 static_cast<double>(strtol(b.c_str(), NULL, 16)),
1092 result = SASS_MEMORY_NEW(Color,
1094 static_cast<double>(strtol(std::string(2,hext[0]).c_str(), NULL, 16)),
1095 static_cast<double>(strtol(std::string(2,hext[1]).c_str(), NULL, 16)),
1096 static_cast<double>(strtol(std::string(2,hext[2]).c_str(), NULL, 16)),
1102 result->is_interpolant(t->is_interpolant());
1103 return result.detach();
1106 Expression_Ptr Eval::operator()(Color_Ptr c)
1111 Expression_Ptr Eval::operator()(Number_Ptr n)
1116 Expression_Ptr Eval::operator()(Boolean_Ptr b)
1121 void Eval::interpolation(Context& ctx, std::string& res, Expression_Obj ex, bool into_quotes, bool was_itpl) {
1123 bool needs_closing_brace = false;
1125 if (Arguments_Ptr args = Cast<Arguments>(ex)) {
1126 List_Ptr ll = SASS_MEMORY_NEW(List, args->pstate(), 0, SASS_COMMA);
1127 for(auto arg : args->elements()) {
1128 ll->append(arg->value());
1130 ll->is_interpolant(args->is_interpolant());
1131 needs_closing_brace = true;
1135 if (Number_Ptr nr = Cast<Number>(ex)) {
1136 if (!nr->is_valid_css_unit()) {
1137 throw Exception::InvalidValue(*nr);
1140 if (Argument_Ptr arg = Cast<Argument>(ex)) {
1143 if (String_Quoted_Ptr sq = Cast<String_Quoted>(ex)) {
1145 bool was_interpolant = ex->is_interpolant();
1146 ex = SASS_MEMORY_NEW(String_Constant, sq->pstate(), sq->value());
1147 ex->is_interpolant(was_interpolant);
1151 if (Cast<Null>(ex)) { return; }
1153 // parent selector needs another go
1154 if (Cast<Parent_Selector>(ex)) {
1155 // XXX: this is never hit via spec tests
1156 ex = ex->perform(this);
1159 if (List_Ptr l = Cast<List>(ex)) {
1160 List_Obj ll = SASS_MEMORY_NEW(List, l->pstate(), 0, l->separator());
1161 // this fixes an issue with bourbon sample, not really sure why
1162 // if (l->size() && Cast<Null>((*l)[0])) { res += ""; }
1163 for(Expression_Obj item : *l) {
1164 item->is_interpolant(l->is_interpolant());
1165 std::string rl(""); interpolation(ctx, rl, item, into_quotes, l->is_interpolant());
1166 bool is_null = Cast<Null>(item) != 0; // rl != ""
1167 if (!is_null) ll->append(SASS_MEMORY_NEW(String_Quoted, item->pstate(), rl));
1169 // Check indicates that we probably should not get a list
1170 // here. Normally single list items are already unwrapped.
1171 if (l->size() > 1) {
1172 // string_to_output would fail "#{'_\a' '_\a'}";
1173 std::string str(ll->to_string(ctx.c_options));
1174 newline_to_space(str); // replace directly
1175 res += str; // append to result string
1177 res += (ll->to_string(ctx.c_options));
1179 ll->is_interpolant(l->is_interpolant());
1189 // Binary_Expression
1191 // ex = ex->perform(this);
1192 if (into_quotes && ex->is_interpolant()) {
1193 res += evacuate_escapes(ex ? ex->to_string(ctx.c_options) : "");
1195 res += ex ? ex->to_string(ctx.c_options) : "";
1199 if (needs_closing_brace) res += ")";
1203 Expression_Ptr Eval::operator()(String_Schema_Ptr s)
1205 size_t L = s->length();
1206 bool into_quotes = false;
1208 if (!Cast<String_Quoted>((*s)[0]) && !Cast<String_Quoted>((*s)[L - 1])) {
1209 if (String_Constant_Ptr l = Cast<String_Constant>((*s)[0])) {
1210 if (String_Constant_Ptr r = Cast<String_Constant>((*s)[L - 1])) {
1211 if (r->value().size() > 0) {
1212 if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true;
1213 if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true;
1219 bool was_quoted = false;
1220 bool was_interpolant = false;
1221 std::string res("");
1222 for (size_t i = 0; i < L; ++i) {
1223 bool is_quoted = Cast<String_Quoted>((*s)[i]) != NULL;
1224 if (was_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " "; }
1225 else if (i > 0 && is_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " "; }
1226 Expression_Obj ex = (*s)[i]->perform(this);
1227 interpolation(ctx, res, ex, into_quotes, ex->is_interpolant());
1228 was_quoted = Cast<String_Quoted>((*s)[i]) != NULL;
1229 was_interpolant = (*s)[i]->is_interpolant();
1232 if (!s->is_interpolant()) {
1233 if (s->length() > 1 && res == "") return SASS_MEMORY_NEW(Null, s->pstate());
1234 return SASS_MEMORY_NEW(String_Constant, s->pstate(), res);
1236 // string schema seems to have a special unquoting behavior (also handles "nested" quotes)
1237 String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), res, 0, false, false, false);
1238 // if (s->is_interpolant()) str->quote_mark(0);
1239 // String_Constant_Ptr str = SASS_MEMORY_NEW(String_Constant, s->pstate(), res);
1240 if (str->quote_mark()) str->quote_mark('*');
1241 else if (!is_in_comment) str->value(string_to_output(str->value()));
1242 str->is_interpolant(s->is_interpolant());
1243 return str.detach();
1247 Expression_Ptr Eval::operator()(String_Constant_Ptr s)
1249 if (!s->is_delayed() && name_to_color(s->value())) {
1250 Color_Ptr c = SASS_MEMORY_COPY(name_to_color(s->value())); // copy
1251 c->pstate(s->pstate());
1252 c->disp(s->value());
1253 c->is_delayed(true);
1259 Expression_Ptr Eval::operator()(String_Quoted_Ptr s)
1261 String_Quoted_Ptr str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), "");
1262 str->value(s->value());
1263 str->quote_mark(s->quote_mark());
1264 str->is_interpolant(s->is_interpolant());
1268 Expression_Ptr Eval::operator()(Supports_Operator_Ptr c)
1270 Expression_Ptr left = c->left()->perform(this);
1271 Expression_Ptr right = c->right()->perform(this);
1272 Supports_Operator_Ptr cc = SASS_MEMORY_NEW(Supports_Operator,
1274 Cast<Supports_Condition>(left),
1275 Cast<Supports_Condition>(right),
1280 Expression_Ptr Eval::operator()(Supports_Negation_Ptr c)
1282 Expression_Ptr condition = c->condition()->perform(this);
1283 Supports_Negation_Ptr cc = SASS_MEMORY_NEW(Supports_Negation,
1285 Cast<Supports_Condition>(condition));
1289 Expression_Ptr Eval::operator()(Supports_Declaration_Ptr c)
1291 Expression_Ptr feature = c->feature()->perform(this);
1292 Expression_Ptr value = c->value()->perform(this);
1293 Supports_Declaration_Ptr cc = SASS_MEMORY_NEW(Supports_Declaration,
1300 Expression_Ptr Eval::operator()(Supports_Interpolation_Ptr c)
1302 Expression_Ptr value = c->value()->perform(this);
1303 Supports_Interpolation_Ptr cc = SASS_MEMORY_NEW(Supports_Interpolation,
1309 Expression_Ptr Eval::operator()(At_Root_Query_Ptr e)
1311 Expression_Obj feature = e->feature();
1312 feature = (feature ? feature->perform(this) : 0);
1313 Expression_Obj value = e->value();
1314 value = (value ? value->perform(this) : 0);
1315 Expression_Ptr ee = SASS_MEMORY_NEW(At_Root_Query,
1317 Cast<String>(feature),
1322 Expression_Ptr Eval::operator()(Media_Query_Ptr q)
1324 String_Obj t = q->media_type();
1325 t = static_cast<String_Ptr>(t.isNull() ? 0 : t->perform(this));
1326 Media_Query_Obj qq = SASS_MEMORY_NEW(Media_Query,
1331 q->is_restricted());
1332 for (size_t i = 0, L = q->length(); i < L; ++i) {
1333 qq->append(static_cast<Media_Query_Expression_Ptr>((*q)[i]->perform(this)));
1338 Expression_Ptr Eval::operator()(Media_Query_Expression_Ptr e)
1340 Expression_Obj feature = e->feature();
1341 feature = (feature ? feature->perform(this) : 0);
1342 if (feature && Cast<String_Quoted>(feature)) {
1343 feature = SASS_MEMORY_NEW(String_Quoted,
1345 Cast<String_Quoted>(feature)->value());
1347 Expression_Obj value = e->value();
1348 value = (value ? value->perform(this) : 0);
1349 if (value && Cast<String_Quoted>(value)) {
1350 // XXX: this is never hit via spec tests
1351 value = SASS_MEMORY_NEW(String_Quoted,
1353 Cast<String_Quoted>(value)->value());
1355 return SASS_MEMORY_NEW(Media_Query_Expression,
1359 e->is_interpolated());
1362 Expression_Ptr Eval::operator()(Null_Ptr n)
1367 Expression_Ptr Eval::operator()(Argument_Ptr a)
1369 Expression_Obj val = a->value()->perform(this);
1370 bool is_rest_argument = a->is_rest_argument();
1371 bool is_keyword_argument = a->is_keyword_argument();
1373 if (a->is_rest_argument()) {
1374 if (val->concrete_type() == Expression::MAP) {
1375 is_rest_argument = false;
1376 is_keyword_argument = true;
1378 else if(val->concrete_type() != Expression::LIST) {
1379 List_Obj wrapper = SASS_MEMORY_NEW(List,
1384 wrapper->append(val);
1388 return SASS_MEMORY_NEW(Argument,
1393 is_keyword_argument);
1396 Expression_Ptr Eval::operator()(Arguments_Ptr a)
1398 Arguments_Obj aa = SASS_MEMORY_NEW(Arguments, a->pstate());
1399 if (a->length() == 0) return aa.detach();
1400 for (size_t i = 0, L = a->length(); i < L; ++i) {
1401 Expression_Obj rv = (*a)[i]->perform(this);
1402 Argument_Ptr arg = Cast<Argument>(rv);
1403 if (!(arg->is_rest_argument() || arg->is_keyword_argument())) {
1408 if (a->has_rest_argument()) {
1409 Expression_Obj rest = a->get_rest_argument()->perform(this);
1410 Expression_Obj splat = Cast<Argument>(rest)->value()->perform(this);
1412 Sass_Separator separator = SASS_COMMA;
1413 List_Ptr ls = Cast<List>(splat);
1414 Map_Ptr ms = Cast<Map>(splat);
1416 List_Obj arglist = SASS_MEMORY_NEW(List,
1419 ls ? ls->separator() : separator,
1422 if (ls && ls->is_arglist()) {
1423 arglist->concat(ls);
1425 aa->append(SASS_MEMORY_NEW(Argument, splat->pstate(), ms, "", false, true));
1427 arglist->concat(ls);
1429 arglist->append(splat);
1431 if (arglist->length()) {
1432 aa->append(SASS_MEMORY_NEW(Argument, splat->pstate(), arglist, "", true));
1436 if (a->has_keyword_argument()) {
1437 Expression_Obj rv = a->get_keyword_argument()->perform(this);
1438 Argument_Ptr rvarg = Cast<Argument>(rv);
1439 Expression_Obj kwarg = rvarg->value()->perform(this);
1441 aa->append(SASS_MEMORY_NEW(Argument, kwarg->pstate(), kwarg, "", false, true));
1446 Expression_Ptr Eval::operator()(Comment_Ptr c)
1451 inline Expression_Ptr Eval::fallback_impl(AST_Node_Ptr n)
1453 return static_cast<Expression_Ptr>(n);
1456 // All the binary helpers.
1458 bool Eval::eq(Expression_Obj lhs, Expression_Obj rhs)
1460 // use compare operator from ast node
1461 return lhs && rhs && *lhs == *rhs;
1464 bool Eval::lt(Expression_Obj lhs, Expression_Obj rhs, std::string op)
1466 Number_Obj l = Cast<Number>(lhs);
1467 Number_Obj r = Cast<Number>(rhs);
1468 // use compare operator from ast node
1469 if (!l || !r) throw Exception::UndefinedOperation(lhs, rhs, op);
1470 // use compare operator from ast node
1474 Value_Ptr Eval::op_numbers(enum Sass_OP op, const Number& l, const Number& r, struct Sass_Inspect_Options opt, ParserState* pstate)
1476 double lv = l.value();
1477 double rv = r.value();
1478 if (op == Sass_OP::DIV && rv == 0) {
1479 // XXX: this is never hit via spec tests
1480 return SASS_MEMORY_NEW(String_Quoted, pstate ? *pstate : l.pstate(), lv ? "Infinity" : "NaN");
1482 if (op == Sass_OP::MOD && !rv) {
1483 // XXX: this is never hit via spec tests
1484 throw Exception::ZeroDivisionError(l, r);
1487 Number tmp(&r); // copy
1488 bool strict = op != Sass_OP::MUL && op != Sass_OP::DIV;
1489 tmp.normalize(l.find_convertible_unit(), strict);
1490 std::string l_unit(l.unit());
1491 std::string r_unit(tmp.unit());
1492 Number_Obj v = SASS_MEMORY_COPY(&l); // copy
1493 v->pstate(pstate ? *pstate : l.pstate());
1494 if (l_unit.empty() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {
1495 v->numerator_units() = r.numerator_units();
1496 v->denominator_units() = r.denominator_units();
1499 if (op == Sass_OP::MUL) {
1500 v->value(ops[op](lv, rv));
1501 for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) {
1502 v->numerator_units().push_back(r.numerator_units()[i]);
1504 for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) {
1505 v->denominator_units().push_back(r.denominator_units()[i]);
1508 else if (op == Sass_OP::DIV) {
1509 v->value(ops[op](lv, rv));
1510 for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) {
1511 v->denominator_units().push_back(r.numerator_units()[i]);
1513 for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) {
1514 v->numerator_units().push_back(r.denominator_units()[i]);
1517 v->value(ops[op](lv, r.value() * r.convert_factor(l)));
1521 v->value(ops[op](lv, tmp.value()));
1527 Value_Ptr Eval::op_number_color(enum Sass_OP op, const Number& l, const Color& r, struct Sass_Inspect_Options opt, ParserState* pstate)
1529 double lv = l.value();
1532 case Sass_OP::MUL: {
1533 return SASS_MEMORY_NEW(Color,
1534 pstate ? *pstate : l.pstate(),
1541 case Sass_OP::DIV: {
1542 std::string sep(op == Sass_OP::SUB ? "-" : "/");
1543 std::string color(r.to_string(opt));
1544 return SASS_MEMORY_NEW(String_Quoted,
1545 pstate ? *pstate : l.pstate(),
1550 case Sass_OP::MOD: {
1551 throw Exception::UndefinedOperation(&l, &r, sass_op_to_name(op));
1553 default: break; // caller should ensure that we don't get here
1559 Value_Ptr Eval::op_color_number(enum Sass_OP op, const Color& l, const Number& r, struct Sass_Inspect_Options opt, ParserState* pstate)
1561 double rv = r.value();
1562 if (op == Sass_OP::DIV && !rv) {
1563 // comparison of Fixnum with Float failed?
1564 throw Exception::ZeroDivisionError(l, r);
1566 return SASS_MEMORY_NEW(Color,
1567 pstate ? *pstate : l.pstate(),
1574 Value_Ptr Eval::op_colors(enum Sass_OP op, const Color& l, const Color& r, struct Sass_Inspect_Options opt, ParserState* pstate)
1576 if (l.a() != r.a()) {
1577 throw Exception::AlphaChannelsNotEqual(&l, &r, "+");
1579 if (op == Sass_OP::DIV && (!r.r() || !r.g() ||!r.b())) {
1580 // comparison of Fixnum with Float failed?
1581 throw Exception::ZeroDivisionError(l, r);
1583 return SASS_MEMORY_NEW(Color,
1584 pstate ? *pstate : l.pstate(),
1585 ops[op](l.r(), r.r()),
1586 ops[op](l.g(), r.g()),
1587 ops[op](l.b(), r.b()),
1591 Value_Ptr Eval::op_strings(Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, ParserState* pstate, bool delayed)
1593 Expression::Concrete_Type ltype = lhs.concrete_type();
1594 Expression::Concrete_Type rtype = rhs.concrete_type();
1595 enum Sass_OP op = operand.operand;
1597 String_Quoted_Ptr lqstr = Cast<String_Quoted>(&lhs);
1598 String_Quoted_Ptr rqstr = Cast<String_Quoted>(&rhs);
1600 std::string lstr(lqstr ? lqstr->value() : lhs.to_string(opt));
1601 std::string rstr(rqstr ? rqstr->value() : rhs.to_string(opt));
1603 if (ltype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op));
1604 if (rtype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op));
1605 if (op == Sass_OP::MOD) throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op));
1606 if (op == Sass_OP::MUL) throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op));
1609 case Sass_OP::SUB: sep = "-"; break;
1610 case Sass_OP::DIV: sep = "/"; break;
1611 case Sass_OP::MUL: sep = "*"; break;
1612 case Sass_OP::MOD: sep = "%"; break;
1613 case Sass_OP::EQ: sep = "=="; break;
1614 case Sass_OP::NEQ: sep = "!="; break;
1615 case Sass_OP::LT: sep = "<"; break;
1616 case Sass_OP::GT: sep = ">"; break;
1617 case Sass_OP::LTE: sep = "<="; break;
1618 case Sass_OP::GTE: sep = ">="; break;
1622 if ( (sep == "") /* &&
1623 (sep != "/" || !rqstr || !rqstr->quote_mark()) */
1625 // create a new string that might be quoted on output (but do not unquote what we pass)
1626 return SASS_MEMORY_NEW(String_Quoted, pstate ? *pstate : lhs.pstate(), lstr + rstr, 0, false, true);
1629 if (sep != "" && !delayed) {
1630 if (operand.ws_before) sep = " " + sep;
1631 if (operand.ws_after) sep = sep + " ";
1634 if (op == Sass_OP::SUB || op == Sass_OP::DIV) {
1635 if (lqstr && lqstr->quote_mark()) lstr = quote(lstr);
1636 if (rqstr && rqstr->quote_mark()) rstr = quote(rstr);
1639 return SASS_MEMORY_NEW(String_Constant, pstate ? *pstate : lhs.pstate(), lstr + sep + rstr);
1642 Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtrace* backtrace, ParserState pstate)
1646 Expression_Ptr e = NULL;
1647 switch (sass_value_get_tag(v)) {
1648 case SASS_BOOLEAN: {
1649 e = SASS_MEMORY_NEW(Boolean, pstate, !!sass_boolean_get_value(v));
1652 e = SASS_MEMORY_NEW(Number, pstate, sass_number_get_value(v), sass_number_get_unit(v));
1655 e = SASS_MEMORY_NEW(Color, pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v));
1658 if (sass_string_is_quoted(v))
1659 e = SASS_MEMORY_NEW(String_Quoted, pstate, sass_string_get_value(v));
1661 e = SASS_MEMORY_NEW(String_Constant, pstate, sass_string_get_value(v));
1665 List_Ptr l = SASS_MEMORY_NEW(List, pstate, sass_list_get_length(v), sass_list_get_separator(v));
1666 for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) {
1667 l->append(cval_to_astnode(sass_list_get_value(v, i), backtrace, pstate));
1669 l->is_bracketed(sass_list_get_is_bracketed(v));
1673 Map_Ptr m = SASS_MEMORY_NEW(Map, pstate);
1674 for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {
1675 *m << std::make_pair(
1676 cval_to_astnode(sass_map_get_key(v, i), backtrace, pstate),
1677 cval_to_astnode(sass_map_get_value(v, i), backtrace, pstate));
1682 e = SASS_MEMORY_NEW(Null, pstate);
1685 error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, backtrace);
1687 case SASS_WARNING: {
1688 error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, backtrace);
1694 Selector_List_Ptr Eval::operator()(Selector_List_Ptr s)
1696 std::vector<Selector_List_Obj> rv;
1697 Selector_List_Obj sl = SASS_MEMORY_NEW(Selector_List, s->pstate());
1698 sl->is_optional(s->is_optional());
1699 sl->media_block(s->media_block());
1700 sl->is_optional(s->is_optional());
1701 for (size_t i = 0, iL = s->length(); i < iL; ++i) {
1702 rv.push_back(operator()((*s)[i]));
1705 // we should actually permutate parent first
1706 // but here we have permutated the selector first
1708 while (round != std::string::npos) {
1710 for (size_t i = 0, iL = rv.size(); i < iL; ++i) {
1711 if (rv[i]->length() > round) {
1712 sl->append((*rv[i])[round]);
1717 round = std::string::npos;
1727 Selector_List_Ptr Eval::operator()(Complex_Selector_Ptr s)
1729 bool implicit_parent = !exp.old_at_root_without_rule;
1730 return s->resolve_parent_refs(ctx, exp.selector_stack, implicit_parent);
1733 // XXX: this is never hit via spec tests
1734 Attribute_Selector_Ptr Eval::operator()(Attribute_Selector_Ptr s)
1736 String_Obj attr = s->value();
1737 if (attr) { attr = static_cast<String_Ptr>(attr->perform(this)); }
1738 Attribute_Selector_Ptr ss = SASS_MEMORY_COPY(s);
1743 Selector_List_Ptr Eval::operator()(Selector_Schema_Ptr s)
1745 // the parser will look for a brace to end the selector
1746 Expression_Obj sel = s->contents()->perform(this);
1747 std::string result_str(sel->to_string(ctx.c_options));
1748 result_str = unquote(Util::rtrim(result_str));
1749 Parser p = Parser::from_c_str(result_str.c_str(), ctx, s->pstate());
1750 p.last_media_block = s->media_block();
1751 // a selector schema may or may not connect to parent?
1752 bool chroot = s->connect_parent() == false;
1753 Selector_List_Obj sl = p.parse_selector_list(chroot);
1754 return operator()(sl);
1757 Expression_Ptr Eval::operator()(Parent_Selector_Ptr p)
1759 if (Selector_List_Obj pr = selector()) {
1760 exp.selector_stack.pop_back();
1761 Selector_List_Obj rv = operator()(pr);
1762 exp.selector_stack.push_back(rv);
1765 return SASS_MEMORY_NEW(Null, p->pstate());