Commit 6eb7c4ed by Aaron Leung

Overhauled the implementation of arithmetic. Much less janky now.

parent 4aaa7c96
...@@ -227,7 +227,7 @@ namespace Sass { ...@@ -227,7 +227,7 @@ namespace Sass {
for (size_t i = 0, S = expr.size(); i < S; ++i) { for (size_t i = 0, S = expr.size(); i < S; ++i) {
expr[i] = eval(expr[i], prefix, env, f_env, new_Node, ctx); expr[i] = eval(expr[i], prefix, env, f_env, new_Node, ctx);
} }
return reduce(expr, 0, Node(), new_Node); return reduce(expr, 1, expr[0], new_Node);
// Node acc(new_Node(Node::expression, expr.path(), expr.line(), 1)); // Node acc(new_Node(Node::expression, expr.path(), expr.line(), 1));
// acc << eval(expr[0], prefix, env, f_env, new_Node, ctx); // acc << eval(expr[0], prefix, env, f_env, new_Node, ctx);
// Node rhs(eval(expr[2], prefix, env, f_env, new_Node, ctx)); // Node rhs(eval(expr[2], prefix, env, f_env, new_Node, ctx));
...@@ -241,15 +241,19 @@ namespace Sass { ...@@ -241,15 +241,19 @@ namespace Sass {
case Node::term: { case Node::term: {
if (expr.should_eval()) { if (expr.should_eval()) {
Node acc(new_Node(Node::expression, expr.path(), expr.line(), 1)); for (size_t i = 0, S = expr.size(); i < S; ++i) {
acc << eval(expr[0], prefix, env, f_env, new_Node, ctx); expr[i] = eval(expr[i], prefix, env, f_env, new_Node, ctx);
Node rhs(eval(expr[2], prefix, env, f_env, new_Node, ctx));
accumulate(expr[1].type(), acc, rhs, new_Node);
for (size_t i = 3, S = expr.size(); i < S; i += 2) {
Node rhs(eval(expr[i+1], prefix, env, f_env, new_Node, ctx));
accumulate(expr[i].type(), acc, rhs, new_Node);
} }
return acc.size() == 1 ? acc[0] : acc; return reduce(expr, 1, expr[0], new_Node);
// Node acc(new_Node(Node::expression, expr.path(), expr.line(), 1));
// acc << eval(expr[0], prefix, env, f_env, new_Node, ctx);
// Node rhs(eval(expr[2], prefix, env, f_env, new_Node, ctx));
// accumulate(expr[1].type(), acc, rhs, new_Node);
// for (size_t i = 3, S = expr.size(); i < S; i += 2) {
// Node rhs(eval(expr[i+1], prefix, env, f_env, new_Node, ctx));
// accumulate(expr[i].type(), acc, rhs, new_Node);
// }
// return acc.size() == 1 ? acc[0] : acc;
} }
else { else {
return expr; return expr;
...@@ -487,210 +491,229 @@ namespace Sass { ...@@ -487,210 +491,229 @@ namespace Sass {
return expr; return expr;
} }
// Accumulate arithmetic operations. It's done this way because arithmetic // Reduce arithmetic operations. It's done this way because arithmetic
// expressions are stored as vectors of operands with operators interspersed, // expressions are stored as vectors of operands with operators interspersed,
// rather than as the usual binary tree. // rather than as the usual binary tree. (This function is essentially a
// left fold.)
Node reduce(Node list, size_t head, Node acc, Node_Factory& new_Node) Node reduce(Node list, size_t head, Node acc, Node_Factory& new_Node)
{ {
if (head >= list.size()) return acc; if (head >= list.size()) return acc;
Node rhs(list[head]); Node op(list[head]);
Node op(list[head + 1]); Node rhs(list[head + 1]);
Node::Type optype = op.type(); Node::Type optype = op.type();
if (acc.is_string()) { Node::Type ltype = acc.type();
Node::Type rtype = rhs.type();
if (ltype == Node::concatenation && rtype == Node::concatenation) {
if (optype != Node::add) acc << op;
acc += rhs;
}
else if (ltype == Node::concatenation) {
if (optype != Node::add) acc << op;
acc << rhs;
}
else if (acc.is_string()) {
acc = (new_Node(Node::concatenation, list.path(), list.line(), 2) << acc); acc = (new_Node(Node::concatenation, list.path(), list.line(), 2) << acc);
if (optype == Node::sub || optype == Node::div || optype == Node::mul) { if (optype != Node::add) acc << op;
acc << op;
}
acc << rhs; acc << rhs;
} }
Node::Type ltype = acc.type(); else if (ltype == Node::number && rtype == Node::number) {
Node::Type rtype = rhs.type(); acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value()));
if (ltype == Node::number && rtype == Node::number) {
acc = new_Node(list.path(), list.line(), operate(optype, acc.numeric_value(), rhs.numeric_value()));
} }
else if (ltype == Node::number && rtype == Node::numeric_dimension) { else if (ltype == Node::number && rtype == Node::numeric_dimension) {
acc = new_Node(list.path(), list.line(), operate(optype, acc.numeric_value(), rhs.numeric_value()), rhs.unit()); acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value()), rhs.unit());
} }
else if (ltype == Node::number && rtype == Node::numeric_dimension) { else if (ltype == Node::numeric_dimension && rtype == Node::number) {
acc = new_Node(list.path(), list.line(), operate(optype, acc.numeric_value(), rhs.numeric_value()), acc.unit()); acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value()), acc.unit());
} }
else if (ltype == Node::numeric_dimension && rtype == Node::numeric_dimension) { else if (ltype == Node::numeric_dimension && rtype == Node::numeric_dimension) {
// TO DO: TRUE UNIT ARITHMETIC // TO DO: TRUE UNIT ARITHMETIC
if (optype == Node::div) { if (optype == Node::div) {
acc = new_Node(list.path(), list.line(), operate(optype, acc.numeric_value(), rhs.numeric_value())); acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value()));
} }
else { else {
acc = new_Node(list.path(), list.line(), operate(optype, acc.numeric_value(), rhs.numeric_value()), acc.unit()); acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value()), acc.unit());
} }
} }
else if (ltype == Node::number && rtype == Node::numeric_color) { else if (ltype == Node::number && rtype == Node::numeric_color) {
if (optype != Node::sub && optype != Node::div) { if (optype == Node::add || optype == Node::mul) {
double r = operate(optype, acc.numeric_value(), rhs[0].numeric_value()); double r = operate(op, acc.numeric_value(), rhs[0].numeric_value());
double g = operate(optype, acc.numeric_value(), rhs[1].numeric_value()); double g = operate(op, acc.numeric_value(), rhs[1].numeric_value());
double b = operate(optype, acc.numeric_value(), rhs[2].numeric_value()); double b = operate(op, acc.numeric_value(), rhs[2].numeric_value());
double a = rhs[3].numeric_value(); double a = rhs[3].numeric_value();
acc = new_Node(list.path(), list.line(), r, g, b, a); acc = new_Node(list.path(), list.line(), r, g, b, a);
} }
else (optype == Node::div || optype) { else {
acc = (new_Node(Node::concatenation, list.path(), list.line(), 3) << acc); acc = (new_Node(Node::concatenation, list.path(), list.line(), 3) << acc);
acc << op; acc << op;
acc << rhs; acc << rhs;
} }
} }
else if (ltype == Node::numeric_color && rtype == Node::number) { else if (ltype == Node::numeric_color && rtype == Node::number) {
double r = operate(optype, acc[0].numeric_value(), rhs.numeric_value()); double r = operate(op, acc[0].numeric_value(), rhs.numeric_value());
double g = operate(optype, acc[1].numeric_value(), rhs.numeric_value()); double g = operate(op, acc[1].numeric_value(), rhs.numeric_value());
double b = operate(optype, acc[2].numeric_value(), rhs.numeric_value()); double b = operate(op, acc[2].numeric_value(), rhs.numeric_value());
double a = acc[3].numeric_value(); double a = acc[3].numeric_value();
acc = new_Node(list.path(), list.line(), r, g, b, a); acc = new_Node(list.path(), list.line(), r, g, b, a);
} }
else if (ltype == Node::numeric_color && rtype == Node::numeric_color) { else if (ltype == Node::numeric_color && rtype == Node::numeric_color) {
if (acc[3].numeric_value() != rhs[3].numeric_value()) throw_eval_error("alpha channels must be equal for " + acc.to_string() + " + " + rhs.to_string(), acc.path(), acc.line()); if (acc[3].numeric_value() != rhs[3].numeric_value()) throw_eval_error("alpha channels must be equal for " + acc.to_string() + " + " + rhs.to_string(), acc.path(), acc.line());
double r = operate(optype, acc[0].numeric_value(), rhs[0].numeric_value()); double r = operate(op, acc[0].numeric_value(), rhs[0].numeric_value());
double g = operate(optype, acc[1].numeric_value(), rhs[1].numeric_value()); double g = operate(op, acc[1].numeric_value(), rhs[1].numeric_value());
double b = operate(optype, acc[2].numeric_value(), rhs[2].numeric_value()); double b = operate(op, acc[2].numeric_value(), rhs[2].numeric_value());
double a = acc[3].numeric_value(); double a = acc[3].numeric_value();
acc = new_Node(list.path(), list.line(), r, g, b, a); acc = new_Node(list.path(), list.line(), r, g, b, a);
} }
// NOT DONE YET! else { // two lists
return reduce(list, head + 3, acc, new_Node); if (optype != Node::mul) {
} acc = (new_Node(Node::value_schema, list.path(), list.line(), 2) << acc);
if (optype != Node::add) acc << op;
Node accumulate(Node::Type op, Node acc, Node rhs, Node_Factory& new_Node)
{
Node lhs(acc.back());
double lnum = lhs.numeric_value();
double rnum = rhs.numeric_value();
if (lhs.type() == Node::number && rhs.type() == Node::number) {
Node result(new_Node(acc.path(), acc.line(), operate(op, lnum, rnum)));
acc.pop_back();
acc.push_back(result);
}
// TO DO: find a way to merge the following two clauses
else if (lhs.type() == Node::number && rhs.type() == Node::numeric_dimension) {
Node result(new_Node(acc.path(), acc.line(), operate(op, lnum, rnum), rhs.unit()));
acc.pop_back();
acc.push_back(result);
}
else if (lhs.type() == Node::numeric_dimension && rhs.type() == Node::number) {
Node result(new_Node(acc.path(), acc.line(), operate(op, lnum, rnum), lhs.unit()));
acc.pop_back();
acc.push_back(result);
}
else if (lhs.type() == Node::numeric_dimension && rhs.type() == Node::numeric_dimension) {
// TO DO: CHECK FOR MISMATCHED UNITS HERE
Node result;
if (op == Node::div)
{ result = new_Node(acc.path(), acc.line(), operate(op, lnum, rnum)); }
else
{ result = new_Node(acc.path(), acc.line(), operate(op, lnum, rnum), lhs.unit()); }
acc.pop_back();
acc.push_back(result);
}
// TO DO: find a way to merge the following two clauses
else if (lhs.type() == Node::number && rhs.type() == Node::numeric_color) {
if (op != Node::sub && op != Node::div) {
double r = operate(op, lhs.numeric_value(), rhs[0].numeric_value());
double g = operate(op, lhs.numeric_value(), rhs[1].numeric_value());
double b = operate(op, lhs.numeric_value(), rhs[2].numeric_value());
double a = rhs[3].numeric_value();
acc.pop_back();
acc << new_Node(acc.path(), acc.line(), r, g, b, a);
}
// trying to handle weird edge cases ... not sure if it's worth it
else if (op == Node::div) {
acc << new_Node(Node::div, acc.path(), acc.line(), 0);
acc << rhs;
}
else if (op == Node::sub) {
acc << new_Node(Node::sub, acc.path(), acc.line(), 0);
acc << rhs;
}
else {
acc << rhs;
}
}
else if (lhs.type() == Node::numeric_color && rhs.type() == Node::number) {
double r = operate(op, lhs[0].numeric_value(), rhs.numeric_value());
double g = operate(op, lhs[1].numeric_value(), rhs.numeric_value());
double b = operate(op, lhs[2].numeric_value(), rhs.numeric_value());
double a = lhs[3].numeric_value();
acc.pop_back();
acc << new_Node(acc.path(), acc.line(), r, g, b, a);
}
else if (lhs.type() == Node::numeric_color && rhs.type() == Node::numeric_color) {
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.path(), lhs.line());
double r = operate(op, lhs[0].numeric_value(), rhs[0].numeric_value());
double g = operate(op, lhs[1].numeric_value(), rhs[1].numeric_value());
double b = operate(op, lhs[2].numeric_value(), rhs[2].numeric_value());
double a = lhs[3].numeric_value();
acc.pop_back();
acc << new_Node(acc.path(), acc.line(), r, g, b, a);
}
else if (lhs.type() == Node::concatenation && rhs.type() == Node::concatenation) {
if (op == Node::add) {
lhs += rhs;
}
else {
acc << new_Node(op, acc.path(), acc.line(), Token::make());
acc << rhs;
}
}
else if (lhs.type() == Node::concatenation && rhs.type() == Node::string_constant) {
if (op == Node::add) {
lhs << rhs;
}
else {
acc << new_Node(op, acc.path(), acc.line(), Token::make());
acc << rhs;
}
}
else if (lhs.type() == Node::string_constant && rhs.type() == Node::concatenation) {
if (op == Node::add) {
Node new_cat(new_Node(Node::concatenation, lhs.path(), lhs.line(), 1 + rhs.size()));
new_cat << lhs;
new_cat += rhs;
acc.pop_back();
acc << new_cat;
}
else {
acc << new_Node(op, acc.path(), acc.line(), Token::make());
acc << rhs; acc << rhs;
} }
}
else if (lhs.type() == Node::string_constant && rhs.type() == Node::string_constant) {
if (op == Node::add) {
Node new_cat(new_Node(Node::concatenation, lhs.path(), lhs.line(), 2));
new_cat << lhs << rhs;
acc.pop_back();
acc << new_cat;
}
else { else {
acc << new_Node(op, acc.path(), acc.line(), Token::make()); throw_eval_error("cannot multiply lists", op.path(), op.line());
acc << rhs;
} }
} }
else { return reduce(list, head + 2, acc, new_Node);
// TO DO: disallow division and multiplication on lists
if (op == Node::sub) acc << new_Node(Node::sub, acc.path(), acc.line(), Token::make());
acc.push_back(rhs);
}
return acc;
} }
// Node accumulate(Node::Type op, Node acc, Node rhs, Node_Factory& new_Node)
// {
// Node lhs(acc.back());
// double lnum = lhs.numeric_value();
// double rnum = rhs.numeric_value();
// if (lhs.type() == Node::number && rhs.type() == Node::number) {
// Node result(new_Node(acc.path(), acc.line(), operate(op, lnum, rnum)));
// acc.pop_back();
// acc.push_back(result);
// }
// // TO DO: find a way to merge the following two clauses
// else if (lhs.type() == Node::number && rhs.type() == Node::numeric_dimension) {
// Node result(new_Node(acc.path(), acc.line(), operate(op, lnum, rnum), rhs.unit()));
// acc.pop_back();
// acc.push_back(result);
// }
// else if (lhs.type() == Node::numeric_dimension && rhs.type() == Node::number) {
// Node result(new_Node(acc.path(), acc.line(), operate(op, lnum, rnum), lhs.unit()));
// acc.pop_back();
// acc.push_back(result);
// }
// else if (lhs.type() == Node::numeric_dimension && rhs.type() == Node::numeric_dimension) {
// // TO DO: CHECK FOR MISMATCHED UNITS HERE
// Node result;
// if (op == Node::div)
// { result = new_Node(acc.path(), acc.line(), operate(op, lnum, rnum)); }
// else
// { result = new_Node(acc.path(), acc.line(), operate(op, lnum, rnum), lhs.unit()); }
// acc.pop_back();
// acc.push_back(result);
// }
// // TO DO: find a way to merge the following two clauses
// else if (lhs.type() == Node::number && rhs.type() == Node::numeric_color) {
// if (op != Node::sub && op != Node::div) {
// double r = operate(op, lhs.numeric_value(), rhs[0].numeric_value());
// double g = operate(op, lhs.numeric_value(), rhs[1].numeric_value());
// double b = operate(op, lhs.numeric_value(), rhs[2].numeric_value());
// double a = rhs[3].numeric_value();
// acc.pop_back();
// acc << new_Node(acc.path(), acc.line(), r, g, b, a);
// }
// // trying to handle weird edge cases ... not sure if it's worth it
// else if (op == Node::div) {
// acc << new_Node(Node::div, acc.path(), acc.line(), 0);
// acc << rhs;
// }
// else if (op == Node::sub) {
// acc << new_Node(Node::sub, acc.path(), acc.line(), 0);
// acc << rhs;
// }
// else {
// acc << rhs;
// }
// }
// else if (lhs.type() == Node::numeric_color && rhs.type() == Node::number) {
// double r = operate(op, lhs[0].numeric_value(), rhs.numeric_value());
// double g = operate(op, lhs[1].numeric_value(), rhs.numeric_value());
// double b = operate(op, lhs[2].numeric_value(), rhs.numeric_value());
// double a = lhs[3].numeric_value();
// acc.pop_back();
// acc << new_Node(acc.path(), acc.line(), r, g, b, a);
// }
// else if (lhs.type() == Node::numeric_color && rhs.type() == Node::numeric_color) {
// 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.path(), lhs.line());
// double r = operate(op, lhs[0].numeric_value(), rhs[0].numeric_value());
// double g = operate(op, lhs[1].numeric_value(), rhs[1].numeric_value());
// double b = operate(op, lhs[2].numeric_value(), rhs[2].numeric_value());
// double a = lhs[3].numeric_value();
// acc.pop_back();
// acc << new_Node(acc.path(), acc.line(), r, g, b, a);
// }
// else if (lhs.type() == Node::concatenation && rhs.type() == Node::concatenation) {
// if (op == Node::add) {
// lhs += rhs;
// }
// else {
// acc << new_Node(op, acc.path(), acc.line(), Token::make());
// acc << rhs;
// }
// }
// else if (lhs.type() == Node::concatenation && rhs.type() == Node::string_constant) {
// if (op == Node::add) {
// lhs << rhs;
// }
// else {
// acc << new_Node(op, acc.path(), acc.line(), Token::make());
// acc << rhs;
// }
// }
// else if (lhs.type() == Node::string_constant && rhs.type() == Node::concatenation) {
// if (op == Node::add) {
// Node new_cat(new_Node(Node::concatenation, lhs.path(), lhs.line(), 1 + rhs.size()));
// new_cat << lhs;
// new_cat += rhs;
// acc.pop_back();
// acc << new_cat;
// }
// else {
// acc << new_Node(op, acc.path(), acc.line(), Token::make());
// acc << rhs;
// }
// }
// else if (lhs.type() == Node::string_constant && rhs.type() == Node::string_constant) {
// if (op == Node::add) {
// Node new_cat(new_Node(Node::concatenation, lhs.path(), lhs.line(), 2));
// new_cat << lhs << rhs;
// acc.pop_back();
// acc << new_cat;
// }
// else {
// acc << new_Node(op, acc.path(), acc.line(), Token::make());
// acc << rhs;
// }
// }
// else {
// // TO DO: disallow division and multiplication on lists
// if (op == Node::sub) acc << new_Node(Node::sub, acc.path(), acc.line(), Token::make());
// acc.push_back(rhs);
// }
// return acc;
// }
// Helper for doing the actual arithmetic. // Helper for doing the actual arithmetic.
double operate(Node::Type op, double lhs, double rhs) double operate(Node op, double lhs, double rhs)
{ {
switch (op) switch (op.type())
{ {
case Node::add: return lhs + rhs; break; case Node::add: return lhs + rhs; break;
case Node::sub: return lhs - rhs; break; case Node::sub: return lhs - rhs; break;
case Node::mul: return lhs * rhs; break; case Node::mul: return lhs * rhs; break;
case Node::div: return lhs / rhs; break; case Node::div: {
if (rhs == 0) throw_eval_error("divide by zero", op.path(), op.line());
return lhs / rhs;
} break;
default: return 0; break; default: return 0; break;
} }
} }
...@@ -728,7 +751,7 @@ namespace Sass { ...@@ -728,7 +751,7 @@ namespace Sass {
if (!env.query(arg_name)) { if (!env.query(arg_name)) {
throw_eval_error(callee_name + " has no parameter named " + arg_name.to_string(), arg.path(), arg.line()); throw_eval_error(callee_name + " has no parameter named " + arg_name.to_string(), arg.path(), arg.line());
} }
if (!env[arg_name].is_stub()) { if (!env[arg_name].is_none()) {
throw_eval_error(callee_name + " was passed argument " + arg_name.to_string() + " both by position and by name", arg.path(), arg.line()); 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; env[arg_name] = arg_value;
...@@ -739,7 +762,7 @@ namespace Sass { ...@@ -739,7 +762,7 @@ namespace Sass {
for (size_t i = 0, S = params.size(); i < S; ++i) { for (size_t i = 0, S = params.size(); i < S; ++i) {
Node param(params[i]); Node param(params[i]);
Token param_name((param.type() == Node::assignment ? param[0] : param).token()); Token param_name((param.type() == Node::assignment ? param[0] : param).token());
if (env[param_name].is_stub()) { if (env[param_name].is_none()) {
if (param.type() != Node::assignment) { if (param.type() != Node::assignment) {
throw_eval_error(callee_name + " is missing argument " + param_name.to_string(), args.path(), args.line()); throw_eval_error(callee_name + " is missing argument " + param_name.to_string(), args.path(), args.line());
} }
...@@ -780,7 +803,7 @@ namespace Sass { ...@@ -780,7 +803,7 @@ namespace Sass {
// bind arguments in the extended environment // bind arguments in the extended environment
stringstream mixin_name; stringstream mixin_name;
mixin_name << "mixin"; mixin_name << "mixin";
if (mixin[0].type() != Node::none) mixin_name << " " << mixin[0].to_string(); if (!mixin[0].is_none()) mixin_name << " " << mixin[0].to_string();
bind_arguments(mixin_name.str(), params, args, prefix, bindings, f_env, new_Node, ctx); bind_arguments(mixin_name.str(), params, args, prefix, bindings, f_env, new_Node, ctx);
// evaluate the mixin's body // evaluate the mixin's body
for (size_t i = 0, S = body.size(); i < S; ++i) { for (size_t i = 0, S = body.size(); i < S; ++i) {
......
...@@ -15,8 +15,9 @@ namespace Sass { ...@@ -15,8 +15,9 @@ namespace Sass {
Node eval(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 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);
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);
double operate(Node::Type op, double lhs, double rhs); double operate(Node op, double lhs, double rhs);
Node apply_mixin(Node mixin, const Node args, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, bool dynamic_scope = false); Node apply_mixin(Node mixin, const Node args, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, bool dynamic_scope = false);
Node apply_function(const Function& f, const Node args, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, string& path, size_t line); Node apply_function(const Function& f, const Node args, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, string& path, size_t line);
......
...@@ -182,7 +182,7 @@ namespace Sass { ...@@ -182,7 +182,7 @@ namespace Sass {
Type type() const; Type type() const;
bool is_stub() const; bool is_none() const;
bool has_children() const; bool has_children() const;
bool has_statements() const; bool has_statements() const;
bool has_blocks() const; bool has_blocks() const;
...@@ -416,7 +416,7 @@ namespace Sass { ...@@ -416,7 +416,7 @@ namespace Sass {
inline Node::Type Node::type() const { return ip_->type; } inline Node::Type Node::type() const { return ip_->type; }
inline bool Node::is_stub() const { return !ip_; } inline bool Node::is_none() const { return !ip_; }
inline bool Node::has_children() const { return ip_->has_children; } inline bool Node::has_children() const { return ip_->has_children; }
inline bool Node::has_statements() const { return ip_->has_statements; } inline bool Node::has_statements() const { return ip_->has_statements; }
inline bool Node::has_blocks() const { return ip_->has_blocks; } inline bool Node::has_blocks() const { return ip_->has_blocks; }
......
...@@ -140,6 +140,7 @@ namespace Sass { ...@@ -140,6 +140,7 @@ namespace Sass {
return result; return result;
} break; } break;
// still necessary for unevaluated expressions
case expression: case expression:
case term: { case term: {
string result(at(0).to_string()); string result(at(0).to_string());
...@@ -348,12 +349,20 @@ namespace Sass { ...@@ -348,12 +349,20 @@ namespace Sass {
case concatenation: { case concatenation: {
string result; string result;
bool quoted = (at(0).type() == string_constant || at(0).type() == string_schema) ? true : false;
for (size_t i = 0, S = size(); i < S; ++i) { for (size_t i = 0, S = size(); i < S; ++i) {
result += at(i).to_string().substr(1, at(i).token().length()-2); // result += at(i).to_string().substr(1, at(i).token().length()-2);
Node::Type itype = at(i).type();
if (itype == Node::string_constant || itype == Node::string_schema) {
result += at(i).unquote();
}
else {
result += at(i).to_string();
}
} }
// if (inside_of == identifier_schema || inside_of == property) return result; // if (inside_of == identifier_schema || inside_of == property) return result;
// else return "\"" + result + "\""; // else return "\"" + result + "\"";
if (!(inside_of == identifier_schema || inside_of == property) && !is_unquoted()) { if (!(inside_of == identifier_schema || inside_of == property) && quoted && !is_unquoted()) {
result = "\"" + result + "\""; result = "\"" + result + "\"";
} }
return result; return result;
......
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