Commit 6ff683eb by Aaron Leung

Pushing the refactor through the eval/apply functions.

parent bd841192
#include "prelexer.hpp"
#include "eval_apply.hpp" #include "eval_apply.hpp"
#include "error.hpp" #include "error.hpp"
#include <iostream> #include <iostream>
#include <sstream>
#include <cstdlib> #include <cstdlib>
namespace Sass { namespace Sass {
using std::cerr; using std::endl; using std::cerr; using std::endl;
static void throw_eval_error(string message, size_t line, const char* path)
{
string fn;
if (path) {
const char* end = Prelexer::string_constant(path);
if (end) fn = path.substr(1, path.size() - 1);
else fn = path;
}
throw Error(Error::evaluation, line, fn, message);
}
static void throw_eval_error(string message, string path, size_t line) static void throw_eval_error(string message, string path, size_t line)
{ {
...@@ -27,7 +18,7 @@ namespace Sass { ...@@ -27,7 +18,7 @@ namespace Sass {
Node eval(Node& expr, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node) Node eval(Node& expr, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node)
{ {
switch (expr.type) switch (expr.type())
{ {
case Node::mixin: { case Node::mixin: {
env[expr[0].token()] = expr; env[expr[0].token()] = expr;
...@@ -37,46 +28,46 @@ namespace Sass { ...@@ -37,46 +28,46 @@ namespace Sass {
case Node::expansion: { case Node::expansion: {
Token name(expr[0].token()); Token name(expr[0].token());
Node args(expr[1]); Node args(expr[1]);
if (!env.query(name)) throw_eval_error("mixin " + name.to_string() + " is undefined", expr.line(), expr.path()); if (!env.query(name)) throw_eval_error("mixin " + name.to_string() + " is undefined", expr.path(), expr.line());
Node mixin(env[name]); Node mixin(env[name]);
Node expansion(apply_mixin(mixin, args, env, f_env, registry)); Node expansion(apply_mixin(mixin, args, env, f_env, new_Node));
expr.content.children->pop_back(); expr.pop_back();
expr.content.children->pop_back(); expr.pop_back();
expr += expansion; expr += expansion;
return expr; return expr;
} break; } break;
case Node::propset: case Node::propset:
case Node::ruleset: { case Node::ruleset: {
eval(expr[1], env, f_env, registry); eval(expr[1], env, f_env, new_Node);
return expr; return expr;
} break; } break;
case Node::root: { case Node::root: {
for (size_t i = 0; i < expr.size(); ++i) { for (size_t i = 0, S = expr.size(); i < S; ++i) {
eval(expr[i], env, f_env, registry); eval(expr[i], env, f_env, new_Node);
} }
return expr; return expr;
} break; } break;
case Node::block: { case Node::block: {
Environment current; Environment new_frame;
current.link(env); new_frame.link(env);
for (size_t i = 0; i < expr.size(); ++i) { for (size_t i = 0, S = expr.size(); i < S; ++i) {
eval(expr[i], current, f_env, registry); eval(expr[i], new_frame, f_env, new_Node);
} }
return expr; return expr;
} break; } break;
case Node::assignment: { case Node::assignment: {
Node val(expr[1]); Node val(expr[1]);
if (val.type == Node::comma_list || val.type == Node::space_list) { if (val.type() == Node::comma_list || val.type() == Node::space_list) {
for (size_t i = 0; i < val.size(); ++i) { for (size_t i = 0, S = val.size(); i < S; ++i) {
if (val[i].eval_me) val[i] = eval(val[i], env, f_env, registry); if (val[i].should_eval()) val[i] = eval(val[i], env, f_env, new_Node);
} }
} }
else { else {
val = eval(val, env, f_env, registry); val = eval(val, env, f_env, new_Node);
} }
Node var(expr[0]); Node var(expr[0]);
if (env.query(var.token())) { if (env.query(var.token())) {
...@@ -90,33 +81,31 @@ namespace Sass { ...@@ -90,33 +81,31 @@ namespace Sass {
case Node::rule: { case Node::rule: {
Node rhs(expr[1]); Node rhs(expr[1]);
if (rhs.type == Node::comma_list || rhs.type == Node::space_list) { if (rhs.type() == Node::comma_list || rhs.type() == Node::space_list) {
for (size_t i = 0; i < rhs.size(); ++i) { for (size_t i = 0, S = rhs.size(); i < S; ++i) {
if (rhs[i].eval_me) rhs[i] = eval(rhs[i], env, f_env, registry); if (rhs[i].should_eval()) rhs[i] = eval(rhs[i], env, f_env, new_Node);
} }
} }
else if (rhs.type == Node::value_schema || rhs.type == Node::string_schema) { else if (rhs.type() == Node::value_schema || rhs.type() == Node::string_schema) {
eval(rhs, env, f_env, registry); eval(rhs, env, f_env, new_Node);
} }
else { else {
if (rhs.eval_me) expr[1] = eval(rhs, env, f_env, registry); if (rhs.should_eval()) expr[1] = eval(rhs, env, f_env, new_Node);
} }
return expr; return expr;
} break; } break;
case Node::comma_list: case Node::comma_list:
case Node::space_list: { case Node::space_list: {
if (expr.eval_me) expr[0] = eval(expr[0], env, f_env, registry); if (expr.should_eval()) expr[0] = eval(expr[0], env, f_env, new_Node);
return expr; return expr;
} break; } break;
case Node::disjunction: { case Node::disjunction: {
Node result; Node result;
for (size_t i = 0; i < expr.size(); ++i) { for (size_t i = 0, S = expr.size(); i < S; ++i) {
// if (expr[i].type == Node::relation || result = eval(expr[i], env, f_env, new_Node);
// expr[i].type == Node::function_call && expr[0].token().to_string() == "not") { if (result.type() == Node::boolean && result.boolean_value() == false) continue;
result = eval(expr[i], env, f_env, registry);
if (result.type == Node::boolean && result.content.boolean_value == false) continue;
else return result; else return result;
} }
return result; return result;
...@@ -124,26 +113,23 @@ namespace Sass { ...@@ -124,26 +113,23 @@ namespace Sass {
case Node::conjunction: { case Node::conjunction: {
Node result; Node result;
for (size_t i = 0; i < expr.size(); ++i) { for (size_t i = 0, S = expr.size(); i < S; ++i) {
result = eval(expr[i], env, f_env, registry); result = eval(expr[i], env, f_env, new_Node);
if (result.type == Node::boolean && result.content.boolean_value == false) return result; if (result.type() == Node::boolean && result.boolean_value() == false) return result;
} }
return result; return result;
} break; } break;
case Node::relation: { case Node::relation: {
Node lhs(eval(expr[0], env, f_env, registry)); Node lhs(eval(expr[0], env, f_env, new_Node));
Node op(expr[1]); Node op(expr[1]);
Node rhs(eval(expr[2], env, f_env, registry)); Node rhs(eval(expr[2], env, f_env, new_Node));
Node T(Node::boolean); Node T(new_Node(Node::boolean, lhs.path(), lhs.line(), true));
T.line() = lhs.line(); Node F(new_Node(Node::boolean, lhs.path(), lhs.line(), false));
T.content.boolean_value = true;
Node F(T);
F.content.boolean_value = false;
switch (op.type) { switch (op.type()) {
case Node::eq: return (lhs == rhs) ? T : F; case Node::eq: return (lhs == rhs) ? T : F;
case Node::neq: return (lhs != rhs) ? T : F; case Node::neq: return (lhs != rhs) ? T : F;
case Node::gt: return (lhs > rhs) ? T : F; case Node::gt: return (lhs > rhs) ? T : F;
...@@ -151,32 +137,32 @@ namespace Sass { ...@@ -151,32 +137,32 @@ namespace Sass {
case Node::lt: return (lhs < rhs) ? T : F; case Node::lt: return (lhs < rhs) ? T : F;
case Node::lte: return (lhs <= rhs) ? T : F; case Node::lte: return (lhs <= rhs) ? T : F;
default: default:
eval_error("unknown comparison operator " + expr.token().to_string(), expr.line(), expr.path()); throw_eval_error("unknown comparison operator " + expr.token().to_string(), expr.path(), expr.line());
return Node(Node::none); return Node();
} }
} break; } break;
case Node::expression: { case Node::expression: {
Node acc(Node::expression, registry, expr.line(), 1); Node acc(new_Node(Node::expression, expr.path(), expr.line(), 1));
acc << eval(expr[0], env, f_env, registry); acc << eval(expr[0], env, f_env, new_Node);
Node rhs(eval(expr[2], env, f_env, registry)); Node rhs(eval(expr[2], env, f_env, new_Node));
accumulate(expr[1].type, acc, rhs, registry); accumulate(expr[1].type(), acc, rhs, new_Node);
for (size_t i = 3; i < expr.size(); i += 2) { for (size_t i = 3, S = expr.size(); i < S; i += 2) {
Node rhs(eval(expr[i+1], env, f_env, registry)); Node rhs(eval(expr[i+1], env, f_env, new_Node));
accumulate(expr[i].type, acc, rhs, registry); accumulate(expr[i].type(), acc, rhs, new_Node);
} }
return acc.size() == 1 ? acc[0] : acc; return acc.size() == 1 ? acc[0] : acc;
} break; } break;
case Node::term: { case Node::term: {
if (expr.eval_me) { if (expr.should_eval()) {
Node acc(Node::expression, registry, expr.line(), 1); Node acc(new_Node(Node::expression, expr.path(), expr.line(), 1));
acc << eval(expr[0], env, f_env, registry); acc << eval(expr[0], env, f_env, new_Node);
Node rhs(eval(expr[2], env, f_env, registry)); Node rhs(eval(expr[2], env, f_env, new_Node));
accumulate(expr[1].type, acc, rhs, registry); accumulate(expr[1].type(), acc, rhs, new_Node);
for (size_t i = 3; i < expr.size(); i += 2) { for (size_t i = 3, S = expr.size(); i < S; i += 2) {
Node rhs(eval(expr[i+1], env, f_env, registry)); Node rhs(eval(expr[i+1], env, f_env, new_Node));
accumulate(expr[i].type, acc, rhs, registry); accumulate(expr[i].type(), acc, rhs, new_Node);
} }
return acc.size() == 1 ? acc[0] : acc; return acc.size() == 1 ? acc[0] : acc;
} }
...@@ -186,46 +172,44 @@ namespace Sass { ...@@ -186,46 +172,44 @@ namespace Sass {
} break; } break;
case Node::textual_percentage: { case Node::textual_percentage: {
Node pct(expr.line(), std::atof(expr.token().begin)); return new_Node(expr.path(), expr.line(), std::atof(expr.token().begin), Node::numeric_percentage);
pct.type = Node::numeric_percentage;
return pct;
} break; } break;
case Node::textual_dimension: { case Node::textual_dimension: {
return Node(expr.line(), return new_Node(expr.path(), expr.line(),
std::atof(expr.token().begin), std::atof(expr.token().begin),
Token::make(Prelexer::number(expr.token().begin), Token::make(Prelexer::number(expr.token().begin),
expr.token().end)); expr.token().end));
} break; } break;
case Node::textual_number: { case Node::textual_number: {
return Node(expr.line(), std::atof(expr.token().begin)); return new_Node(expr.path(), expr.line(), std::atof(expr.token().begin));
} break; } break;
case Node::textual_hex: { case Node::textual_hex: {
Node triple(Node::numeric_color, registry, expr.line(), 4); Node triple(new_Node(Node::numeric_color, expr.path(), expr.line(), 4));
Token hext(Token::make(expr.token().begin+1, expr.token().end)); Token hext(Token::make(expr.token().begin+1, expr.token().end));
if (hext.length() == 6) { if (hext.length() == 6) {
for (int i = 0; i < 6; i += 2) { for (int i = 0; i < 6; i += 2) {
triple << Node(expr.line(), static_cast<double>(std::strtol(string(hext.begin+i, 2).c_str(), NULL, 16))); triple << new_Node(expr.path(), expr.line(), static_cast<double>(std::strtol(string(hext.begin+i, 2).c_str(), NULL, 16)));
} }
} }
else { else {
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
triple << Node(expr.line(), static_cast<double>(std::strtol(string(2, hext.begin[i]).c_str(), NULL, 16))); triple << new_Node(expr.path(), expr.line(), static_cast<double>(std::strtol(string(2, hext.begin[i]).c_str(), NULL, 16)));
} }
} }
triple << Node(expr.line(), 1.0); triple << new_Node(expr.path(), expr.line(), 1.0);
return triple; return triple;
} break; } break;
case Node::variable: { case Node::variable: {
if (!env.query(expr.token())) eval_error("reference to unbound variable " + expr.token().to_string(), expr.line(), expr.path()); if (!env.query(expr.token())) throw_eval_error("reference to unbound variable " + expr.token().to_string(), expr.path(), expr.line());
return env[expr.token()]; return env[expr.token()];
} break; } break;
case Node::function_call: { case Node::function_call: {
// TO DO: default-constructed Function should be a generic callback // TO DO: default-constructed Function should be a generic callback (maybe)
pair<string, size_t> sig(expr[0].token().to_string(), expr[1].size()); pair<string, size_t> sig(expr[0].token().to_string(), expr[1].size());
if (!f_env.count(sig)) { if (!f_env.count(sig)) {
// stringstream ss; // stringstream ss;
...@@ -233,12 +217,14 @@ namespace Sass { ...@@ -233,12 +217,14 @@ namespace Sass {
// eval_error(ss.str(), expr.line(), expr.path()); // eval_error(ss.str(), expr.line(), expr.path());
return expr; return expr;
} }
return apply_function(f_env[sig], expr[1], env, f_env, registry); return apply_function(f_env[sig], expr[1], env, f_env, new_Node);
} break; } break;
case Node::unary_plus: { case Node::unary_plus: {
Node arg(eval(expr[0], env, f_env, registry)); Node arg(eval(expr[0], env, f_env, new_Node));
if (arg.is_numeric()) return arg; if (arg.is_numeric()) {
return arg;
}
else { else {
expr[0] = arg; expr[0] = arg;
return expr; return expr;
...@@ -246,9 +232,9 @@ namespace Sass { ...@@ -246,9 +232,9 @@ namespace Sass {
} break; } break;
case Node::unary_minus: { case Node::unary_minus: {
Node arg(eval(expr[0], env, f_env, registry)); Node arg(eval(expr[0], env, f_env, new_Node));
if (arg.is_numeric()) { if (arg.is_numeric()) {
arg.set_numeric_value(-arg.numeric_value()); return new_Node(expr.path(), expr.line(), -arg.numeric_value());
} }
else { else {
expr[0] = arg; expr[0] = arg;
...@@ -259,14 +245,14 @@ namespace Sass { ...@@ -259,14 +245,14 @@ namespace Sass {
case Node::string_schema: case Node::string_schema:
case Node::value_schema: { case Node::value_schema: {
// cerr << "evaluating schema of size " << expr.size() << endl; // cerr << "evaluating schema of size " << expr.size() << endl;
for (size_t i = 0; i < expr.size(); ++i) { for (size_t i = 0, S = expr.size(); i < S; ++i) {
expr[i] = eval(expr[i], env, f_env, registry); expr[i] = eval(expr[i], env, f_env, new_Node);
} }
return expr; return expr;
} break; } break;
case Node::css_import: { case Node::css_import: {
expr[0] = eval(expr[0], env, f_env, registry); expr[0] = eval(expr[0], env, f_env, new_Node);
return expr; return expr;
} break; } break;
...@@ -280,88 +266,79 @@ namespace Sass { ...@@ -280,88 +266,79 @@ namespace Sass {
Node accumulate(Node::Type op, Node& acc, Node& rhs, Node_Factory& new_Node) Node accumulate(Node::Type op, Node& acc, Node& rhs, Node_Factory& new_Node)
{ {
Node lhs(acc.content.children->back()); Node lhs(acc.back());
double lnum = lhs.numeric_value(); double lnum = lhs.numeric_value();
double rnum = rhs.numeric_value(); double rnum = rhs.numeric_value();
if (lhs.type == Node::number && rhs.type == Node::number) { if (lhs.type() == Node::number && rhs.type() == Node::number) {
Node result(acc.line(), operate(op, lnum, rnum)); Node result(new_Node(acc.path(), acc.line(), operate(op, lnum, rnum)));
acc.content.children->pop_back(); acc.pop_back();
acc.content.children->push_back(result); acc.push_back(result);
} }
// TO DO: find a way to merge the following two clauses // TO DO: find a way to merge the following two clauses
else if (lhs.type == Node::number && rhs.type == Node::numeric_dimension) { else if (lhs.type() == Node::number && rhs.type() == Node::numeric_dimension) {
Node result(acc.line(), operate(op, lnum, rnum), Token::make(rhs.content.dimension.unit, Prelexer::identifier(rhs.content.dimension.unit))); Node result(new_Node(acc.path(), acc.line(), operate(op, lnum, rnum), rhs.unit()));
acc.content.children->pop_back(); acc.pop_back();
acc.content.children->push_back(result); acc.push_back(result);
} }
else if (lhs.type == Node::numeric_dimension && rhs.type == Node::number) { else if (lhs.type() == Node::numeric_dimension && rhs.type() == Node::number) {
Node result(acc.line(), operate(op, lnum, rnum), Token::make(lhs.content.dimension.unit, Prelexer::identifier(rhs.content.dimension.unit))); Node result(new_Node(acc.path(), acc.line(), operate(op, lnum, rnum), lhs.unit()));
acc.content.children->pop_back(); acc.pop_back();
acc.content.children->push_back(result); acc.push_back(result);
} }
else if (lhs.type == Node::numeric_dimension && rhs.type == Node::numeric_dimension) { else if (lhs.type() == Node::numeric_dimension && rhs.type() == Node::numeric_dimension) {
// TO DO: CHECK FOR MISMATCHED UNITS HERE // TO DO: CHECK FOR MISMATCHED UNITS HERE
Node result; Node result;
if (op == Node::div) if (op == Node::div)
{ result = Node(acc.line(), operate(op, lnum, rnum)); } { result = new_Node(acc.path(), acc.line(), operate(op, lnum, rnum)); }
else else
{ result = Node(acc.line(), operate(op, lnum, rnum), Token::make(lhs.content.dimension.unit, Prelexer::identifier(rhs.content.dimension.unit))); } { result = new_Node(acc.path(), acc.line(), operate(op, lnum, rnum), lhs.unit()); }
acc.content.children->pop_back(); acc.pop_back();
acc.content.children->push_back(result); acc.push_back(result);
} }
// TO DO: find a way to merge the following two clauses // TO DO: find a way to merge the following two clauses
else if (lhs.type == Node::number && rhs.type == Node::numeric_color) { else if (lhs.type() == Node::number && rhs.type() == Node::numeric_color) {
if (op != Node::sub && op != Node::div) { if (op != Node::sub && op != Node::div) {
double r = operate(op, lhs.content.numeric_value, rhs[0].content.numeric_value); double r = operate(op, lhs.numeric_value(), rhs[0].numeric_value());
double g = operate(op, lhs.content.numeric_value, rhs[1].content.numeric_value); double g = operate(op, lhs.numeric_value(), rhs[1].numeric_value());
double b = operate(op, lhs.content.numeric_value, rhs[2].content.numeric_value); double b = operate(op, lhs.numeric_value(), rhs[2].numeric_value());
double a = rhs[3].content.numeric_value; double a = rhs[3].numeric_value();
acc.content.children->pop_back(); acc.pop_back();
acc << Node(registry, acc.line(), r, g, b, a); acc << new_Node(acc.path(), acc.line(), r, g, b, a);
} }
// trying to handle weird edge cases ... not sure if it's worth it // trying to handle weird edge cases ... not sure if it's worth it
else if (op == Node::div) { else if (op == Node::div) {
acc << Node(Node::div); acc << new_Node(Node::div, acc.path(), acc.line(), 0);
acc << rhs; acc << rhs;
} }
else if (op == Node::sub) { else if (op == Node::sub) {
acc << Node(Node::sub); acc << new_Node(Node::sub, acc.path(), acc.line(), 0);
acc << rhs; acc << rhs;
} }
else { else {
acc << rhs; acc << rhs;
} }
} }
else if (lhs.type == Node::numeric_color && rhs.type == Node::number) { else if (lhs.type() == Node::numeric_color && rhs.type() == Node::number) {
double r = operate(op, lhs[0].content.numeric_value, rhs.content.numeric_value); double r = operate(op, lhs[0].numeric_value(), rhs.numeric_value());
double g = operate(op, lhs[1].content.numeric_value, rhs.content.numeric_value); double g = operate(op, lhs[1].numeric_value(), rhs.numeric_value());
double b = operate(op, lhs[2].content.numeric_value, rhs.content.numeric_value); double b = operate(op, lhs[2].numeric_value(), rhs.numeric_value());
double a = lhs[3].content.numeric_value; double a = lhs[3].numeric_value();
acc.content.children->pop_back(); acc.pop_back();
acc << Node(registry, acc.line(), r, g, b, a); acc << new_Node(acc.path(), acc.line(), r, g, b, a);
} }
else if (lhs.type == Node::numeric_color && rhs.type == Node::numeric_color) { else if (lhs.type() == Node::numeric_color && rhs.type() == Node::numeric_color) {
if (lhs[3].content.numeric_value != rhs[3].content.numeric_value) eval_error("alpha channels must be equal for " + lhs.to_string("") + " + " + rhs.to_string(""), lhs.line(), lhs.path()); if (lhs[3].numeric_value() != rhs[3].numeric_value()) throw_eval_error("alpha channels must be equal for " + lhs.to_string("") + " + " + rhs.to_string(""), lhs.line(), lhs.path());
double r = operate(op, lhs[0].content.numeric_value, rhs[0].content.numeric_value); double r = operate(op, lhs[0].numeric_value(), rhs[0].numeric_value());
double g = operate(op, lhs[1].content.numeric_value, rhs[1].content.numeric_value); double g = operate(op, lhs[1].numeric_value(), rhs[1].numeric_value());
double b = operate(op, lhs[2].content.numeric_value, rhs[2].content.numeric_value); double b = operate(op, lhs[2].numeric_value(), rhs[2].numeric_value());
double a = lhs[3].content.numeric_value; double a = lhs[3].numeric_value();
acc.content.children->pop_back(); acc.pop_back();
acc << Node(registry, acc.line(), r, g, b, a); acc << new_Node(acc.path(), acc.line(), r, g, b, a);
} }
// else if (lhs.type == Node::concatenation) {
// lhs << rhs;
// }
// else if (lhs.type == Node::string_constant || rhs.type == Node::string_constant) {
// acc.content.children->pop_back();
// Node cat(Node::concatenation, lhs.line(), 2);
// cat << lhs << rhs;
// acc << cat;
// }
else { else {
// TO DO: disallow division and multiplication on lists // TO DO: disallow division and multiplication on lists
acc.content.children->push_back(rhs); acc.push_back(rhs);
} }
return acc; return acc;
...@@ -382,26 +359,26 @@ namespace Sass { ...@@ -382,26 +359,26 @@ namespace Sass {
Node apply_mixin(Node& mixin, const Node& args, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node) Node apply_mixin(Node& mixin, const Node& args, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node)
{ {
Node params(mixin[1]); Node params(mixin[1]);
Node body(mixin[2].clone(registry)); Node body(new_Node(mixin[2])); // clone the body
Environment bindings; Environment bindings;
// bind arguments // bind arguments
for (size_t i = 0, j = 0; i < args.size(); ++i) { for (size_t i = 0, j = 0, S = args.size(); i < S; ++i) {
if (args[i].type == Node::assignment) { if (args[i].type() == Node::assignment) {
Node arg(args[i]); Node arg(args[i]);
Token name(arg[0].token()); Token name(arg[0].token());
// check that the keyword arg actually names a formal parameter // check that the keyword arg actually names a formal parameter
bool valid_param = false; bool valid_param = false;
for (size_t k = 0; k < params.size(); ++k) { for (size_t k = 0, S = params.size(); k < S; ++k) {
Node param_k = params[k]; Node param_k = params[k];
if (param_k.type == Node::assignment) param_k = param_k[0]; if (param_k.type() == Node::assignment) param_k = param_k[0];
if (arg[0] == param_k) { if (arg[0] == param_k) {
valid_param = true; valid_param = true;
break; break;
} }
} }
if (!valid_param) eval_error("mixin " + mixin[0].to_string("") + " has no parameter named " + name.to_string(), arg.line(), arg.path()); if (!valid_param) throw_eval_error("mixin " + mixin[0].to_string("") + " has no parameter named " + name.to_string(), arg.line(), arg.path());
if (!bindings.query(name)) { if (!bindings.query(name)) {
bindings[name] = eval(arg[1], env, f_env, registry); bindings[name] = eval(arg[1], env, f_env, new_Node);
} }
} }
else { else {
...@@ -409,28 +386,28 @@ namespace Sass { ...@@ -409,28 +386,28 @@ namespace Sass {
if (j >= params.size()) { if (j >= params.size()) {
stringstream ss; stringstream ss;
ss << "mixin " << mixin[0].to_string("") << " only takes " << params.size() << ((params.size() == 1) ? " argument" : " arguments"); ss << "mixin " << mixin[0].to_string("") << " only takes " << params.size() << ((params.size() == 1) ? " argument" : " arguments");
eval_error(ss.str(), args[i].line(), args[i].path()); throw_eval_error(ss.str(), args[i].path(), args[i].line());
} }
Node param(params[j]); Node param(params[j]);
Token name(param.type == Node::variable ? param.token() : param[0].token()); Token name(param.type() == Node::variable ? param.token() : param[0].token());
bindings[name] = eval(args[i], env, f_env, registry); bindings[name] = eval(args[i], env, f_env, new_Node);
++j; ++j;
} }
} }
// plug the holes with default arguments if any // plug the holes with default arguments if any
for (size_t i = 0; i < params.size(); ++i) { for (size_t i = 0, S = params.size(); i < S; ++i) {
if (params[i].type == Node::assignment) { if (params[i].type() == Node::assignment) {
Node param(params[i]); Node param(params[i]);
Token name(param[0].token()); Token name(param[0].token());
if (!bindings.query(name)) { if (!bindings.query(name)) {
bindings[name] = eval(param[1], env, f_env, registry); bindings[name] = eval(param[1], env, f_env, new_Node);
} }
} }
} }
// lexically link the new environment and eval the mixin's body // lexically link the new environment and eval the mixin's body
bindings.link(env.global ? *env.global : env); bindings.link(env.global ? *env.global : env);
for (size_t i = 0; i < body.size(); ++i) { for (size_t i = 0, S = body.size(); i < S; ++i) {
body[i] = eval(body[i], bindings, f_env, registry); body[i] = eval(body[i], bindings, f_env, new_Node);
} }
return body; return body;
} }
...@@ -439,19 +416,19 @@ namespace Sass { ...@@ -439,19 +416,19 @@ namespace Sass {
{ {
map<Token, Node> bindings; map<Token, Node> bindings;
// bind arguments // bind arguments
for (size_t i = 0, j = 0; i < args.size(); ++i) { for (size_t i = 0, j = 0, S = args.size(); i < S; ++i) {
if (args[i].type == Node::assignment) { if (args[i].type() == Node::assignment) {
Node arg(args[i]); Node arg(args[i]);
Token name(arg[0].token()); Token name(arg[0].token());
bindings[name] = eval(arg[1], env, f_env, registry); bindings[name] = eval(arg[1], env, f_env, new_Node);
} }
else { else {
// TO DO: ensure (j < f.parameters.size()) // TO DO: ensure (j < f.parameters.size())
bindings[f.parameters[j]] = eval(args[i], env, f_env, registry); bindings[f.parameters[j]] = eval(args[i], env, f_env, new_Node);
++j; ++j;
} }
} }
return f(bindings, registry); return f(bindings, new_Node);
} }
} }
...@@ -169,6 +169,7 @@ namespace Sass { ...@@ -169,6 +169,7 @@ namespace Sass {
bool empty() const; bool empty() const;
Node& at(size_t i) const; Node& at(size_t i) const;
Node& back() const;
Node& operator[](size_t i) const; Node& operator[](size_t i) const;
void pop_back(); void pop_back();
Node& push_back(Node n); Node& push_back(Node n);
...@@ -302,6 +303,7 @@ namespace Sass { ...@@ -302,6 +303,7 @@ namespace Sass {
inline bool Node::empty() const { return ip_->empty(); } inline bool Node::empty() const { return ip_->empty(); }
inline Node& Node::at(size_t i) const { return ip_->at(i); } inline Node& Node::at(size_t i) const { return ip_->at(i); }
inline Node& Node::back() const { return ip_->back(); }
inline Node& Node::operator[](size_t i) const { return at(i); } inline Node& Node::operator[](size_t i) const { return at(i); }
inline void Node::pop_back() { ip_->pop_back(); } inline void Node::pop_back() { ip_->pop_back(); }
inline Node& Node::push_back(Node n) inline Node& Node::push_back(Node n)
......
...@@ -61,9 +61,9 @@ namespace Sass { ...@@ -61,9 +61,9 @@ namespace Sass {
// } // }
// for making nodes representing numbers // for making nodes representing numbers
Node Node_Factory::operator()(string path, size_t line, double v) Node Node_Factory::operator()(string path, size_t line, double v, Node::Type type)
{ {
Node_Impl* ip = alloc_Node_Impl(Node::number, path, line); Node_Impl* ip = alloc_Node_Impl(type, path, line);
ip->value.numeric = v; ip->value.numeric = v;
return Node(ip); return Node(ip);
} }
......
...@@ -25,7 +25,7 @@ namespace Sass { ...@@ -25,7 +25,7 @@ namespace Sass {
// // for making nodes representing boolean values // // for making nodes representing boolean values
// Node operator()(Node::Type type, string file, size_t line, bool b); // Node operator()(Node::Type type, string file, size_t line, bool b);
// for making nodes representing numbers // for making nodes representing numbers
Node operator()(string file, size_t line, double v); Node operator()(string file, size_t line, double v, Node::Type type = Node::number);
// for making nodes representing numeric dimensions (e.g. 5px, 3em) // for making nodes representing numeric dimensions (e.g. 5px, 3em)
Node operator()(string file, size_t line, double v, const Token& t); Node operator()(string file, size_t line, double v, const Token& t);
// for making nodes representing rgba color quads // for making nodes representing rgba color quads
......
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