Commit a087ed82 by Andrew Nesbitt

Updated libsass, closes #52

parent 119743ae
...@@ -41,6 +41,17 @@ namespace Sass { ...@@ -41,6 +41,17 @@ namespace Sass {
return ss.str(); return ss.str();
} }
size_t depth()
{
size_t d = 0;
Backtrace* p = parent;
while (p) {
++d;
p = p->parent;
}
return d-1;
}
}; };
} }
\ No newline at end of file
...@@ -86,6 +86,7 @@ namespace Sass { ...@@ -86,6 +86,7 @@ namespace Sass {
extern const char rparen[] = ")"; extern const char rparen[] = ")";
extern const char sign_chars[] = "-+"; extern const char sign_chars[] = "-+";
extern const char hyphen[] = "-"; extern const char hyphen[] = "-";
extern const char ellipsis[] = "...";
// type names // type names
extern const char numeric_name[] = "numeric value"; extern const char numeric_name[] = "numeric value";
...@@ -96,6 +97,7 @@ namespace Sass { ...@@ -96,6 +97,7 @@ namespace Sass {
extern const char bool_name[] = "bool"; extern const char bool_name[] = "bool";
extern const char color_name[] = "color"; extern const char color_name[] = "color";
extern const char list_name[] = "list"; extern const char list_name[] = "list";
extern const char arglist_name[] = "arglist";
} }
} }
\ No newline at end of file
...@@ -86,6 +86,7 @@ namespace Sass { ...@@ -86,6 +86,7 @@ namespace Sass {
extern const char rparen[]; extern const char rparen[];
extern const char sign_chars[]; extern const char sign_chars[];
extern const char hyphen[]; extern const char hyphen[];
extern const char ellipsis[];
// type names // type names
extern const char numeric_name[]; extern const char numeric_name[];
...@@ -96,5 +97,6 @@ namespace Sass { ...@@ -96,5 +97,6 @@ namespace Sass {
extern const char bool_name[]; extern const char bool_name[];
extern const char color_name[]; extern const char color_name[];
extern const char list_name[]; extern const char list_name[];
extern const char arglist_name[];
} }
} }
\ No newline at end of file
...@@ -177,23 +177,32 @@ namespace Sass { ...@@ -177,23 +177,32 @@ namespace Sass {
Node::Type param_type = Node::none; Node::Type param_type = Node::none;
if (lex< exactly<'('> >()) { if (lex< exactly<'('> >()) {
if (peek< variable >()) { if (peek< variable >()) {
Node param(parse_parameter(param_type)); do {
if (param.type() == Node::assignment) param_type = Node::assignment;
params << param;
while (lex< exactly<','> >()) {
if (!peek< variable >()) throw_syntax_error("expected a variable name (e.g. $x) for the parameter list for " + name.to_string()); if (!peek< variable >()) throw_syntax_error("expected a variable name (e.g. $x) for the parameter list for " + name.to_string());
Node param(parse_parameter(param_type)); Node param(parse_parameter(param_type));
if (param.type() == Node::assignment) param_type = Node::assignment; if (param.type() == Node::assignment) param_type = Node::assignment;
params << param; params << param;
if (param.type() == Node::rest) {
param_type = Node::rest;
break;
}
} while (lex< exactly<','> >());
if (!lex< exactly<')'> >()) {
if (params.back().type() == Node::rest && peek< exactly<','> >()) {
throw_syntax_error("variable-length parameter must appear last in a parameter list");
}
else {
throw_syntax_error("parameter list for " + name.to_string() + " requires a ')'");
}
} }
if (!lex< exactly<')'> >()) throw_syntax_error("parameter list for " + name.to_string() + " requires a ')'");
} }
else if (!lex< exactly<')'> >()) throw_syntax_error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name.to_string()); else if (!lex< exactly<')'> >()) throw_syntax_error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name.to_string());
} }
return params; return params;
} }
Node Document::parse_parameter(Node::Type param_type) { Node Document::parse_parameter(Node::Type param_type)
{
lex< variable >(); lex< variable >();
Node var(context.new_Node(Node::variable, path, line, lexed)); Node var(context.new_Node(Node::variable, path, line, lexed));
if (param_type == Node::assignment) { if (param_type == Node::assignment) {
...@@ -213,6 +222,9 @@ namespace Sass { ...@@ -213,6 +222,9 @@ namespace Sass {
par_and_val << var << val; par_and_val << var << val;
return par_and_val; return par_and_val;
} }
else if (lex< exactly< ellipsis > >()) {
return context.new_Node(Node::rest, var.path(), var.line(), var.token());
}
return var; return var;
} }
...@@ -241,16 +253,26 @@ namespace Sass { ...@@ -241,16 +253,26 @@ namespace Sass {
Node::Type arg_type = Node::none; Node::Type arg_type = Node::none;
if (lex< exactly<'('> >()) { if (lex< exactly<'('> >()) {
if (!peek< exactly<')'> >(position)) { if (!peek< exactly<')'> >(position)) {
Node arg(parse_argument(Node::none)); do {
args << arg;
if (arg.type() == Node::assignment) arg_type = Node::assignment;
while (lex< exactly<','> >()) {
Node arg(parse_argument(arg_type)); Node arg(parse_argument(arg_type));
args << arg; args << arg;
if (arg.type() == Node::assignment) arg_type = Node::assignment; if (arg.type() == Node::assignment) {
arg_type = Node::assignment;
}
else if (arg.type() == Node::rest) {
arg_type = Node::rest;
break;
}
} while (lex< exactly<','> >());
}
if (!lex< exactly<')'> >()) {
if (args.back().is_arglist() && peek< exactly<','> >()) {
throw_syntax_error("variable-length argument must appear last in an argument list");
}
else {
throw_syntax_error("improperly terminated argument list for " + name.to_string());
} }
} }
if (!lex< exactly<')'> >()) throw_syntax_error("improperly terminated argument list for " + name.to_string());
} }
return args; return args;
} }
...@@ -274,8 +296,8 @@ namespace Sass { ...@@ -274,8 +296,8 @@ namespace Sass {
} }
} }
// otherwise accept either, and let the caller set the arg_type flag // otherwise accept either, and let the caller set the arg_type flag
if (arg_type == Node::none && else if (arg_type == Node::none &&
peek< sequence < variable, spaces_and_comments, exactly<':'> > >()) { peek< sequence < variable, spaces_and_comments, exactly<':'> > >()) {
lex< variable >(); lex< variable >();
Node var(context.new_Node(Node::variable, path, line, lexed)); Node var(context.new_Node(Node::variable, path, line, lexed));
lex< exactly<':'> >(); lex< exactly<':'> >();
...@@ -285,8 +307,18 @@ namespace Sass { ...@@ -285,8 +307,18 @@ namespace Sass {
assn << var << val; assn << var << val;
return assn; return assn;
} }
// else if (arg_type == Node::none &&
// peek< sequence < variable, spaces_and_comments, exactly< ellipsis > > >()) {
// lex< variable >();
// lex< exactly< ellipsis > >();
// return context.new_Node(Node::rest, path, line, lexed);
// }
Node val(parse_space_list()); Node val(parse_space_list());
val.should_eval() = true; val.should_eval() = true;
if (lex< exactly< ellipsis > >()) {
val.is_arglist() = true;
val.is_splat() = true;
}
return val; return val;
} }
...@@ -711,7 +743,8 @@ namespace Sass { ...@@ -711,7 +743,8 @@ namespace Sass {
if (peek< exactly<';'> >(position) || if (peek< exactly<';'> >(position) ||
peek< exactly<'}'> >(position) || peek< exactly<'}'> >(position) ||
peek< exactly<'{'> >(position) || peek< exactly<'{'> >(position) ||
peek< exactly<')'> >(position)) peek< exactly<')'> >(position) ||
peek< exactly<ellipsis> >(position))
{ return context.new_Node(Node::list, path, line, 0); } { return context.new_Node(Node::list, path, line, 0); }
Node list1(parse_space_list()); Node list1(parse_space_list());
// if it's a singleton, return it directly; don't wrap it // if it's a singleton, return it directly; don't wrap it
...@@ -741,6 +774,7 @@ namespace Sass { ...@@ -741,6 +774,7 @@ namespace Sass {
peek< exactly<'{'> >(position) || peek< exactly<'{'> >(position) ||
peek< exactly<')'> >(position) || peek< exactly<')'> >(position) ||
peek< exactly<','> >(position) || peek< exactly<','> >(position) ||
peek< exactly<ellipsis> >(position) ||
peek< default_flag >(position)) peek< default_flag >(position))
{ return disj1; } { return disj1; }
...@@ -753,6 +787,7 @@ namespace Sass { ...@@ -753,6 +787,7 @@ namespace Sass {
peek< exactly<'{'> >(position) || peek< exactly<'{'> >(position) ||
peek< exactly<')'> >(position) || peek< exactly<')'> >(position) ||
peek< exactly<','> >(position) || peek< exactly<','> >(position) ||
peek< exactly<ellipsis> >(position) ||
peek< default_flag >(position))) peek< default_flag >(position)))
{ {
Node disj(parse_disjunction()); Node disj(parse_disjunction());
......
...@@ -57,9 +57,11 @@ namespace Sass { ...@@ -57,9 +57,11 @@ namespace Sass {
} break; } break;
case Node::mixin_content: { case Node::mixin_content: {
expr += new_Node(content); if (!content.is_null()) {
for (size_t i = 0, S = expr.size(); i < S; ++i) { expr += new_Node(content);
re_expand(expr[i], prefix, env, f_env, new_Node, ctx, bt, false, 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; } break;
...@@ -388,12 +390,19 @@ namespace Sass { ...@@ -388,12 +390,19 @@ namespace Sass {
switch (expr.type()) switch (expr.type())
{ {
case Node::list: { case Node::list: {
// cerr << string(bt.depth(), '\t') << "about to eval a list: " << expr.to_string() << "::" << expr.is_arglist() << endl;
bool is_arglist = expr.is_arglist();
bool is_splat = expr.is_splat();
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, bt); 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];
result.is_arglist() = is_arglist;
result.is_splat() = is_splat;
// cerr << string(bt.depth(), '\t') << "evaluated a list: " << result.to_string() << "::" << result.is_arglist() << endl;
} }
// else cerr << string(bt.depth(), '\t') << "evaluated a list (sort of): " << expr.to_string() << "::" << expr.is_arglist() << endl;
} break; } break;
case Node::disjunction: { case Node::disjunction: {
...@@ -493,6 +502,7 @@ namespace Sass { ...@@ -493,6 +502,7 @@ namespace Sass {
case Node::variable: { case Node::variable: {
if (!env.query(expr.token())) throw_eval_error(bt, "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()];
// cerr << string(bt.depth(), '\t') << "evaluated a lookup: " << expr.to_string() << " -> " << result.to_string() << "::" << result.is_arglist() << endl;
} break; } break;
case Node::uri: { case Node::uri: {
...@@ -756,20 +766,31 @@ namespace Sass { ...@@ -756,20 +766,31 @@ namespace Sass {
Node eval_arguments(Node args, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt) Node eval_arguments(Node args, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx, Backtrace& bt)
{ {
// cerr << string(bt.depth(), '\t') << "evaluating arguments" << endl;
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) {
// bool is_arglist = args[i].is_arglist();
bool is_splat = args[i].is_splat();
evaluated_args << eval(args[i], prefix, env, f_env, new_Node, ctx, bt); evaluated_args << eval(args[i], prefix, env, f_env, new_Node, ctx, bt);
if (evaluated_args.back().type() == Node::list) { // cerr << string(bt.depth(), '\t') << "just pushed an evaluated arg: " << evaluated_args[i].to_string() << "::" << evaluated_args[i].is_arglist() << endl;
Node arg_list(evaluated_args.back()); // if (evaluated_args[i].type() == Node::list) {
Node new_arg_list(new_Node(Node::list, arg_list.path(), arg_list.line(), arg_list.size())); // Node arg_list(evaluated_args[i]);
for (size_t j = 0, S = arg_list.size(); j < S; ++j) { // Node new_arg_list(new_Node(Node::list, arg_list.path(), arg_list.line(), arg_list.size()));
if (arg_list[j].should_eval()) new_arg_list << eval(arg_list[j], prefix, env, f_env, new_Node, ctx, bt); // new_arg_list.is_arglist() = arg_list.is_arglist();
else new_arg_list << arg_list[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, bt);
} // else new_arg_list << arg_list[j];
// }
// evaluated_args[i] = new_arg_list;
// }
// evaluated_args[i].is_arglist() = is_arglist;
// evaluated_args[i].is_arglist() = is_arglist;
evaluated_args[i].is_splat() = is_splat;
// cerr << string(bt.depth(), '\t') << "after first arg evaluation: " << evaluated_args[i].to_string() << "::" << evaluated_args[i].is_arglist() << endl;
} }
else { else {
// cerr << "shouldn't be taking this branch!" << endl;
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, bt); kwdarg << eval(args[i][1], prefix, env, f_env, new_Node, ctx, bt);
...@@ -785,9 +806,12 @@ namespace Sass { ...@@ -785,9 +806,12 @@ namespace Sass {
evaluated_args << kwdarg; evaluated_args << kwdarg;
} }
} }
// cerr << string(bt.depth(), '\t') << "after first arg evaluation: " << evaluated_args.back().to_string() << "::" << evaluated_args.back().is_arglist() << endl;
// eval twice because args may be delayed // eval twice because args may be delayed
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) {
// bool is_arglist = evaluated_args[i].is_arglist();
bool is_splat = evaluated_args[i].is_splat();
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, bt); 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) {
...@@ -796,6 +820,8 @@ namespace Sass { ...@@ -796,6 +820,8 @@ namespace Sass {
if (arg_list[j].should_eval()) arg_list[j] = eval(arg_list[j], prefix, env, f_env, new_Node, ctx, bt); if (arg_list[j].should_eval()) arg_list[j] = eval(arg_list[j], prefix, env, f_env, new_Node, ctx, bt);
} }
} }
// evaluated_args[i].is_arglist() = is_arglist;
evaluated_args[i].is_splat() = is_splat;
} }
else { else {
Node kwdarg(evaluated_args[i]); Node kwdarg(evaluated_args[i]);
...@@ -810,6 +836,7 @@ namespace Sass { ...@@ -810,6 +836,7 @@ namespace Sass {
evaluated_args[i] = kwdarg; evaluated_args[i] = kwdarg;
} }
} }
// cerr << string(bt.depth(), '\t') << "after second arg evaluation: " << evaluated_args.back().to_string() << "::" << evaluated_args.back().is_arglist() << endl;
return evaluated_args; return evaluated_args;
} }
...@@ -820,27 +847,88 @@ namespace Sass { ...@@ -820,27 +847,88 @@ namespace Sass {
{ {
// 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
bool has_rest_params = false;
for (size_t i = 0, S = params.size(); i < S; ++i) { for (size_t i = 0, S = params.size(); i < S; ++i) {
Node param(params[i]); Node param(params[i]);
env.current_frame[param.type() == Node::variable ? param.token() : param[0].token()] = Node(); // env.current_frame[param.type() == Node::assignment ? param[0].token() : param.token()] = Node();
if (param.type() == Node::variable) {
env.current_frame[param.token()] = Node();
}
else if (param.type() == Node::assignment) {
env.current_frame[param[0].token()] = Node();
}
else {
Node arglist(new_Node(Node::list, args.path(), args.line(), 0));
arglist.is_arglist() = true;
arglist.is_comma_separated() = true;
env.current_frame[param.token()] = arglist;
has_rest_params = true;
}
} }
// now do the actual binding // now do the actual binding
size_t args_bound = 0, num_params = params.size(); size_t args_bound = 0, num_params = params.size();
for (size_t i = 0, j = 0, S = args.size(); i < S; ++i) { for (size_t i = 0, j = 0, S = args.size(); i < S; ++i) {
if (j >= num_params) { if (j == num_params-1 && has_rest_params) {
Node arglist(env[params[j].token()]);
// collect rest-args
if (args[i].type() == Node::list && args[i].is_splat()) {
arglist += args[i];
arglist.is_comma_separated() = args[i].is_comma_separated();
}
else {
for (size_t k = i; k < S; ++k) {
arglist << args[k];
}
}
break;
}
else 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(bt, msg.str(), args.path(), args.line()); throw_eval_error(bt, msg.str(), args.path(), args.line());
} }
Node arg(args[i]), param(params[j]); // ordinal argument; just bind it and keep going
// ordinal argument; just bind and keep going else if (args[i].type() != Node::assignment) {
if (arg.type() != Node::assignment) { // if it's a splat
env[param.type() == Node::variable ? param.token() : param[0].token()] = arg; if (args[i].type() == Node::list && args[i].is_splat()) {
++j; // loop through the splatted list and bind each element to the remaining parameters
for (size_t rest_i = 0, rest_length = args[i].size(); rest_i < rest_length; ++rest_i) {
// if we're binding the last parameter
if (j == num_params-1) {
Node leftovers;
if (has_rest_params) {
leftovers = env[params[j].token()];
leftovers.is_comma_separated() = args[i].is_comma_separated();
for (; rest_i < rest_length; ++rest_i) {
leftovers << args[i][rest_i];
}
}
else {
leftovers = args[i][rest_i];
}
Node param(params[j]);
env[param.type() != Node::assignment ? param.token() : param[0].token()] = leftovers;
break;
}
// otherwise keep going normally
else {
Node param(params[j]);
Token name = ((param.type() == Node::variable) ? param.token() : param[0].token());
env[name] = args[i][rest_i];
++j;
}
}
}
else {
Node arg(args[i]), param(params[j]);
env[param.type() == Node::variable ? param.token() : param[0].token()] = arg;
++j;
}
} }
// keyword argument -- need to check for correctness // keyword argument -- need to check for correctness
else { else {
Node arg(args[i]), param(params[j]);
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)) {
...@@ -872,6 +960,10 @@ namespace Sass { ...@@ -872,6 +960,10 @@ namespace Sass {
// environment. // environment.
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 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)
{ {
stringstream mixin_name;
mixin_name << "mixin";
if (!mixin[0].is_null()) mixin_name << " " << mixin[0].to_string();
// cerr << string(bt.depth(), '\t') << "applying " << mixin_name.str() << endl;
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, bt)); Node evaluated_args(eval_arguments(args, prefix, env, f_env, new_Node, ctx, bt));
...@@ -886,10 +978,11 @@ namespace Sass { ...@@ -886,10 +978,11 @@ namespace Sass {
bindings.link(env.global ? *env.global : env); bindings.link(env.global ? *env.global : env);
} }
// bind arguments in the extended environment // bind arguments in the extended environment
stringstream mixin_name; // stringstream mixin_name;
mixin_name << "mixin"; // mixin_name << "mixin";
if (!mixin[0].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, bt); bind_arguments(mixin_name.str(), params, evaluated_args, prefix, bindings, f_env, new_Node, ctx, bt);
// cerr << string(bt.depth(), '\t') << "last binding: " << params.back().to_string() << " -> " << bindings[params.back().token()].to_string() << "::" << bindings[params.back().token()].is_arglist() << endl;
// evaluate the mixin's body // evaluate the mixin's body
expand(body, prefix, bindings, f_env, new_Node, ctx, bt, false, content); expand(body, prefix, bindings, f_env, new_Node, ctx, bt, false, content);
return body; return body;
...@@ -899,13 +992,18 @@ namespace Sass { ...@@ -899,13 +992,18 @@ namespace Sass {
// 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, Backtrace& bt, 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)
{ {
// cerr << string(bt.depth(), '\t') << "applying " << f.name << endl;
// cerr << string(bt.depth(), '\t') << "last pre-evaluated arg: " << args.back().to_string() << endl;
Node evaluated_args(eval_arguments(args, prefix, env, f_env, new_Node, ctx, bt)); Node evaluated_args(eval_arguments(args, prefix, env, f_env, new_Node, ctx, bt));
// cerr << string(bt.depth(), '\t') << "last evaluated arg: " << evaluated_args.back().to_string() << "::" << evaluated_args.back().is_arglist() << endl;
// 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, bt); bind_arguments("function " + f.name, params, evaluated_args, prefix, bindings, f_env, new_Node, ctx, bt);
// cerr << string(bt.depth(), '\t') << "last binding: " << params.back().to_string() << " -> " << bindings[params.back().token()].to_string() << "::" << bindings[params.back().token()].is_arglist() << endl;
if (f.primitive) { if (f.primitive) {
return f.primitive(f.parameter_names, bindings, new_Node, bt, path, line); return f.primitive(f.parameter_names, bindings, new_Node, bt, path, line);
} }
......
...@@ -1049,7 +1049,13 @@ namespace Sass { ...@@ -1049,7 +1049,13 @@ namespace Sass {
type_name = Token::make(color_name); type_name = Token::make(color_name);
} break; } break;
case Node::list: { case Node::list: {
type_name = Token::make(list_name); // cerr << val.to_string() << endl;
// cerr << val.is_arglist() << endl;
// throw (42);
if (val.is_arglist())
type_name = Token::make(arglist_name);
else
type_name = Token::make(list_name);
} break; } break;
default: { default: {
type_name = Token::make(string_name); type_name = Token::make(string_name);
......
...@@ -158,6 +158,7 @@ namespace Sass { ...@@ -158,6 +158,7 @@ namespace Sass {
mixin_content, mixin_content,
parameters, parameters,
arguments, arguments,
rest,
extend_directive, extend_directive,
...@@ -199,6 +200,8 @@ namespace Sass { ...@@ -199,6 +200,8 @@ namespace Sass {
bool& has_been_extended() const; bool& has_been_extended() const;
bool is_false() const; bool is_false() const;
bool& is_comma_separated() const; bool& is_comma_separated() const;
bool& is_arglist() const;
bool& is_splat() const;
string& path() const; string& path() const;
size_t line() const; size_t line() const;
...@@ -240,7 +243,7 @@ namespace Sass { ...@@ -240,7 +243,7 @@ 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 space = " ") const; string to_string(Type inside_of = none, const string space = " ", const bool in_media_feature = false) const;
void emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel = false, bool in_media_query = false, bool source_comments = false); void emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel = false, bool in_media_query = false, bool source_comments = false);
void emit_propset(stringstream& buf, size_t depth, const string& prefix, const bool compressed = false); void 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);
...@@ -277,6 +280,8 @@ namespace Sass { ...@@ -277,6 +280,8 @@ namespace Sass {
bool is_quoted; bool is_quoted;
bool has_been_extended; bool has_been_extended;
bool is_comma_separated; bool is_comma_separated;
bool is_arglist;
bool is_splat;
Node_Impl() Node_Impl()
: /* value(value_t()), : /* value(value_t()),
...@@ -294,7 +299,9 @@ namespace Sass { ...@@ -294,7 +299,9 @@ namespace Sass {
should_eval(false), should_eval(false),
is_quoted(false), is_quoted(false),
has_been_extended(false), has_been_extended(false),
is_comma_separated(false) is_comma_separated(false),
is_arglist(false),
is_splat(false)
{ } { }
bool is_numeric() bool is_numeric()
...@@ -467,6 +474,8 @@ namespace Sass { ...@@ -467,6 +474,8 @@ namespace Sass {
inline bool& Node::has_been_extended() const { return ip_->has_been_extended; } inline bool& Node::has_been_extended() const { return ip_->has_been_extended; }
inline bool Node::is_false() const { return (type() == boolean) && (boolean_value() == false); } inline bool Node::is_false() const { return (type() == boolean) && (boolean_value() == false); }
inline bool& Node::is_comma_separated() const { return ip_->is_comma_separated; } inline bool& Node::is_comma_separated() const { return ip_->is_comma_separated; }
inline bool& Node::is_arglist() const { return ip_->is_arglist; }
inline bool& Node::is_splat() const { return ip_->is_splat; }
inline string& Node::path() const { return ip_->path; } inline string& Node::path() const { return ip_->path; }
inline size_t Node::line() const { return ip_->line; } inline size_t Node::line() const { return ip_->line; }
......
...@@ -15,7 +15,7 @@ using std::endl; ...@@ -15,7 +15,7 @@ using std::endl;
namespace Sass { namespace Sass {
string Node::to_string(Type inside_of, const string space) const string Node::to_string(Type inside_of, const string space, const bool in_media_feature) const
{ {
if (is_null()) return ""; if (is_null()) return "";
switch (type()) switch (type())
...@@ -39,22 +39,27 @@ namespace Sass { ...@@ -39,22 +39,27 @@ namespace Sass {
string result; string result;
if (at(0).type() == rule) { if (at(0).type() == rule) {
result += "("; result += "(";
result += at(0).to_string(none, space); result += at(0).to_string(none, space, true);
result += ")"; result += ")";
} }
else { else {
result += at(0).to_string(none, space); string tmp = at(0).to_string(none, space);
result += tmp;
if (tmp == "and" && space == "") result += " ";
} }
for (size_t i = 1, S = size(); i < S; ++i) { for (size_t i = 1, S = size(); i < S; ++i) {
if (at(i).type() == rule) { if (at(i).type() == rule) {
result += space; result += space;
result += "("; result += "(";
result += at(i).to_string(none, space); result += at(i).to_string(none, space, true);
result += ")"; result += ")";
} }
else { else {
result += " "; result += " ";
result += at(i).to_string(none, space); // result += at(i).to_string(none, space);
string tmp = at(i).to_string(none, space);
result += tmp;
if (tmp == "and" && space == "") result += " ";
} }
} }
return result; return result;
...@@ -125,6 +130,7 @@ namespace Sass { ...@@ -125,6 +130,7 @@ namespace Sass {
string result(at(0).to_string(property, space)); string result(at(0).to_string(property, space));
result += ":"; result += ":";
result += space; result += space;
if (space == "" && in_media_feature) result += " ";
result += at(1).to_string(none, space); result += at(1).to_string(none, space);
return result; return result;
} break; } break;
......
This diff was suppressed by a .gitattributes entry.
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