Commit 0259be64 by Aaron Leung

Trying a new expansion/evaluation process. Will hopefully fix some more little bugs.

parent f7c3321b
......@@ -598,7 +598,7 @@ namespace Sass {
semicolon = true;
}
else if (lex< extend >()) {
if (surrounding_ruleset.is_null_ptr()) throw_syntax_error("@extend directive may only be used within rules");
if (surrounding_ruleset.is_null()) throw_syntax_error("@extend directive may only be used within rules");
Node extendee(parse_simple_selector_sequence());
context.extensions.insert(pair<Node, Node>(extendee, surrounding_ruleset));
context.has_extensions = true;
......
......@@ -20,43 +20,62 @@ namespace Sass {
throw Error(Error::evaluation, path, line, message);
}
// Evaluate the parse tree in-place (mostly). Most nodes will be left alone.
Node eval(Node expr, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, bool function_name)
// Expansion function for nodes in an expansion context.
void expand(Node expr, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, bool function_name)
{
switch (expr.type())
{
case Node::mixin: {
case Node::root: {
for (size_t i = 0, S = expr.size(); i < S; ++i) {
expand(expr[i], prefix, env, f_env, new_Node, ctx);
}
} break;
case Node::mixin: { // mixin definition
env[expr[0].token()] = expr;
return expr;
} break;
case Node::function: {
case Node::function: { // function definition
f_env[expr[0].to_string()] = Function(expr);
return expr;
} break;
case Node::expansion: {
case Node::expansion: { // mixin invocation
Token name(expr[0].token());
Node args(expr[1]);
if (!env.query(name)) throw_eval_error("mixin " + name.to_string() + " is undefined", expr.path(), expr.line());
Node mixin(env[name]);
Node expansion(apply_mixin(mixin, args, prefix, env, f_env, new_Node, ctx));
expr.pop_back();
expr.pop_back();
expr += expansion;
return expr;
expr.pop_back(); // pop the mixin name
expr.pop_back(); // pop the mixin args
expr += expansion; // push the expansion
} break;
case Node::propset: {
eval(expr[1], prefix, env, f_env, new_Node, ctx);
return expr;
// TO DO: perform the property expansion here, rather than in the emitter
expand(expr[1], prefix, env, f_env, new_Node, ctx);
} break;
case Node::ruleset: {
// if the selector contains interpolants, eval it and re-parse
if (expr[0].type() == Node::selector_schema) {
expr[0] = eval(expr[0], prefix, env, f_env, new_Node, ctx);
// expr[0] = eval(expr[0], prefix, env, f_env, new_Node, ctx);
Node schema(expr[0]);
string expansion;
for (size_t i = 0, S = schema.size(); i < S; ++i) {
schema[i] = eval(schema[i], prefix, env, f_env, new_Node, ctx);
if (schema[i].type() == Node::string_constant) {
expansion += schema[i].token().unquote();
}
else {
expansion += schema[i].to_string();
}
}
expansion += " {"; // the parser looks for an lbrace to end a selector
char* expn_src = new char[expansion.size() + 1];
strcpy(expn_src, expansion.c_str());
Document needs_reparsing(Document::make_from_source_chars(ctx, expn_src, schema.path(), true));
needs_reparsing.line = schema.line(); // set the line number to the original node's line
expr[0] = needs_reparsing.parse_selector_group();
}
// expand the selector with the prefix and save it in expr[2]
......@@ -87,67 +106,39 @@ namespace Sass {
}
}
// eval the body with the current selector as the prefix
eval(expr[1], expr.back(), env, f_env, new_Node, ctx);
return expr;
// expand the body with the current selector as the prefix
expand(expr[1], expr.back(), env, f_env, new_Node, ctx);
} break;
case Node::media_query: {
Node block(expr[1]);
Node new_ruleset(new_Node(Node::ruleset, expr.path(), expr.line(), 3));
new_ruleset << prefix << block << prefix;
expr[1] = eval(new_ruleset, new_Node(Node::none, expr.path(), expr.line(), 0), env, f_env, new_Node, ctx);
return expr;
expr[1] = new_ruleset << prefix << block << prefix;
expand(expr[1], new_Node(Node::none, expr.path(), expr.line(), 0), env, f_env, new_Node, ctx);
} break;
case Node::selector_schema: {
string expansion;
for (size_t i = 0, S = expr.size(); i < S; ++i) {
expr[i] = eval(expr[i], prefix, env, f_env, new_Node, ctx);
if (expr[i].type() == Node::string_constant) {
expansion += expr[i].token().unquote();
}
else {
expansion += expr[i].to_string();
}
}
expansion += " {"; // the parser looks for an lbrace to end a selector
char* expn_src = new char[expansion.size() + 1];
strcpy(expn_src, expansion.c_str());
Document needs_reparsing(Document::make_from_source_chars(ctx, expn_src, expr.path(), true));
needs_reparsing.line = expr.line(); // set the line number to the original node's line
Node sel(needs_reparsing.parse_selector_group());
return sel;
} break;
case Node::root: {
for (size_t i = 0, S = expr.size(); i < S; ++i) {
expr[i] = eval(expr[i], prefix, env, f_env, new_Node, ctx);
}
return expr;
} break;
case Node::block: {
Environment new_frame;
new_frame.link(env);
for (size_t i = 0, S = expr.size(); i < S; ++i) {
expr[i] = eval(expr[i], prefix, new_frame, f_env, new_Node, ctx);
expand(expr[i], prefix, new_frame, f_env, new_Node, ctx);
}
return expr;
} break;
case Node::assignment: {
Node val(expr[1]);
if (val.type() == Node::list) {
for (size_t i = 0, S = val.size(); i < S; ++i) {
if (val[i].should_eval()) val[i] = eval(val[i], prefix, env, f_env, new_Node, ctx);
}
}
else {
val = eval(val, prefix, env, f_env, new_Node, ctx);
}
Node var(expr[0]);
if (expr.is_guarded() && env.query(var.token())) return expr;
if (expr.is_guarded() && env.query(var.token())) return;
Node val(expr[1]);
val = eval(val, prefix, env, f_env, new_Node, ctx);
// if (val.type() == Node::list) {
// for (size_t i = 0, S = val.size(); i < S; ++i) {
// if (val[i].should_eval()) val[i] = eval(val[i], prefix, env, f_env, new_Node, ctx);
// }
// }
// else {
// val = eval(val, prefix, env, f_env, new_Node, ctx);
// }
// If a binding exists (possible upframe), then update it.
// Otherwise, make a new on in the current frame.
if (env.query(var.token())) {
......@@ -156,49 +147,185 @@ namespace Sass {
else {
env.current_frame[var.token()] = val;
}
return expr;
} break;
case Node::rule: {
Node lhs(expr[0]);
if (lhs.should_eval()) eval(lhs, prefix, env, f_env, new_Node, ctx);
if (lhs.is_schema()) {
expr[0] = eval(lhs, prefix, env, f_env, new_Node, ctx);
}
Node rhs(expr[1]);
if (rhs.type() == Node::list) {
for (size_t i = 0, S = rhs.size(); i < S; ++i) {
if (rhs[i].should_eval()) rhs[i] = eval(rhs[i], prefix, env, f_env, new_Node, ctx);
if (rhs.is_schema() || rhs.should_eval()) {
expr[1] = eval(rhs, prefix, env, f_env, new_Node, ctx);
}
// if (rhs.type() == Node::list) {
// for (size_t i = 0, S = rhs.size(); i < S; ++i) {
// if (rhs[i].should_eval()) rhs[i] = eval(rhs[i], prefix, env, f_env, new_Node, ctx);
// }
// }
// else if (rhs.type() == Node::value_schema || rhs.type() == Node::string_schema) {
// eval(rhs, prefix, env, f_env, new_Node, ctx);
// }
// else {
// if (rhs.should_eval()) expr[1] = eval(rhs, prefix, env, f_env, new_Node, ctx);
// }
} break;
case Node::if_directive: {
Node expansion = Node();
for (size_t i = 0, S = expr.size(); i < S; i += 2) {
if (expr[i].type() != Node::block) {
Node predicate_val(eval(expr[i], prefix, env, f_env, new_Node, ctx));
if ((predicate_val.type() != Node::boolean) || predicate_val.boolean_value()) {
expand(expansion = expr[i+1], prefix, env, f_env, new_Node, ctx);
break;
}
}
else {
expand(expansion = expr[i], prefix, env, f_env, new_Node, ctx);
break;
}
}
else if (rhs.type() == Node::value_schema || rhs.type() == Node::string_schema) {
eval(rhs, prefix, env, f_env, new_Node, ctx);
expr.pop_all();
if (!expansion.is_null()) expr += expansion;
} break;
case Node::for_through_directive:
case Node::for_to_directive: {
Node fake_mixin(new_Node(Node::mixin, expr.path(), expr.line(), 3));
Node fake_param(new_Node(Node::parameters, expr.path(), expr.line(), 1));
fake_mixin << new_Node(Node::identifier, "", 0, Token::make(for_kwd)) // stub name for debugging
<< (fake_param << expr[0]) // iteration variable
<< expr[3]; // body
Node lower_bound(eval(expr[1], prefix, env, f_env, new_Node, ctx));
Node upper_bound(eval(expr[2], prefix, env, f_env, new_Node, ctx));
if (!(lower_bound.is_numeric() && upper_bound.is_numeric())) {
throw_eval_error("bounds of @for directive must be numeric", expr.path(), expr.line());
}
else {
if (rhs.should_eval()) expr[1] = eval(rhs, prefix, env, f_env, new_Node, ctx);
expr.pop_all();
for (double i = lower_bound.numeric_value(),
U = upper_bound.numeric_value() + ((expr.type() == Node::for_to_directive) ? 0 : 1);
i < U;
++i) {
Node i_node(new_Node(expr.path(), expr.line(), i));
Node fake_arg(new_Node(Node::arguments, expr.path(), expr.line(), 1));
fake_arg << i_node;
expr += apply_mixin(fake_mixin, fake_arg, prefix, env, f_env, new_Node, ctx, true);
}
} break;
case Node::each_directive: {
Node fake_mixin(new_Node(Node::mixin, expr.path(), expr.line(), 3));
Node fake_param(new_Node(Node::parameters, expr.path(), expr.line(), 1));
fake_mixin << new_Node(Node::identifier, "", 0, Token::make(each_kwd)) // stub name for debugging
<< (fake_param << expr[0]) // iteration variable
<< expr[2]; // body
Node list(eval(expr[1], prefix, env, f_env, new_Node, ctx));
// If the list isn't really a list, make a singleton out of it.
if (list.type() != Node::list) {
list = (new_Node(Node::list, list.path(), list.line(), 1) << list);
}
expr.pop_all();
for (size_t i = 0, S = list.size(); i < S; ++i) {
Node fake_arg(new_Node(Node::arguments, expr.path(), expr.line(), 1));
fake_arg << eval(list[i], prefix, env, f_env, new_Node, ctx);
expr += apply_mixin(fake_mixin, fake_arg, prefix, env, f_env, new_Node, ctx, true);
}
} break;
case Node::while_directive: {
Node fake_mixin(new_Node(Node::mixin, expr.path(), expr.line(), 3));
Node fake_param(new_Node(Node::parameters, expr.path(), expr.line(), 0));
Node fake_arg(new_Node(Node::arguments, expr.path(), expr.line(), 0));
fake_mixin << new_Node(Node::identifier, "", 0, Token::make(while_kwd)) // stub name for debugging
<< fake_param // no iteration variable
<< expr[1]; // body
Node pred(expr[0]);
expr.pop_back();
expr.pop_back();
Node ev_pred(eval(pred, prefix, env, f_env, new_Node, ctx));
while ((ev_pred.type() != Node::boolean) || ev_pred.boolean_value()) {
expr += apply_mixin(fake_mixin, fake_arg, prefix, env, f_env, new_Node, ctx, true);
ev_pred = eval(pred, prefix, env, f_env, new_Node, ctx);
}
} break;
case Node::block_directive: {
// TO DO: eval the directive name for interpolants
expand(expr[1], new_Node(Node::none, expr.path(), expr.line(), 0), env, f_env, new_Node, ctx);
} break;
case Node::warning: {
// expr = new_Node(expr);
Node contents(eval(expr[0], Node(), env, f_env, new_Node, ctx));
string prefix("WARNING: ");
string indent(" ");
string result(contents.to_string());
if (contents.type() == Node::string_constant || contents.type() == Node::string_schema) {
result = result.substr(1, result.size()-2); // unquote if it's a single string
}
return expr;
// These cerrs aren't log lines! They're supposed to be here!
cerr << prefix << result << endl;
cerr << indent << "on line " << expr.line() << " of " << expr.path();
cerr << endl << endl;
} break;
default: {
// do nothing
} break;
}
}
// Evaluation function for nodes in a value context.
Node eval(Node expr, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, bool function_name)
{
Node result;
switch (expr.type())
{
// case Node::selector_schema: {
// string expansion;
// for (size_t i = 0, S = expr.size(); i < S; ++i) {
// expr[i] = eval(expr[i], prefix, env, f_env, new_Node, ctx);
// if (expr[i].type() == Node::string_constant) {
// expansion += expr[i].token().unquote();
// }
// else {
// expansion += expr[i].to_string();
// }
// }
// expansion += " {"; // the parser looks for an lbrace to end a selector
// char* expn_src = new char[expansion.size() + 1];
// strcpy(expn_src, expansion.c_str());
// Document needs_reparsing(Document::make_from_source_chars(ctx, expn_src, expr.path(), true));
// needs_reparsing.line = expr.line(); // set the line number to the original node's line
// Node sel(needs_reparsing.parse_selector_group());
// return sel;
// } break;
case Node::list: {
if (expr.should_eval()) expr[0] = eval(expr[0], prefix, env, f_env, new_Node, ctx);
return expr;
if (expr.should_eval()) {
result = new_Node(expr);
result[0] = eval(expr[0], prefix, env, f_env, new_Node, ctx);
}
} break;
case Node::disjunction: {
Node result;
for (size_t i = 0, S = expr.size(); i < S; ++i) {
result = eval(expr[i], prefix, env, f_env, new_Node, ctx);
if (result.type() == Node::boolean && result.boolean_value() == false) continue;
else return result;
if (result.is_false()) continue;
else break;
}
return result;
} break;
case Node::conjunction: {
Node result;
for (size_t i = 0, S = expr.size(); i < S; ++i) {
result = eval(expr[i], prefix, env, f_env, new_Node, ctx);
if (result.type() == Node::boolean && result.boolean_value() == false) return result;
if (result.is_false()) break;
}
return result;
} break;
case Node::relation: {
......@@ -206,106 +333,97 @@ namespace Sass {
Node lhs(eval(expr[0], prefix, env, f_env, new_Node, ctx));
Node op(expr[1]);
Node rhs(eval(expr[2], prefix, env, f_env, new_Node, ctx));
// TO DO: don't allocate both T and F
Node T(new_Node(Node::boolean, lhs.path(), lhs.line(), true));
Node F(new_Node(Node::boolean, lhs.path(), lhs.line(), false));
switch (op.type())
{
case Node::eq: return (lhs == rhs) ? T : F;
case Node::neq: return (lhs != rhs) ? T : F;
case Node::gt: return (lhs > rhs) ? T : F;
case Node::gte: return (lhs >= rhs) ? T : F;
case Node::lt: return (lhs < rhs) ? T : F;
case Node::lte: return (lhs <= rhs) ? T : F;
case Node::eq: result = ((lhs == rhs) ? T : F);
case Node::neq: result = ((lhs != rhs) ? T : F);
case Node::gt: result = ((lhs > rhs) ? T : F);
case Node::gte: result = ((lhs >= rhs) ? T : F);
case Node::lt: result = ((lhs < rhs) ? T : F);
case Node::lte: result = ((lhs <= rhs) ? T : F);
default:
throw_eval_error("unknown comparison operator " + expr.token().to_string(), expr.path(), expr.line());
return Node();
}
} break;
case Node::expression: {
Node list(new_Node(Node::expression, expr.path(), expr.line(), expr.size()));
for (size_t i = 0, S = expr.size(); i < S; ++i) {
expr[i] = eval(expr[i], prefix, env, f_env, new_Node, ctx);
list << eval(expr[i], prefix, env, f_env, new_Node, ctx);
}
return reduce(expr, 1, expr[0], new_Node);
result = reduce(list, 1, list[0], new_Node);
} break;
case Node::term: {
if (expr.should_eval()) {
Node list(new_Node(Node::term, expr.path(), expr.line(), expr.size()));
for (size_t i = 0, S = expr.size(); i < S; ++i) {
expr[i] = eval(expr[i], prefix, env, f_env, new_Node, ctx);
list << eval(expr[i], prefix, env, f_env, new_Node, ctx);
}
return reduce(expr, 1, expr[0], new_Node);
}
else {
return expr;
result = reduce(list, 1, list[0], new_Node);
}
} break;
case Node::textual_percentage: {
return new_Node(expr.path(), expr.line(), std::atof(expr.token().begin), Node::numeric_percentage);
} break;
// case Node::textual_percentage: {
// result = new_Node(expr.path(), expr.line(), std::atof(expr.token().begin), Node::numeric_percentage);
// } break;
case Node::textual_dimension: {
return new_Node(expr.path(), expr.line(),
std::atof(expr.token().begin),
Token::make(Prelexer::number(expr.token().begin),
expr.token().end));
} break;
// case Node::textual_dimension: {
// result = new_Node(expr.path(), expr.line(),
// std::atof(expr.token().begin),
// Token::make(Prelexer::number(expr.token().begin),
// expr.token().end));
// } break;
case Node::textual_number: {
return new_Node(expr.path(), expr.line(), std::atof(expr.token().begin));
} break;
case Node::textual_hex: {
Node triple(new_Node(Node::numeric_color, expr.path(), expr.line(), 4));
Token hext(Token::make(expr.token().begin+1, expr.token().end));
if (hext.length() == 6) {
for (int i = 0; i < 6; i += 2) {
triple << new_Node(expr.path(), expr.line(), static_cast<double>(std::strtol(string(hext.begin+i, 2).c_str(), NULL, 16)));
}
}
else {
for (int i = 0; i < 3; ++i) {
triple << new_Node(expr.path(), expr.line(), static_cast<double>(std::strtol(string(2, hext.begin[i]).c_str(), NULL, 16)));
}
}
triple << new_Node(expr.path(), expr.line(), 1.0);
return triple;
} break;
// case Node::textual_number: {
// result = new_Node(expr.path(), expr.line(), std::atof(expr.token().begin));
// } break;
// case Node::textual_hex: {
// result = new_Node(Node::numeric_color, expr.path(), expr.line(), 4));
// Token hext(Token::make(expr.token().begin+1, expr.token().end));
// if (hext.length() == 6) {
// for (int i = 0; i < 6; i += 2) {
// result << new_Node(expr.path(), expr.line(), static_cast<double>(std::strtol(string(hext.begin+i, 2).c_str(), NULL, 16)));
// }
// }
// else {
// for (int i = 0; i < 3; ++i) {
// result << new_Node(expr.path(), expr.line(), static_cast<double>(std::strtol(string(2, hext.begin[i]).c_str(), NULL, 16)));
// }
// }
// result << new_Node(expr.path(), expr.line(), 1.0);
// } break;
case Node::variable: {
if (!env.query(expr.token())) throw_eval_error("reference to unbound variable " + expr.token().to_string(), expr.path(), expr.line());
// cerr << "ACCESSING VARIABLE " << expr.token().to_string() << endl;
// cerr << endl << "*** ENV DUMP ***" << endl;
// env.print();
// cerr << "*** END ENV ***" << endl << endl;
return env[expr.token()];
result = env[expr.token()];
} break;
case Node::uri: {
expr[0] = eval(expr[0], prefix, env, f_env, new_Node, ctx);
return expr;
result = new_Node(Node::uri, expr.path(), expr.line(), 1);
result << eval(expr[0], prefix, env, f_env, new_Node, ctx);
} break;
case Node::function_call: {
// TO DO: default-constructed Function should be a generic callback (maybe)
// eval the function name in case it's interpolated
expr[0] = eval(expr[0], prefix, env, f_env, new_Node, ctx, true);
string name(expr[0].to_string());
Node name_node(eval(expr[0], prefix, env, f_env, new_Node, ctx, true));
string name(name_node.to_string());
if (!f_env.count(name)) {
// no definition available; just pass it through (with evaluated args)
Node args(expr[1]);
Node evaluated_args(new_Node(Node::arguments, args.path(), args.line(), args.size()));
for (size_t i = 0, S = args.size(); i < S; ++i) {
args[i] = eval(args[i], prefix, env, f_env, new_Node, ctx);
evaluated_args << eval(args[i], prefix, env, f_env, new_Node, ctx);
}
return expr;
result = new_Node(Node::function_call, expr.path(), expr.line(), 2);
result << name_node << evaluated_args;
}
else {
// check to see if the function is primitive/built-in
......@@ -317,29 +435,29 @@ namespace Sass {
if (!f_env.count(resolved_name)) throw_eval_error("wrong number of arguments to " + name, expr.path(), expr.line());
f = f_env[resolved_name];
}
return apply_function(f, expr[1], prefix, env, f_env, new_Node, ctx, expr.path(), expr.line());
result = apply_function(f, expr[1], prefix, env, f_env, new_Node, ctx, expr.path(), expr.line());
}
} break;
case Node::unary_plus: {
Node arg(eval(expr[0], prefix, env, f_env, new_Node, ctx));
if (arg.is_numeric()) {
return arg;
result = arg;
}
else {
expr[0] = arg;
return expr;
result = new_Node(Node::unary_plus, expr.path(), expr.line(), 1);
result << arg;
}
} break;
case Node::unary_minus: {
Node arg(eval(expr[0], prefix, env, f_env, new_Node, ctx));
if (arg.is_numeric()) {
return new_Node(expr.path(), expr.line(), -arg.numeric_value());
result = new_Node(expr.path(), expr.line(), -arg.numeric_value());
}
else {
expr[0] = arg;
return expr;
result = new_Node(Node::unary_minus, expr.path(), expr.line(), 1);
result << arg;
}
} break;
......@@ -352,138 +470,33 @@ namespace Sass {
Node g(color_orig[1]);
Node b(color_orig[2]);
Node a(color_orig[3]);
return new_Node(expr.path(), expr.line(),
r.numeric_value(),
g.numeric_value(),
b.numeric_value(),
a.numeric_value());
}
else {
return expr;
result = new_Node(expr.path(), expr.line(),
r.numeric_value(),
g.numeric_value(),
b.numeric_value(),
a.numeric_value());
}
} break;
case Node::string_schema:
case Node::value_schema:
case Node::identifier_schema: {
result = new_Node(expr.type(), expr.path(), expr.line(), expr.size());
for (size_t i = 0, S = expr.size(); i < S; ++i) {
expr[i] = eval(expr[i], prefix, env, f_env, new_Node, ctx);
result << eval(expr[i], prefix, env, f_env, new_Node, ctx);
}
return expr;
} break;
case Node::css_import: {
expr[0] = eval(expr[0], prefix, env, f_env, new_Node, ctx);
return expr;
} break;
case Node::if_directive: {
for (size_t i = 0, S = expr.size(); i < S; i += 2) {
if (expr[i].type() != Node::block) {
// cerr << "EVALUATING PREDICATE " << (i/2+1) << endl;
Node predicate_val(eval(expr[i], prefix, env, f_env, new_Node, ctx));
if ((predicate_val.type() != Node::boolean) || predicate_val.boolean_value()) {
// cerr << "EVALUATING CONSEQUENT " << (i/2+1) << endl;
return eval(expr[i+1], prefix, env, f_env, new_Node, ctx);
}
}
else {
// cerr << "EVALUATING ALTERNATIVE" << endl;
return eval(expr[i], prefix, env, f_env, new_Node, ctx);
}
}
} break;
case Node::for_through_directive:
case Node::for_to_directive: {
Node fake_mixin(new_Node(Node::mixin, expr.path(), expr.line(), 3));
Node fake_param(new_Node(Node::parameters, expr.path(), expr.line(), 1));
fake_mixin << new_Node(Node::identifier, "", 0, Token::make(for_kwd)) << (fake_param << expr[0]) << expr[3];
Node lower_bound(eval(expr[1], prefix, env, f_env, new_Node, ctx));
Node upper_bound(eval(expr[2], prefix, env, f_env, new_Node, ctx));
if (!(lower_bound.is_numeric() && upper_bound.is_numeric())) {
throw_eval_error("bounds of @for directive must be numeric", expr.path(), expr.line());
}
expr.pop_back();
expr.pop_back();
expr.pop_back();
expr.pop_back();
for (double i = lower_bound.numeric_value(),
U = upper_bound.numeric_value() + ((expr.type() == Node::for_to_directive) ? 0 : 1);
i < U;
++i) {
Node i_node(new_Node(expr.path(), expr.line(), i));
Node fake_arg(new_Node(Node::arguments, expr.path(), expr.line(), 1));
fake_arg << i_node;
expr += apply_mixin(fake_mixin, fake_arg, prefix, env, f_env, new_Node, ctx, true);
}
} break;
case Node::each_directive: {
Node fake_mixin(new_Node(Node::mixin, expr.path(), expr.line(), 3));
Node fake_param(new_Node(Node::parameters, expr.path(), expr.line(), 1));
fake_mixin << new_Node(Node::identifier, "", 0, Token::make(each_kwd)) << (fake_param << expr[0]) << expr[2];
Node list(eval(expr[1], prefix, env, f_env, new_Node, ctx));
// If the list isn't really a list, make a singleton out of it.
if (list.type() != Node::list) {
list = (new_Node(Node::list, list.path(), list.line(), 1) << list);
}
expr.pop_back();
expr.pop_back();
expr.pop_back();
for (size_t i = 0, S = list.size(); i < S; ++i) {
Node fake_arg(new_Node(Node::arguments, expr.path(), expr.line(), 1));
fake_arg << eval(list[i], prefix, env, f_env, new_Node, ctx);
expr += apply_mixin(fake_mixin, fake_arg, prefix, env, f_env, new_Node, ctx, true);
}
} break;
case Node::while_directive: {
Node fake_mixin(new_Node(Node::mixin, expr.path(), expr.line(), 3));
Node fake_param(new_Node(Node::parameters, expr.path(), expr.line(), 0));
Node fake_arg(new_Node(Node::arguments, expr.path(), expr.line(), 0));
fake_mixin << new_Node(Node::identifier, "", 0, Token::make(while_kwd)) << fake_param << expr[1];
Node pred(expr[0]);
expr.pop_back();
expr.pop_back();
Node ev_pred(eval(pred, prefix, env, f_env, new_Node, ctx));
while ((ev_pred.type() != Node::boolean) || ev_pred.boolean_value()) {
expr += apply_mixin(fake_mixin, fake_arg, prefix, env, f_env, new_Node, ctx, true);
ev_pred = eval(pred, prefix, env, f_env, new_Node, ctx);
}
} break;
case Node::block_directive: {
// TO DO: eval the directive name for interpolants
eval(expr[1], new_Node(Node::none, expr.path(), expr.line(), 0), env, f_env, new_Node, ctx);
return expr;
} break;
case Node::warning: {
expr = new_Node(expr);
expr[0] = eval(expr[0], Node(), env, f_env, new_Node, ctx);
string prefix("WARNING: ");
string indent(" ");
Node contents(expr[0]);
string result(contents.to_string());
if (contents.type() == Node::string_constant || contents.type() == Node::string_schema) {
result = result.substr(1, result.size()-2); // unquote if it's a single string
}
// These cerrs aren't log lines! They're supposed to be here!
cerr << prefix << result << endl;
cerr << indent << "on line " << expr.line() << " of " << expr.path();
cerr << endl << endl;
return expr;
result = new_Node(Node::css_import, expr.path(), expr.line(), 1);
result << eval(expr[0], prefix, env, f_env, new_Node, ctx);
} break;
default: {
return expr;
result = expr;
} break;
}
return expr;
return result;
}
// Reduce arithmetic operations. Arithmetic expressions are stored as vectors
......@@ -649,7 +662,7 @@ namespace Sass {
if (!env.query(arg_name)) {
throw_eval_error(callee_name + " has no parameter named " + arg_name.to_string(), arg.path(), arg.line());
}
if (!env[arg_name].is_none()) {
if (!env[arg_name].is_null()) {
throw_eval_error(callee_name + " was passed argument " + arg_name.to_string() + " both by position and by name", arg.path(), arg.line());
}
env[arg_name] = arg_value;
......@@ -660,7 +673,7 @@ namespace Sass {
for (size_t i = 0, S = params.size(); i < S; ++i) {
Node param(params[i]);
Token param_name((param.type() == Node::assignment ? param[0] : param).token());
if (env[param_name].is_none()) {
if (env[param_name].is_null()) {
if (param.type() != Node::assignment) {
throw_eval_error(callee_name + " is missing argument " + param_name.to_string(), args.path(), args.line());
}
......@@ -712,7 +725,7 @@ namespace Sass {
// bind arguments in the extended environment
stringstream mixin_name;
mixin_name << "mixin";
if (!mixin[0].is_none()) mixin_name << " " << mixin[0].to_string();
if (!mixin[0].is_null()) mixin_name << " " << mixin[0].to_string();
bind_arguments(mixin_name.str(), params, args, prefix, bindings, f_env, new_Node, ctx);
// evaluate the mixin's body
for (size_t i = 0, S = body.size(); i < S; ++i) {
......@@ -807,13 +820,13 @@ namespace Sass {
Node predicate_val(eval(pred, Node(), bindings, ctx.function_env, new_Node, ctx));
if ((predicate_val.type() != Node::boolean) || predicate_val.boolean_value()) {
Node v(function_eval(name, stm[j+1], bindings, new_Node, ctx));
if (v.is_null_ptr()) break;
if (v.is_null()) break;
else return v;
}
}
else {
Node v(function_eval(name, stm[j], bindings, new_Node, ctx));
if (v.is_null_ptr()) break;
if (v.is_null()) break;
else return v;
}
}
......@@ -833,7 +846,7 @@ namespace Sass {
j += 1) {
for_env.current_frame[iter_var.token()] = new_Node(lower_bound.path(), lower_bound.line(), j);
Node v(function_eval(name, for_body, for_env, new_Node, ctx));
if (v.is_null_ptr()) continue;
if (v.is_null()) continue;
else return v;
}
} break;
......@@ -854,7 +867,7 @@ namespace Sass {
// cerr << endl << "*** ENV DUMP ***" << endl;
// each_env.print();
// cerr << "*** END ENV ***" << endl << endl;
if (v.is_null_ptr()) continue;
if (v.is_null()) continue;
else return v;
}
} break;
......@@ -867,7 +880,7 @@ namespace Sass {
Node pred_val(eval(pred_expr, Node(), bindings, ctx.function_env, new_Node, ctx));
while ((pred_val.type() != Node::boolean) || pred_val.boolean_value()) {
Node v(function_eval(name, while_body, while_env, new_Node, ctx));
if (v.is_null_ptr()) {
if (v.is_null()) {
pred_val = eval(new_Node(stm[0]), Node(), bindings, ctx.function_env, new_Node, ctx);
continue;
}
......
......@@ -12,7 +12,8 @@
namespace Sass {
using std::map;
void expand(Node expr, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, bool function_name = false);
Node eval(Node expr, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, bool function_name = false);
Node function_eval(string name, Node stm, Environment& bindings, Node_Factory& new_Node, Context& ctx, bool toplevel = false);
Node reduce(Node list, size_t head, Node acc, Node_Factory& new_Node);
......
......@@ -20,6 +20,7 @@ namespace Sass {
case block:
case expansion:
case root:
case if_directive:
case for_through_directive:
case for_to_directive:
case each_directive:
......@@ -35,6 +36,7 @@ namespace Sass {
{
case expansion:
case block:
case if_directive:
case for_through_directive:
case for_to_directive:
case each_directive:
......
......@@ -177,8 +177,8 @@ namespace Sass {
Node(Node_Impl* ip = 0);
Type type() const;
Type type(Type);
bool is_none() const;
bool has_children() const;
bool has_statements() const;
bool has_blocks() const;
......@@ -204,6 +204,7 @@ namespace Sass {
Node& back() const;
Node& operator[](size_t i) const;
void pop_back();
void pop_all();
Node& push_back(Node n);
Node& push_front(Node n);
Node& operator<<(Node n);
......@@ -220,7 +221,7 @@ namespace Sass {
Token token() const;
Token unit() const;
bool is_null_ptr() const { return !ip_; }
bool is_null() const { return !ip_; }
bool is(Node n) const { return ip_ == n.ip_; }
void flatten();
......@@ -413,6 +414,9 @@ namespace Sass {
void pop_back()
{ children.pop_back(); }
void pop_all()
{ for (size_t i = 0, S = size(); i < S; ++i) pop_back(); }
bool& boolean_value()
{ return value.boolean; }
......@@ -430,8 +434,8 @@ namespace Sass {
inline Node::Node(Node_Impl* ip) : ip_(ip) { }
inline Node::Type Node::type() const { return ip_->type; }
inline Node::Type Node::type(Type t) { return ip_->type = t; }
inline bool Node::is_none() const { return !ip_; }
inline bool Node::has_children() const { return ip_->has_children; }
inline bool Node::has_statements() const { return ip_->has_statements; }
inline bool Node::has_blocks() const { return ip_->has_blocks; }
......@@ -457,6 +461,7 @@ namespace Sass {
inline Node& Node::back() const { return ip_->back(); }
inline Node& Node::operator[](size_t i) const { return at(i); }
inline void Node::pop_back() { ip_->pop_back(); }
inline void Node::pop_all() { ip_->pop_all(); }
inline Node& Node::push_back(Node n)
{
ip_->push_back(n);
......
......@@ -41,12 +41,12 @@ extern "C" {
{
using namespace Sass;
doc.parse_scss();
eval(doc.root,
doc.context.new_Node(Node::none, doc.path, doc.line, 0),
doc.context.global_env,
doc.context.function_env,
doc.context.new_Node,
doc.context);
expand(doc.root,
doc.context.new_Node(Node::none, doc.path, doc.line, 0),
doc.context.global_env,
doc.context.function_env,
doc.context.new_Node,
doc.context);
extend_selectors(doc.context.pending_extensions, doc.context.extensions, doc.context.new_Node);
string output(doc.emit_css(static_cast<Document::CSS_Style>(style)));
char* c_output = (char*) malloc(output.size() + 1);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment