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);