Commit 2e7085c0 by Aaron Leung

Implemented @for. Ran into some stupid bugs.

parent f032b652
......@@ -161,6 +161,7 @@ namespace Sass {
Node parse_string();
Node parse_value_schema();
Node parse_if_directive(Node surrounding_ruleset);
Node parse_for_directive(Node surrounding_ruleset);
Selector_Lookahead lookahead_for_selector(const char* start = 0);
......
......@@ -39,6 +39,9 @@ namespace Sass {
else if (peek< if_directive >()) {
root << parse_if_directive(Node());
}
else if (peek< for_directive >()) {
root << parse_for_directive(Node());
}
else {
lex< spaces_and_comments >();
throw_syntax_error("invalid top-level expression");
......@@ -490,6 +493,9 @@ namespace Sass {
else if (peek< if_directive >()) {
block << parse_if_directive(surrounding_ruleset);
}
else if (peek< for_directive >()) {
block << parse_for_directive(surrounding_ruleset);
}
else if (!peek< exactly<';'> >()) {
Node rule(parse_rule());
// check for lbrace; if it's there, we have a namespace property with a value
......@@ -899,6 +905,26 @@ namespace Sass {
return conditional;
}
Node Document::parse_for_directive(Node surrounding_ruleset)
{
lex< for_directive >();
size_t for_line = line;
if (!lex< variable >()) throw_syntax_error("@for directive requires an iteration variable");
Node var(context.new_Node(Node::variable, path, line, lexed));
if (!lex< from >()) throw_syntax_error("expected 'from' keyword in @for directive");
Node lower_bound(parse_expression());
Node::Type for_type = Node::for_through_directive;
if (lex< through >()) for_type = Node::for_through_directive;
else if (lex< to >()) for_type = Node::for_to_directive;
else throw_syntax_error("expected 'through' or 'to' keywod in @for directive");
Node upper_bound(parse_expression());
if (!peek< exactly<'{'> >()) throw_syntax_error("expected '{' after the upper bound in @for directive");
Node body(parse_block(surrounding_ruleset));
Node loop(context.new_Node(for_type, path, for_line, 3));
loop << var << lower_bound << upper_bound << body;
return loop;
}
Selector_Lookahead Document::lookahead_for_selector(const char* start)
{
const char* p = start ? start : position;
......
......@@ -316,6 +316,31 @@ namespace Sass {
}
} break;
case Node::for_through_directive:
case Node::for_to_directive: {
Node fake_mixin(new_Node(Node::mixin, expr.path(), expr.line(), 3));
Node fake_param(new_Node(Node::parameters, expr.path(), expr.line(), 1));
fake_mixin << new_Node(Node::none, "", 0, 0) << (fake_param << expr[0]) << expr[3];
Node lower_bound(eval(expr[1], prefix, env, f_env, new_Node, ctx));
Node upper_bound(eval(expr[2], prefix, env, f_env, new_Node, ctx));
if (!(lower_bound.is_numeric() && upper_bound.is_numeric())) {
throw_eval_error("bounds of @for directive must be numeric", expr.path(), expr.line());
}
expr.pop_back();
expr.pop_back();
expr.pop_back();
expr.pop_back();
for (double i = lower_bound.numeric_value(),
U = upper_bound.numeric_value() + ((expr.type() == Node::for_to_directive) ? 0 : 1);
i < U;
++i) {
Node i_node(new_Node(expr.path(), expr.line(), i));
Node fake_arg(new_Node(Node::arguments, expr.path(), expr.line(), 1));
fake_arg << i_node;
expr += apply_mixin(fake_mixin, fake_arg, prefix, env, f_env, new_Node, ctx);
}
} break;
default: {
return expr;
} break;
......
......@@ -13,10 +13,11 @@ namespace Sass {
void Node::flatten()
{
if (type() != block && type() != expansion && type() != root) return;
if (type() != block && type() != expansion && type() != root && type() != for_through_directive && type() != for_to_directive) return;
// size can change during flattening, so we need to call size() on each pass
for (size_t i = 0; i < size(); ++i) {
if ((at(i).type() == expansion) || (at(i).type() == block)) {
Type i_type = at(i).type();
if ((i_type == expansion) || (i_type == block) || (i_type == for_through_directive) || (i_type == for_to_directive)) {
Node expn(at(i));
if (expn.has_expansions()) expn.flatten();
ip_->has_statements |= expn.has_statements();
......@@ -25,6 +26,8 @@ namespace Sass {
// TO DO: make this more efficient -- replace with a dummy node instead of erasing
ip_->children.erase(begin() + i);
insert(begin() + i, expn.begin(), expn.end());
// skip over what we just spliced in
i += expn.size() - 1;
}
}
}
......
......@@ -148,7 +148,8 @@ namespace Sass {
arguments,
if_directive,
for_directive,
for_through_directive,
for_to_directive,
each_directive,
while_directive,
......@@ -280,10 +281,18 @@ namespace Sass {
case Node::css_import:
case Node::rule:
case Node::propset: has_statements = true; break;
case Node::ruleset: has_blocks = true; break;
case Node::if_directive:
case Node::for_through_directive:
case Node::for_to_directive:
case Node::each_directive:
case Node::while_directive:
case Node::expansion: has_expansions = true; break;
case Node::backref: has_backref = true; break;
default: break;
}
if (n.has_backref()) has_backref = true;
......
......@@ -115,6 +115,7 @@ namespace Sass {
const char* extend(const char* src) {
return exactly<extend_kwd>(src);
}
extern const char if_kwd[] = "@if";
extern const char if_chars[] = "if";
const char* if_directive(const char* src) {
......@@ -129,10 +130,24 @@ namespace Sass {
spaces_and_comments,
exactly< if_chars > >(src);
}
extern const char for_kwd[] = "@for";
const char* for_directive(const char* src) {
return exactly<for_kwd>(src);
}
extern const char from_kwd[] = "from";
const char* from(const char* src) {
return exactly<from_kwd>(src);
}
extern const char to_kwd[] = "to";
const char* to(const char* src) {
return exactly<to_kwd>(src);
}
extern const char through_kwd[] = "through";
const char* through(const char* src) {
return exactly<through_kwd>(src);
}
extern const char each_kwd[] = "@each";
const char* each_directive(const char* src) {
return exactly<each_kwd>(src);
......
......@@ -315,8 +315,14 @@ namespace Sass {
const char* if_directive(const char* src);
const char* else_directive(const char* src);
const char* elseif_directive(const char* src);
const char* for_directive(const char* src);
const char* from(const char* src);
const char* to(const char* src);
const char* through(const char* src);
const char* each_directive(const char* src);
const char* while_directive(const char* src);
// Match CSS type selectors
......
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