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