Commit b19c9594 by Aaron Leung

Updated the builtin functions to use the refactored API.

parent d12389e7
#ifndef SASS_PRELEXER_INCLUDED
#include "prelexer.hpp"
#endif
#include "node_factory.hpp"
#include "functions.hpp"
#include "error.hpp"
#include <iostream>
......@@ -12,7 +13,7 @@ namespace Sass {
static void throw_eval_error(string message, string path, size_t line)
{
if (!path.empty() && Prelexer::string_constant(file_name))
if (!path.empty() && Prelexer::string_constant(path.c_str()))
path = path.substr(1, path.length() - 1);
throw Error(Error::evaluation, path, line, message);
......@@ -53,7 +54,7 @@ namespace Sass {
Node g(color[1]);
Node b(color[2]);
Node a(bindings[parameters[1]]);
if (color.type() != Node::numeric_color || a.type != Node::number) throw_eval_error("arguments to rgba must be a color and a number", color.path(), color.line());
if (color.type() != Node::numeric_color || a.type() != Node::number) throw_eval_error("arguments to rgba must be a color and a number", color.path(), color.line());
return new_Node(color.path(), color.line(), r.numeric_value(), g.numeric_value(), b.numeric_value(), a.numeric_value());
}
......@@ -186,7 +187,7 @@ namespace Sass {
{ "invert", "$color", 0 };
Node invert(const vector<Token>& parameters, map<Token, Node>& bindings, Node_Factory& new_Node) {
Node orig(bindings[parameters[0]]);
if (orig.type() != Node::numeric_color) throw_eval_error("argument to invert must be a color", orig.line_number, orig.file_name);
if (orig.type() != Node::numeric_color) throw_eval_error("argument to invert must be a color", orig.path(), orig.line());
return new_Node(orig.path(), orig.line(),
255 - orig[0].numeric_value(),
255 - orig[1].numeric_value(),
......@@ -219,11 +220,11 @@ namespace Sass {
if (delta.numeric_value() < 0 || delta.numeric_value() > 1) {
throw_eval_error("amount must be between 0 and 1 for opacify/fade-in", delta.path(), delta.line());
}
double alpha = orig[3].numeric_value() + delta.numeric_value();
double alpha = color[3].numeric_value() + delta.numeric_value();
if (alpha > 1) alpha = 1;
else if (alpha < 0) alpha = 0;
return new_Node(orig.path(), orig.line(),
orig[0].numeric_value(), orig[1].numeric_value(), orig[2].numeric_value(), alpha);
return new_Node(color.path(), color.line(),
color[0].numeric_value(), color[1].numeric_value(), color[2].numeric_value(), alpha);
}
Function_Descriptor transparentize_descriptor =
......@@ -234,16 +235,16 @@ namespace Sass {
Node color(bindings[parameters[0]]);
Node delta(bindings[parameters[1]]);
if (color.type() != Node::numeric_color || !delta.is_numeric()) {
throw_eval_error("arguments to transparentize/fade_out must be a color and a numeric value", orig.path(), orig.line());
throw_eval_error("arguments to transparentize/fade_out must be a color and a numeric value", color.path(), color.line());
}
if (delta.numeric_value() < 0 || delta.numeric_value() > 1) {
throw_eval_error("amount must be between 0 and 1 for transparentize/fade-out", delta.path(), delta.line());
}
double alpha = orig[3].numeric_value() - delta.numeric_value();
double alpha = color[3].numeric_value() - delta.numeric_value();
if (alpha > 1) alpha = 1;
else if (alpha < 0) alpha = 0;
return new_Node(orig.path(), orig.line(),
orig[0].numeric_value(), orig[1].numeric_value(), orig[2].numeric_value(), alpha);
return new_Node(color.path(), color.line(),
color[0].numeric_value(), color[1].numeric_value(), color[2].numeric_value(), alpha);
}
// String Functions ////////////////////////////////////////////////////
......@@ -266,7 +267,7 @@ namespace Sass {
if (orig.type() != Node::string_constant && orig.type() != Node::identifier) {
throw_eval_error("argument to quote must be a string or identifier", orig.path(), orig.line());
}
Node cpy(new_Node(Node::string_constant, orig.path(), orig.line(), orig.token())
Node cpy(new_Node(orig));
cpy.is_unquoted() = false;
return cpy;
}
......@@ -276,76 +277,131 @@ namespace Sass {
Function_Descriptor percentage_descriptor =
{ "percentage", "$value", 0 };
Node percentage(const vector<Token>& parameters, map<Token, Node>& bindings, Node_Factory& new_Node) {
Node cpy(bindings[parameters[0]].clone(new_Node));
// TO DO: make sure it's not already a percentage
if (cpy.type() != Node::number) throw_eval_error("argument to percentage must be a unitless number", cpy.path(), cpy.line());
cpy.numeric_value() = cpy.numeric_value() * 100;
cpy.type() = Node::numeric_percentage;
return cpy;
Node orig(bindings[parameters[0]]);
if (orig.type() != Node::number) {
throw_eval_error("argument to percentage must be a unitless number", orig.path(), orig.line());
}
return new_Node(orig.path(), orig.line(), orig.numeric_value() * 100, Node::numeric_percentage);
}
Function_Descriptor round_descriptor =
{ "round", "$value", 0 };
Node round(const vector<Token>& parameters, map<Token, Node>& bindings, Node_Factory& new_Node) {
Node cpy(bindings[parameters[0]].clone(new_Node));
if (cpy.type() == Node::numeric_dimension) {
cpy.content.dimension.numeric_value = std::floor(cpy.content.dimension.numeric_value + 0.5);
}
else if (cpy.type() == Node::number || cpy.type() == Node::numeric_percentage) {
cpy.numeric_value() = std::floor(cpy.numeric_value() + 0.5);
}
else {
throw_eval_error("argument to round must be numeric", cpy.path(), cpy.line());
Node orig(bindings[parameters[0]]);
switch (orig.type())
{
case Node::numeric_dimension: {
return new_Node(orig.path(), orig.line(),
std::floor(orig.numeric_value() + 0.5), orig.unit());
} break;
case Node::number: {
return new_Node(orig.path(), orig.line(),
std::floor(orig.numeric_value() + 0.5));
} break;
case Node::numeric_percentage: {
return new_Node(orig.path(), orig.line(),
std::floor(orig.numeric_value() + 0.5),
Node::numeric_percentage);
} break;
default: {
throw_eval_error("argument to round must be numeric", orig.path(), orig.line());
} break;
}
return cpy;
// unreachable statement
return Node();
}
Function_Descriptor ceil_descriptor =
{ "ceil", "$value", 0 };
Node ceil(const vector<Token>& parameters, map<Token, Node>& bindings, Node_Factory& new_Node) {
Node cpy(bindings[parameters[0]].clone(new_Node));
if (cpy.type() == Node::numeric_dimension) {
cpy.content.dimension.numeric_value = std::ceil(cpy.content.dimension.numeric_value);
}
else if (cpy.type() == Node::number || cpy.type() == Node::numeric_percentage){
cpy.numeric_value() = std::ceil(cpy.numeric_value());
}
else {
throw_eval_error("argument to ceil must be numeric", cpy.path(), cpy.line());
Node orig(bindings[parameters[0]]);
switch (orig.type())
{
case Node::numeric_dimension: {
return new_Node(orig.path(), orig.line(),
std::ceil(orig.numeric_value()), orig.unit());
} break;
case Node::number: {
return new_Node(orig.path(), orig.line(),
std::ceil(orig.numeric_value()));
} break;
case Node::numeric_percentage: {
return new_Node(orig.path(), orig.line(),
std::ceil(orig.numeric_value()),
Node::numeric_percentage);
} break;
default: {
throw_eval_error("argument to ceil must be numeric", orig.path(), orig.line());
} break;
}
return cpy;
// unreachable statement
return Node();
}
Function_Descriptor floor_descriptor =
{ "floor", "$value", 0 };
Node floor(const vector<Token>& parameters, map<Token, Node>& bindings, Node_Factory& new_Node) {
Node cpy(bindings[parameters[0]].clone(new_Node));
if (cpy.type() == Node::numeric_dimension) {
cpy.content.dimension.numeric_value = std::floor(cpy.content.dimension.numeric_value);
}
else if (cpy.type() == Node::number || cpy.type() == Node::numeric_percentage){
cpy.numeric_value() = std::floor(cpy.numeric_value());
}
else {
throw_eval_error("argument to floor must be numeric", cpy.path(), cpy.line());
Node orig(bindings[parameters[0]]);
switch (orig.type())
{
case Node::numeric_dimension: {
return new_Node(orig.path(), orig.line(),
std::floor(orig.numeric_value()), orig.unit());
} break;
case Node::number: {
return new_Node(orig.path(), orig.line(),
std::floor(orig.numeric_value()));
} break;
case Node::numeric_percentage: {
return new_Node(orig.path(), orig.line(),
std::floor(orig.numeric_value()),
Node::numeric_percentage);
} break;
default: {
throw_eval_error("argument to floor must be numeric", orig.path(), orig.line());
} break;
}
return cpy;
// unreachable statement
return Node();
}
Function_Descriptor abs_descriptor =
{ "abs", "$value", 0 };
Node abs(const vector<Token>& parameters, map<Token, Node>& bindings, Node_Factory& new_Node) {
Node cpy(bindings[parameters[0]].clone(new_Node));
if (cpy.type() == Node::numeric_dimension) {
cpy.content.dimension.numeric_value = std::fabs(cpy.content.dimension.numeric_value);
}
else if (cpy.type() == Node::number || cpy.type() == Node::numeric_percentage){
cpy.numeric_value() = std::abs(cpy.numeric_value());
}
else {
throw_eval_error("argument to abs must be numeric", cpy.path(), cpy.line());
Node orig(bindings[parameters[0]]);
switch (orig.type())
{
case Node::numeric_dimension: {
return new_Node(orig.path(), orig.line(),
std::abs(orig.numeric_value()), orig.unit());
} break;
case Node::number: {
return new_Node(orig.path(), orig.line(),
std::abs(orig.numeric_value()));
} break;
case Node::numeric_percentage: {
return new_Node(orig.path(), orig.line(),
std::abs(orig.numeric_value()),
Node::numeric_percentage);
} break;
default: {
throw_eval_error("argument to abs must be numeric", orig.path(), orig.line());
} break;
}
return cpy;
// unreachable statement
return Node();
}
// List Functions //////////////////////////////////////////////////////
......@@ -354,31 +410,46 @@ namespace Sass {
{ "length", "$list", 0 };
Node length(const vector<Token>& parameters, map<Token, Node>& bindings, Node_Factory& new_Node) {
Node arg(bindings[parameters[0]]);
if (arg.type() == Node::space_list || arg.type() == Node::comma_list) {
return Node(arg.line_number, arg.size());
}
else if (arg.type() == Node::nil) {
return Node(arg.line_number, 0);
}
switch (arg.type())
{
case Node::space_list:
case Node::comma_list: {
return new_Node(arg.path(), arg.line(), arg.size());
} break;
case Node::nil: {
return new_Node(arg.path(), arg.line(), 0);
} break;
default: {
// single objects should be reported as lists of length 1
else {
return Node(arg.line_number, 1);
return new_Node(arg.path(), arg.line(), 1);
} break;
}
// unreachable statement
return Node();
}
Function_Descriptor nth_descriptor =
{ "nth", "$list", "$n", 0 };
Node nth(const vector<Token>& parameters, map<Token, Node>& bindings, Node_Factory& new_Node) {
Node l(bindings[parameters[0]]);
// TO DO: check for empty list
if (l.type() == Node::nil) throw_eval_error("cannot index into an empty list", l.line_number, l.file_name);
Node n(bindings[parameters[1]]);
if (n.type() != Node::number) {
throw_eval_error("second argument to nth must be a number", n.path(), n.line());
}
if (l.type() == Node::nil) {
throw_eval_error("cannot index into an empty list", l.path(), l.line());
}
// wrap the first arg if it isn't a list
if (l.type() != Node::space_list && l.type() != Node::comma_list) {
l = Node(Node::space_list, new_Node, l.line_number, 1) << l;
l = new_Node(Node::space_list, l.path(), l.line(), 1) << l;
}
Node n(bindings[parameters[1]]);
if (n.type() != Node::number) throw_eval_error("second argument to nth must be a number", n.line_number, n.file_name);
if (n.numeric_value() < 1 || n.numeric_value() > l.size()) throw_eval_error("out of range index for nth", n.line_number, n.file_name);
return l[bindings[parameters[1]].numeric_value() - 1];
double n_prim = n.numeric_value();
if (n_prim < 1 || n_prim > l.size()) {
throw_eval_error("out of range index for nth", n.path(), n.line());
}
return l[n_prim - 1];
}
extern const char separator_kwd[] = "$separator";
......@@ -386,33 +457,35 @@ namespace Sass {
// if the args aren't lists, turn them into singleton lists
Node l1(bindings[parameters[0]]);
if (l1.type() != Node::space_list && l1.type() != Node::comma_list && l1.type() != Node::nil) {
l1 = Node(Node::space_list, new_Node, l1.line_number, 1) << l1;
l1 = new_Node(Node::space_list, l1.path(), l1.line(), 1) << l1;
}
Node l2(bindings[parameters[1]]);
if (l2.type() != Node::space_list && l2.type() != Node::comma_list && l2.type() != Node::nil) {
l2 = Node(Node::space_list, new_Node, l2.line_number, 1) << l2;
l2 = new_Node(Node::space_list, l2.path(), l2.line(), 1) << l2;
}
// nil + nil = nil
if (l1.type() == Node::nil && l2.type() == Node::nil) {
return new_Node(Node::nil, l1.path(), l1.line(), 0);
}
// nil and nil is nil
if (l1.type() == Node::nil && l2.type() == Node::nil) return Node(Node::nil, new_Node, l1.line_number);
// figure out the combined size in advance
size_t size = 0;
if (l1.type() != Node::nil) size += l1.size();
if (l2.type() != Node::nil) size += l2.size();
// accumulate the result
Node lr(l1.type(), new_Node, l1.line_number, size);
// figure out the result type in advance
Node::Type rtype;
if (has_sep) {
string sep(bindings[parameters[2]].content.token.unquote());
if (sep == "comma") lr.type() = Node::comma_list;
else if (sep == "space") lr.type() = Node::space_list;
else if (sep == "auto") ; // leave it alone
string sep(bindings[parameters[2]].token().unquote());
if (sep == "comma") rtype = Node::comma_list;
else if (sep == "space") rtype = Node::space_list;
else if (sep == "auto") rtype = l1.type();
else {
throw_eval_error("third argument to join must be 'space', 'comma', or 'auto'", l2.line_number, l2.file_name);
throw_eval_error("third argument to join must be 'space', 'comma', or 'auto'", l2.path(), l2.line());
}
}
else if (l1.type() != Node::nil) lr.type() = l1.type();
else if (l2.type() != Node::nil) lr.type() = l2.type();
else if (l1.type() != Node::nil) rtype = l1.type();
else if (l2.type() != Node::nil) rtype = l2.type();
// accumulate the result
Node lr(new_Node(rtype, l1.path(), l1.line(), size));
if (l1.type() != Node::nil) lr += l1;
if (l2.type() != Node::nil) lr += l2;
return lr;
......@@ -442,33 +515,35 @@ namespace Sass {
{ "type-of", "$value", 0 };
Node type_of(const vector<Token>& parameters, map<Token, Node>& bindings, Node_Factory& new_Node) {
Node val(bindings[parameters[0]]);
Node type(Node::string_constant, val.line_number, Token::make());
type.unquoted = true;
Token type_name;
switch (val.type())
{
case Node::number:
case Node::numeric_dimension:
case Node::numeric_percentage:
type.content.token = Token::make(number_name);
break;
case Node::boolean:
type.content.token = Token::make(bool_name);
break;
case Node::numeric_percentage: {
type_name = Token::make(number_name);
} break;
case Node::boolean: {
type_name = Token::make(bool_name);
} break;
case Node::string_constant:
case Node::value_schema:
type.content.token = Token::make(string_name);
break;
case Node::numeric_color:
type.content.token = Token::make(color_name);
break;
case Node::value_schema: {
type_name = Token::make(string_name);
} break;
case Node::numeric_color: {
type_name = Token::make(color_name);
} break;
case Node::comma_list:
case Node::space_list:
case Node::nil:
type.content.token = Token::make(list_name);
break;
default:
type.content.token = Token::make(string_name);
case Node::nil: {
type_name = Token::make(list_name);
} break;
default: {
type_name = Token::make(string_name);
} break;
}
Node type(new_Node(Node::string_constant, val.path(), val.line(), type_name));
type.is_unquoted() = true;
return type;
}
......@@ -479,23 +554,23 @@ namespace Sass {
{ "unit", "$number", 0 };
Node unit(const vector<Token>& parameters, map<Token, Node>& bindings, Node_Factory& new_Node) {
Node val(bindings[parameters[0]]);
Node u(Node::string_constant, val.line_number, Token::make());
switch (val.type())
{
case Node::number:
u.content.token = Token::make(empty_str);
break;
case Node::numeric_percentage:
u.content.token = Token::make(percent_str);
break;
case Node::number: {
return new_Node(Node::string_constant, val.path(), val.line(), Token::make(empty_str));
} break;
case Node::numeric_dimension:
u.content.token = Token::make(val.content.dimension.unit, Prelexer::identifier(val.content.dimension.unit));
break;
default:
throw_eval_error("argument to unit must be numeric", val.line_number, val.file_name);
break;
case Node::numeric_percentage: {
return new_Node(Node::string_constant, val.path(), val.line(), val.unit());
} break;
default: {
throw_eval_error("argument to unit must be numeric", val.path(), val.line());
} break;
}
return u;
// unreachable statement
return Node();
}
extern const char true_str[] = "true";
......@@ -505,28 +580,23 @@ namespace Sass {
{ "unitless", "$number", 0 };
Node unitless(const vector<Token>& parameters, map<Token, Node>& bindings, Node_Factory& new_Node) {
Node val(bindings[parameters[0]]);
Node result(Node::string_constant, val.line_number, Token::make());
result.unquoted = true;
switch (val.type())
{
case Node::number: {
Node T(Node::boolean);
T.line_number = val.line_number;
T.content.boolean_value = true;
return T;
return new_Node(Node::boolean, val.path(), val.line(), true);
} break;
case Node::numeric_percentage:
case Node::numeric_dimension: {
Node F(Node::boolean);
F.line_number = val.line_number;
F.content.boolean_value = false;
return F;
return new_Node(Node::boolean, val.path(), val.line(), false);
} break;
default: {
throw_eval_error("argument to unitless must be numeric", val.path(), val.line());
} break;
default:
throw_eval_error("argument to unitless must be numeric", val.line_number, val.file_name);
break;
}
return result;
// unreachable statement
return Node();
}
Function_Descriptor comparable_descriptor =
......@@ -536,46 +606,30 @@ namespace Sass {
Node n2(bindings[parameters[1]]);
Node::Type t1 = n1.type();
Node::Type t2 = n2.type();
if (t1 == Node::number && n2.is_numeric() ||
n1.is_numeric() && t2 == Node::number) {
Node T(Node::boolean);
T.line_number = n1.line_number;
T.content.boolean_value = true;
return T;
if (t1 == Node::number && n2.is_numeric() || n1.is_numeric() && t2 == Node::number) {
return new_Node(Node::boolean, n1.path(), n1.line(), true);
}
else if (t1 == Node::numeric_percentage && t2 == Node::numeric_percentage) {
Node T(Node::boolean);
T.line_number = n1.line_number;
T.content.boolean_value = true;
return T;
return new_Node(Node::boolean, n1.path(), n1.line(), true);
}
else if (t1 == Node::numeric_dimension && t2 == Node::numeric_dimension) {
string u1(Token::make(n1.content.dimension.unit, Prelexer::identifier(n1.content.dimension.unit)).to_string());
string u2(Token::make(n2.content.dimension.unit, Prelexer::identifier(n2.content.dimension.unit)).to_string());
string u1(n1.unit().to_string());
string u2(n2.unit().to_string());
if (u1 == "ex" && u2 == "ex" ||
u1 == "em" && u2 == "em" ||
(u1 == "in" || u1 == "cm" || u1 == "mm" || u1 == "pt" || u1 == "pc") &&
(u2 == "in" || u2 == "cm" || u2 == "mm" || u2 == "pt" || u2 == "pc")) {
Node T(Node::boolean);
T.line_number = n1.line_number;
T.content.boolean_value = true;
return T;
return new_Node(Node::boolean, n1.path(), n1.line(), true);
}
else {
Node F(Node::boolean);
F.line_number = n1.line_number;
F.content.boolean_value = false;
return F;
return new_Node(Node::boolean, n1.path(), n1.line(), false);
}
}
else if (!n1.is_numeric() && !n2.is_numeric()) {
throw_eval_error("arguments to comparable must be numeric", n1.line_number, n1.file_name);
throw_eval_error("arguments to comparable must be numeric", n1.path(), n1.line());
}
Node F(Node::boolean);
F.line_number = n1.line_number;
F.content.boolean_value = false;
return F;
// default to false if we missed anything
return new_Node(Node::boolean, n1.path(), n1.line(), false);
}
// Boolean Functions ///////////////////////////////////////////////////
......@@ -583,17 +637,11 @@ namespace Sass {
{ "not", "value", 0 };
Node not_impl(const vector<Token>& parameters, map<Token, Node>& bindings, Node_Factory& new_Node) {
Node val(bindings[parameters[0]]);
if (val.type() == Node::boolean && val.content.boolean_value == false) {
Node T(Node::boolean);
T.line_number = val.line_number;
T.content.boolean_value = true;
return T;
if (val.type() == Node::boolean && val.boolean_value() == false) {
return new_Node(Node::boolean, val.path(), val.line(), true);
}
else {
Node F(Node::boolean);
F.line_number = val.line_number;
F.content.boolean_value = false;
return F;
return new_Node(Node::boolean, val.path(), val.line(), false);
}
}
......
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