ext/libsass/eval.cpp in sassc-0.0.9 vs ext/libsass/eval.cpp in sassc-0.0.10

- old
+ new

@@ -7,10 +7,11 @@ #include "inspect.hpp" #include "to_c.hpp" #include "context.hpp" #include "backtrace.hpp" #include "prelexer.hpp" +#include "parser.hpp" #include <cstdlib> #include <cmath> #include <iostream> #include <iomanip> @@ -79,37 +80,37 @@ Expression* Eval::operator()(For* f) { string variable(f->variable()); Expression* low = f->lower_bound()->perform(this); if (low->concrete_type() != Expression::NUMBER) { - error("lower bound of `@for` directive must be numeric", low->path(), low->position()); + error("lower bound of `@for` directive must be numeric", low->pstate()); } Expression* high = f->upper_bound()->perform(this); if (high->concrete_type() != Expression::NUMBER) { - error("upper bound of `@for` directive must be numeric", high->path(), high->position()); + error("upper bound of `@for` directive must be numeric", high->pstate()); } double start = static_cast<Number*>(low)->value(); double end = static_cast<Number*>(high)->value(); Env new_env; - new_env[variable] = new (ctx.mem) Number(low->path(), low->position(), start); + new_env[variable] = new (ctx.mem) Number(low->pstate(), start); new_env.link(env); env = &new_env; Block* body = f->block(); Expression* val = 0; if (start < end) { if (f->is_inclusive()) ++end; for (double i = start; i < end; - (*env)[variable] = new (ctx.mem) Number(low->path(), low->position(), ++i)) { + (*env)[variable] = new (ctx.mem) Number(low->pstate(), ++i)) { val = body->perform(this); if (val) break; } } else { if (f->is_inclusive()) --end; for (double i = start; i > end; - (*env)[variable] = new (ctx.mem) Number(low->path(), low->position(), --i)) { + (*env)[variable] = new (ctx.mem) Number(low->pstate(), --i)) { val = body->perform(this); if (val) break; } } env = new_env.parent(); @@ -124,11 +125,11 @@ Map* map = 0; if (expr->concrete_type() == Expression::MAP) { map = static_cast<Map*>(expr); } else if (expr->concrete_type() != Expression::LIST) { - list = new (ctx.mem) List(expr->path(), expr->position(), 1, List::COMMA); + list = new (ctx.mem) List(expr->pstate(), 1, List::COMMA); *list << expr; } else { list = static_cast<List*>(expr); } @@ -142,11 +143,11 @@ if (map) { for (auto key : map->keys()) { Expression* value = map->at(key); if (variables.size() == 1) { - List* variable = new (ctx.mem) List(map->path(), map->position(), 2, List::SPACE); + List* variable = new (ctx.mem) List(map->pstate(), 2, List::SPACE); *variable << key; *variable << value; (*env)[variables[0]] = variable; } else { (*env)[variables[0]] = key; @@ -159,22 +160,22 @@ } else { for (size_t i = 0, L = list->length(); i < L; ++i) { List* variable = 0; if ((*list)[i]->concrete_type() != Expression::LIST || variables.size() == 1) { - variable = new (ctx.mem) List((*list)[i]->path(), (*list)[i]->position(), 1, List::COMMA); + variable = new (ctx.mem) List((*list)[i]->pstate(), 1, List::COMMA); *variable << (*list)[i]; } else { variable = static_cast<List*>((*list)[i]); } for (size_t j = 0, K = variables.size(); j < K; ++j) { if (j < variable->length()) { (*env)[variables[j]] = (*variable)[j]; } else { - (*env)[variables[j]] = new (ctx.mem) Null(expr->path(), expr->position()); + (*env)[variables[j]] = new (ctx.mem) Null(expr->pstate()); } val = body->perform(this); if (val) break; } if (val) break; @@ -201,11 +202,11 @@ } Expression* Eval::operator()(Warning* w) { Expression* message = w->message()->perform(this); - To_String to_string; + To_String to_string(&ctx); // try to use generic function if (env->has("@warn[f]")) { Definition* def = static_cast<Definition*>((*env)["@warn[f]"]); @@ -222,21 +223,21 @@ return 0; } string result(unquote(message->perform(&to_string))); - Backtrace top(backtrace, w->path(), w->position(), ""); + Backtrace top(backtrace, w->pstate(), ""); cerr << "WARNING: " << result; cerr << top.to_string(true); cerr << endl << endl; return 0; } Expression* Eval::operator()(Error* e) { Expression* message = e->message()->perform(this); - To_String to_string; + To_String to_string(&ctx); // try to use generic function if (env->has("@error[f]")) { Definition* def = static_cast<Definition*>((*env)["@error[f]"]); @@ -253,21 +254,21 @@ return 0; } string result(unquote(message->perform(&to_string))); - Backtrace top(backtrace, e->path(), e->position(), ""); + Backtrace top(backtrace, e->pstate(), ""); cerr << "Error: " << result; cerr << top.to_string(true); cerr << endl << endl; return 0; } Expression* Eval::operator()(Debug* d) { Expression* message = d->value()->perform(this); - To_String to_string; + To_String to_string(&ctx); // try to use generic function if (env->has("@debug[f]")) { Definition* def = static_cast<Definition*>((*env)["@debug[f]"]); @@ -285,21 +286,20 @@ } string cwd(ctx.get_cwd()); string result(unquote(message->perform(&to_string))); - string rel_path(Sass::File::resolve_relative_path(d->path(), cwd, cwd)); - cerr << rel_path << ":" << d->position().line << ":" << " DEBUG: " << result; + string rel_path(Sass::File::resolve_relative_path(d->pstate().path, cwd, cwd)); + cerr << rel_path << ":" << d->pstate().line << ":" << " DEBUG: " << result; cerr << endl; return 0; } Expression* Eval::operator()(List* l) { if (l->is_expanded()) return l; - List* ll = new (ctx.mem) List(l->path(), - l->position(), + List* ll = new (ctx.mem) List(l->pstate(), l->length(), l->separator(), l->is_arglist()); for (size_t i = 0, L = l->length(); i < L; ++i) { *ll << (*l)[i]->perform(this); @@ -309,12 +309,11 @@ } Expression* Eval::operator()(Map* m) { if (m->is_expanded()) return m; - Map* mm = new (ctx.mem) Map(m->path(), - m->position(), + Map* mm = new (ctx.mem) Map(m->pstate(), m->length()); for (auto key : m->keys()) { *mm << std::make_pair(key->perform(this), m->at(key)->perform(this)); } mm->is_expanded(true); @@ -335,13 +334,14 @@ { Binary_Expression::Type op_type = b->type(); // don't eval delayed expressions (the '/' when used as a separator) if (op_type == Binary_Expression::DIV && b->is_delayed()) return b; // if one of the operands is a '/' then make sure it's evaluated - if (typeid(*b->left()) == typeid(Binary_Expression)) b->left()->is_delayed(false); - // the logical connectives need to short-circuit Expression* lhs = b->left()->perform(this); + lhs->is_delayed(false); + while (typeid(*lhs) == typeid(Binary_Expression)) lhs = lhs->perform(this); + switch (op_type) { case Binary_Expression::AND: return *lhs ? b->right()->perform(this) : lhs; break; @@ -352,19 +352,21 @@ default: break; } // not a logical connective, so go ahead and eval the rhs Expression* rhs = b->right()->perform(this); + rhs->is_delayed(false); + while (typeid(*rhs) == typeid(Binary_Expression)) rhs = rhs->perform(this); // see if it's a relational expression switch(op_type) { - case Binary_Expression::EQ: return new (ctx.mem) Boolean(b->path(), b->position(), eq(lhs, rhs, ctx)); - case Binary_Expression::NEQ: return new (ctx.mem) Boolean(b->path(), b->position(), !eq(lhs, rhs, ctx)); - case Binary_Expression::GT: return new (ctx.mem) Boolean(b->path(), b->position(), !lt(lhs, rhs, ctx) && !eq(lhs, rhs, ctx)); - case Binary_Expression::GTE: return new (ctx.mem) Boolean(b->path(), b->position(), !lt(lhs, rhs, ctx)); - case Binary_Expression::LT: return new (ctx.mem) Boolean(b->path(), b->position(), lt(lhs, rhs, ctx)); - case Binary_Expression::LTE: return new (ctx.mem) Boolean(b->path(), b->position(), lt(lhs, rhs, ctx) || eq(lhs, rhs, ctx)); + case Binary_Expression::EQ: return new (ctx.mem) Boolean(b->pstate(), eq(lhs, rhs, ctx)); + case Binary_Expression::NEQ: return new (ctx.mem) Boolean(b->pstate(), !eq(lhs, rhs, ctx)); + case Binary_Expression::GT: return new (ctx.mem) Boolean(b->pstate(), !lt(lhs, rhs, ctx) && !eq(lhs, rhs, ctx)); + case Binary_Expression::GTE: return new (ctx.mem) Boolean(b->pstate(), !lt(lhs, rhs, ctx)); + case Binary_Expression::LT: return new (ctx.mem) Boolean(b->pstate(), lt(lhs, rhs, ctx)); + case Binary_Expression::LTE: return new (ctx.mem) Boolean(b->pstate(), lt(lhs, rhs, ctx) || eq(lhs, rhs, ctx)); default: break; } Expression::Concrete_Type l_type = lhs->concrete_type(); @@ -380,18 +382,29 @@ return op_color_number(ctx, op_type, lhs, rhs); } if (l_type == Expression::COLOR && r_type == Expression::COLOR) { return op_colors(ctx, op_type, lhs, rhs); } - return op_strings(ctx, op_type, lhs, rhs); + + Expression* ex = op_strings(ctx, op_type, lhs, rhs); + if (String_Constant* str = (String_Constant*) ex) + { + if (str->concrete_type() != Expression::STRING) return ex; + String_Constant* lstr = dynamic_cast<String_Constant*>(lhs); + String_Constant* rstr = dynamic_cast<String_Constant*>(rhs); + if (String_Constant* org = lstr ? lstr : rstr) + { str->quote_mark(org->quote_mark()); } + } + return ex; + } Expression* Eval::operator()(Unary_Expression* u) { Expression* operand = u->operand()->perform(this); if (u->type() == Unary_Expression::NOT) { - Boolean* result = new (ctx.mem) Boolean(u->path(), u->position(), (bool)*operand); + Boolean* result = new (ctx.mem) Boolean(u->pstate(), (bool)*operand); result->value(!result->value()); return result; } else if (operand->concrete_type() == Expression::NUMBER) { Number* result = new (ctx.mem) Number(*static_cast<Number*>(operand)); @@ -399,19 +412,18 @@ ? -result->value() : result->value()); return result; } else { - To_String to_string; + To_String to_string(&ctx); // Special cases: +/- variables which evaluate to null ouput just +/-, // but +/- null itself outputs the string if (operand->concrete_type() == Expression::NULL_VAL && typeid(*(u->operand())) == typeid(Variable)) { - u->operand(new (ctx.mem) String_Constant(u->path(), u->position(), "")); + u->operand(new (ctx.mem) String_Constant(u->pstate(), "")); } else u->operand(operand); - String_Constant* result = new (ctx.mem) String_Constant(u->path(), - u->position(), + String_Constant* result = new (ctx.mem) String_Constant(u->pstate(), u->perform(&to_string)); return result; } // unreachable return u; @@ -432,17 +444,15 @@ } } // if it doesn't exist, just pass it through as a literal if (!env->has(full_name)) { - Function_Call* lit = new (ctx.mem) Function_Call(c->path(), - c->position(), + Function_Call* lit = new (ctx.mem) Function_Call(c->pstate(), c->name(), args); - To_String to_string; - return new (ctx.mem) String_Constant(c->path(), - c->position(), + To_String to_string(&ctx); + return new (ctx.mem) String_Constant(c->pstate(), lit->perform(&to_string)); } Expression* result = c; Definition* def = static_cast<Definition*>((*env)[full_name]); @@ -471,16 +481,16 @@ bind("function " + c->name(), params, args, ctx, &new_env, this); Env* old_env = env; env = &new_env; - Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`"); + Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`"); backtrace = &here; result = body->perform(this); if (!result) { - error(string("function ") + c->name() + " did not return a value", c->path(), c->position()); + error(string("function ") + c->name() + " did not return a value", c->pstate()); } backtrace = here.parent; env = old_env; } // if it's native, invoke the underlying CPP function @@ -488,35 +498,35 @@ bind("function " + c->name(), params, args, ctx, &new_env, this); Env* old_env = env; env = &new_env; - Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`"); + Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`"); backtrace = &here; - result = func(*env, *old_env, ctx, def->signature(), c->path(), c->position(), backtrace); + result = func(*env, *old_env, ctx, def->signature(), c->pstate(), backtrace); backtrace = here.parent; env = old_env; } // else if it's a user-defined c function else if (c_func) { if (full_name == "*[f]") { - String_Constant *str = new (ctx.mem) String_Constant(c->path(), c->position(), c->name()); - Arguments* new_args = new (ctx.mem) Arguments(c->path(), c->position()); - *new_args << new (ctx.mem) Argument(c->path(), c->position(), str); + String_Constant *str = new (ctx.mem) String_Constant(c->pstate(), c->name()); + Arguments* new_args = new (ctx.mem) Arguments(c->pstate()); + *new_args << new (ctx.mem) Argument(c->pstate(), str); *new_args += args; args = new_args; } // populates env with default values for params bind("function " + c->name(), params, args, ctx, &new_env, this); Env* old_env = env; env = &new_env; - Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`"); + Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`"); backtrace = &here; To_C to_c; union Sass_Value* c_args = sass_make_list(env->current_frame().size(), SASS_COMMA); for(size_t i = 0; i < params[0].length(); i++) { @@ -525,15 +535,15 @@ Expression* arg = static_cast<Expression*>(node); sass_list_set_value(c_args, i, arg->perform(&to_c)); } Sass_Value* c_val = c_func(c_args, def->cookie()); if (sass_value_get_tag(c_val) == SASS_ERROR) { - error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->path(), c->position(), backtrace); + error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), backtrace); } else if (sass_value_get_tag(c_val) == SASS_WARNING) { - error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->path(), c->position(), backtrace); + error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), backtrace); } - result = cval_to_astnode(c_val, ctx, backtrace, c->path(), c->position()); + result = cval_to_astnode(c_val, ctx, backtrace, c->pstate()); backtrace = here.parent; sass_delete_value(c_args); if (c_val != c_args) sass_delete_value(c_val); @@ -543,68 +553,90 @@ else if (def->is_overload_stub()) { size_t arity = args->length(); stringstream ss; ss << full_name << arity; string resolved_name(ss.str()); - if (!env->has(resolved_name)) error("overloaded function `" + string(c->name()) + "` given wrong number of arguments", c->path(), c->position()); + if (!env->has(resolved_name)) error("overloaded function `" + string(c->name()) + "` given wrong number of arguments", c->pstate()); Definition* resolved_def = static_cast<Definition*>((*env)[resolved_name]); params = resolved_def->parameters(); Env newer_env; newer_env.link(resolved_def->environment()); bind("function " + c->name(), params, args, ctx, &newer_env, this); Env* old_env = env; env = &newer_env; - Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`"); + Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`"); backtrace = &here; - result = resolved_def->native_function()(*env, *old_env, ctx, resolved_def->signature(), c->path(), c->position(), backtrace); + result = resolved_def->native_function()(*env, *old_env, ctx, resolved_def->signature(), c->pstate(), backtrace); backtrace = here.parent; env = old_env; } // backtrace = here.parent; // env = old_env; - result->position(c->position()); + + + // link back to function definition + // only do this for custom functions + if (result->pstate().file == string::npos) + result->pstate(c->pstate()); + + do { + result->is_delayed(result->concrete_type() == Expression::STRING); + result = result->perform(this); + } while (result->concrete_type() == Expression::NONE); return result; } Expression* Eval::operator()(Function_Call_Schema* s) { Expression* evaluated_name = s->name()->perform(this); Expression* evaluated_args = s->arguments()->perform(this); - String_Schema* ss = new (ctx.mem) String_Schema(s->path(), s->position(), 2); + String_Schema* ss = new (ctx.mem) String_Schema(s->pstate(), 2); (*ss) << evaluated_name << evaluated_args; return ss->perform(this); } Expression* Eval::operator()(Variable* v) { - To_String to_string; + To_String to_string(&ctx); string name(v->name()); Expression* value = 0; if (env->has(name)) value = static_cast<Expression*>((*env)[name]); - else error("unbound variable " + v->name(), v->path(), v->position()); + else error("Undefined variable: \"" + v->name() + "\".", v->pstate()); // cerr << "name: " << v->name() << "; type: " << typeid(*value).name() << "; value: " << value->perform(&to_string) << endl; if (typeid(*value) == typeid(Argument)) value = static_cast<Argument*>(value)->value(); // behave according to as ruby sass (add leading zero) if (value->concrete_type() == Expression::NUMBER) { - Number* n = static_cast<Number*>(value); - value = new (ctx.mem) Number(n->path(), - n->position(), - n->value(), - n->unit(), - true); + value = new (ctx.mem) Number(*static_cast<Number*>(value)); + static_cast<Number*>(value)->zero(true); } else if (value->concrete_type() == Expression::STRING) { - String_Constant* s = static_cast<String_Constant*>(value); - value = new (ctx.mem) String_Constant(s->path(), - s->position(), - s->value()); + if (auto str = dynamic_cast<String_Quoted*>(value)) { + value = new (ctx.mem) String_Quoted(*str); + } else if (auto str = dynamic_cast<String_Constant*>(value)) { + value = new (ctx.mem) String_Constant(*str); + } } + else if (value->concrete_type() == Expression::LIST) { + value = new (ctx.mem) List(*static_cast<List*>(value)); + } + else if (value->concrete_type() == Expression::MAP) { + value = new (ctx.mem) Map(*static_cast<Map*>(value)); + } + else if (value->concrete_type() == Expression::BOOLEAN) { + value = new (ctx.mem) Boolean(*static_cast<Boolean*>(value)); + } + else if (value->concrete_type() == Expression::COLOR) { + value = new (ctx.mem) Color(*static_cast<Color*>(value)); + } + else if (value->concrete_type() == Expression::NULL_VAL) { + value = new (ctx.mem) Null(value->pstate()); + } // cerr << "\ttype is now: " << typeid(*value).name() << endl << endl; return value; } @@ -623,47 +655,42 @@ const string& num = text.substr(num_pos, unit_pos - num_pos); switch (t->type()) { case Textual::NUMBER: - result = new (ctx.mem) Number(t->path(), - t->position(), - atof(num.c_str()), + result = new (ctx.mem) Number(t->pstate(), + sass_atof(num.c_str()), "", zero); break; case Textual::PERCENTAGE: - result = new (ctx.mem) Number(t->path(), - t->position(), - atof(num.c_str()), + result = new (ctx.mem) Number(t->pstate(), + sass_atof(num.c_str()), "%", zero); break; case Textual::DIMENSION: - result = new (ctx.mem) Number(t->path(), - t->position(), - atof(num.c_str()), - Token(number(text.c_str())), + result = new (ctx.mem) Number(t->pstate(), + sass_atof(num.c_str()), + Token(number(text.c_str()), t->pstate()), zero); break; case Textual::HEX: { string hext(t->value().substr(1)); // chop off the '#' if (hext.length() == 6) { string r(hext.substr(0,2)); string g(hext.substr(2,2)); string b(hext.substr(4,2)); - result = new (ctx.mem) Color(t->path(), - t->position(), + result = new (ctx.mem) Color(t->pstate(), static_cast<double>(strtol(r.c_str(), NULL, 16)), static_cast<double>(strtol(g.c_str(), NULL, 16)), static_cast<double>(strtol(b.c_str(), NULL, 16)), 1, true, t->value()); } else { - result = new (ctx.mem) Color(t->path(), - t->position(), + result = new (ctx.mem) Color(t->pstate(), static_cast<double>(strtol(string(2,hext[0]).c_str(), NULL, 16)), static_cast<double>(strtol(string(2,hext[1]).c_str(), NULL, 16)), static_cast<double>(strtol(string(2,hext[2]).c_str(), NULL, 16)), 1, false, t->value()); @@ -674,12 +701,11 @@ } Expression* Eval::operator()(Number* n) { // behave according to as ruby sass (add leading zero) - return new (ctx.mem) Number(n->path(), - n->position(), + return new (ctx.mem) Number(n->pstate(), n->value(), n->unit(), true); } @@ -698,46 +724,106 @@ else { return 0; } } + string Eval::interpolation(Expression* s) { + + if (String_Quoted* str_quoted = dynamic_cast<String_Quoted*>(s)) { + + if (str_quoted->quote_mark()) { + return string_escape(str_quoted->value()); + } else { + return evacuate_escapes(str_quoted->value()); + } + + } else if (String_Constant* str_constant = dynamic_cast<String_Constant*>(s)) { + + return evacuate_escapes(str_constant->value()); + + } else if (String_Schema* str_schema = dynamic_cast<String_Schema*>(s)) { + + string res = ""; + for(auto i : str_schema->elements()) + res += (interpolation(i)); + //ToDo: do this in one step + auto esc = evacuate_escapes(res); + auto unq = unquote(esc); + if (unq == esc) { + return string_to_output(res); + } else { + return evacuate_quotes(unq); + } + + } else if (List* list = dynamic_cast<List*>(s)) { + + string acc = ""; // ToDo: different output styles + string sep = list->separator() == List::Separator::COMMA ? "," : " "; + if (ctx.output_style != COMPRESSED && sep == ",") sep += " "; + bool initial = false; + for(auto item : list->elements()) { + if (initial) acc += sep; + acc += interpolation(item); + initial = true; + } + return evacuate_quotes(acc); + + } else if (Variable* var = dynamic_cast<Variable*>(s)) { + + string name(var->name()); + if (!env->has(name)) error("Undefined variable: \"" + var->name() + "\".", var->pstate()); + Expression* value = static_cast<Expression*>((*env)[name]); + return evacuate_quotes(interpolation(value)); + + } else if (Binary_Expression* var = dynamic_cast<Binary_Expression*>(s)) { + + Expression* ex = operator()(var); + return evacuate_quotes(interpolation(ex)); + + } else if (Function_Call* var = dynamic_cast<Function_Call*>(s)) { + + Expression* ex = operator()(var); + return evacuate_quotes(interpolation(ex)); + + } else { + + To_String to_string(&ctx); + // to_string.in_decl_list = true; + return evacuate_quotes(s->perform(&to_string)); + + } + } + Expression* Eval::operator()(String_Schema* s) { string acc; - ctx._skip_source_map_update = true; - To_String to_string(&ctx); - ctx._skip_source_map_update = false; for (size_t i = 0, L = s->length(); i < L; ++i) { - string chunk((*s)[i]->perform(this)->perform(&to_string)); - if (((s->quote_mark() && is_quoted(chunk)) || !s->quote_mark()) && (*s)[i]->is_interpolant()) { // some redundancy in that test - acc += unquote(chunk); - } - else { - acc += chunk; - } + acc += interpolation((*s)[i]); } - return new (ctx.mem) String_Constant(s->path(), - s->position(), - acc); + String_Quoted* str = new (ctx.mem) String_Quoted(s->pstate(), acc); + if (!str->quote_mark()) { + str->value(string_unescape(str->value())); + } else if (str->quote_mark()) { + str->quote_mark('*'); + } + return str; } Expression* Eval::operator()(String_Constant* s) { - if (!s->is_delayed() && ctx.names_to_colors.count(s->value())) { + if (!s->quote_mark() && !s->is_delayed() && ctx.names_to_colors.count(s->value())) { Color* c = new (ctx.mem) Color(*ctx.names_to_colors[s->value()]); - c->path(s->path()); - c->position(s->position()); + c->pstate(s->pstate()); c->disp(s->value()); return c; } return s; } Expression* Eval::operator()(Feature_Query* q) { - Feature_Query* qq = new (ctx.mem) Feature_Query(q->path(), - q->position(), + Feature_Query* qq = new (ctx.mem) Feature_Query(q->pstate(), q->length()); for (size_t i = 0, L = q->length(); i < L; ++i) { *qq << static_cast<Feature_Query_Condition*>((*q)[i]->perform(this)); } return qq; @@ -746,12 +832,11 @@ Expression* Eval::operator()(Feature_Query_Condition* c) { String* feature = c->feature(); Expression* value = c->value(); value = (value ? value->perform(this) : 0); - Feature_Query_Condition* cc = new (ctx.mem) Feature_Query_Condition(c->path(), - c->position(), + Feature_Query_Condition* cc = new (ctx.mem) Feature_Query_Condition(c->pstate(), c->length(), feature, value, c->operand(), c->is_root()); @@ -759,16 +844,29 @@ *cc << static_cast<Feature_Query_Condition*>((*c)[i]->perform(this)); } return cc; } + Expression* Eval::operator()(At_Root_Expression* e) + { + Expression* feature = e->feature(); + feature = (feature ? feature->perform(this) : 0); + Expression* value = e->value(); + value = (value ? value->perform(this) : 0); + Expression* ee = new (ctx.mem) At_Root_Expression(e->pstate(), + static_cast<String*>(feature), + value, + e->is_interpolated()); + return ee; + } + Expression* Eval::operator()(Media_Query* q) { + To_String to_string(&ctx); String* t = q->media_type(); t = static_cast<String*>(t ? t->perform(this) : 0); - Media_Query* qq = new (ctx.mem) Media_Query(q->path(), - q->position(), + Media_Query* qq = new (ctx.mem) Media_Query(q->pstate(), t, q->length(), q->is_negated(), q->is_restricted()); for (size_t i = 0, L = q->length(); i < L; ++i) { @@ -781,12 +879,11 @@ { Expression* feature = e->feature(); feature = (feature ? feature->perform(this) : 0); Expression* value = e->value(); value = (value ? value->perform(this) : 0); - return new (ctx.mem) Media_Query_Expression(e->path(), - e->position(), + return new (ctx.mem) Media_Query_Expression(e->pstate(), feature, value, e->is_interpolated()); } @@ -810,36 +907,39 @@ is_rest_argument = false; is_keyword_argument = true; } else if(val->concrete_type() != Expression::LIST) { - List* wrapper = new (ctx.mem) List(val->path(), - val->position(), + List* wrapper = new (ctx.mem) List(val->pstate(), 0, List::COMMA, true); *wrapper << val; val = wrapper; } } - return new (ctx.mem) Argument(a->path(), - a->position(), + return new (ctx.mem) Argument(a->pstate(), val, a->name(), is_rest_argument, is_keyword_argument); } Expression* Eval::operator()(Arguments* a) { - Arguments* aa = new (ctx.mem) Arguments(a->path(), a->position()); + Arguments* aa = new (ctx.mem) Arguments(a->pstate()); for (size_t i = 0, L = a->length(); i < L; ++i) { *aa << static_cast<Argument*>((*a)[i]->perform(this)); } return aa; } + Expression* Eval::operator()(Comment* c) + { + return 0; + } + inline Expression* Eval::fallback_impl(AST_Node* n) { return static_cast<Expression*>(n); } @@ -911,19 +1011,19 @@ bool lt(Expression* lhs, Expression* rhs, Context& ctx) { if (lhs->concrete_type() != Expression::NUMBER || rhs->concrete_type() != Expression::NUMBER) - error("may only compare numbers", lhs->path(), lhs->position()); + error("may only compare numbers", lhs->pstate()); Number* l = static_cast<Number*>(lhs); Number* r = static_cast<Number*>(rhs); Number tmp_r(*r); tmp_r.normalize(l->find_convertible_unit()); string l_unit(l->unit()); string r_unit(tmp_r.unit()); if (!l_unit.empty() && !r_unit.empty() && l->unit() != tmp_r.unit()) { - error("cannot compare numbers with incompatible units", l->path(), l->position()); + error("cannot compare numbers with incompatible units", l->pstate()); } return l->value() < tmp_r.value(); } Expression* op_numbers(Context& ctx, Binary_Expression* b, Expression* lhs, Expression* rhs) @@ -932,26 +1032,26 @@ Number* r = static_cast<Number*>(rhs); double lv = l->value(); double rv = r->value(); Binary_Expression::Type op = b->type(); if (op == Binary_Expression::DIV && !rv) { - return new (ctx.mem) String_Constant(l->path(), b->position(), "Infinity"); + return new (ctx.mem) String_Constant(l->pstate(), "Infinity"); } if (op == Binary_Expression::MOD && !rv) { - error("division by zero", r->path(), r->position()); + error("division by zero", r->pstate()); } Number tmp(*r); tmp.normalize(l->find_convertible_unit()); string l_unit(l->unit()); string r_unit(tmp.unit()); if (l_unit != r_unit && !l_unit.empty() && !r_unit.empty() && (op == Binary_Expression::ADD || op == Binary_Expression::SUB)) { - error("cannot add or subtract numbers with incompatible units", l->path(), l->position()); + error("Incompatible units: '"+r_unit+"' and '"+l_unit+"'.", l->pstate()); } Number* v = new (ctx.mem) Number(*l); - v->position(b->position()); + v->pstate(b->pstate()); if (l_unit.empty() && (op == Binary_Expression::ADD || op == Binary_Expression::SUB || op == Binary_Expression::MOD)) { v->numerator_units() = r->numerator_units(); v->denominator_units() = r->denominator_units(); } @@ -986,31 +1086,30 @@ r->disp(""); double lv = l->value(); switch (op) { case Binary_Expression::ADD: case Binary_Expression::MUL: { - return new (ctx.mem) Color(l->path(), - l->position(), + return new (ctx.mem) Color(l->pstate(), ops[op](lv, r->r()), ops[op](lv, r->g()), ops[op](lv, r->b()), r->a()); } break; case Binary_Expression::SUB: case Binary_Expression::DIV: { string sep(op == Binary_Expression::SUB ? "-" : "/"); - To_String to_string; - string color(r->sixtuplet() ? r->perform(&to_string) : + To_String to_string(&ctx); + string color(r->sixtuplet() && (ctx.output_style != COMPRESSED) ? + r->perform(&to_string) : Util::normalize_sixtuplet(r->perform(&to_string))); - return new (ctx.mem) String_Constant(l->path(), - l->position(), + return new (ctx.mem) String_Constant(l->pstate(), l->perform(&to_string) + sep + color); } break; case Binary_Expression::MOD: { - error("cannot divide a number by a color", r->path(), r->position()); + error("cannot divide a number by a color", r->pstate()); } break; default: break; // caller should ensure that we don't get here } // unreachable return l; @@ -1019,13 +1118,12 @@ Expression* op_color_number(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression* rhs) { Color* l = static_cast<Color*>(lhs); Number* r = static_cast<Number*>(rhs); double rv = r->value(); - if (op == Binary_Expression::DIV && !rv) error("division by zero", r->path(), r->position()); - return new (ctx.mem) Color(l->path(), - l->position(), + if (op == Binary_Expression::DIV && !rv) error("division by zero", r->pstate()); + return new (ctx.mem) Color(l->pstate(), ops[op](l->r(), rv), ops[op](l->g(), rv), ops[op](l->b(), rv), l->a()); } @@ -1033,40 +1131,37 @@ Expression* op_colors(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression* rhs) { Color* l = static_cast<Color*>(lhs); Color* r = static_cast<Color*>(rhs); if (l->a() != r->a()) { - error("alpha channels must be equal when combining colors", r->path(), r->position()); + error("alpha channels must be equal when combining colors", r->pstate()); } if ((op == Binary_Expression::DIV || op == Binary_Expression::MOD) && (!r->r() || !r->g() ||!r->b())) { - error("division by zero", r->path(), r->position()); + error("division by zero", r->pstate()); } - return new (ctx.mem) Color(l->path(), - l->position(), + return new (ctx.mem) Color(l->pstate(), ops[op](l->r(), r->r()), ops[op](l->g(), r->g()), ops[op](l->b(), r->b()), l->a()); } Expression* op_strings(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression*rhs) { - To_String to_string; + To_String to_string(&ctx); Expression::Concrete_Type ltype = lhs->concrete_type(); Expression::Concrete_Type rtype = rhs->concrete_type(); string lstr(lhs->perform(&to_string)); string rstr(rhs->perform(&to_string)); - bool l_str_quoted = ((Sass::String*)lhs) && ((Sass::String*)lhs)->needs_unquoting(); - bool r_str_quoted = ((Sass::String*)rhs) && ((Sass::String*)rhs)->needs_unquoting(); + bool l_str_quoted = ((Sass::String*)lhs) && ((Sass::String*)lhs)->sass_fix_1291(); + bool r_str_quoted = ((Sass::String*)rhs) && ((Sass::String*)rhs)->sass_fix_1291(); bool l_str_color = ltype == Expression::STRING && ctx.names_to_colors.count(lstr) && !l_str_quoted; bool r_str_color = rtype == Expression::STRING && ctx.names_to_colors.count(rstr) && !r_str_quoted; - bool unquoted = false; - if (ltype == Expression::STRING && lstr[0] != '"' && lstr[0] != '\'') unquoted = true; if (l_str_color && r_str_color) { return op_colors(ctx, op, ctx.names_to_colors[lstr], ctx.names_to_colors[rstr]); } else if (l_str_color && rtype == Expression::COLOR) { return op_colors(ctx, op, ctx.names_to_colors[lstr], rhs); @@ -1078,70 +1173,67 @@ return op_number_color(ctx, op, lhs, ctx.names_to_colors[rstr]); } else if (ltype == Expression::NUMBER && r_str_color) { return op_number_color(ctx, op, lhs, ctx.names_to_colors[rstr]); } - if (op == Binary_Expression::MUL) error("invalid operands for multiplication", lhs->path(), lhs->position()); - if (op == Binary_Expression::MOD) error("invalid operands for modulo", lhs->path(), lhs->position()); + if (op == Binary_Expression::MUL) error("invalid operands for multiplication", lhs->pstate()); + if (op == Binary_Expression::MOD) error("invalid operands for modulo", lhs->pstate()); string sep; switch (op) { case Binary_Expression::SUB: sep = "-"; break; case Binary_Expression::DIV: sep = "/"; break; default: break; } - if (ltype == Expression::NULL_VAL) error("invalid null operation: \"null plus "+quote(unquote(rstr), '"')+"\".", lhs->path(), lhs->position()); - if (rtype == Expression::NULL_VAL) error("invalid null operation: \""+quote(unquote(lstr), '"')+" plus null\".", lhs->path(), lhs->position()); - char q = '\0'; - if (lstr[0] == '"' || lstr[0] == '\'') q = lstr[0]; - else if (rstr[0] == '"' || rstr[0] == '\'') q = rstr[0]; - string result(unquote(lstr) + sep + unquote(rstr)); - return new (ctx.mem) String_Constant(lhs->path(), - lhs->position(), - unquoted ? result : quote(result, q)); + if (ltype == Expression::NULL_VAL) error("invalid null operation: \"null plus "+quote(unquote(rstr), '"')+"\".", lhs->pstate()); + if (rtype == Expression::NULL_VAL) error("invalid null operation: \""+quote(unquote(lstr), '"')+" plus null\".", lhs->pstate()); + string result((lstr) + sep + (rstr)); + String_Quoted* str = new (ctx.mem) String_Quoted(lhs->pstate(), result); + str->quote_mark(0); + return str; } - Expression* cval_to_astnode(Sass_Value* v, Context& ctx, Backtrace* backtrace, string path, Position position) + Expression* cval_to_astnode(Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate) { using std::strlen; using std::strcpy; Expression* e = 0; switch (sass_value_get_tag(v)) { case SASS_BOOLEAN: { - e = new (ctx.mem) Boolean(path, position, !!sass_boolean_get_value(v)); + e = new (ctx.mem) Boolean(pstate, !!sass_boolean_get_value(v)); } break; case SASS_NUMBER: { - e = new (ctx.mem) Number(path, position, sass_number_get_value(v), sass_number_get_unit(v)); + e = new (ctx.mem) Number(pstate, sass_number_get_value(v), sass_number_get_unit(v)); } break; case SASS_COLOR: { - e = new (ctx.mem) Color(path, position, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v)); + e = new (ctx.mem) Color(pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v)); } break; case SASS_STRING: { - e = new (ctx.mem) String_Constant(path, position, sass_string_get_value(v)); + e = new (ctx.mem) String_Constant(pstate, sass_string_get_value(v)); } break; case SASS_LIST: { - List* l = new (ctx.mem) List(path, position, sass_list_get_length(v), sass_list_get_separator(v) == SASS_COMMA ? List::COMMA : List::SPACE); + List* l = new (ctx.mem) List(pstate, sass_list_get_length(v), sass_list_get_separator(v) == SASS_COMMA ? List::COMMA : List::SPACE); for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) { - *l << cval_to_astnode(sass_list_get_value(v, i), ctx, backtrace, path, position); + *l << cval_to_astnode(sass_list_get_value(v, i), ctx, backtrace, pstate); } e = l; } break; case SASS_MAP: { - Map* m = new (ctx.mem) Map(path, position); + Map* m = new (ctx.mem) Map(pstate); for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) { *m << std::make_pair( - cval_to_astnode(sass_map_get_key(v, i), ctx, backtrace, path, position), - cval_to_astnode(sass_map_get_value(v, i), ctx, backtrace, path, position)); + cval_to_astnode(sass_map_get_key(v, i), ctx, backtrace, pstate), + cval_to_astnode(sass_map_get_value(v, i), ctx, backtrace, pstate)); } e = m; } break; case SASS_NULL: { - e = new (ctx.mem) Null(path, position); + e = new (ctx.mem) Null(pstate); } break; case SASS_ERROR: { - error("Error in C function: " + string(sass_error_get_message(v)), path, position, backtrace); + error("Error in C function: " + string(sass_error_get_message(v)), pstate, backtrace); } break; case SASS_WARNING: { - error("Warning in C function: " + string(sass_warning_get_message(v)), path, position, backtrace); + error("Warning in C function: " + string(sass_warning_get_message(v)), pstate, backtrace); } break; } return e; }