ext/libsass/eval.cpp in sassc-0.0.10 vs ext/libsass/eval.cpp in sassc-0.0.11
- old
+ new
@@ -29,21 +29,30 @@
0, 0, // and, or
0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte
add, sub, mul, div, fmod
};
- Eval::Eval(Context& ctx, Env* env, Backtrace* bt)
- : ctx(ctx), env(env), backtrace(bt) { }
+ Eval::Eval(Context& ctx, Contextualize* contextualize, Listize* listize, Env* env, Backtrace* bt)
+ : ctx(ctx), contextualize(contextualize), listize(listize), env(env), backtrace(bt) { }
Eval::~Eval() { }
Eval* Eval::with(Env* e, Backtrace* bt) // for setting the env before eval'ing an expression
{
+ contextualize = contextualize->with(0, e, bt);
env = e;
backtrace = bt;
return this;
}
+ Eval* Eval::with(Selector* c, Env* e, Backtrace* bt, Selector* p, Selector* ex) // for setting the env before eval'ing an expression
+ {
+ contextualize = contextualize->with(c, e, bt, p, ex);
+ env = e;
+ backtrace = bt;
+ return this;
+ }
+
Expression* Eval::operator()(Block* b)
{
Expression* val = 0;
for (size_t i = 0, L = b->length(); i < L; ++i) {
val = (*b)[i]->perform(this);
@@ -53,16 +62,63 @@
}
Expression* Eval::operator()(Assignment* a)
{
string var(a->variable());
- if (env->has(var)) {
- Expression* v = static_cast<Expression*>((*env)[var]);
- if (!a->is_guarded() || v->concrete_type() == Expression::NULL_VAL) (*env)[var] = a->value()->perform(this);
+ if (a->is_global()) {
+ if (a->is_default()) {
+ if (env->has_global(var)) {
+ Expression* e = dynamic_cast<Expression*>(env->get_global(var));
+ if (!e || e->concrete_type() == Expression::NULL_VAL) {
+ env->set_global(var, a->value()->perform(this));
+ }
+ }
+ else {
+ env->set_global(var, a->value()->perform(this));
+ }
+ }
+ else {
+ env->set_global(var, a->value()->perform(this));
+ }
}
+ else if (a->is_default()) {
+ if (env->has_lexical(var)) {
+ auto cur = env;
+ while (cur && cur->is_lexical()) {
+ if (cur->has_local(var)) {
+ if (AST_Node* node = cur->get_local(var)) {
+ Expression* e = dynamic_cast<Expression*>(node);
+ if (!e || e->concrete_type() == Expression::NULL_VAL) {
+ cur->set_local(var, a->value()->perform(this));
+ }
+ }
+ else {
+ throw runtime_error("Env not in sync");
+ }
+ return 0;
+ }
+ cur = cur->parent();
+ }
+ throw runtime_error("Env not in sync");
+ }
+ else if (env->has_global(var)) {
+ if (AST_Node* node = env->get_global(var)) {
+ Expression* e = dynamic_cast<Expression*>(node);
+ if (!e || e->concrete_type() == Expression::NULL_VAL) {
+ env->set_global(var, a->value()->perform(this));
+ }
+ }
+ }
+ else if (env->is_lexical()) {
+ env->set_local(var, a->value()->perform(this));
+ }
+ else {
+ env->set_local(var, a->value()->perform(this));
+ }
+ }
else {
- env->current_frame()[var] = a->value()->perform(this);
+ env->set_lexical(var, a->value()->perform(this));
}
return 0;
}
Expression* Eval::operator()(If* i)
@@ -75,10 +131,12 @@
if (alt) return alt->perform(this);
}
return 0;
}
+ // For does not create a new env scope
+ // But iteration vars are reset afterwards
Expression* Eval::operator()(For* f)
{
string variable(f->variable());
Expression* low = f->lower_bound()->perform(this);
if (low->concrete_type() != Expression::NUMBER) {
@@ -86,39 +144,56 @@
}
Expression* high = f->upper_bound()->perform(this);
if (high->concrete_type() != Expression::NUMBER) {
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->pstate(), start);
- new_env.link(env);
- env = &new_env;
+ Number* sass_start = static_cast<Number*>(low);
+ Number* sass_end = static_cast<Number*>(high);
+ // check if units are valid for sequence
+ if (sass_start->unit() != sass_end->unit()) {
+ stringstream msg; msg << "Incompatible units: '"
+ << sass_start->unit() << "' and '"
+ << sass_end->unit() << "'.";
+ error(msg.str(), low->pstate(), backtrace);
+ }
+ double start = sass_start->value();
+ double end = sass_end->value();
+ // only create iterator once in this environment
+ Number* it = new (env->mem) Number(low->pstate(), start, sass_end->unit());
+ AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0;
+ env->set_local(variable, it);
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->pstate(), ++i)) {
+ ++i) {
+ it->value(i);
+ env->set_local(variable, it);
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->pstate(), --i)) {
+ --i) {
+ it->value(i);
+ env->set_local(variable, it);
val = body->perform(this);
if (val) break;
}
}
- env = new_env.parent();
+ // restore original environment
+ if (!old_var) env->del_local(variable);
+ else env->set_local(variable, old_var);
return val;
}
+ // Eval does not create a new env scope
+ // But iteration vars are reset afterwards
Expression* Eval::operator()(Each* e)
{
vector<string> variables(e->variables());
Expression* expr = e->list()->perform(this);
List* list = 0;
@@ -131,14 +206,16 @@
*list << expr;
}
else {
list = static_cast<List*>(expr);
}
- Env new_env;
- for (size_t i = 0, L = variables.size(); i < L; ++i) new_env[variables[i]] = 0;
- new_env.link(env);
- env = &new_env;
+ // remember variables and then reset them
+ vector<AST_Node*> old_vars(variables.size());
+ for (size_t i = 0, L = variables.size(); i < L; ++i) {
+ old_vars[i] = env->has_local(variables[i]) ? env->get_local(variables[i]) : 0;
+ env->set_local(variables[i], 0);
+ }
Block* body = e->block();
Expression* val = 0;
if (map) {
for (auto key : map->keys()) {
@@ -146,14 +223,14 @@
if (variables.size() == 1) {
List* variable = new (ctx.mem) List(map->pstate(), 2, List::SPACE);
*variable << key;
*variable << value;
- (*env)[variables[0]] = variable;
+ env->set_local(variables[0], variable);
} else {
- (*env)[variables[0]] = key;
- (*env)[variables[1]] = value;
+ env->set_local(variables[0], key);
+ env->set_local(variables[1], value);
}
val = body->perform(this);
if (val) break;
}
@@ -168,22 +245,26 @@
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];
+ env->set_local(variables[j], (*variable)[j]);
}
else {
- (*env)[variables[j]] = new (ctx.mem) Null(expr->pstate());
+ env->set_local(variables[j], new (ctx.mem) Null(expr->pstate()));
}
val = body->perform(this);
if (val) break;
}
if (val) break;
}
}
- env = new_env.parent();
+ // restore original environment
+ for (size_t j = 0, K = variables.size(); j < K; ++j) {
+ if(!old_vars[j]) env->del_local(variables[j]);
+ else env->set_local(variables[j], old_vars[j]);
+ }
return val;
}
Expression* Eval::operator()(While* w)
{
@@ -210,16 +291,17 @@
if (env->has("@warn[f]")) {
Definition* def = static_cast<Definition*>((*env)["@warn[f]"]);
// Block* body = def->block();
// Native_Function func = def->native_function();
- Sass_C_Function c_func = def->c_function();
+ Sass_Function_Entry c_function = def->c_function();
+ Sass_Function_Fn c_func = sass_function_get_function(c_function);
To_C to_c;
union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);
sass_list_set_value(c_args, 0, message->perform(&to_c));
- Sass_Value* c_val = c_func(c_args, def->cookie());
+ Sass_Value* c_val = c_func(c_args, c_function, ctx.c_options);
sass_delete_value(c_args);
sass_delete_value(c_val);
return 0;
}
@@ -241,27 +323,25 @@
if (env->has("@error[f]")) {
Definition* def = static_cast<Definition*>((*env)["@error[f]"]);
// Block* body = def->block();
// Native_Function func = def->native_function();
- Sass_C_Function c_func = def->c_function();
+ Sass_Function_Entry c_function = def->c_function();
+ Sass_Function_Fn c_func = sass_function_get_function(c_function);
To_C to_c;
union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);
sass_list_set_value(c_args, 0, message->perform(&to_c));
- Sass_Value* c_val = c_func(c_args, def->cookie());
+ Sass_Value* c_val = c_func(c_args, c_function, ctx.c_options);
sass_delete_value(c_args);
sass_delete_value(c_val);
return 0;
}
string result(unquote(message->perform(&to_string)));
- Backtrace top(backtrace, e->pstate(), "");
- cerr << "Error: " << result;
- cerr << top.to_string(true);
- cerr << endl << endl;
+ error(result, e->pstate());
return 0;
}
Expression* Eval::operator()(Debug* d)
{
@@ -272,16 +352,17 @@
if (env->has("@debug[f]")) {
Definition* def = static_cast<Definition*>((*env)["@debug[f]"]);
// Block* body = def->block();
// Native_Function func = def->native_function();
- Sass_C_Function c_func = def->c_function();
+ Sass_Function_Entry c_function = def->c_function();
+ Sass_Function_Fn c_func = sass_function_get_function(c_function);
To_C to_c;
union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);
sass_list_set_value(c_args, 0, message->perform(&to_c));
- Sass_Value* c_val = c_func(c_args, def->cookie());
+ Sass_Value* c_val = c_func(c_args, c_function, ctx.c_options);
sass_delete_value(c_args);
sass_delete_value(c_val);
return 0;
}
@@ -429,11 +510,12 @@
return u;
}
Expression* Eval::operator()(Function_Call* c)
{
- string full_name(c->name() + "[f]");
+ string name(Util::normalize_underscores(c->name()));
+ string full_name(name + "[f]");
Arguments* args = c->arguments();
if (full_name != "if[f]") {
args = static_cast<Arguments*>(args->perform(this));
}
@@ -456,11 +538,11 @@
Expression* result = c;
Definition* def = static_cast<Definition*>((*env)[full_name]);
Block* body = def->block();
Native_Function func = def->native_function();
- Sass_C_Function c_func = def->c_function();
+ Sass_Function_Entry c_function = def->c_function();
if (full_name != "if[f]") {
for (size_t i = 0, L = args->length(); i < L; ++i) {
(*args)[i]->value((*args)[i]->value()->perform(this));
}
@@ -507,12 +589,13 @@
backtrace = here.parent;
env = old_env;
}
// else if it's a user-defined c function
- else if (c_func) {
+ else if (c_function) {
+ Sass_Function_Fn c_func = sass_function_get_function(c_function);
if (full_name == "*[f]") {
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;
@@ -526,18 +609,18 @@
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);
+ union Sass_Value* c_args = sass_make_list(env->local_frame().size(), SASS_COMMA);
for(size_t i = 0; i < params[0].length(); i++) {
string key = params[0][i]->name();
- AST_Node* node = env->current_frame().at(key);
+ AST_Node* node = env->local_frame().at(key);
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());
+ Sass_Value* c_val = c_func(c_args, c_function, ctx.c_options);
if (sass_value_get_tag(c_val) == SASS_ERROR) {
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->pstate(), backtrace);
}
@@ -669,11 +752,11 @@
zero);
break;
case Textual::DIMENSION:
result = new (ctx.mem) Number(t->pstate(),
sass_atof(num.c_str()),
- Token(number(text.c_str()), t->pstate()),
+ Token(number(text.c_str())),
zero);
break;
case Textual::HEX: {
string hext(t->value().substr(1)); // chop off the '#'
if (hext.length() == 6) {
@@ -725,25 +808,19 @@
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);
@@ -751,47 +828,45 @@
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);
+ Expression* ex = var->perform(this);
return evacuate_quotes(interpolation(ex));
-
} else if (Function_Call* var = dynamic_cast<Function_Call*>(s)) {
-
- Expression* ex = operator()(var);
+ Expression* ex = var->perform(this);
return evacuate_quotes(interpolation(ex));
-
+ } else if (Parent_Selector* var = dynamic_cast<Parent_Selector*>(s)) {
+ Expression* ex = var->perform(this);
+ return evacuate_quotes(interpolation(ex));
+ } else if (Unary_Expression* var = dynamic_cast<Unary_Expression*>(s)) {
+ Expression* ex = var->perform(this);
+ return evacuate_quotes(interpolation(ex));
+ } else if (Selector* var = dynamic_cast<Selector*>(s)) {
+ Expression* ex = var->perform(this);
+ 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)
{
@@ -934,9 +1009,18 @@
}
Expression* Eval::operator()(Comment* c)
{
return 0;
+ }
+
+ Expression* Eval::operator()(Parent_Selector* p)
+ {
+ Selector* s = p->perform(contextualize);
+ // access to parent selector may return 0
+ Selector_List* l = static_cast<Selector_List*>(s);
+ if (!s) { l = new (ctx.mem) Selector_List(p->pstate()); }
+ return l->perform(listize);
}
inline Expression* Eval::fallback_impl(AST_Node* n)
{
return static_cast<Expression*>(n);