Commit e7824065 by Sam Saccone

update libsass

parent 60f66275
#define SASS_BACKTRACE
#include <sstream>
namespace Sass {
using namespace std;
struct Backtrace {
Backtrace* parent;
string path;
size_t line;
string caller;
Backtrace(Backtrace* prn, string pth, size_t ln, string c)
: parent(prn),
path(pth),
line(ln),
caller(c)
{ }
string to_string(bool warning = false)
{
stringstream ss;
Backtrace* this_point = this;
if (!warning) ss << endl << "Backtrace:";
// the first tracepoint (which is parent-less) is an empty placeholder
while (this_point->parent) {
ss << endl
<< "\t"
<< (warning ? " " : "")
<< this_point->path
<< ":"
<< this_point->line
<< this_point->parent->caller;
this_point = this_point->parent;
}
return ss.str();
}
};
}
\ No newline at end of file
......@@ -12,6 +12,7 @@ namespace Sass {
extern const char function_kwd[] = "@function";
extern const char return_kwd[] = "@return";
extern const char include_kwd[] = "@include";
extern const char content_kwd[] = "@content";
extern const char extend_kwd[] = "@extend";
extern const char if_kwd[] = "@if";
extern const char else_kwd[] = "@else";
......@@ -43,6 +44,7 @@ namespace Sass {
extern const char kHz_kwd[] = "kHz";
// css functions and keywords
extern const char charset_kwd[] = "@charset";
extern const char media_kwd[] = "@media";
extern const char only_kwd[] = "only";
extern const char rgb_kwd[] = "rgb(";
......@@ -83,6 +85,7 @@ namespace Sass {
extern const char rbrace[] = "}";
extern const char rparen[] = ")";
extern const char sign_chars[] = "-+";
extern const char hyphen[] = "-";
// type names
extern const char numeric_name[] = "numeric value";
......
......@@ -12,6 +12,7 @@ namespace Sass {
extern const char function_kwd[];
extern const char return_kwd[];
extern const char include_kwd[];
extern const char content_kwd[];
extern const char extend_kwd[];
extern const char if_kwd[];
extern const char else_kwd[];
......@@ -43,6 +44,7 @@ namespace Sass {
extern const char kHz_kwd[];
// css functions and keywords
extern const char charset_kwd[];
extern const char media_kwd[];
extern const char only_kwd[];
extern const char rgb_kwd[];
......@@ -83,6 +85,7 @@ namespace Sass {
extern const char rbrace[];
extern const char rparen[];
extern const char sign_chars[];
extern const char hyphen[];
// type names
extern const char numeric_name[];
......
#ifdef _WIN32
#include <direct.h>
#define getcwd _getcwd
#define PATH_SEP ';'
#else
#include <unistd.h>
#define PATH_SEP ':'
#endif
#include <cstring>
......@@ -27,7 +29,7 @@ namespace Sass {
if (paths_str) {
const char* beg = paths_str;
const char* end = Prelexer::find_first<':'>(beg);
const char* end = Prelexer::find_first<PATH_SEP>(beg);
while (end) {
string path(beg, end - beg);
......@@ -36,7 +38,7 @@ namespace Sass {
include_paths.push_back(path);
}
beg = end + 1;
end = Prelexer::find_first<':'>(beg);
end = Prelexer::find_first<PATH_SEP>(beg);
}
string path(beg);
......@@ -51,19 +53,20 @@ namespace Sass {
// }
}
Context::Context(const char* paths_str, const char* img_path_str)
Context::Context(const char* paths_str, const char* img_path_str, bool sc)
: global_env(Environment()),
function_env(map<string, Function>()),
extensions(multimap<Node, Node>()),
pending_extensions(vector<pair<Node, Node> >()),
source_refs(vector<char*>()),
source_refs(vector<const char*>()),
include_paths(vector<string>()),
color_names_to_values(map<string, Node>()),
color_values_to_names(map<Node, string>()),
new_Node(Node_Factory()),
image_path(0),
ref_count(0),
has_extensions(false)
has_extensions(false),
source_comments(sc)
{
register_functions();
collect_include_paths(paths_str);
......@@ -144,6 +147,7 @@ namespace Sass {
register_function(adjust_color_sig, adjust_color);
register_function(scale_color_sig, scale_color);
register_function(change_color_sig, change_color);
register_function(ie_hex_str_sig, ie_hex_str);
// String Functions
register_function(unquote_sig, unquote);
register_function(quote_sig, quote);
......
......@@ -23,7 +23,7 @@ namespace Sass {
map<string, Function> function_env;
multimap<Node, Node> extensions;
vector<pair<Node, Node> > pending_extensions;
vector<char*> source_refs; // all the source c-strings
vector<const char*> source_refs; // all the source c-strings
vector<string> include_paths;
map<string, Node> color_names_to_values;
map<Node, string> color_values_to_names;
......@@ -33,9 +33,10 @@ namespace Sass {
// string sass_path;
// string css_path;
bool has_extensions;
bool source_comments;
void collect_include_paths(const char* paths_str);
Context(const char* paths_str = 0, const char* img_path_str = 0);
Context(const char* paths_str = 0, const char* img_path_str = 0, bool sc = false);
~Context();
void register_function(Signature sig, Primitive ip);
......
......@@ -37,24 +37,40 @@ namespace Sass {
const char* path_str = path.c_str();
struct stat st;
string tmp;
// Resolution order for ambiguous imports:
// (1) filename as given
// (2) underscore + given
// (3) underscore + given + extension
// (4) given + extension
// if the file as given isn't found ...
if (stat(path_str, &st) == -1 || S_ISDIR(st.st_mode)) {
tmp = path + ".scss";
path_str = tmp.c_str();
if (stat(path_str, &st) == -1 || S_ISDIR(st.st_mode)) {
// then try "_" + given
const char *full_path_str = path.c_str();
const char *file_name_str = Prelexer::folders(full_path_str);
tmp = Token::make(full_path_str, file_name_str).to_string() +
"_" +
string(file_name_str);
string folder(Token::make(full_path_str, file_name_str).to_string());
string partial_filename("_" + string(file_name_str));
tmp = folder + partial_filename;
path_str = tmp.c_str();
// if "_" + given isn't found ...
if (stat(path_str, &st) == -1 || S_ISDIR(st.st_mode)) {
// then try "_" + given + ".scss"
tmp += ".scss";
path_str = tmp.c_str();
// if "_" + given + ".scss" isn't found ...
if (stat(path_str, &st) == -1 || S_ISDIR(st.st_mode)) {
tmp = tmp + ".scss";
// then try given + ".scss"
string non_partial_filename(string(file_name_str) + ".scss");
tmp = folder + non_partial_filename;
path_str = tmp.c_str();
if (stat(path_str, &st) == -1 || S_ISDIR(st.st_mode))
// if we still can't find the file, then throw an error
if (stat(path_str, &st) == -1 || S_ISDIR(st.st_mode)) {
throw path;
}
}
}
}
f = std::fopen(path_str, "rb");
size_t len = st.st_size;
char* source = new char[len + 1];
......@@ -70,7 +86,7 @@ namespace Sass {
string include_path(path_str, file_name_str - path_str);
Document doc(ctx);
doc.path = path;
doc.path = path_str;
doc.line = 1;
doc.root = ctx.new_Node(Node::root, path, 1, 0);
doc.lexed = Token::make();
......@@ -79,14 +95,11 @@ namespace Sass {
doc.end = end;
doc.position = source;
doc.context.source_refs.push_back(source);
if (!include_path.empty()) {
doc.context.include_paths.push_back(include_path);
}
return doc;
}
Document Document::make_from_source_chars(Context& ctx, char* src, string path, bool own_source)
Document Document::make_from_source_chars(Context& ctx, const char* src, string path, bool own_source)
{
Document doc(ctx);
doc.path = path;
......@@ -110,7 +123,7 @@ namespace Sass {
doc.root = ctx.new_Node(Node::root, path, 1, 0);
doc.lexed = Token::make();
doc.own_source = false;
doc.source = const_cast<char*>(t.begin);
doc.source = t.begin;
doc.end = t.end;
doc.position = doc.source;
......@@ -134,11 +147,14 @@ namespace Sass {
root.echo(output);
break;
case nested:
root.emit_nested_css(output, 0, true);
root.emit_nested_css(output, 0, true, false, context.source_comments);
break;
case expanded:
root.emit_expanded_css(output, "");
break;
case compressed:
root.emit_compressed_css(output);
break;
default:
break;
}
......
......@@ -29,7 +29,7 @@ namespace Sass {
enum CSS_Style { nested, expanded, compact, compressed, echo };
string path;
char* source;
const char* source;
const char* position;
const char* end;
size_t line;
......@@ -48,7 +48,7 @@ namespace Sass {
~Document();
static Document make_from_file(Context& ctx, string path);
static Document make_from_source_chars(Context& ctx, char* src, string path = "", bool own_source = false);
static Document make_from_source_chars(Context& ctx, const char* src, string path = "", bool own_source = false);
static Document make_from_token(Context& ctx, Token t, string path = "", size_t line_number = 1);
template <prelexer mx>
......@@ -133,7 +133,7 @@ namespace Sass {
Node parse_function_definition();
Node parse_parameters();
Node parse_parameter(Node::Type);
Node parse_mixin_call();
Node parse_mixin_call(Node::Type inside_of = Node::none);
Node parse_arguments();
Node parse_argument(Node::Type);
Node parse_assignment();
......
......@@ -26,7 +26,7 @@ namespace Sass {
else root += importee;
if (!lex< exactly<';'> >()) throw_syntax_error("top-level @import directive must be terminated by ';'");
}
else if (peek< mixin >() || peek< exactly<'='> >()) {
else if (peek< mixin >() /* || peek< exactly<'='> >() */) {
root << parse_mixin_definition();
}
else if (peek< function >()) {
......@@ -36,15 +36,16 @@ namespace Sass {
root << parse_assignment();
if (!lex< exactly<';'> >()) throw_syntax_error("top-level variable binding must be terminated by ';'");
}
else if (peek< sequence< identifier, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
root << parse_propset();
}
else if ((lookahead_result = lookahead_for_selector(position)).found) {
root << parse_ruleset(lookahead_result);
}
else if (peek< include >() || peek< exactly<'+'> >()) {
root << parse_mixin_call();
if (!lex< exactly<';'> >()) throw_syntax_error("top-level @include directive must be terminated by ';'");
else if (peek< include >() /* || peek< exactly<'+'> >() */) {
Node mixin_call(parse_mixin_call());
root << mixin_call;
if (mixin_call.size() < 3 && !lex< exactly<';'> >()) throw_syntax_error("top-level @include directive must be terminated by ';'");
}
else if (peek< if_directive >()) {
root << parse_if_directive(Node(), Node::none);
......@@ -65,6 +66,11 @@ namespace Sass {
root << parse_warning();
if (!lex< exactly<';'> >()) throw_syntax_error("top-level @warn directive must be terminated by ';'");
}
// ignore the @charset directive for now
else if (lex< exactly< charset_kwd > >()) {
lex< string_constant >();
lex< exactly<';'> >();
}
else if (peek< directive >()) {
Node dir(parse_directive(Node(), Node::none));
if (dir.type() == Node::blockless_directive) {
......@@ -106,8 +112,20 @@ namespace Sass {
}
}
if (!lex< string_constant >()) throw_syntax_error("@import directive requires a url or quoted path");
// TO DO: BETTER PATH HANDLING
string import_path(lexed.unquote());
// Try the folder containing the current file first. If that fails, loop
// through the include-paths.
try {
const char* base_str = path.c_str();
string base_path(Token::make(base_str, Prelexer::folders(base_str)).to_string());
string resolved_path(base_path + import_path);
Document importee(Document::make_from_file(context, resolved_path));
importee.parse_scss();
return importee.root;
}
catch (string& path) {
// suppress the error and try the include paths
}
for (vector<string>::iterator path = context.include_paths.begin(); path < context.include_paths.end(); ++path) {
try {
Document importee(Document::make_from_file(context, *path + import_path));
......@@ -115,16 +133,18 @@ namespace Sass {
return importee.root;
}
catch (string& path) {
// continue looping
}
}
// fail after we've tried all include-paths
throw_read_error("error reading file \"" + import_path + "\"");
// unreached statement
// unreachable statement
return Node();
}
Node Document::parse_mixin_definition()
{
lex< mixin >() || lex< exactly<'='> >();
lex< mixin >() /* || lex< exactly<'='> >() */;
if (!lex< identifier >()) throw_syntax_error("invalid name in @mixin directive");
Node name(context.new_Node(Node::identifier, path, line, lexed));
Node params(parse_parameters());
......@@ -196,14 +216,21 @@ namespace Sass {
return var;
}
Node Document::parse_mixin_call()
Node Document::parse_mixin_call(Node::Type inside_of)
{
lex< include >() || lex< exactly<'+'> >();
lex< include >() /* || lex< exactly<'+'> >() */;
if (!lex< identifier >()) throw_syntax_error("invalid name in @include directive");
Node name(context.new_Node(Node::identifier, path, line, lexed));
Node args(parse_arguments());
Node the_call(context.new_Node(Node::mixin_call, path, line, 2));
Node content;
bool has_content = false;
if (peek< exactly<'{'> >()) {
content = parse_block(Node(), inside_of);
has_content = true;
}
Node the_call(context.new_Node(Node::mixin_call, path, line, has_content ? 3 : 2));
the_call << name << args;
if (has_content) the_call << content;
return the_call;
}
......@@ -277,20 +304,27 @@ namespace Sass {
Node Document::parse_propset()
{
lex< identifier >();
Node property_segment(context.new_Node(Node::identifier, path, line, lexed));
lex< exactly<':'> >();
lex< exactly<'{'> >();
Node block(context.new_Node(Node::block, path, line, 1));
while (!lex< exactly<'}'> >()) {
if (peek< sequence< identifier, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
block << parse_propset();
Node property_segment;
if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
property_segment = parse_identifier_schema();
}
else {
block << parse_rule();
lex< exactly<';'> >();
}
lex< sequence< optional< exactly<'*'> >, identifier > >();
property_segment = context.new_Node(Node::identifier, path, line, lexed);
}
lex< exactly<':'> >();
Node block(parse_block(Node()));
// lex< exactly<'{'> >();
// Node block(context.new_Node(Node::block, path, line, 1));
// while (!lex< exactly<'}'> >()) {
// if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
// block << parse_propset();
// }
// else {
// block << parse_rule();
// lex< exactly<';'> >();
// }
// }
if (block.empty()) throw_syntax_error("namespaced property cannot be empty");
Node propset(context.new_Node(Node::propset, path, line, 2));
propset << property_segment;
......@@ -382,7 +416,7 @@ namespace Sass {
if (lex< exactly<'&'> >()) {
simp1 = context.new_Node(Node::backref, path, line, lexed);
}
else if (lex< alternatives< type_selector, universal > >()) {
else if (lex< alternatives< type_selector, universal, string_constant, number > >()) {
simp1 = context.new_Node(Node::simple_selector, path, line, lexed);
}
else {
......@@ -422,7 +456,7 @@ namespace Sass {
Node Document::parse_simple_selector()
{
if (lex< id_name >() || lex< class_name >()) {
if (lex< id_name >() || lex< class_name >() || lex< string_constant >() || lex< number >()) {
return context.new_Node(Node::simple_selector, path, line, lexed);
}
else if (peek< exactly<':'> >(position)) {
......@@ -454,8 +488,10 @@ namespace Sass {
pseudo << context.new_Node(Node::value, path, line, lexed);
}
else if (peek< binomial >(position)) {
if (peek< coefficient >()) {
lex< coefficient >();
pseudo << context.new_Node(Node::value, path, line, lexed);
}
lex< exactly<'n'> >();
pseudo << context.new_Node(Node::value, path, line, lexed);
lex< sign >();
......@@ -577,19 +613,28 @@ namespace Sass {
throw_syntax_error("only variable declarations and control directives are allowed inside functions");
}
else if (peek< include >(position)) {
block << parse_mixin_call();
block << parse_mixin_call(inside_of);
semicolon = true;
}
else if (lex< content >()) {
if (inside_of != Node::mixin) {
throw_syntax_error("@content may only be used within a mixin");
}
block << context.new_Node(Node::mixin_content, path, line, 0); // just an expansion stub
semicolon = true;
}
else if (peek< sequence< identifier, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
block << parse_propset();
}
else if ((lookahead_result = lookahead_for_selector(position)).found) {
block << parse_ruleset(lookahead_result, inside_of);
}
/*
else if (peek< exactly<'+'> >()) {
block << parse_mixin_call();
semicolon = true;
}
*/
else if (lex< extend >()) {
Node request(context.new_Node(Node::extend_directive, path, line, 1));
Selector_Lookahead lookahead = lookahead_for_extension_target(position);
......@@ -605,6 +650,11 @@ namespace Sass {
else if (peek< media >()) {
block << parse_media_query(inside_of);
}
// ignore the @charset directive for now
else if (lex< exactly< charset_kwd > >()) {
lex< string_constant >();
lex< exactly<';'> >();
}
else if (peek< directive >()) {
Node dir(parse_directive(surrounding_ruleset, inside_of));
if (dir.type() == Node::blockless_directive) semicolon = true;
......@@ -868,11 +918,13 @@ namespace Sass {
result << parse_string();
result.should_eval() = true;
}
else if (lex< url_schema >()) {
else if (peek< sequence< url_schema, spaces_and_comments, exactly<')'> > >()) {
lex< url_schema >();
result << Document::make_from_token(context, lexed, path, line).parse_url_schema();
result.should_eval() = true;
}
else if (lex< url_value >()) {
else if (peek< sequence< url_value, spaces_and_comments, exactly<')'> > >()) {
lex< url_value >();
result << context.new_Node(Node::identifier, path, line, lexed);
}
else {
......
......@@ -13,22 +13,25 @@ namespace Sass {
using namespace Constants;
using std::cerr; using std::endl;
static void throw_eval_error(string message, string path, size_t line)
static void throw_eval_error(Backtrace& bt, string message, string path, size_t line)
{
if (!path.empty() && Prelexer::string_constant(path.c_str()))
path = path.substr(1, path.size() - 1);
Backtrace top(&bt, path, line, "");
message += top.to_string();
throw Error(Error::evaluation, path, line, message);
}
// 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)
void expand(Node expr, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt, bool function_name, const Node content)
{
switch (expr.type())
{
case Node::root: {
for (size_t i = 0, S = expr.size(); i < S; ++i) {
expand(expr[i], prefix, env, f_env, new_Node, ctx);
expand(expr[i], prefix, env, f_env, new_Node, ctx, bt, false, content);
}
} break;
......@@ -43,25 +46,54 @@ namespace Sass {
case Node::mixin_call: { // 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 this_content = expr.size() == 3 ? expr[2] : Node();
if (!this_content.is_null()) expand(this_content, prefix, env, f_env, new_Node, ctx, bt, false, content);
if (!env.query(name)) throw_eval_error(bt, "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));
Backtrace here(&bt, expr.path(), expr.line(), ", in mixin '" + name.to_string() + "'");
Node expansion(apply_mixin(mixin, args, this_content, prefix, env, f_env, new_Node, ctx, here));
expr.pop_all(); // pop the mixin metadata
expr += expansion; // push the expansion
} break;
case Node::mixin_content: {
expr += new_Node(content);
for (size_t i = 0, S = expr.size(); i < S; ++i) {
re_expand(expr[i], prefix, env, f_env, new_Node, ctx, bt, false, content);
}
} break;
case Node::propset: {
// TO DO: perform the property expansion here, rather than in the emitter (also requires the parser to allow interpolants in the property names)
expand(expr[1], prefix, env, f_env, new_Node, ctx);
expr[0] = eval(expr[0], prefix, env, f_env, new_Node, ctx, bt);
expand(expr[1], prefix, env, f_env, new_Node, ctx, bt, false, content);
// Node block(expr[1]);
// for (size_t i = 0, S = block.size(); i < S; ++i) {
// Node stm(block[i]);
// switch (stm.type())
// {
// case Node::propset:
// case Node::mixin_call:
// case Node::if_directive:
// case Node::for_through_directive:
// case Node::for_to_directive:
// case Node::each_directive:
// case Node::while_directive:
// case Node::warning: {
// expand(stm, prefix, env, f_env, new_Node, ctx, bt, false, content);
// } break;
// }
// }
} break;
case Node::ruleset: {
// if the selector contains interpolants, eval it and re-parse
if (expr[0].type() == Node::selector_schema) {
if (!expr[0].is_null() && expr[0].type() == Node::selector_schema) {
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);
schema[i] = eval(schema[i], prefix, env, f_env, new_Node, ctx, bt);
if (schema[i].type() == Node::string_constant) {
expansion += schema[i].token().unquote();
}
......@@ -78,52 +110,22 @@ namespace Sass {
expr[0] = needs_reparsing.parse_selector_group();
}
// expand the selector with the prefix and save it in expr[2]
// Expand the selector with the prefix and save it in expr[2].
expr << expand_selector(expr[0], prefix, new_Node);
// // gather selector extensions into a pending queue
// if (ctx.has_extensions) {
// // check single selector
// if (expr.back().type() != Node::selector_group) {
// Node sel(selector_base(expr.back()));
// if (ctx.extensions.count(sel)) {
// for (multimap<Node, Node>::iterator i = ctx.extensions.lower_bound(sel); i != ctx.extensions.upper_bound(sel); ++i) {
// ctx.pending_extensions.push_back(pair<Node, Node>(expr, i->second));
// }
// }
// }
// // individually check each selector in a group
// else {
// Node group(expr.back());
// for (size_t i = 0, S = group.size(); i < S; ++i) {
// Node sel(selector_base(group[i]));
// if (ctx.extensions.count(sel)) {
// for (multimap<Node, Node>::iterator j = ctx.extensions.lower_bound(sel); j != ctx.extensions.upper_bound(sel); ++j) {
// ctx.pending_extensions.push_back(pair<Node, Node>(expr, j->second));
// }
// }
// }
// }
// }
// expand the body with the newly expanded selector as the prefix
// cerr << "ORIGINAL SELECTOR:\t" << expr[2].to_string() << endl;
// cerr << "NORMALIZED SELECTOR:\t" << normalize_selector(expr[2], new_Node).to_string() << endl << endl;
expand(expr[1], expr.back(), env, f_env, new_Node, ctx);
expand(expr[1], expr.back(), env, f_env, new_Node, ctx, bt, false, content);
} break;
case Node::media_query: {
Node block(expr[1]);
Node new_ruleset(new_Node(Node::ruleset, expr.path(), expr.line(), 3));
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);
expand(expr[1], prefix, env, f_env, new_Node, ctx, bt, false, content);
expr << prefix;
} break;
case Node::block: {
Environment new_frame;
new_frame.link(env);
for (size_t i = 0, S = expr.size(); i < S; ++i) {
expand(expr[i], prefix, new_frame, f_env, new_Node, ctx);
expand(expr[i], prefix, new_frame, f_env, new_Node, ctx, bt, false, content);
}
} break;
......@@ -133,11 +135,11 @@ namespace Sass {
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);
if (val[i].should_eval()) val[i] = eval(val[i], prefix, env, f_env, new_Node, ctx, bt);
}
}
else {
val = eval(val, prefix, env, f_env, new_Node, ctx);
val = eval(val, prefix, env, f_env, new_Node, ctx, bt);
}
// If a binding exists (possibly upframe), then update it.
......@@ -153,30 +155,33 @@ namespace Sass {
case Node::rule: {
Node lhs(expr[0]);
if (lhs.is_schema()) {
expr[0] = eval(lhs, prefix, env, f_env, new_Node, ctx);
expr[0] = eval(lhs, prefix, env, f_env, new_Node, ctx, bt);
}
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);
rhs[i] = eval(rhs[i], prefix, env, f_env, new_Node, ctx, bt);
}
}
}
else if (rhs.is_schema() || rhs.should_eval()) {
expr[1] = eval(rhs, prefix, env, f_env, new_Node, ctx);
expr[1] = eval(rhs, prefix, env, f_env, new_Node, ctx, bt);
}
if (expr[1].type() == Node::list && expr[1].empty()) {
throw_eval_error(bt, "the value of a style property may not be empty", expr[1].path(), expr[1].line());
}
} break;
case Node::extend_directive: {
if (prefix.is_null()) throw_eval_error("@extend directive may only be used within rules", expr.path(), expr.line());
if (prefix.is_null()) throw_eval_error(bt, "@extend directive may only be used within rules", expr.path(), expr.line());
// if the extendee contains interpolants, eval it and re-parse
if (expr[0].type() == Node::selector_schema) {
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);
schema[i] = eval(schema[i], prefix, env, f_env, new_Node, ctx, bt);
if (schema[i].type() == Node::string_constant) {
expansion += schema[i].token().unquote();
}
......@@ -197,10 +202,10 @@ namespace Sass {
switch (expr[0].type())
{
case Node::selector_group:
throw_eval_error("selector groups may not be extended", expr[0].path(), expr[0].line());
throw_eval_error(bt, "selector groups may not be extended", expr[0].path(), expr[0].line());
break;
case Node::selector:
throw_eval_error("nested selectors may not be extended", expr[0].path(), expr[0].line());
throw_eval_error(bt, "nested selectors may not be extended", expr[0].path(), expr[0].line());
break;
default:
break;
......@@ -225,14 +230,14 @@ namespace Sass {
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));
Node predicate_val(eval(expr[i], prefix, env, f_env, new_Node, ctx, bt));
if (!predicate_val.is_false()) {
expand(expansion = expr[i+1], prefix, env, f_env, new_Node, ctx);
expand(expansion = expr[i+1], prefix, env, f_env, new_Node, ctx, bt, false, content);
break;
}
}
else {
expand(expansion = expr[i], prefix, env, f_env, new_Node, ctx);
expand(expansion = expr[i], prefix, env, f_env, new_Node, ctx, bt, false, content);
break;
}
}
......@@ -247,10 +252,10 @@ namespace Sass {
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));
Node lower_bound(eval(expr[1], prefix, env, f_env, new_Node, ctx, bt));
Node upper_bound(eval(expr[2], prefix, env, f_env, new_Node, ctx, bt));
if (!(lower_bound.is_numeric() && upper_bound.is_numeric())) {
throw_eval_error("bounds of @for directive must be numeric", expr.path(), expr.line());
throw_eval_error(bt, "bounds of @for directive must be numeric", expr.path(), expr.line());
}
expr.pop_all();
for (double i = lower_bound.numeric_value(),
......@@ -260,7 +265,7 @@ namespace Sass {
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);
expr += apply_mixin(fake_mixin, fake_arg, content, prefix, env, f_env, new_Node, ctx, bt, true);
}
} break;
......@@ -270,7 +275,7 @@ namespace Sass {
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));
Node list(eval(expr[1], prefix, env, f_env, new_Node, ctx, bt));
// 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);
......@@ -279,8 +284,8 @@ namespace Sass {
for (size_t i = 0, S = list.size(); i < S; ++i) {
Node fake_arg(new_Node(Node::arguments, expr.path(), expr.line(), 1));
list[i].should_eval() = true;
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);
fake_arg << eval(list[i], prefix, env, f_env, new_Node, ctx, bt);
expr += apply_mixin(fake_mixin, fake_arg, content, prefix, env, f_env, new_Node, ctx, bt, true);
}
} break;
......@@ -294,20 +299,20 @@ namespace Sass {
Node pred(expr[0]);
expr.pop_back();
expr.pop_back();
Node ev_pred(eval(pred, prefix, env, f_env, new_Node, ctx));
Node ev_pred(eval(pred, prefix, env, f_env, new_Node, ctx, bt));
while (!ev_pred.is_false()) {
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);
expr += apply_mixin(fake_mixin, fake_arg, content, prefix, env, f_env, new_Node, ctx, bt, true);
ev_pred = eval(pred, prefix, env, f_env, new_Node, ctx, bt);
}
} 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);
expand(expr[1], new_Node(Node::none, expr.path(), expr.line(), 0), env, f_env, new_Node, ctx, bt, false, content);
} break;
case Node::warning: {
Node contents(eval(expr[0], Node(), env, f_env, new_Node, ctx));
Node contents(eval(expr[0], Node(), env, f_env, new_Node, ctx, bt));
string prefix("WARNING: ");
string indent(" ");
......@@ -316,8 +321,10 @@ namespace Sass {
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 << prefix << result;
// cerr << indent << "on line " << expr.line() << " of " << expr.path();
Backtrace top(&bt, expr.path(), expr.line(), "");
cerr << top.to_string(true);
cerr << endl << endl;
} break;
......@@ -328,16 +335,54 @@ namespace Sass {
}
}
void expand_list(Node list, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx)
void re_expand(Node expr, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt, bool function_name, const Node content)
{
switch (expr.type())
{
case Node::ruleset: {
expr[2] = expand_selector(expr[0], prefix, new_Node);
re_expand(expr[1], expr[2], env, f_env, new_Node, ctx, bt, false, content);
} break;
case Node::block:
case Node::mixin_call:
case Node::mixin_content:
case Node::if_directive:
case Node::for_through_directive:
case Node::for_to_directive:
case Node::each_directive:
case Node::while_directive: {
for (size_t i = 0, S = expr.size(); i < S; ++i) {
re_expand(expr[i], prefix, env, f_env, new_Node, ctx, bt, false, content);
}
} break;
case Node::media_query: {
expr[2] = prefix;
re_expand(expr[1], prefix, env, f_env, new_Node, ctx, bt, false, content);
} break;
case Node::block_directive: {
re_expand(expr[1], prefix, env, f_env, new_Node, ctx, bt, false, content);
} break;
default: {
return;
} break;
}
return;
}
void expand_list(Node list, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt)
{
for (size_t i = 0, S = list.size(); i < S; ++i) {
list[i].should_eval() = true;
list[i] = eval(list[i], prefix, env, f_env, new_Node, ctx);
list[i] = eval(list[i], prefix, env, f_env, new_Node, ctx, bt);
}
}
// 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 eval(Node expr, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt, bool function_name)
{
Node result = Node();
switch (expr.type())
......@@ -346,14 +391,14 @@ namespace Sass {
if (expr.should_eval() && expr.size() > 0) {
result = new_Node(Node::list, expr.path(), expr.line(), expr.size());
result.is_comma_separated() = expr.is_comma_separated();
result << eval(expr[0], prefix, env, f_env, new_Node, ctx);
result << eval(expr[0], prefix, env, f_env, new_Node, ctx, bt);
for (size_t i = 1, S = expr.size(); i < S; ++i) result << expr[i];
}
} break;
case Node::disjunction: {
for (size_t i = 0, S = expr.size(); i < S; ++i) {
result = eval(expr[i], prefix, env, f_env, new_Node, ctx);
result = eval(expr[i], prefix, env, f_env, new_Node, ctx, bt);
if (result.is_false()) continue;
else break;
}
......@@ -361,7 +406,7 @@ namespace Sass {
case Node::conjunction: {
for (size_t i = 0, S = expr.size(); i < S; ++i) {
result = eval(expr[i], prefix, env, f_env, new_Node, ctx);
result = eval(expr[i], prefix, env, f_env, new_Node, ctx, bt);
if (result.is_false()) break;
}
} break;
......@@ -373,12 +418,12 @@ namespace Sass {
lhs << expr[0];
rhs << expr[2];
lhs = eval_arguments(lhs, prefix, env, f_env, new_Node, ctx);
rhs = eval_arguments(rhs, prefix, env, f_env, new_Node, ctx);
lhs = eval_arguments(lhs, prefix, env, f_env, new_Node, ctx, bt);
rhs = eval_arguments(rhs, prefix, env, f_env, new_Node, ctx, bt);
lhs = lhs[0];
rhs = rhs[0];
if (lhs.type() == Node::list) expand_list(lhs, prefix, env, f_env, new_Node, ctx);
if (rhs.type() == Node::list) expand_list(rhs, prefix, env, f_env, new_Node, ctx);
if (lhs.type() == Node::list) expand_list(lhs, prefix, env, f_env, new_Node, ctx, bt);
if (rhs.type() == Node::list) expand_list(rhs, prefix, env, f_env, new_Node, ctx, bt);
Node T(new_Node(Node::boolean, lhs.path(), lhs.line(), true));
Node F(new_Node(Node::boolean, lhs.path(), lhs.line(), false));
......@@ -392,25 +437,25 @@ namespace Sass {
case Node::lt: result = ((lhs < rhs) ? T : F); break;
case Node::lte: result = ((lhs <= rhs) ? T : F); break;
default:
throw_eval_error("unknown comparison operator " + expr.token().to_string(), expr.path(), expr.line());
throw_eval_error(bt, "unknown comparison operator " + expr.token().to_string(), expr.path(), expr.line());
}
} 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) {
list << eval(expr[i], prefix, env, f_env, new_Node, ctx);
list << eval(expr[i], prefix, env, f_env, new_Node, ctx, bt);
}
result = reduce(list, 1, list[0], new_Node);
result = reduce(list, 1, list[0], new_Node, bt);
} 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) {
list << eval(expr[i], prefix, env, f_env, new_Node, ctx);
list << eval(expr[i], prefix, env, f_env, new_Node, ctx, bt);
}
result = reduce(list, 1, list[0], new_Node);
result = reduce(list, 1, list[0], new_Node, bt);
}
} break;
......@@ -446,31 +491,31 @@ namespace Sass {
} break;
case Node::variable: {
if (!env.query(expr.token())) throw_eval_error("reference to unbound variable " + expr.token().to_string(), expr.path(), expr.line());
if (!env.query(expr.token())) throw_eval_error(bt, "reference to unbound variable " + expr.token().to_string(), expr.path(), expr.line());
result = env[expr.token()];
} break;
case Node::uri: {
result = new_Node(Node::uri, expr.path(), expr.line(), 1);
result << eval(expr[0], prefix, env, f_env, new_Node, ctx);
result << eval(expr[0], prefix, env, f_env, new_Node, ctx, bt);
} 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
Node name_node(eval(expr[0], prefix, env, f_env, new_Node, ctx, true));
Node name_node(eval(expr[0], prefix, env, f_env, new_Node, ctx, bt, 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) {
evaluated_args << eval(args[i], prefix, env, f_env, new_Node, ctx);
evaluated_args << eval(args[i], prefix, env, f_env, new_Node, ctx, bt);
if (evaluated_args.back().type() == Node::list) {
Node arg_list(evaluated_args.back());
for (size_t j = 0, S = arg_list.size(); j < S; ++j) {
if (arg_list[j].should_eval()) arg_list[j] = eval(arg_list[j], prefix, env, f_env, new_Node, ctx);
if (arg_list[j].should_eval()) arg_list[j] = eval(arg_list[j], prefix, env, f_env, new_Node, ctx, bt);
}
}
}
......@@ -478,21 +523,22 @@ namespace Sass {
result << name_node << evaluated_args;
}
else {
// check to see if the function is primitive/built-in
// check to see if the function is an overloaded primitive
Function f(f_env[name]);
if (f.overloaded) {
stringstream s;
s << name << " " << expr[1].size();
string resolved_name(s.str());
if (!f_env.count(resolved_name)) throw_eval_error("wrong number of arguments to " + name, expr.path(), expr.line());
if (!f_env.count(resolved_name)) throw_eval_error(bt, "wrong number of arguments to " + name, expr.path(), expr.line());
f = f_env[resolved_name];
}
result = apply_function(f, expr[1], prefix, env, f_env, new_Node, ctx, expr.path(), expr.line());
Backtrace here(&bt, expr.path(), expr.line(), ", in function '" + name + "'");
result = apply_function(f, expr[1], prefix, env, f_env, new_Node, ctx, here, expr.path(), expr.line());
}
} break;
case Node::unary_plus: {
Node arg(eval(expr[0], prefix, env, f_env, new_Node, ctx));
Node arg(eval(expr[0], prefix, env, f_env, new_Node, ctx, bt));
if (arg.is_numeric()) {
result = arg;
}
......@@ -503,9 +549,26 @@ namespace Sass {
} break;
case Node::unary_minus: {
Node arg(eval(expr[0], prefix, env, f_env, new_Node, ctx));
Node arg(eval(expr[0], prefix, env, f_env, new_Node, ctx, bt));
if (arg.is_numeric()) {
result = new_Node(expr.path(), expr.line(), -arg.numeric_value());
double neg_val = -arg.numeric_value();
Node::Type t = arg.type();
switch (t)
{
case Node::number:
case Node::numeric_percentage: {
result = new_Node(expr.path(), expr.line(), neg_val, t);
} break;
case Node::numeric_dimension: {
result = new_Node(expr.path(), expr.line(), neg_val, arg.unit());
} break;
default: {
// unreachable
result = arg;
} break;
}
}
else {
result = new_Node(Node::unary_minus, expr.path(), expr.line(), 1);
......@@ -535,14 +598,14 @@ namespace Sass {
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) {
result << eval(expr[i], prefix, env, f_env, new_Node, ctx);
result << eval(expr[i], prefix, env, f_env, new_Node, ctx, bt);
}
result.is_quoted() = expr.is_quoted();
} break;
case Node::css_import: {
result = new_Node(Node::css_import, expr.path(), expr.line(), 1);
result << eval(expr[0], prefix, env, f_env, new_Node, ctx);
result << eval(expr[0], prefix, env, f_env, new_Node, ctx, bt);
} break;
default: {
......@@ -556,7 +619,7 @@ namespace Sass {
// Reduce arithmetic operations. Arithmetic expressions are stored as vectors
// of operands with operators interspersed, 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, Backtrace& bt)
{
if (head >= list.size()) return acc;
Node op(list[head]);
......@@ -565,28 +628,43 @@ namespace Sass {
Node::Type ltype = acc.type();
Node::Type rtype = rhs.type();
if (ltype == Node::number && rtype == Node::number) {
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value()));
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value(), bt));
}
else if (ltype == Node::number && rtype == Node::numeric_dimension) {
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value()), rhs.unit());
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value(), bt), rhs.unit());
}
else if (ltype == Node::number && rtype == Node::numeric_percentage) {
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value(), bt), Node::numeric_percentage);
}
else if (ltype == Node::numeric_dimension && rtype == Node::number) {
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value()), acc.unit());
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value(), bt), acc.unit());
}
else if (ltype == Node::numeric_percentage && rtype == Node::number) {
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value(), bt), Node::numeric_percentage);
}
else if (ltype == Node::numeric_dimension && rtype == Node::numeric_dimension) {
// TO DO: TRUE UNIT ARITHMETIC
if (optype == Node::div) {
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value()));
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value(), bt));
}
else {
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value(), bt), acc.unit());
}
}
else if (ltype == Node::numeric_percentage && rtype == Node::numeric_percentage) {
// TO DO: TRUE UNIT ARITHMETIC
if (optype == Node::div) {
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value(), bt));
}
else {
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value()), acc.unit());
acc = new_Node(list.path(), list.line(), operate(op, acc.numeric_value(), rhs.numeric_value(), bt), Node::numeric_percentage);
}
}
else if (ltype == Node::number && rtype == Node::numeric_color) {
if (optype == Node::add || optype == Node::mul) {
double r = operate(op, acc.numeric_value(), rhs[0].numeric_value());
double g = operate(op, acc.numeric_value(), rhs[1].numeric_value());
double b = operate(op, acc.numeric_value(), rhs[2].numeric_value());
double r = operate(op, acc.numeric_value(), rhs[0].numeric_value(), bt);
double g = operate(op, acc.numeric_value(), rhs[1].numeric_value(), bt);
double b = operate(op, acc.numeric_value(), rhs[2].numeric_value(), bt);
double a = rhs[3].numeric_value();
acc = new_Node(list.path(), list.line(), r, g, b, a);
}
......@@ -597,17 +675,17 @@ namespace Sass {
}
}
else if (ltype == Node::numeric_color && rtype == Node::number) {
double r = operate(op, acc[0].numeric_value(), rhs.numeric_value());
double g = operate(op, acc[1].numeric_value(), rhs.numeric_value());
double b = operate(op, acc[2].numeric_value(), rhs.numeric_value());
double r = operate(op, acc[0].numeric_value(), rhs.numeric_value(), bt);
double g = operate(op, acc[1].numeric_value(), rhs.numeric_value(), bt);
double b = operate(op, acc[2].numeric_value(), rhs.numeric_value(), bt);
double a = acc[3].numeric_value();
acc = new_Node(list.path(), list.line(), r, g, b, a);
}
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());
double r = operate(op, acc[0].numeric_value(), rhs[0].numeric_value());
double g = operate(op, acc[1].numeric_value(), rhs[1].numeric_value());
double b = operate(op, acc[2].numeric_value(), rhs[2].numeric_value());
if (acc[3].numeric_value() != rhs[3].numeric_value()) throw_eval_error(bt, "alpha channels must be equal for " + acc.to_string() + " + " + rhs.to_string(), acc.path(), acc.line());
double r = operate(op, acc[0].numeric_value(), rhs[0].numeric_value(), bt);
double g = operate(op, acc[1].numeric_value(), rhs[1].numeric_value(), bt);
double b = operate(op, acc[2].numeric_value(), rhs[2].numeric_value(), bt);
double a = acc[3].numeric_value();
acc = new_Node(list.path(), list.line(), r, g, b, a);
}
......@@ -657,11 +735,11 @@ namespace Sass {
}
acc.is_quoted() = false;
}
return reduce(list, head + 2, acc, new_Node);
return reduce(list, head + 2, acc, new_Node, bt);
}
// Helper for doing the actual arithmetic.
double operate(Node op, double lhs, double rhs)
double operate(Node op, double lhs, double rhs, Backtrace& bt)
{
switch (op.type())
{
......@@ -669,24 +747,24 @@ namespace Sass {
case Node::sub: return lhs - rhs; break;
case Node::mul: return lhs * rhs; break;
case Node::div: {
if (rhs == 0) throw_eval_error("divide by zero", op.path(), op.line());
if (rhs == 0) throw_eval_error(bt, "divide by zero", op.path(), op.line());
return lhs / rhs;
} break;
default: return 0; break;
}
}
Node eval_arguments(Node args, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx)
Node eval_arguments(Node args, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt)
{
Node evaluated_args(new_Node(Node::arguments, args.path(), args.line(), args.size()));
for (size_t i = 0, S = args.size(); i < S; ++i) {
if (args[i].type() != Node::assignment) {
evaluated_args << eval(args[i], prefix, env, f_env, new_Node, ctx);
evaluated_args << eval(args[i], prefix, env, f_env, new_Node, ctx, bt);
if (evaluated_args.back().type() == Node::list) {
Node arg_list(evaluated_args.back());
Node new_arg_list(new_Node(Node::list, arg_list.path(), arg_list.line(), arg_list.size()));
for (size_t j = 0, S = arg_list.size(); j < S; ++j) {
if (arg_list[j].should_eval()) new_arg_list << eval(arg_list[j], prefix, env, f_env, new_Node, ctx);
if (arg_list[j].should_eval()) new_arg_list << eval(arg_list[j], prefix, env, f_env, new_Node, ctx, bt);
else new_arg_list << arg_list[j];
}
}
......@@ -694,12 +772,12 @@ namespace Sass {
else {
Node kwdarg(new_Node(Node::assignment, args[i].path(), args[i].line(), 2));
kwdarg << args[i][0];
kwdarg << eval(args[i][1], prefix, env, f_env, new_Node, ctx);
kwdarg << eval(args[i][1], prefix, env, f_env, new_Node, ctx, bt);
if (kwdarg.back().type() == Node::list) {
Node arg_list(kwdarg.back());
Node new_arg_list(new_Node(Node::list, arg_list.path(), arg_list.line(), arg_list.size()));
for (size_t j = 0, S = arg_list.size(); j < S; ++j) {
if (arg_list[j].should_eval()) new_arg_list << eval(arg_list[j], prefix, env, f_env, new_Node, ctx);
if (arg_list[j].should_eval()) new_arg_list << eval(arg_list[j], prefix, env, f_env, new_Node, ctx, bt);
else new_arg_list << arg_list[j];
}
kwdarg[1] = new_arg_list;
......@@ -711,22 +789,22 @@ namespace Sass {
for (size_t i = 0, S = evaluated_args.size(); i < S; ++i) {
if (evaluated_args[i].type() != Node::assignment) {
evaluated_args[i].should_eval() = true;
evaluated_args[i] = eval(evaluated_args[i], prefix, env, f_env, new_Node, ctx);
evaluated_args[i] = eval(evaluated_args[i], prefix, env, f_env, new_Node, ctx, bt);
if (evaluated_args[i].type() == Node::list) {
Node arg_list(evaluated_args[i]);
for (size_t j = 0, S = arg_list.size(); j < S; ++j) {
if (arg_list[j].should_eval()) arg_list[j] = eval(arg_list[j], prefix, env, f_env, new_Node, ctx);
if (arg_list[j].should_eval()) arg_list[j] = eval(arg_list[j], prefix, env, f_env, new_Node, ctx, bt);
}
}
}
else {
Node kwdarg(evaluated_args[i]);
kwdarg[1].should_eval() = true;
kwdarg[1] = eval(kwdarg[1], prefix, env, f_env, new_Node, ctx);
kwdarg[1] = eval(kwdarg[1], prefix, env, f_env, new_Node, ctx, bt);
if (kwdarg[1].type() == Node::list) {
Node arg_list(kwdarg[1]);
for (size_t j = 0, S = arg_list.size(); j < S; ++j) {
if (arg_list[j].should_eval()) arg_list[j] = eval(arg_list[j], prefix, env, f_env, new_Node, ctx);
if (arg_list[j].should_eval()) arg_list[j] = eval(arg_list[j], prefix, env, f_env, new_Node, ctx, bt);
}
}
evaluated_args[i] = kwdarg;
......@@ -738,7 +816,7 @@ namespace Sass {
// Helper function for binding arguments in function and mixin calls.
// Needs the environment containing the bindings to be passed in by the
// caller. Also expects the caller to have pre-evaluated the arguments.
void bind_arguments(string callee_name, const Node params, const Node args, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx)
void bind_arguments(string callee_name, const Node params, const Node args, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt)
{
// populate the env with the names of the parameters so we can check for
// correctness further down
......@@ -753,7 +831,7 @@ namespace Sass {
if (j >= num_params) {
stringstream msg;
msg << callee_name << " only takes " << num_params << " arguments";
throw_eval_error(msg.str(), args.path(), args.line());
throw_eval_error(bt, msg.str(), args.path(), args.line());
}
Node arg(args[i]), param(params[j]);
// ordinal argument; just bind and keep going
......@@ -766,10 +844,10 @@ namespace Sass {
Token arg_name(arg[0].token());
Node arg_value(arg[1]);
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(bt, callee_name + " has no parameter named " + arg_name.to_string(), arg.path(), arg.line());
}
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());
throw_eval_error(bt, callee_name + " was passed argument " + arg_name.to_string() + " both by position and by name", arg.path(), arg.line());
}
env[arg_name] = arg_value;
++args_bound;
......@@ -781,10 +859,10 @@ namespace Sass {
Token param_name((param.type() == Node::assignment ? param[0] : param).token());
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());
throw_eval_error(bt, callee_name + " is missing argument " + param_name.to_string(), args.path(), args.line());
}
// eval default values in an environment where the previous vals have already been evaluated
env[param_name] = eval(param[1], prefix, env, f_env, new_Node, ctx);
env[param_name] = eval(param[1], prefix, env, f_env, new_Node, ctx, bt);
}
}
}
......@@ -792,11 +870,11 @@ namespace Sass {
// Apply a mixin -- bind the arguments in a new environment, link the new
// environment to the current one, then copy the body and eval in the new
// environment.
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)
Node apply_mixin(Node mixin, const Node args, const Node content, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt, bool dynamic_scope)
{
Node params(mixin[1]);
Node body(new_Node(mixin[2])); // clone the body
Node evaluated_args(eval_arguments(args, prefix, env, f_env, new_Node, ctx));
Node evaluated_args(eval_arguments(args, prefix, env, f_env, new_Node, ctx, bt));
// Create a new environment for the mixin and link it to the appropriate parent
Environment bindings;
if (dynamic_scope) {
......@@ -811,29 +889,29 @@ namespace Sass {
stringstream mixin_name;
mixin_name << "mixin";
if (!mixin[0].is_null()) mixin_name << " " << mixin[0].to_string();
bind_arguments(mixin_name.str(), params, evaluated_args, prefix, bindings, f_env, new_Node, ctx);
bind_arguments(mixin_name.str(), params, evaluated_args, prefix, bindings, f_env, new_Node, ctx, bt);
// evaluate the mixin's body
expand(body, prefix, bindings, f_env, new_Node, ctx);
expand(body, prefix, bindings, f_env, new_Node, ctx, bt, false, content);
return body;
}
// Apply a function -- bind the arguments and pass them to the underlying
// primitive function implementation, then return its value.
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, Backtrace& bt, string& path, size_t line)
{
Node evaluated_args(eval_arguments(args, prefix, env, f_env, new_Node, ctx));
Node evaluated_args(eval_arguments(args, prefix, env, f_env, new_Node, ctx, bt));
// bind arguments
Environment bindings;
Node params(f.primitive ? f.parameters : f.definition[1]);
bindings.link(env.global ? *env.global : env);
bind_arguments("function " + f.name, params, evaluated_args, prefix, bindings, f_env, new_Node, ctx);
bind_arguments("function " + f.name, params, evaluated_args, prefix, bindings, f_env, new_Node, ctx, bt);
if (f.primitive) {
return f.primitive(f.parameter_names, bindings, new_Node, path, line);
return f.primitive(f.parameter_names, bindings, new_Node, bt, path, line);
}
else {
// TO DO: consider cloning the function body?
return eval_function(f.name, f.definition[2], bindings, new_Node, ctx, true);
return eval_function(f.name, f.definition[2], bindings, new_Node, ctx, bt, true);
}
}
......@@ -841,7 +919,7 @@ namespace Sass {
// algorithm is different in this case because the body needs to be
// executed and a single value needs to be returned directly, rather than
// styles being expanded and spliced in place.
Node eval_function(string name, Node body, Environment& bindings, Node_Factory& new_Node, Context& ctx, bool at_toplevel)
Node eval_function(string name, Node body, Environment& bindings, Node_Factory& new_Node, Context& ctx, Backtrace& bt, bool at_toplevel)
{
for (size_t i = 0, S = body.size(); i < S; ++i) {
Node stm(body[i]);
......@@ -854,12 +932,12 @@ namespace Sass {
newval = new_Node(Node::list, val.path(), val.line(), val.size());
newval.is_comma_separated() = val.is_comma_separated();
for (size_t i = 0, S = val.size(); i < S; ++i) {
if (val[i].should_eval()) newval << eval(val[i], Node(), bindings, ctx.function_env, new_Node, ctx);
if (val[i].should_eval()) newval << eval(val[i], Node(), bindings, ctx.function_env, new_Node, ctx, bt);
else newval << val[i];
}
}
else {
newval = eval(val, Node(), bindings, ctx.function_env, new_Node, ctx);
newval = eval(val, Node(), bindings, ctx.function_env, new_Node, ctx, bt);
}
Node var(stm[0]);
if (stm.is_guarded() && bindings.query(var.token())) continue;
......@@ -876,15 +954,15 @@ namespace Sass {
case Node::if_directive: {
for (size_t j = 0, S = stm.size(); j < S; j += 2) {
if (stm[j].type() != Node::block) {
Node predicate_val(eval(stm[j], Node(), bindings, ctx.function_env, new_Node, ctx));
Node predicate_val(eval(stm[j], Node(), bindings, ctx.function_env, new_Node, ctx, bt));
if (!predicate_val.is_false()) {
Node v(eval_function(name, stm[j+1], bindings, new_Node, ctx));
Node v(eval_function(name, stm[j+1], bindings, new_Node, ctx, bt));
if (v.is_null()) break;
else return v;
}
}
else {
Node v(eval_function(name, stm[j], bindings, new_Node, ctx));
Node v(eval_function(name, stm[j], bindings, new_Node, ctx, bt));
if (v.is_null()) break;
else return v;
}
......@@ -895,8 +973,8 @@ namespace Sass {
case Node::for_to_directive: {
Node::Type for_type = stm.type();
Node iter_var(stm[0]);
Node lower_bound(eval(stm[1], Node(), bindings, ctx.function_env, new_Node, ctx));
Node upper_bound(eval(stm[2], Node(), bindings, ctx.function_env, new_Node, ctx));
Node lower_bound(eval(stm[1], Node(), bindings, ctx.function_env, new_Node, ctx, bt));
Node upper_bound(eval(stm[2], Node(), bindings, ctx.function_env, new_Node, ctx, bt));
Node for_body(stm[3]);
Environment for_env; // re-use this env for each iteration
for_env.link(bindings);
......@@ -904,7 +982,7 @@ namespace Sass {
j < T;
j += 1) {
for_env.current_frame[iter_var.token()] = new_Node(lower_bound.path(), lower_bound.line(), j);
Node v(eval_function(name, for_body, for_env, new_Node, ctx));
Node v(eval_function(name, for_body, for_env, new_Node, ctx, bt));
if (v.is_null()) continue;
else return v;
}
......@@ -912,7 +990,7 @@ namespace Sass {
case Node::each_directive: {
Node iter_var(stm[0]);
Node list(eval(stm[1], Node(), bindings, ctx.function_env, new_Node, ctx));
Node list(eval(stm[1], Node(), bindings, ctx.function_env, new_Node, ctx, bt));
if (list.type() != Node::list) {
list = (new_Node(Node::list, list.path(), list.line(), 1) << list);
}
......@@ -921,8 +999,8 @@ namespace Sass {
each_env.link(bindings);
for (size_t j = 0, T = list.size(); j < T; ++j) {
list[j].should_eval() = true;
each_env.current_frame[iter_var.token()] = eval(list[j], Node(), bindings, ctx.function_env, new_Node, ctx);
Node v(eval_function(name, each_body, each_env, new_Node, ctx));
each_env.current_frame[iter_var.token()] = eval(list[j], Node(), bindings, ctx.function_env, new_Node, ctx, bt);
Node v(eval_function(name, each_body, each_env, new_Node, ctx, bt));
if (v.is_null()) continue;
else return v;
}
......@@ -933,11 +1011,11 @@ namespace Sass {
Node while_body(stm[1]);
Environment while_env; // re-use this env for each iteration
while_env.link(bindings);
Node pred_val(eval(pred_expr, Node(), bindings, ctx.function_env, new_Node, ctx));
Node pred_val(eval(pred_expr, Node(), bindings, ctx.function_env, new_Node, ctx, bt));
while (!pred_val.is_false()) {
Node v(eval_function(name, while_body, while_env, new_Node, ctx));
Node v(eval_function(name, while_body, while_env, new_Node, ctx, bt));
if (v.is_null()) {
pred_val = eval(pred_expr, Node(), bindings, ctx.function_env, new_Node, ctx);
pred_val = eval(pred_expr, Node(), bindings, ctx.function_env, new_Node, ctx, bt);
continue;
}
else return v;
......@@ -947,24 +1025,26 @@ namespace Sass {
case Node::warning: {
string prefix("WARNING: ");
string indent(" ");
Node contents(eval(stm[0], Node(), bindings, ctx.function_env, new_Node, ctx));
Node contents(eval(stm[0], Node(), bindings, ctx.function_env, new_Node, ctx, bt));
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 " << stm.line() << " of " << stm.path();
cerr << prefix << result;
// cerr << indent << "on line " << stm.line() << " of " << stm.path();
Backtrace top(&bt, stm.path(), stm.line(), "");
cerr << top.to_string(true);
cerr << endl << endl;
} break;
case Node::return_directive: {
Node retval(eval(stm[0], Node(), bindings, ctx.function_env, new_Node, ctx));
Node retval(eval(stm[0], Node(), bindings, ctx.function_env, new_Node, ctx, bt));
if (retval.type() == Node::list) {
Node new_list(new_Node(Node::list, retval.path(), retval.line(), retval.size()));
new_list.is_comma_separated() = retval.is_comma_separated();
for (size_t i = 0, S = retval.size(); i < S; ++i) {
new_list << eval(retval[i], Node(), bindings, ctx.function_env, new_Node, ctx);
new_list << eval(retval[i], Node(), bindings, ctx.function_env, new_Node, ctx, bt);
}
retval = new_list;
}
......@@ -976,7 +1056,7 @@ namespace Sass {
} break;
}
}
if (at_toplevel) throw_eval_error("function finished without @return", body.path(), body.line());
if (at_toplevel) throw_eval_error(bt, "function finished without @return", body.path(), body.line());
return Node();
}
......@@ -987,7 +1067,7 @@ namespace Sass {
// combine the various kinds of selectors.
Node expand_selector(Node sel, Node pre, Node_Factory& new_Node)
{
if (pre.is_null()) return sel;
if (pre.is_null() || sel.is_null()) return sel;
if (sel.has_backref()) {
if ((pre.type() == Node::selector_group) && (sel.type() == Node::selector_group)) {
......
......@@ -10,19 +10,23 @@
#include "context.hpp"
#endif
#ifndef SASS_BACKTRACE
#include "backtrace.hpp"
#endif
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 eval_arguments(Node args, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx);
Node eval_function(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);
double operate(Node op, double lhs, double rhs);
void expand(Node expr, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt, bool function_name = false, const Node content = Node());
void re_expand(Node expr, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt, bool function_name, const Node content);
Node eval(Node expr, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt, bool function_name = false);
Node eval_arguments(Node args, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt);
Node eval_function(string name, Node stm, Environment& bindings, Node_Factory& new_Node, Context& ctx, Backtrace& bt, bool toplevel = false);
Node reduce(Node list, size_t head, Node acc, Node_Factory& new_Node, Backtrace& bt);
double operate(Node op, double lhs, double rhs, Backtrace& bt);
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_mixin(Node mixin, const Node args, const Node content, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt, 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, Backtrace& bt, string& path, size_t line);
Node expand_selector(Node sel, Node pre, Node_Factory& new_Node);
Node expand_backref(Node sel, Node pre);
void extend(Node expr, multimap<Node, Node>& extension_requests, Node_Factory& new_Node);
......
......@@ -40,18 +40,22 @@ namespace Sass {
else {
parameter_names << param[0];
// assume it's safe to evaluate default args just once at initialization
param[1] = eval(param[1], Node(), ctx.global_env, ctx.function_env, ctx.new_Node, ctx);
Backtrace dummy_trace(0, signature, 0, "default argument");
param[1] = eval(param[1], Node(), ctx.global_env, ctx.function_env, ctx.new_Node, ctx, dummy_trace);
}
}
}
namespace Functions {
static void throw_eval_error(string message, string& path, size_t line)
static void throw_eval_error(Backtrace& bt, string message, string& path, size_t line)
{
if (!path.empty() && Prelexer::string_constant(path.c_str()))
path = path.substr(1, path.length() - 1);
// Backtrace top(&bt, path, line, "");
message += bt.to_string();
throw Error(Error::evaluation, path, line, message);
}
......@@ -105,7 +109,7 @@ namespace Sass {
}
// Functions for fetching and checking arguments.
static Node arg(Signature sig, string& path, size_t line, const Node parameter_names, Environment& bindings, size_t param_num, Node::Type param_type) {
static Node arg(Signature sig, string& path, size_t line, const Node parameter_names, Environment& bindings, size_t param_num, Node::Type param_type, Backtrace& bt) {
Node the_arg(bindings[parameter_names[param_num].token()]);
Node::Type arg_type = the_arg.type();
switch (param_type)
......@@ -139,23 +143,23 @@ namespace Sass {
}
stringstream msg;
msg << nameof(param_type) << " required for argument " << param_num+1 << " in call to '" << sig << "'";
throw_eval_error(msg.str(), path, line);
throw_eval_error(bt, msg.str(), path, line);
// unreachable statement
return Node();
}
static Node arg(Signature sig, string& path, size_t line, const Node parameter_names, Environment& bindings, size_t param_num, Node::Type t, double low, double high) {
Node the_arg(arg(sig, path, line, parameter_names, bindings, param_num, t));
static Node arg(Signature sig, string& path, size_t line, const Node parameter_names, Environment& bindings, size_t param_num, Node::Type t, double low, double high, Backtrace& bt) {
Node the_arg(arg(sig, path, line, parameter_names, bindings, param_num, t, bt));
if (!the_arg.is_numeric()) {
stringstream msg;
msg << "numeric value required for argument " << param_num+1 << " in call to '" << sig << "'";
throw_eval_error(msg.str(), path, line);
throw_eval_error(bt, msg.str(), path, line);
}
double val = the_arg.numeric_value();
if (val < low || high < val) {
stringstream msg;
msg << "argument " << param_num+1 << " must be between " << low << " and " << high << " in call to '" << sig << "'";
throw_eval_error(msg.str(), path, line);
throw_eval_error(bt, msg.str(), path, line);
}
return the_arg;
}
......@@ -165,26 +169,26 @@ namespace Sass {
////////////////////////////////////////////////////////////////////////
extern Signature rgb_sig = "rgb($red, $green, $blue)";
Node rgb(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
double r = arg(rgb_sig, path, line, parameter_names, bindings, 0, Node::numeric, 0, 255).numeric_value();
double g = arg(rgb_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 255).numeric_value();
double b = arg(rgb_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 255).numeric_value();
Node rgb(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
double r = arg(rgb_sig, path, line, parameter_names, bindings, 0, Node::numeric, 0, 255, bt).numeric_value();
double g = arg(rgb_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 255, bt).numeric_value();
double b = arg(rgb_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 255, bt).numeric_value();
return new_Node(path, line, std::floor(r), std::floor(g), std::floor(b), 1.0);
}
extern Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";
Node rgba_4(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
double r = arg(rgba_4_sig, path, line, parameter_names, bindings, 0, Node::numeric, 0, 255).numeric_value();
double g = arg(rgba_4_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 255).numeric_value();
double b = arg(rgba_4_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 255).numeric_value();
double a = arg(rgba_4_sig, path, line, parameter_names, bindings, 3, Node::numeric, 0, 1).numeric_value();
Node rgba_4(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
double r = arg(rgba_4_sig, path, line, parameter_names, bindings, 0, Node::numeric, 0, 255, bt).numeric_value();
double g = arg(rgba_4_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 255, bt).numeric_value();
double b = arg(rgba_4_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 255, bt).numeric_value();
double a = arg(rgba_4_sig, path, line, parameter_names, bindings, 3, Node::numeric, 0, 1, bt).numeric_value();
return new_Node(path, line, std::floor(r), std::floor(g), std::floor(b), a);
}
extern Signature rgba_2_sig = "rgba($color, $alpha)";
Node rgba_2(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color_arg(arg(rgba_2_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node alpha_arg(arg(rgba_2_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1));
Node rgba_2(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color_arg(arg(rgba_2_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node alpha_arg(arg(rgba_2_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1, bt));
double r = color_arg[0].numeric_value();
double g = color_arg[1].numeric_value();
double b = color_arg[2].numeric_value();
......@@ -193,28 +197,28 @@ namespace Sass {
}
extern Signature red_sig = "red($color)";
Node red(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(red_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node red(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(red_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
return new_Node(path, line, color[0]);
}
extern Signature green_sig = "green($color)";
Node green(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(green_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node green(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(green_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
return new_Node(path, line, color[1]);
}
extern Signature blue_sig = "blue($color)";
Node blue(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(blue_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node blue(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(blue_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
return new_Node(path, line, color[2]);
}
extern Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)";
Node mix(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color1(arg(mix_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node color2(arg(mix_sig, path, line, parameter_names, bindings, 1, Node::numeric_color));
Node weight(arg(mix_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 100));
Node mix(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color1(arg(mix_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node color2(arg(mix_sig, path, line, parameter_names, bindings, 1, Node::numeric_color, bt));
Node weight(arg(mix_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 100, bt));
double p = weight.numeric_value()/100;
double w = 2*p - 1;
......@@ -302,25 +306,25 @@ namespace Sass {
}
extern Signature hsl_sig = "hsl($hue, $saturation, $lightness)";
Node hsl(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
double h = arg(hsl_sig, path, line, parameter_names, bindings, 0, Node::numeric).numeric_value();
double s = arg(hsl_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100).numeric_value();
double l = arg(hsl_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 100).numeric_value();
Node hsl(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
double h = arg(hsl_sig, path, line, parameter_names, bindings, 0, Node::numeric, bt).numeric_value();
double s = arg(hsl_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100, bt).numeric_value();
double l = arg(hsl_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 100, bt).numeric_value();
return hsla_impl(h, s, l, 1.0, new_Node, path, line);
}
extern Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)";
Node hsla(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
double h = arg(hsla_sig, path, line, parameter_names, bindings, 0, Node::numeric).numeric_value();
double s = arg(hsla_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100).numeric_value();
double l = arg(hsla_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 100).numeric_value();
double a = arg(hsla_sig, path, line, parameter_names, bindings, 3, Node::numeric, 0, 1).numeric_value();
Node hsla(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
double h = arg(hsla_sig, path, line, parameter_names, bindings, 0, Node::numeric, bt).numeric_value();
double s = arg(hsla_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100, bt).numeric_value();
double l = arg(hsla_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 100, bt).numeric_value();
double a = arg(hsla_sig, path, line, parameter_names, bindings, 3, Node::numeric, 0, 1, bt).numeric_value();
return hsla_impl(h, s, l, a, new_Node, path, line);
}
extern Signature hue_sig = "hue($color)";
Node hue(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node rgb_color(arg(hue_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node hue(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node rgb_color(arg(hue_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node hsl_color(rgb_to_hsl(rgb_color[0].numeric_value(),
rgb_color[1].numeric_value(),
rgb_color[2].numeric_value(),
......@@ -329,8 +333,8 @@ namespace Sass {
}
extern Signature saturation_sig = "saturation($color)";
Node saturation(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node rgb_color(arg(saturation_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node saturation(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node rgb_color(arg(saturation_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node hsl_color(rgb_to_hsl(rgb_color[0].numeric_value(),
rgb_color[1].numeric_value(),
rgb_color[2].numeric_value(),
......@@ -339,8 +343,8 @@ namespace Sass {
}
extern Signature lightness_sig = "lightness($color)";
Node lightness(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node rgb_color(arg(lightness_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node lightness(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node rgb_color(arg(lightness_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node hsl_color(rgb_to_hsl(rgb_color[0].numeric_value(),
rgb_color[1].numeric_value(),
rgb_color[2].numeric_value(),
......@@ -349,9 +353,9 @@ namespace Sass {
}
extern Signature adjust_hue_sig = "adjust-hue($color, $degrees)";
Node adjust_hue(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node rgb_col(arg(adjust_hue_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node degrees(arg(adjust_hue_sig, path, line, parameter_names, bindings, 1, Node::numeric));
Node adjust_hue(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node rgb_col(arg(adjust_hue_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node degrees(arg(adjust_hue_sig, path, line, parameter_names, bindings, 1, Node::numeric, bt));
Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(),
rgb_col[1].numeric_value(),
rgb_col[2].numeric_value(),
......@@ -364,9 +368,9 @@ namespace Sass {
}
extern Signature lighten_sig = "lighten($color, $amount)";
Node lighten(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node rgb_col(arg(lighten_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node amount(arg(lighten_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100));
Node lighten(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node rgb_col(arg(lighten_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node amount(arg(lighten_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100, bt));
Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(),
rgb_col[1].numeric_value(),
rgb_col[2].numeric_value(),
......@@ -379,9 +383,9 @@ namespace Sass {
}
extern Signature darken_sig = "darken($color, $amount)";
Node darken(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node rgb_col(arg(darken_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node amount(arg(darken_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100));
Node darken(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node rgb_col(arg(darken_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node amount(arg(darken_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100, bt));
Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(),
rgb_col[1].numeric_value(),
rgb_col[2].numeric_value(),
......@@ -394,9 +398,9 @@ namespace Sass {
}
extern Signature saturate_sig = "saturate($color, $amount)";
Node saturate(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node rgb_col(arg(saturate_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node amount(arg(saturate_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100));
Node saturate(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node rgb_col(arg(saturate_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node amount(arg(saturate_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100, bt));
Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(),
rgb_col[1].numeric_value(),
rgb_col[2].numeric_value(),
......@@ -409,9 +413,9 @@ namespace Sass {
}
extern Signature desaturate_sig = "desaturate($color, $amount)";
Node desaturate(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node rgb_col(arg(desaturate_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node amount(arg(desaturate_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100));
Node desaturate(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node rgb_col(arg(desaturate_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node amount(arg(desaturate_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100, bt));
Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(),
rgb_col[1].numeric_value(),
rgb_col[2].numeric_value(),
......@@ -424,8 +428,8 @@ namespace Sass {
}
extern Signature grayscale_sig = "grayscale($color)";
Node grayscale(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(grayscale_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node grayscale(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(grayscale_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node hsl_color(rgb_to_hsl(color[0].numeric_value(),
color[1].numeric_value(),
color[2].numeric_value(),
......@@ -438,8 +442,8 @@ namespace Sass {
}
extern Signature complement_sig = "complement($color)";
Node complement(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(complement_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node complement(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(complement_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node hsl_color(rgb_to_hsl(color[0].numeric_value(),
color[1].numeric_value(),
color[2].numeric_value(),
......@@ -452,8 +456,8 @@ namespace Sass {
}
extern Signature invert_sig = "invert($color)";
Node invert(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(invert_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node invert(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(invert_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
return new_Node(path, line,
255 - color[0].numeric_value(),
255 - color[1].numeric_value(),
......@@ -466,21 +470,21 @@ namespace Sass {
////////////////////////////////////////////////////////////////////////
extern Signature alpha_sig = "alpha($color)";
Node alpha(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(alpha_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node alpha(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(alpha_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
return new_Node(path, line, color[3]);
}
extern Signature opacity_sig = "opacity($color)";
Node opacity(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(opacity_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node opacity(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(opacity_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
return new_Node(path, line, color[3]);
}
extern Signature opacify_sig = "opacify($color, $amount)";
Node opacify(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(opacify_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
double delta = arg(opacify_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1).numeric_value();
Node opacify(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(opacify_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
double delta = arg(opacify_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1, bt).numeric_value();
delta += color[3].numeric_value();
if (delta > 1) delta = 1;
return new_Node(path, line,
......@@ -491,9 +495,9 @@ namespace Sass {
}
extern Signature fade_in_sig = "fade-in($color, $amount)";
Node fade_in(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(fade_in_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
double delta = arg(fade_in_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1).numeric_value();
Node fade_in(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(fade_in_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
double delta = arg(fade_in_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1, bt).numeric_value();
delta += color[3].numeric_value();
if (delta > 1) delta = 1;
return new_Node(path, line,
......@@ -504,9 +508,9 @@ namespace Sass {
}
extern Signature transparentize_sig = "transparentize($color, $amount)";
Node transparentize(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(transparentize_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
double delta = arg(transparentize_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1).numeric_value();
Node transparentize(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(transparentize_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
double delta = arg(transparentize_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1, bt).numeric_value();
double alpha = color[3].numeric_value() - delta;
if (alpha < 0) alpha = 0;
return new_Node(path, line,
......@@ -517,9 +521,9 @@ namespace Sass {
}
extern Signature fade_out_sig = "fade-out($color, $amount)";
Node fade_out(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(fade_out_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
double delta = arg(fade_out_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1).numeric_value();
Node fade_out(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(fade_out_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
double delta = arg(fade_out_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1, bt).numeric_value();
double alpha = color[3].numeric_value() - delta;
if (alpha < 0) alpha = 0;
return new_Node(path, line,
......@@ -535,7 +539,7 @@ namespace Sass {
// not worth using the arg(...) functions
extern Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";
Node adjust_color(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node adjust_color(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(bindings[parameter_names[0].token()]);
Node r(bindings[parameter_names[1].token()]);
Node g(bindings[parameter_names[2].token()]);
......@@ -549,16 +553,16 @@ namespace Sass {
bool no_hsl = h.is_false() && s.is_false() && l.is_false();
if (color.type() != Node::numeric_color) {
throw_eval_error("first argument to 'adjust-color' must be a color", color.path(), color.line());
throw_eval_error(bt, "first argument to 'adjust-color' must be a color", color.path(), color.line());
}
else if (!no_rgb && !no_hsl) {
throw_eval_error("cannot specify RGB and HSL values for a color at the same time for 'adjust-color'", r.path(), r.line());
throw_eval_error(bt, "cannot specify RGB and HSL values for a color at the same time for 'adjust-color'", r.path(), r.line());
}
else if (!no_rgb) {
if (!r.is_false() && !r.is_numeric()) throw_eval_error("argument $red of 'adjust-color' must be numeric", r.path(), r.line());
if (!g.is_false() && !g.is_numeric()) throw_eval_error("argument $green of 'adjust-color' must be numeric", g.path(), g.line());
if (!b.is_false() && !b.is_numeric()) throw_eval_error("argument $blue of 'adjust-color' must be numeric", b.path(), b.line());
if (!a.is_false() && !a.is_numeric()) throw_eval_error("argument $alpha of 'adjust-color' must be numeric", a.path(), a.line());
if (!r.is_false() && !r.is_numeric()) throw_eval_error(bt, "argument $red of 'adjust-color' must be numeric", r.path(), r.line());
if (!g.is_false() && !g.is_numeric()) throw_eval_error(bt, "argument $green of 'adjust-color' must be numeric", g.path(), g.line());
if (!b.is_false() && !b.is_numeric()) throw_eval_error(bt, "argument $blue of 'adjust-color' must be numeric", b.path(), b.line());
if (!a.is_false() && !a.is_numeric()) throw_eval_error(bt, "argument $alpha of 'adjust-color' must be numeric", a.path(), a.line());
double new_r = color[0].numeric_value() + (r.is_false() ? 0 : r.numeric_value());
double new_g = color[1].numeric_value() + (g.is_false() ? 0 : g.numeric_value());
double new_b = color[2].numeric_value() + (b.is_false() ? 0 : b.numeric_value());
......@@ -570,10 +574,10 @@ namespace Sass {
color[1].numeric_value(),
color[2].numeric_value(),
new_Node, path, line));
if (!h.is_false() && !h.is_numeric()) throw_eval_error("argument $hue of 'adjust-color' must be numeric", h.path(), h.line());
if (!s.is_false() && !s.is_numeric()) throw_eval_error("argument $saturation of 'adjust-color' must be numeric", s.path(), s.line());
if (!l.is_false() && !l.is_numeric()) throw_eval_error("argument $lightness of 'adjust-color' must be numeric", l.path(), l.line());
if (!a.is_false() && !a.is_numeric()) throw_eval_error("argument $alpha of 'adjust-color' must be numeric", a.path(), a.line());
if (!h.is_false() && !h.is_numeric()) throw_eval_error(bt, "argument $hue of 'adjust-color' must be numeric", h.path(), h.line());
if (!s.is_false() && !s.is_numeric()) throw_eval_error(bt, "argument $saturation of 'adjust-color' must be numeric", s.path(), s.line());
if (!l.is_false() && !l.is_numeric()) throw_eval_error(bt, "argument $lightness of 'adjust-color' must be numeric", l.path(), l.line());
if (!a.is_false() && !a.is_numeric()) throw_eval_error(bt, "argument $alpha of 'adjust-color' must be numeric", a.path(), a.line());
double new_h = (h.is_false() ? 0 : h.numeric_value()) + hsl_node[0].numeric_value();
double new_s = (s.is_false() ? 0 : s.numeric_value()) + hsl_node[1].numeric_value();
double new_l = (l.is_false() ? 0 : l.numeric_value()) + hsl_node[2].numeric_value();
......@@ -581,7 +585,7 @@ namespace Sass {
return hsla_impl(new_h, new_s, new_l, new_a, new_Node, path, line);
}
else if (!a.is_false()) {
if (!a.is_numeric()) throw_eval_error("argument $alpha of 'adjust-color' must be numeric", a.path(), a.line());
if (!a.is_numeric()) throw_eval_error(bt, "argument $alpha of 'adjust-color' must be numeric", a.path(), a.line());
return new_Node(path, line,
color[0].numeric_value(),
color[1].numeric_value(),
......@@ -589,14 +593,14 @@ namespace Sass {
color[3].numeric_value() + a.numeric_value());
}
else {
throw_eval_error("not enough arguments for 'adjust-color'", color.path(), color.line());
throw_eval_error(bt, "not enough arguments for 'adjust-color'", color.path(), color.line());
}
// unreachable statement
return Node();
}
extern Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";
Node scale_color(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node scale_color(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(bindings[parameter_names[0].token()]);
Node r(bindings[parameter_names[1].token()]);
Node g(bindings[parameter_names[2].token()]);
......@@ -612,10 +616,10 @@ namespace Sass {
bool is_hsl = false;
if (color.type() != Node::numeric_color) {
throw_eval_error("first argument to 'scale-color' must be a color", path, line);
throw_eval_error(bt, "first argument to 'scale-color' must be a color", path, line);
}
if (!no_rgb && !no_hsl) {
throw_eval_error("cannot specify RGB and HSL values for a color at the same time for 'scale-color'", path, line);
throw_eval_error(bt, "cannot specify RGB and HSL values for a color at the same time for 'scale-color'", path, line);
}
else if (!no_rgb) {
low = 1; high = 4;
......@@ -633,19 +637,19 @@ namespace Sass {
result << new_Node(path, line, color[i].numeric_value());
}
double current = color[3].numeric_value();
double scale = arg(scale_color_sig, path, line, parameter_names, bindings, 7, Node::numeric_percentage, -100, 100).numeric_value() / 100;
double scale = arg(scale_color_sig, path, line, parameter_names, bindings, 7, Node::numeric_percentage, -100, 100, bt).numeric_value() / 100;
double diff = scale > 0 ? 1 - current : current;
result << new_Node(path, line, current + diff*scale);
return result;
}
else {
throw_eval_error("not enough arguments for 'scale-color'", color.path(), color.line());
throw_eval_error(bt, "not enough arguments for 'scale-color'", color.path(), color.line());
}
Node result(new_Node(Node::numeric_color, path, line, 4));
for (size_t i = low, j = 0; i < high; ++i, ++j) {
double current = color[j].numeric_value();
if (!bindings[parameter_names[i].token()].is_false()) {
double scale = arg(scale_color_sig, path, line, parameter_names, bindings, i, Node::numeric_percentage, -100, 100).numeric_value() / 100;
double scale = arg(scale_color_sig, path, line, parameter_names, bindings, i, Node::numeric_percentage, -100, 100, bt).numeric_value() / 100;
double diff = scale > 0 ? (is_hsl ? 100 : 255) - current : current;
result << new_Node(path, line, current + diff*scale);
}
......@@ -655,7 +659,7 @@ namespace Sass {
}
if (!a.is_false()) {
double current = color[3].numeric_value();
double scale = arg(scale_color_sig, path, line, parameter_names, bindings, 7, Node::numeric_percentage, -100, 100).numeric_value() / 100;
double scale = arg(scale_color_sig, path, line, parameter_names, bindings, 7, Node::numeric_percentage, -100, 100, bt).numeric_value() / 100;
double diff = scale > 0 ? 1 - current : current;
result << new_Node(path, line, current + diff*scale);
}
......@@ -673,7 +677,7 @@ namespace Sass {
}
extern Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";
Node change_color(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node change_color(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(bindings[parameter_names[0].token()]);
Node r(bindings[parameter_names[1].token()]);
Node g(bindings[parameter_names[2].token()]);
......@@ -687,16 +691,16 @@ namespace Sass {
bool no_hsl = h.is_false() && s.is_false() && l.is_false();
if (color.type() != Node::numeric_color) {
throw_eval_error("first argument to 'change-color' must be a color", color.path(), color.line());
throw_eval_error(bt, "first argument to 'change-color' must be a color", color.path(), color.line());
}
if (!no_rgb && !no_hsl) {
throw_eval_error("cannot specify RGB and HSL values for a color at the same time for 'change-color'", r.path(), r.line());
throw_eval_error(bt, "cannot specify RGB and HSL values for a color at the same time for 'change-color'", r.path(), r.line());
}
else if (!no_rgb) {
if (!r.is_false() && !r.is_numeric()) throw_eval_error("argument $red of 'change-color' must be numeric", r.path(), r.line());
if (!g.is_false() && !g.is_numeric()) throw_eval_error("argument $green of 'change-color' must be numeric", g.path(), g.line());
if (!b.is_false() && !b.is_numeric()) throw_eval_error("argument $blue of 'change-color' must be numeric", b.path(), b.line());
if (!a.is_false() && !a.is_numeric()) throw_eval_error("argument $alpha of 'change-color' must be numeric", a.path(), a.line());
if (!r.is_false() && !r.is_numeric()) throw_eval_error(bt, "argument $red of 'change-color' must be numeric", r.path(), r.line());
if (!g.is_false() && !g.is_numeric()) throw_eval_error(bt, "argument $green of 'change-color' must be numeric", g.path(), g.line());
if (!b.is_false() && !b.is_numeric()) throw_eval_error(bt, "argument $blue of 'change-color' must be numeric", b.path(), b.line());
if (!a.is_false() && !a.is_numeric()) throw_eval_error(bt, "argument $alpha of 'change-color' must be numeric", a.path(), a.line());
double new_r = (r.is_false() ? color[0] : r).numeric_value();
double new_g = (g.is_false() ? color[1] : g).numeric_value();
double new_b = (b.is_false() ? color[2] : b).numeric_value();
......@@ -708,10 +712,10 @@ namespace Sass {
color[1].numeric_value(),
color[2].numeric_value(),
new_Node, path, line));
if (!h.is_false() && !h.is_numeric()) throw_eval_error("argument $hue of 'change-color' must be numeric", h.path(), h.line());
if (!s.is_false() && !s.is_numeric()) throw_eval_error("argument $saturation of 'change-color' must be numeric", s.path(), s.line());
if (!l.is_false() && !l.is_numeric()) throw_eval_error("argument $lightness of 'change-color' must be numeric", l.path(), l.line());
if (!a.is_false() && !a.is_numeric()) throw_eval_error("argument $alpha of 'change-color' must be numeric", a.path(), a.line());
if (!h.is_false() && !h.is_numeric()) throw_eval_error(bt, "argument $hue of 'change-color' must be numeric", h.path(), h.line());
if (!s.is_false() && !s.is_numeric()) throw_eval_error(bt, "argument $saturation of 'change-color' must be numeric", s.path(), s.line());
if (!l.is_false() && !l.is_numeric()) throw_eval_error(bt, "argument $lightness of 'change-color' must be numeric", l.path(), l.line());
if (!a.is_false() && !a.is_numeric()) throw_eval_error(bt, "argument $alpha of 'change-color' must be numeric", a.path(), a.line());
double new_h = (h.is_false() ? hsl_node[0].numeric_value() : h.numeric_value());
double new_s = (s.is_false() ? hsl_node[1].numeric_value() : s.numeric_value());
double new_l = (l.is_false() ? hsl_node[2].numeric_value() : l.numeric_value());
......@@ -719,7 +723,7 @@ namespace Sass {
return hsla_impl(new_h, new_s, new_l, new_a, new_Node, path, line);
}
else if (!a.is_false()) {
if (!a.is_numeric()) throw_eval_error("argument $alpha of 'change-color' must be numeric", a.path(), a.line());
if (!a.is_numeric()) throw_eval_error(bt, "argument $alpha of 'change-color' must be numeric", a.path(), a.line());
return new_Node(path, line,
color[0].numeric_value(),
color[1].numeric_value(),
......@@ -727,26 +731,36 @@ namespace Sass {
a.numeric_value());
}
else {
throw_eval_error("not enough arguments for 'change-color'", color.path(), color.line());
throw_eval_error(bt, "not enough arguments for 'change-color'", color.path(), color.line());
}
// unreachable statement
return Node();
}
extern Signature ie_hex_str_sig = "ie-hex-str($color)";
Node ie_hex_str(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node color(arg(ie_hex_str_sig, path, line, parameter_names, bindings, 0, Node::numeric_color, bt));
Node result(new_Node(Node::ie_hex_str, color.path(), color.line(), 4));
result << color[0] << color[1] << color[2] << color[3];
Node wrapped_result(new_Node(Node::concatenation, color.path(), color.line(), 1));
wrapped_result << result;
return wrapped_result;
}
////////////////////////////////////////////////////////////////////////
// String Functions ////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
extern Signature unquote_sig = "unquote($string)";
Node unquote(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node unquote(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node cpy(new_Node(path, line, bindings[parameter_names[0].token()]));
cpy.is_quoted() = false;
return cpy;
}
extern Signature quote_sig = "quote($string)";
Node quote(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node orig(arg(quote_sig, path, line, parameter_names, bindings, 0, Node::string_t));
Node quote(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node orig(arg(quote_sig, path, line, parameter_names, bindings, 0, Node::string_t, bt));
Node copy(new_Node(path, line, orig));
copy.is_quoted() = true;
return copy;
......@@ -757,14 +771,14 @@ namespace Sass {
////////////////////////////////////////////////////////////////////////
extern Signature percentage_sig = "percentage($value)";
Node percentage(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node orig(arg(percentage_sig, path, line, parameter_names, bindings, 0, Node::number));
Node percentage(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node orig(arg(percentage_sig, path, line, parameter_names, bindings, 0, Node::number, bt));
return new_Node(path, line, orig.numeric_value() * 100, Node::numeric_percentage);
}
extern Signature round_sig = "round($value)";
Node round(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node orig(arg(round_sig, path, line, parameter_names, bindings, 0, Node::numeric));
Node round(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node orig(arg(round_sig, path, line, parameter_names, bindings, 0, Node::numeric, bt));
switch (orig.type())
{
case Node::numeric_dimension: {
......@@ -786,7 +800,7 @@ namespace Sass {
default: {
// unreachable
throw_eval_error("argument to round must be numeric", path, line);
throw_eval_error(bt, "argument to round must be numeric", path, line);
} break;
}
// unreachable statement
......@@ -794,8 +808,8 @@ namespace Sass {
}
extern Signature ceil_sig = "ceil($value)";
Node ceil(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node orig(arg(ceil_sig, path, line, parameter_names, bindings, 0, Node::numeric));
Node ceil(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node orig(arg(ceil_sig, path, line, parameter_names, bindings, 0, Node::numeric, bt));
switch (orig.type())
{
case Node::numeric_dimension: {
......@@ -817,7 +831,7 @@ namespace Sass {
default: {
// unreachable
throw_eval_error("argument to ceil must be numeric", path, line);
throw_eval_error(bt, "argument to ceil must be numeric", path, line);
} break;
}
// unreachable statement
......@@ -825,8 +839,8 @@ namespace Sass {
}
extern Signature floor_sig = "floor($value)";
Node floor(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node orig(arg(floor_sig, path, line, parameter_names, bindings, 0, Node::numeric));
Node floor(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node orig(arg(floor_sig, path, line, parameter_names, bindings, 0, Node::numeric, bt));
switch (orig.type())
{
case Node::numeric_dimension: {
......@@ -848,7 +862,7 @@ namespace Sass {
default: {
// unreachable
throw_eval_error("argument to floor must be numeric", path, line);
throw_eval_error(bt, "argument to floor must be numeric", path, line);
} break;
}
// unreachable statement
......@@ -856,8 +870,8 @@ namespace Sass {
}
extern Signature abs_sig = "abs($value)";
Node abs(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node orig(arg(abs_sig, path, line, parameter_names, bindings, 0, Node::numeric));
Node abs(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node orig(arg(abs_sig, path, line, parameter_names, bindings, 0, Node::numeric, bt));
switch (orig.type())
{
case Node::numeric_dimension: {
......@@ -879,7 +893,7 @@ namespace Sass {
default: {
// unreachable
throw_eval_error("argument to abs must be numeric", path, line);
throw_eval_error(bt, "argument to abs must be numeric", path, line);
} break;
}
// unreachable statement
......@@ -891,28 +905,28 @@ namespace Sass {
////////////////////////////////////////////////////////////////////////
extern Signature length_sig = "length($list)";
Node length(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node arg(bindings[parameter_names[0].token()]);
return new_Node(path, line, arg.type() == Node::list ? arg.size() : 1);
Node length(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node the_arg(bindings[parameter_names[0].token()]);
return new_Node(path, line, the_arg.type() == Node::list ? the_arg.size() : 1);
}
extern Signature nth_sig = "nth($list, $n)";
Node nth(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node nth(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node l(bindings[parameter_names[0].token()]);
// wrap the first arg if it isn't a list
if (l.type() != Node::list) {
l = new_Node(Node::list, path, line, 1) << l;
}
if (l.size() == 0) {
throw_eval_error("cannot index into an empty list", path, line);
throw_eval_error(bt, "cannot index into an empty list", path, line);
}
// just truncate the index if it's not an integer ... more permissive than Ruby Sass
size_t n = std::floor(arg(nth_sig, path, line, parameter_names, bindings, 1, Node::numeric, 1, l.size()).numeric_value());
size_t n = std::floor(arg(nth_sig, path, line, parameter_names, bindings, 1, Node::numeric, 1, l.size(), bt).numeric_value());
return l[n - 1];
}
extern Signature index_sig = "index($list, $value)";
Node index(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node index(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node lst(bindings[parameter_names[0].token()]);
Node val(bindings[parameter_names[1].token()]);
// if $list isn't a list, wrap it in a singleton list
......@@ -926,7 +940,7 @@ namespace Sass {
}
extern Signature join_sig = "join($list1, $list2, $separator: auto)";
Node join(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node join(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
// if the args aren't lists, turn them into singleton lists
Node l1(bindings[parameter_names[0].token()]);
if (l1.type() != Node::list) {
......@@ -946,7 +960,7 @@ namespace Sass {
if (sep == "comma") comma_sep = true;
else if (sep == "space") comma_sep = false;
else if (sep == "auto") comma_sep = l1.is_comma_separated();
else throw_eval_error("third argument to 'join' must be 'space', 'comma', or 'auto'", path, line);
else throw_eval_error(bt, "third argument to 'join' must be 'space', 'comma', or 'auto'", path, line);
if (l1.size() == 0) comma_sep = l2.is_comma_separated();
......@@ -959,7 +973,7 @@ namespace Sass {
}
extern Signature append_sig = "append($list1, $list2, $separator: auto)";
Node append(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node append(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node list(bindings[parameter_names[0].token()]);
// if the first arg isn't a list, wrap it in a singleton
......@@ -971,7 +985,7 @@ namespace Sass {
if (sep == "comma") comma_sep = true;
else if (sep == "space") comma_sep = false;
else if (sep == "auto") comma_sep = list.is_comma_separated();
else throw_eval_error("third argument to 'append' must be 'space', 'comma', or 'auto'", path, line);
else throw_eval_error(bt, "third argument to 'append' must be 'space', 'comma', or 'auto'", path, line);
Node new_list(new_Node(Node::list, path, line, list.size() + 1));
new_list += list;
......@@ -981,7 +995,7 @@ namespace Sass {
}
extern Signature compact_1_sig = "compact($arg1)";
Node compact_1(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node compact_1(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node the_arg(bindings[parameter_names[0].token()]);
if (the_arg.type() == Node::list) {
......@@ -997,7 +1011,7 @@ namespace Sass {
}
extern Signature compact_n_sig = "compact($arg1: false, $arg2: false, $arg3: false, $arg4: false, $arg5: false, $arg6: false, $arg7: false, $arg8: false, $arg9: false, $arg10: false, $arg11: false, $arg12: false)";
Node compact_n(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node compact_n(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node non_nils(new_Node(Node::list, path, line, 0));
non_nils.is_comma_separated() = true;
......@@ -1014,7 +1028,7 @@ namespace Sass {
////////////////////////////////////////////////////////////////////////
extern Signature type_of_sig = "type-of($value)";
Node type_of(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node type_of(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node val(bindings[parameter_names[0].token()]);
Token type_name;
switch (val.type())
......@@ -1045,8 +1059,8 @@ namespace Sass {
}
extern Signature unit_sig = "unit($number)";
Node unit(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node val(arg(unit_sig, path, line, parameter_names, bindings, 0, Node::numeric));
Node unit(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node val(arg(unit_sig, path, line, parameter_names, bindings, 0, Node::numeric, bt));
switch (val.type())
{
case Node::number: {
......@@ -1064,7 +1078,7 @@ namespace Sass {
// unreachable
default: {
throw_eval_error("argument to 'unit' must be numeric", path, line);
throw_eval_error(bt, "argument to 'unit' must be numeric", path, line);
} break;
}
// unreachable statement
......@@ -1072,8 +1086,8 @@ namespace Sass {
}
extern Signature unitless_sig = "unitless($number)";
Node unitless(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node val(arg(unitless_sig, path, line, parameter_names, bindings, 0, Node::numeric));
Node unitless(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node val(arg(unitless_sig, path, line, parameter_names, bindings, 0, Node::numeric, bt));
switch (val.type())
{
case Node::number: {
......@@ -1087,7 +1101,7 @@ namespace Sass {
// unreachable
default: {
throw_eval_error("argument to 'unitless' must be numeric", path, line);
throw_eval_error(bt, "argument to 'unitless' must be numeric", path, line);
} break;
}
// unreachable statement
......@@ -1095,9 +1109,9 @@ namespace Sass {
}
extern Signature comparable_sig = "comparable($number-1, $number-2)";
Node comparable(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node n1(arg(comparable_sig, path, line, parameter_names, bindings, 0, Node::numeric));
Node n2(arg(comparable_sig, path, line, parameter_names, bindings, 1, Node::numeric));
Node comparable(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node n1(arg(comparable_sig, path, line, parameter_names, bindings, 0, Node::numeric, bt));
Node n2(arg(comparable_sig, path, line, parameter_names, bindings, 1, Node::numeric, bt));
Node::Type t1 = n1.type();
Node::Type t2 = n2.type();
if ((t1 == Node::number && n2.is_numeric()) ||
......@@ -1129,14 +1143,14 @@ namespace Sass {
////////////////////////////////////////////////////////////////////////
extern Signature not_sig = "not($value)";
Node not_impl(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node not_impl(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node val(bindings[parameter_names[0].token()]);
if (val.is_false()) return new_Node(Node::boolean, path, line, true);
return new_Node(Node::boolean, path, line, false);
}
extern Signature if_sig = "if($condition, $if-true, $if-false)";
Node if_impl(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node if_impl(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node predicate(bindings[parameter_names[0].token()]);
Node consequent(bindings[parameter_names[1].token()]);
Node alternative(bindings[parameter_names[2].token()]);
......@@ -1150,7 +1164,7 @@ namespace Sass {
////////////////////////////////////////////////////////////////////////
extern Signature image_url_sig = "image-url($path, $only-path: false, $cache-buster: false)";
Node image_url(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node image_url(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) {
Node base_path(bindings[parameter_names[0].token()]);
bool only_path = !bindings[parameter_names[1].token()].is_false();
Node image_path_val(bindings[Token::make(image_path_var)]);
......
......@@ -7,6 +7,10 @@
#include "node.hpp"
#endif
#ifndef SASS_BACKTRACE
#include "backtrace.hpp"
#endif
namespace Sass {
struct Environment;
struct Context;
......@@ -14,7 +18,7 @@ namespace Sass {
using std::map;
typedef Node (*Primitive)(const Node, Environment&, Node_Factory&, string&, size_t);
typedef Node (*Primitive)(const Node, Environment&, Node_Factory&, Backtrace& bt, string&, size_t);
typedef const char Signature[];
struct Function {
......@@ -49,9 +53,9 @@ namespace Sass {
Function(char* signature, Primitive ip, Context& ctx);
Node operator()(Environment& bindings, Node_Factory& new_Node, string& path, size_t line) const
Node operator()(Environment& bindings, Node_Factory& new_Node, Backtrace& bt, string& path, size_t line) const
{
if (primitive) return primitive(parameters, bindings, new_Node, path, line);
if (primitive) return primitive(parameters, bindings, new_Node, bt, path, line);
else return Node();
}
......@@ -62,171 +66,174 @@ namespace Sass {
// RGB Functions ///////////////////////////////////////////////////////
extern Signature rgb_sig;
Node rgb(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node rgb(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature rgba_4_sig;
Node rgba_4(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node rgba_4(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature rgba_2_sig;
Node rgba_2(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node rgba_2(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature red_sig;
Node red(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node red(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature green_sig;
Node green(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node green(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature blue_sig;
Node blue(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node blue(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature mix_sig;
Node mix(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node mix(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
// HSL Functions ///////////////////////////////////////////////////////
extern Signature hsl_sig;
Node hsl(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node hsl(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature hsla_sig;
Node hsla(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node hsla(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature hue_sig;
Node hue(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node hue(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature saturation_sig;
Node saturation(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node saturation(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature lightness_sig;
Node lightness(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node lightness(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature adjust_hue_sig;
Node adjust_hue(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node adjust_hue(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature lighten_sig;
Node lighten(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node lighten(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature darken_sig;
Node darken(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node darken(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature saturate_sig;
Node saturate(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node saturate(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature desaturate_sig;
Node desaturate(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node desaturate(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature grayscale_sig;
Node grayscale(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node grayscale(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature complement_sig;
Node complement(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node complement(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature invert_sig;
Node invert(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node invert(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
// Opacity Functions ///////////////////////////////////////////////////
extern Signature alpha_sig;
Node alpha(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node alpha(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature opacity_sig;
Node opacity(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node opacity(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature opacify_sig;
Node opacify(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node opacify(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature fade_in_sig;
Node fade_in(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node fade_in(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature transparentize_sig;
Node transparentize(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node transparentize(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature fade_out_sig;
Node fade_out(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node fade_out(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
// Other Color Functions ///////////////////////////////////////////////
extern Signature adjust_color_sig;
Node adjust_color(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node adjust_color(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature scale_color_sig;
Node scale_color(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node scale_color(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature change_color_sig;
Node change_color(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node change_color(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature ie_hex_str_sig;
Node ie_hex_str(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
// String Functions ////////////////////////////////////////////////////
extern Signature unquote_sig;
Node unquote(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node unquote(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature quote_sig;
Node quote(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node quote(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
// Number Functions ////////////////////////////////////////////////////
extern Signature percentage_sig;
Node percentage(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node percentage(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature round_sig;
Node round(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node round(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature ceil_sig;
Node ceil(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node ceil(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature floor_sig;
Node floor(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node floor(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature abs_sig;
Node abs(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node abs(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
// List Functions //////////////////////////////////////////////////////
extern Signature length_sig;
Node length(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node length(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature nth_sig;
Node nth(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node nth(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature index_sig;
Node index(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node index(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature join_sig;
Node join(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node join(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature append_sig;
Node append(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node append(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature compact_1_sig;
Node compact_1(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node compact_1(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature compact_n_sig;
Node compact_n(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node compact_n(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
// Introspection Functions /////////////////////////////////////////////
extern Signature type_of_sig;
Node type_of(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node type_of(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature unit_sig;
Node unit(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node unit(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature unitless_sig;
Node unitless(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node unitless(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature comparable_sig;
Node comparable(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node comparable(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
// Boolean Functions ///////////////////////////////////////////////////
extern Signature not_sig;
Node not_impl(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node not_impl(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
extern Signature if_sig;
Node if_impl(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node if_impl(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
// Path Functions //////////////////////////////////////////////////////
extern Signature image_url_sig;
Node image_url(const Node, Environment&, Node_Factory&, string& path, size_t line);
Node image_url(const Node, Environment&, Node_Factory&, Backtrace&, string& path, size_t line);
}
}
......@@ -19,6 +19,7 @@ namespace Sass {
{
case block:
case mixin_call:
case mixin_content:
case root:
case if_directive:
case for_through_directive:
......@@ -35,6 +36,7 @@ namespace Sass {
switch (at(i).type())
{
case mixin_call:
case mixin_content:
case block:
case if_directive:
case for_through_directive:
......@@ -44,6 +46,7 @@ namespace Sass {
Node expn(at(i));
if (expn.has_expansions()) expn.flatten();
ip_->has_statements |= expn.has_statements();
ip_->has_comments |= expn.has_comments();
ip_->has_blocks |= expn.has_blocks();
ip_->has_expansions |= expn.has_expansions();
// TO DO: make this more efficient -- replace with a dummy node instead of erasing
......
......@@ -142,6 +142,7 @@ namespace Sass {
numeric_percentage,
numeric_dimension,
numeric_color,
ie_hex_str,
boolean,
important,
......@@ -154,6 +155,7 @@ namespace Sass {
function_call,
mixin,
mixin_call,
mixin_content,
parameters,
arguments,
......@@ -183,6 +185,7 @@ namespace Sass {
bool has_children() const;
bool has_statements() const;
bool has_comments() const;
bool has_blocks() const;
bool has_expansions() const;
bool has_backref() const;
......@@ -237,11 +240,12 @@ namespace Sass {
bool operator>(Node rhs) const;
bool operator>=(Node rhs) const;
string to_string(Type inside_of = none) const;
void emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel = false, bool in_media_query = false);
void emit_propset(stringstream& buf, size_t depth, const string& prefix);
string to_string(Type inside_of = none, const string space = " ") const;
void emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel = false, bool in_media_query = false, bool source_comments = false);
void emit_propset(stringstream& buf, size_t depth, const string& prefix, const bool compressed = false);
void echo(stringstream& buf, size_t depth = 0);
void emit_expanded_css(stringstream& buf, const string& prefix);
void emit_compressed_css(stringstream& buf);
};
......@@ -264,6 +268,7 @@ namespace Sass {
bool has_children;
bool has_statements;
bool has_comments;
bool has_blocks;
bool has_expansions;
bool has_backref;
......@@ -281,6 +286,7 @@ namespace Sass {
type(Node::none), */
has_children(false),
has_statements(false),
has_comments(false),
has_blocks(false),
has_expansions(false),
has_backref(false),
......@@ -349,9 +355,13 @@ namespace Sass {
{
children.push_back(n);
has_children = true;
if (n.is_null()) return;
switch (n.type())
{
case Node::comment:
case Node::comment: {
has_comments = true;
} break;
case Node::css_import:
case Node::rule:
case Node::propset:
......@@ -372,7 +382,8 @@ namespace Sass {
case Node::for_to_directive:
case Node::each_directive:
case Node::while_directive:
case Node::mixin_call: {
case Node::mixin_call:
case Node::mixin_content: {
has_expansions = true;
} break;
......@@ -391,7 +402,8 @@ namespace Sass {
has_children = true;
switch (n.type())
{
case Node::comment:
case Node::comment: has_comments = true; break;
case Node::css_import:
case Node::rule:
case Node::propset: has_statements = true; break;
......@@ -404,7 +416,8 @@ namespace Sass {
case Node::for_to_directive:
case Node::each_directive:
case Node::while_directive:
case Node::mixin_call: has_expansions = true; break;
case Node::mixin_call:
case Node::mixin_content: has_expansions = true; break;
case Node::backref: has_backref = true; break;
......@@ -440,6 +453,7 @@ namespace Sass {
inline bool Node::has_children() const { return ip_->has_children; }
inline bool Node::has_statements() const { return ip_->has_statements; }
inline bool Node::has_comments() const { return ip_->has_comments; }
inline bool Node::has_blocks() const { return ip_->has_blocks; }
inline bool Node::has_expansions() const { return ip_->has_expansions; }
inline bool Node::has_backref() const { return ip_->has_backref; }
......
......@@ -15,8 +15,9 @@ using std::endl;
namespace Sass {
string Node::to_string(Type inside_of) const
string Node::to_string(Type inside_of, const string space) const
{
if (is_null()) return "";
switch (type())
{
case none: {
......@@ -25,9 +26,10 @@ namespace Sass {
case selector_group:
case media_expression_group: { // really only needed for arg to :not
string result(at(0).to_string());
string result(at(0).to_string(none, space));
for (size_t i = 1, S = size(); i < S; ++i) {
result += ", ";
result += ",";
result += space;
result += at(i).to_string();
}
return result;
......@@ -37,21 +39,22 @@ namespace Sass {
string result;
if (at(0).type() == rule) {
result += "(";
result += at(0).to_string();
result += at(0).to_string(none, space);
result += ")";
}
else {
result += at(0).to_string();
result += at(0).to_string(none, space);
}
for (size_t i = 1, S = size(); i < S; ++i) {
result += " ";
if (at(i).type() == rule) {
result += space;
result += "(";
result += at(i).to_string();
result += at(i).to_string(none, space);
result += ")";
}
else {
result += at(i).to_string();
result += " ";
result += at(i).to_string(none, space);
}
}
return result;
......@@ -60,10 +63,15 @@ namespace Sass {
case selector: {
string result;
result += at(0).to_string();
result += at(0).to_string(none, space);
for (size_t i = 1, S = size(); i < S; ++i) {
if (at(i).type() == selector_combinator || at(i-1).type() == selector_combinator) {
result += space;
}
else {
result += " ";
result += at(i).to_string();
}
result += at(i).to_string(none, space);
}
return result;
} break;
......@@ -75,7 +83,7 @@ namespace Sass {
case simple_selector_sequence: {
string result;
for (size_t i = 0, S = size(); i < S; ++i) {
result += at(i).to_string();
result += at(i).to_string(none, space);
}
return result;
} break;
......@@ -87,17 +95,17 @@ namespace Sass {
case pseudo_negation: {
string result;
result += at(0).to_string();
result += at(1).to_string();
result += at(0).to_string(none, space);
result += at(1).to_string(none, space);
result += ')';
return result;
} break;
case functional_pseudo: {
string result;
result += at(0).to_string();
result += at(0).to_string(none, space);
for (size_t i = 1, S = size(); i < S; ++i) {
result += at(i).to_string();
result += at(i).to_string(none, space);
}
result += ')';
return result;
......@@ -107,27 +115,34 @@ namespace Sass {
string result;
result += "[";
for (size_t i = 0, S = size(); i < S; ++i) {
result += at(i).to_string();
result += at(i).to_string(none, space);
}
result += ']';
return result;
} break;
case rule: {
string result(at(0).to_string(property));
result += ": ";
result += at(1).to_string();
string result(at(0).to_string(property, space));
result += ":";
result += space;
result += at(1).to_string(none, space);
return result;
} break;
case list: {
if (size() == 0) return "";
string result(at(0).to_string());
string result(at(0).to_string(none, space));
for (size_t i = 1, S = size(); i < S; ++i) {
if (at(i).is_null()) continue;
if (at(i).type() == list && at(i).size() == 0) continue;
result += is_comma_separated() ? ", " : " ";
result += at(i).to_string();
if (is_comma_separated()) {
result += ",";
result += space;
}
else {
result += " ";
}
result += at(i).to_string(none, space);
}
return result;
} break;
......@@ -135,10 +150,10 @@ namespace Sass {
// still necessary for unevaluated expressions
case expression:
case term: {
string result(at(0).to_string());
string result(at(0).to_string(none, space));
for (size_t i = 1, S = size(); i < S; ++i) {
if (at(i).type() != add && at(i).type() != mul) {
result += at(i).to_string();
result += at(i).to_string(none, space);
}
}
return result;
......@@ -156,16 +171,16 @@ namespace Sass {
case css_import: {
stringstream ss;
ss << "@import url(";
ss << at(0).to_string();
ss << at(0).to_string(none, space);
ss << ")";
return ss.str();
}
case function_call: {
stringstream ss;
ss << at(0).to_string();
ss << at(0).to_string(none, space);
ss << "(";
ss << at(1).to_string();
ss << at(1).to_string(none, space);
ss << ")";
return ss.str();
}
......@@ -174,10 +189,11 @@ namespace Sass {
stringstream ss;
size_t S = size();
if (S > 0) {
ss << at(0).to_string();
ss << at(0).to_string(none, space);
for (size_t i = 1; i < S; ++i) {
ss << ", ";
ss << at(i).to_string();
ss << ",";
ss << space;
ss << at(i).to_string(none, space);
}
}
return ss.str();
......@@ -186,14 +202,14 @@ namespace Sass {
case unary_plus: {
stringstream ss;
ss << "+";
ss << at(0).to_string();
ss << at(0).to_string(none, space);
return ss.str();
}
case unary_minus: {
stringstream ss;
ss << "-";
ss << at(0).to_string();
ss << at(0).to_string(none, space);
return ss.str();
}
......@@ -256,17 +272,36 @@ namespace Sass {
stringstream ss;
ss << "rgba(" << static_cast<unsigned long>(at(0).numeric_value());
for (size_t i = 1; i < 3; ++i) {
ss << ", " << static_cast<unsigned long>(at(i).numeric_value());
ss << "," << space << static_cast<unsigned long>(at(i).numeric_value());
}
ss << ", " << at(3).numeric_value() << ')';
ss << "," << space << at(3).numeric_value() << ')';
return ss.str();
}
} break;
case ie_hex_str: {
stringstream ss;
ss << '#' << std::setw(2) << std::setfill('0') << std::hex;
double x = at(3).numeric_value() * 255;
if (x > 0xff) x = 0xff;
else if (x < 0) x = 0;
ss << std::hex << std::setw(2) << std::uppercase << static_cast<unsigned long>(std::floor(x+0.5));
for (size_t i = 0; i < 3; ++i) {
double x = at(i).numeric_value();
if (x > 0xff) x = 0xff;
else if (x < 0) x = 0;
ss << std::hex << std::setw(2) << std::uppercase << static_cast<unsigned long>(std::floor(x+0.5));
}
return ss.str();
} break;
case uri: {
string result("url(");
// result += token().to_string();
result += at(0).to_string();
result += at(0).to_string(none, space);
result += ")";
return result;
} break;
......@@ -308,7 +343,7 @@ namespace Sass {
result += at(i).token().unquote();
}
else {
result += at(i).to_string(identifier_schema);
result += at(i).to_string(identifier_schema, space);
}
}
if (is_quoted()) result = "\"" + result + "\"";
......@@ -318,7 +353,7 @@ namespace Sass {
case string_schema: {
string result;
for (size_t i = 0, S = size(); i < S; ++i) {
string chunk(at(i).to_string());
string chunk(at(i).to_string(none, space));
if (at(i).type() == string_constant) {
result += chunk.substr(1, chunk.size()-2);
}
......@@ -353,14 +388,14 @@ namespace Sass {
}
}
void Node::emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel, bool in_media_query)
void Node::emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel, bool in_media_query, bool source_comments)
{
switch (type())
{
case root: {
if (has_expansions()) flatten();
for (size_t i = 0, S = size(); i < S; ++i) {
at(i).emit_nested_css(buf, depth, true);
at(i).emit_nested_css(buf, depth, true, false, source_comments);
}
} break;
......@@ -369,7 +404,11 @@ namespace Sass {
Node block(at(1));
if (block.has_expansions()) block.flatten();
if (block.has_statements()) {
if (block.has_statements() || block.has_comments()) {
if (source_comments) {
buf << string(2*depth, ' ');
buf << "/* line " << sel_group.line() << ", " << sel_group.path() << " */" << endl;
}
buf << string(2*depth, ' ');
buf << sel_group.to_string();
buf << " {";
......@@ -385,7 +424,7 @@ namespace Sass {
case block_directive:
case blockless_directive:
case warning: {
block[i].emit_nested_css(buf, depth+1);
block[i].emit_nested_css(buf, depth+1, false, false, source_comments);
} break;
default: break;
}
......@@ -397,19 +436,77 @@ namespace Sass {
if (block.has_blocks()) {
for (size_t i = 0, S = block.size(); i < S; ++i) {
if (block[i].type() == ruleset || block[i].type() == media_query) {
block[i].emit_nested_css(buf, depth, false, in_media_query);
block[i].emit_nested_css(buf, depth, false, in_media_query, source_comments);
}
}
}
if (block.has_statements()) --depth; // see previous comment
if (block.has_statements() || block.has_comments()) --depth; // see previous comment
if ((depth == 0) && at_toplevel && !in_media_query) buf << endl;
} break;
case media_query: {
buf << string(2*depth, ' ');
buf << "@media " << at(0).to_string() << " {" << endl;
at(1).emit_nested_css(buf, depth+1, false, true);
buf << "@media " << at(0).to_string() << " {";
// at(1).emit_nested_css(buf, depth+1, false, true);
Node block(at(1));
if (block.has_expansions()) block.flatten();
bool has_comments = block.has_comments();
bool has_statements = block.has_statements();
bool has_blocks = block.has_blocks();
if (has_comments && !has_statements && !has_blocks) {
// just print out the comments without a block
for (size_t i = 0, S = block.size(); i < S; ++i) {
if (block[i].type() == comment)
block[i].emit_nested_css(buf, depth+1, false, false, source_comments);
}
}
if (has_statements) {
++depth;
buf << endl;
buf << string(2*depth, ' ');
buf << at(2).to_string();
buf << " {";
for (size_t i = 0, S = block.size(); i < S; ++i) {
Type stm_type = block[i].type();
if (stm_type == block_directive) buf << endl;
switch (stm_type)
{
case comment:
case rule:
case css_import:
case propset:
case block_directive:
case blockless_directive:
case warning: {
// if (stm_type != comment) buf << endl;
block[i].emit_nested_css(buf, depth+1, false, false, source_comments);
} break;
default: break;
}
}
buf << " }";
}
if (block.has_blocks()) {
for (size_t i = 0, S = block.size(); i < S; ++i) {
Type stm_type = block[i].type();
if (stm_type == comment && !has_statements) {
if (i > 0 && block[i-1].type() == ruleset) buf << endl;
block[i].emit_nested_css(buf, depth+1, false, true, source_comments);
}
if (stm_type == ruleset || stm_type == media_query) {
buf << endl;
if (i > 0 &&
block[i-1].type() == ruleset &&
!block[i-1][1].has_blocks())
{ buf << endl; }
block[i].emit_nested_css(buf, depth+1, false, true, source_comments);
}
}
}
buf << " }" << endl;
--depth;
} break;
case blockless_directive: {
......@@ -436,7 +533,7 @@ namespace Sass {
default:
break;
}
block[i].emit_nested_css(buf, depth+1, false, in_media_query);
block[i].emit_nested_css(buf, depth+1, false, in_media_query, source_comments);
}
buf << " }" << endl;
if ((depth == 0) && at_toplevel && !in_media_query) buf << endl;
......@@ -482,30 +579,174 @@ namespace Sass {
}
}
void Node::emit_propset(stringstream& buf, size_t depth, const string& prefix)
void Node::emit_compressed_css(stringstream& buf)
{
switch (type())
{
case root: {
if (has_expansions()) flatten();
for (size_t i = 0, S = size(); i < S; ++i) {
at(i).emit_compressed_css(buf);
}
} break;
case ruleset: {
Node sel_group(at(2));
Node block(at(1));
if (block.has_expansions()) block.flatten();
if (block.has_statements()) {
buf << sel_group.to_string(none, "") << "{";
for (size_t i = 0, S = block.size(); i < S; ++i) {
Type stm_type = block[i].type();
switch (stm_type)
{
case rule:
case css_import:
case propset:
case block_directive:
case blockless_directive:
case warning: {
block[i].emit_compressed_css(buf);
} break;
default: break;
}
}
buf << "}";
}
if (block.has_blocks()) {
for (size_t i = 0, S = block.size(); i < S; ++i) {
if (block[i].type() == ruleset || block[i].type() == media_query) {
block[i].emit_compressed_css(buf);
}
}
}
} break;
case media_query: {
buf << "@media " << at(0).to_string(none, "") << "{";
Node block(at(1));
if (block.has_expansions()) block.flatten();
bool has_statements = block.has_statements();
if (has_statements) {
buf << at(2).to_string(none, "");
buf << "{";
for (size_t i = 0, S = block.size(); i < S; ++i) {
Type stm_type = block[i].type();
switch (stm_type)
{
case rule:
case css_import:
case propset:
case block_directive:
case blockless_directive:
case warning: {
block[i].emit_compressed_css(buf);
} break;
default: break;
}
}
buf << "}";
}
if (block.has_blocks()) {
for (size_t i = 0, S = block.size(); i < S; ++i) {
Type stm_type = block[i].type();
if (stm_type == ruleset || stm_type == media_query) {
block[i].emit_compressed_css(buf);
}
}
}
buf << "}";
} break;
case blockless_directive: {
buf << to_string(none, "");
buf << ";";
} break;
case block_directive: {
Node header(at(0));
Node block(at(1));
if (block.has_expansions()) block.flatten();
buf << header.to_string(none, "");
buf << "{";
for (size_t i = 0, S = block.size(); i < S; ++i) {
block[i].emit_compressed_css(buf);
}
buf << "}";
} break;
case propset: {
emit_propset(buf, 0, "", true);
} break;
case rule: {
buf << to_string(none, "");
buf << ";";
} break;
case css_import: {
buf << to_string(none, "");
buf << ";";
} break;
case property: {
buf << token().to_string() << ":";
} break;
case values: {
for (size_t i = 0, S = size(); i < S; ++i) {
buf << " " << at(i).token().to_string();
}
} break;
case comment: {
// do nothing
} break;
default: {
buf << to_string(none, "");
} break;
}
}
void Node::emit_propset(stringstream& buf, size_t depth, const string& prefix, const bool compressed)
{
string new_prefix(prefix);
// bool has_prefix = false;
if (new_prefix.empty()) {
if (!compressed) {
new_prefix += "\n";
new_prefix += string(2*depth, ' ');
new_prefix += at(0).token().to_string();
}
new_prefix += at(0).to_string();
}
else {
new_prefix += "-";
new_prefix += at(0).token().to_string();
new_prefix += at(0).to_string();
// has_prefix = true;
}
Node rules(at(1));
rules.flatten();
for (size_t i = 0, S = rules.size(); i < S; ++i) {
if (rules[i].type() == propset) {
rules[i].emit_propset(buf, depth+1, new_prefix);
rules[i].emit_propset(buf, depth+1, new_prefix, compressed);
}
else {
else if (rules[i].type() == rule) {
buf << new_prefix;
if (rules[i][0].token().to_string() != "") buf << '-';
if (rules[i][0].to_string() != "") buf << '-';
if (!compressed) {
rules[i][0].emit_nested_css(buf, depth);
if (rules[i][0].type() == identifier_schema) buf << ": ";
rules[i][1].emit_nested_css(buf, depth);
}
else {
rules[i][0].emit_compressed_css(buf);
if (rules[i][0].type() == identifier_schema) buf << ": ";
rules[i][1].emit_compressed_css(buf);
}
buf << ';';
}
}
......@@ -513,4 +754,5 @@ namespace Sass {
void Node::echo(stringstream& buf, size_t depth) { }
void Node::emit_expanded_css(stringstream& buf, const string& prefix) { }
}
\ No newline at end of file
......@@ -26,9 +26,7 @@ namespace Sass {
Node operator()(Node::Type type, string file, size_t line, Token t);
// for making boolean values or interior nodes that have children
Node operator()(Node::Type type, string file, size_t line, size_t size);
// // for making nodes representing boolean values
// Node operator()(Node::Type type, string file, size_t line, bool b);
// for making nodes representing numbers
// for making nodes representing numbers and numeric percentages
Node operator()(string file, size_t line, double v, Node::Type type = Node::number);
// for making nodes representing numeric dimensions (e.g. 5px, 3em)
Node operator()(string file, size_t line, double v, const Token& t);
......
......@@ -138,6 +138,10 @@ namespace Sass {
return exactly<include_kwd>(src);
}
const char* content(const char* src) {
return exactly<content_kwd>(src);
}
const char* extend(const char* src) {
return exactly<extend_kwd>(src);
}
......@@ -383,5 +387,32 @@ namespace Sass {
const char* folders(const char* src) {
return zero_plus< folder >(src);
}
const char* chunk(const char* src) {
char inside_str = 0;
const char* p = src;
size_t depth = 0;
while (true) {
if (!*p) {
return 0;
}
else if (!inside_str && (*p == '"' || *p == '\'')) {
inside_str = *p;
}
else if (*p == inside_str && *(p-1) != '\\') {
inside_str = 0;
}
else if (*p == '(' && !inside_str) {
++depth;
}
else if (*p == ')' && !inside_str) {
if (depth == 0) return p;
else --depth;
}
++p;
}
// unreachable
return 0;
}
}
}
\ No newline at end of file
......@@ -317,6 +317,7 @@ namespace Sass {
const char* function(const char* src);
const char* return_directive(const char* src);
const char* include(const char* src);
const char* content(const char* src);
const char* extend(const char* src);
const char* if_directive(const char* src);
......@@ -447,5 +448,7 @@ namespace Sass {
}
return counter;
}
const char* chunk(const char* src);
}
}
......@@ -45,13 +45,15 @@ extern "C" {
static char* process_document(Sass::Document& doc, int style)
{
using namespace Sass;
Backtrace root_trace(0, "", 0, "");
doc.parse_scss();
expand(doc.root,
Node(),
doc.context.global_env,
doc.context.function_env,
doc.context.new_Node,
doc.context);
doc.context,
root_trace);
// extend_selectors(doc.context.pending_extensions, doc.context.extensions, doc.context.new_Node);
if (doc.context.has_extensions) extend(doc.root, doc.context.extensions, doc.context.new_Node);
string output(doc.emit_css(static_cast<Document::CSS_Style>(style)));
......@@ -64,7 +66,7 @@ extern "C" {
{
using namespace Sass;
try {
Context cpp_ctx(c_ctx->options.include_paths, c_ctx->options.image_path);
Context cpp_ctx(c_ctx->options.include_paths, c_ctx->options.image_path, c_ctx->options.source_comments);
// cpp_ctx.image_path = c_ctx->options.image_path;
// Document doc(0, c_ctx->input_string, cpp_ctx);
Document doc(Document::make_from_source_chars(cpp_ctx, c_ctx->source_string));
......@@ -74,7 +76,7 @@ extern "C" {
}
catch (Error& e) {
stringstream msg_stream;
msg_stream << "ERROR -- " << e.path << ", line " << e.line << ": " << e.message << endl;
msg_stream << e.path << ":" << e.line << ": error: " << e.message << endl;
string msg(msg_stream.str());
char* msg_str = (char*) malloc(msg.size() + 1);
strcpy(msg_str, msg.c_str());
......@@ -84,7 +86,7 @@ extern "C" {
}
catch(bad_alloc& ba) {
stringstream msg_stream;
msg_stream << "ERROR -- unable to allocate memory: " << ba.what() << endl;
msg_stream << "Unable to allocate memory: " << ba.what() << endl;
string msg(msg_stream.str());
char* msg_str = (char*) malloc(msg.size() + 1);
strcpy(msg_str, msg.c_str());
......@@ -99,22 +101,16 @@ extern "C" {
int sass_compile_file(sass_file_context* c_ctx)
{
using namespace Sass;
Context cpp_ctx(c_ctx->options.include_paths, c_ctx->options.image_path, c_ctx->options.source_comments);
try {
Context cpp_ctx(c_ctx->options.include_paths, c_ctx->options.image_path);
// Document doc(c_ctx->input_path, 0, cpp_ctx);
// string path_string(c_ctx->options.image_path);
// path_string = "'" + path_string + "/";
// cpp_ctx.image_path = c_ctx->options.image_path;
Document doc(Document::make_from_file(cpp_ctx, string(c_ctx->input_path)));
// cerr << "MADE A DOC AND CONTEXT OBJ" << endl;
// cerr << "REGISTRY: " << doc.context.registry.size() << endl;
c_ctx->output_string = process_document(doc, c_ctx->options.output_style);
c_ctx->error_message = 0;
c_ctx->error_status = 0;
}
catch (Error& e) {
stringstream msg_stream;
msg_stream << "ERROR -- " << e.path << ", line " << e.line << ": " << e.message << endl;
msg_stream << e.path << ":" << e.line << ": error: " << e.message << endl;
string msg(msg_stream.str());
char* msg_str = (char*) malloc(msg.size() + 1);
strcpy(msg_str, msg.c_str());
......@@ -124,7 +120,30 @@ extern "C" {
}
catch(bad_alloc& ba) {
stringstream msg_stream;
msg_stream << "ERROR -- unable to allocate memory: " << ba.what() << endl;
msg_stream << "Unable to allocate memory: " << ba.what() << endl;
string msg(msg_stream.str());
char* msg_str = (char*) malloc(msg.size() + 1);
strcpy(msg_str, msg.c_str());
c_ctx->error_status = 1;
c_ctx->output_string = 0;
c_ctx->error_message = msg_str;
}
catch(string& bad_path) {
for (vector<string>::iterator path = cpp_ctx.include_paths.begin(); path < cpp_ctx.include_paths.end(); ++path) {
try {
Document doc(Document::make_from_file(cpp_ctx, *path + string(c_ctx->input_path)));
c_ctx->output_string = process_document(doc, c_ctx->options.output_style);
c_ctx->error_message = 0;
c_ctx->error_status = 0;
return 0;
}
catch (string& bad_path) {
// continue looping
}
}
// couldn't find the specified file in the include paths; report an error
stringstream msg_stream;
msg_stream << "error reading file \"" << bad_path << "\"" << endl;
string msg(msg_stream.str());
char* msg_str = (char*) malloc(msg.size() + 1);
strcpy(msg_str, msg.c_str());
......
#include <node.h>
#define SASS_INTERFACE
#ifdef __cplusplus
extern "C" {
......@@ -11,18 +11,17 @@ extern "C" {
struct sass_options {
int output_style;
int source_comments; // really want a bool, but C doesn't have them
char* include_paths;
char* image_path;
};
struct sass_context {
char* source_string;
const char* source_string;
char* output_string;
struct sass_options options;
int error_status;
char* error_message;
uv_work_t request;
v8::Persistent<v8::Function> callback;
};
struct sass_file_context {
......
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