Commit 3cfac648 by Aaron Leung

Parsing selectors with interpolants.

parent cf1a2eca
...@@ -130,7 +130,8 @@ namespace Sass { ...@@ -130,7 +130,8 @@ namespace Sass {
Node parse_argument(); Node parse_argument();
Node parse_assignment(); Node parse_assignment();
Node parse_propset(); Node parse_propset();
Node parse_ruleset(bool definition = false); Node parse_ruleset(Selector_Lookahead lookahead, bool in_definition = false);
Node parse_selector_schema(const char* end_of_selector);
Node parse_selector_group(); Node parse_selector_group();
Node parse_selector(); Node parse_selector();
Node parse_selector_combinator(); Node parse_selector_combinator();
...@@ -138,7 +139,7 @@ namespace Sass { ...@@ -138,7 +139,7 @@ namespace Sass {
Node parse_simple_selector(); Node parse_simple_selector();
Node parse_pseudo(); Node parse_pseudo();
Node parse_attribute_selector(); Node parse_attribute_selector();
Node parse_block(bool definition = false); Node parse_block(bool in_definition = false);
Node parse_rule(); Node parse_rule();
Node parse_values(); Node parse_values();
Node parse_list(); Node parse_list();
......
...@@ -30,7 +30,7 @@ namespace Sass { ...@@ -30,7 +30,7 @@ namespace Sass {
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(); root << parse_ruleset(lookahead_result);
} }
else if (peek< include >() || peek< exactly<'+'> >()) { else if (peek< include >() || peek< exactly<'+'> >()) {
root << parse_mixin_call(); root << parse_mixin_call();
...@@ -214,15 +214,49 @@ namespace Sass { ...@@ -214,15 +214,49 @@ namespace Sass {
return propset; return propset;
} }
Node Document::parse_ruleset(bool definition) Node Document::parse_ruleset(Selector_Lookahead lookahead, bool in_definition)
{ {
Node ruleset(context.new_Node(Node::ruleset, path, line, 2)); Node ruleset(context.new_Node(Node::ruleset, path, line, 2));
if (lookahead.has_interpolants) {
ruleset << parse_selector_schema(lookahead.found);
}
else {
ruleset << parse_selector_group(); ruleset << parse_selector_group();
}
if (!peek< exactly<'{'> >()) throw_syntax_error("expected a '{' after the selector"); if (!peek< exactly<'{'> >()) throw_syntax_error("expected a '{' after the selector");
ruleset << parse_block(definition); ruleset << parse_block(in_definition);
return ruleset; return ruleset;
} }
extern const char hash_lbrace[] = "#{";
extern const char rbrace[] = "}";
Node Document::parse_selector_schema(const char* end_of_selector)
{
const char* i = position;
const char* p;
Node schema(context.new_Node(Node::selector_schema, path, line, 1));
while (i < end_of_selector) {
p = find_first_in_interval< exactly<hash_lbrace> >(i, end_of_selector);
if (p) {
// accumulate the preceding segment if there is one
if (i < p) schema << context.new_Node(Node::identifier, path, line, Token::make(i, p));
// find the end of the interpolant and parse it
const char* j = find_first_in_interval< exactly<rbrace> >(p, end_of_selector);
Node interp_node(Document::make_from_token(context, Token::make(p+2, j), path, line).parse_list());
interp_node.should_eval() = true;
schema << interp_node;
i = j + 1;
}
else { // no interpolants left; add the last segment if there is one
if (i < end_of_selector) schema << context.new_Node(Node::identifier, path, line, Token::make(i, end_of_selector));
break;
}
}
position = end_of_selector;
return schema;
}
Node Document::parse_selector_group() Node Document::parse_selector_group()
{ {
Node sel1(parse_selector()); Node sel1(parse_selector());
...@@ -389,7 +423,7 @@ namespace Sass { ...@@ -389,7 +423,7 @@ namespace Sass {
return attr_sel; return attr_sel;
} }
Node Document::parse_block(bool definition) Node Document::parse_block(bool in_definition)
{ {
lex< exactly<'{'> >(); lex< exactly<'{'> >();
bool semicolon = false; bool semicolon = false;
...@@ -408,7 +442,7 @@ namespace Sass { ...@@ -408,7 +442,7 @@ namespace Sass {
block << context.new_Node(Node::comment, path, line, lexed); block << context.new_Node(Node::comment, path, line, lexed);
} }
else if (peek< import >(position)) { else if (peek< import >(position)) {
if (definition) { if (in_definition) {
lex< import >(); // to adjust the line number lex< import >(); // to adjust the line number
throw_syntax_error("@import directive not allowed inside mixin definition"); throw_syntax_error("@import directive not allowed inside mixin definition");
} }
...@@ -435,7 +469,7 @@ namespace Sass { ...@@ -435,7 +469,7 @@ namespace Sass {
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(definition); block << parse_ruleset(lookahead_result, in_definition);
} }
else if (peek< exactly<'+'> >()) { else if (peek< exactly<'+'> >()) {
block << parse_mixin_call(); block << parse_mixin_call();
...@@ -740,9 +774,6 @@ namespace Sass { ...@@ -740,9 +774,6 @@ namespace Sass {
return Node(); return Node();
} }
extern const char hash_lbrace[] = "#{";
extern const char rbrace[] = "}";
Node Document::parse_string() Node Document::parse_string()
{ {
lex< string_constant >(); lex< string_constant >();
...@@ -759,15 +790,15 @@ namespace Sass { ...@@ -759,15 +790,15 @@ namespace Sass {
p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, str.end); p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, str.end);
if (p) { if (p) {
if (i < p) { if (i < p) {
schema << context.new_Node(Node::identifier, path, line, Token::make(i, p-2)); // accumulate the preceding segment if it's nonempty schema << context.new_Node(Node::identifier, path, line, Token::make(i, p)); // accumulate the preceding segment if it's nonempty
} }
const char* j = find_first_in_interval< exactly<rbrace> >(p, str.end); // find the closing brace const char* j = find_first_in_interval< exactly<rbrace> >(p, str.end); // find the closing brace
if (j) { if (j) {
// parse the interpolant and accumulate it // parse the interpolant and accumulate it
Node interp_node(Document::make_from_token(context, Token::make(p, j-1), path, line).parse_list()); Node interp_node(Document::make_from_token(context, Token::make(p+2, j), path, line).parse_list());
interp_node.should_eval() = true; interp_node.should_eval() = true;
schema << interp_node; schema << interp_node;
i = j; i = j+1;
} }
else { else {
// throw an error if the interpolant is unterminated // throw an error if the interpolant is unterminated
......
...@@ -87,6 +87,7 @@ namespace Sass { ...@@ -87,6 +87,7 @@ namespace Sass {
pseudo_negation, pseudo_negation,
functional_pseudo, functional_pseudo,
attribute_selector, attribute_selector,
selector_schema,
block, block,
rule, rule,
......
...@@ -105,6 +105,16 @@ namespace Sass { ...@@ -105,6 +105,16 @@ namespace Sass {
return prefix; return prefix;
} break; } break;
case selector_schema: {
string result(prefix);
if (!result.empty()) result += " ";
for (size_t i = 0, S = size(); i < S; ++i) {
if (at(i).type() == identifier) result += at(i).to_string("");
else result += "[INTERPOLANT]";
}
return result;
} break;
case comma_list: { case comma_list: {
string result(at(0).to_string(prefix)); string result(at(0).to_string(prefix));
for (size_t i = 1, S = size(); i < S; ++i) { for (size_t i = 1, S = size(); i < S; ++i) {
......
...@@ -388,10 +388,10 @@ namespace Sass { ...@@ -388,10 +388,10 @@ namespace Sass {
template<prelexer mx> template<prelexer mx>
const char* find_first_in_interval(const char* beg, const char* end) { const char* find_first_in_interval(const char* beg, const char* end) {
while ((beg < end) && *beg) { while ((beg < end) && *beg) {
const char* p = mx(beg); if (mx(beg)) return beg;
if (p) return p;
++beg; ++beg;
} }
return 0; return 0;
} }
template <char c> template <char c>
......
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