Commit 766673fc by Aaron Leung

Re-doing the selector inheritance infrastructure.

parent 247e5ed3
...@@ -175,6 +175,7 @@ namespace Sass { ...@@ -175,6 +175,7 @@ namespace Sass {
Node parse_warning(); Node parse_warning();
Selector_Lookahead lookahead_for_selector(const char* start = 0); Selector_Lookahead lookahead_for_selector(const char* start = 0);
Selector_Lookahead lookahead_for_extension_target(const char* start = 0);
void throw_syntax_error(string message, size_t ln = 0); void throw_syntax_error(string message, size_t ln = 0);
void throw_read_error(string message, size_t ln = 0); void throw_read_error(string message, size_t ln = 0);
......
...@@ -355,12 +355,15 @@ namespace Sass { ...@@ -355,12 +355,15 @@ namespace Sass {
Node seq1(parse_simple_selector_sequence()); Node seq1(parse_simple_selector_sequence());
if (peek< exactly<','> >() || if (peek< exactly<','> >() ||
peek< exactly<')'> >() || peek< exactly<')'> >() ||
peek< exactly<'{'> >()) return seq1; peek< exactly<'{'> >() ||
peek< exactly<';'> >()) return seq1;
Node selector(context.new_Node(Node::selector, path, line, 2)); Node selector(context.new_Node(Node::selector, path, line, 2));
selector << seq1; selector << seq1;
while (!peek< exactly<'{'> >() && !peek< exactly<','> >()) { while (!peek< exactly<'{'> >() &&
!peek< exactly<','> >() &&
!peek< exactly<';'> >()) {
selector << parse_simple_selector_sequence(); selector << parse_simple_selector_sequence();
} }
return selector; return selector;
...@@ -588,12 +591,14 @@ namespace Sass { ...@@ -588,12 +591,14 @@ namespace Sass {
semicolon = true; semicolon = true;
} }
else if (lex< extend >()) { else if (lex< extend >()) {
// if (surrounding_ruleset.is_null()) throw_syntax_error("@extend directive may only be used within rules");
Node extendee(parse_simple_selector_sequence());
// context.extensions.insert(pair<Node, Node>(extendee, surrounding_ruleset));
Node request(context.new_Node(Node::extend_directive, path, line, 1)); Node request(context.new_Node(Node::extend_directive, path, line, 1));
request << extendee; Selector_Lookahead lookahead = lookahead_for_extension_target(position);
// context.has_extensions = true;
if (!lookahead.found) throw_syntax_error("invalid selector for @extend");
if (lookahead.has_interpolants) request << parse_selector_schema(lookahead.found);
else request << parse_selector_group();
semicolon = true; semicolon = true;
block << request; block << request;
} }
...@@ -1339,4 +1344,55 @@ namespace Sass { ...@@ -1339,4 +1344,55 @@ namespace Sass {
return result; return result;
} }
Selector_Lookahead Document::lookahead_for_extension_target(const char* start)
{
const char* p = start ? start : position;
const char* q;
bool saw_interpolant = false;
while ((q = peek< identifier >(p)) ||
(q = peek< id_name >(p)) ||
(q = peek< class_name >(p)) ||
(q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
(q = peek< string_constant >(p)) ||
(q = peek< exactly<'*'> >(p)) ||
(q = peek< exactly<'('> >(p)) ||
(q = peek< exactly<')'> >(p)) ||
(q = peek< exactly<'['> >(p)) ||
(q = peek< exactly<']'> >(p)) ||
(q = peek< exactly<'+'> >(p)) ||
(q = peek< exactly<'~'> >(p)) ||
(q = peek< exactly<'>'> >(p)) ||
(q = peek< exactly<','> >(p)) ||
(q = peek< binomial >(p)) ||
(q = peek< sequence< optional<sign>,
optional<digits>,
exactly<'n'> > >(p)) ||
(q = peek< sequence< optional<sign>,
digits > >(p)) ||
(q = peek< number >(p)) ||
(q = peek< exactly<'&'> >(p)) ||
(q = peek< alternatives<exact_match,
class_match,
dash_match,
prefix_match,
suffix_match,
substring_match> >(p)) ||
(q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||
(q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||
(q = peek< sequence< exactly<'-'>, interpolant > >(p)) ||
(q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
(q = peek< interpolant >(p))) {
p = q;
if (*(p - 1) == '}') saw_interpolant = true;
}
Selector_Lookahead result;
result.found = peek< alternatives< exactly<';'>, exactly<'}'> > >(p) ? p : 0;
result.has_interpolants = saw_interpolant;
return result;
}
} }
...@@ -68,6 +68,7 @@ namespace Sass { ...@@ -68,6 +68,7 @@ namespace Sass {
expansion += schema[i].to_string(); expansion += schema[i].to_string();
} }
} }
// need to re-parse the selector because its structure may have changed
expansion += " {"; // the parser looks for an lbrace to end a selector expansion += " {"; // the parser looks for an lbrace to end a selector
char* expn_src = new char[expansion.size() + 1]; char* expn_src = new char[expansion.size() + 1];
strcpy(expn_src, expansion.c_str()); strcpy(expn_src, expansion.c_str());
...@@ -166,6 +167,42 @@ namespace Sass { ...@@ -166,6 +167,42 @@ namespace Sass {
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("@extend directive may only be used within rules", expr.path(), expr.line());
// if the selector contains interpolants, eval it and re-parse
if (expr[0].type() == Node::selector_schema) {
Node schema(expr[0]);
string expansion;
for (size_t i = 0, S = schema.size(); i < S; ++i) {
schema[i] = eval(schema[i], prefix, env, f_env, new_Node, ctx);
if (schema[i].type() == Node::string_constant) {
expansion += schema[i].token().unquote();
}
else {
expansion += schema[i].to_string();
}
}
// need to re-parse the selector because its structure may have changed
expansion += " {"; // the parser looks for an lbrace to end a selector
char* expn_src = new char[expansion.size() + 1];
strcpy(expn_src, expansion.c_str());
Document needs_reparsing(Document::make_from_source_chars(ctx, expn_src, schema.path(), true));
needs_reparsing.line = schema.line(); // set the line number to the original node's line
expr[0] = needs_reparsing.parse_selector_group();
}
// only simple selector sequences may be extended
switch (expr[0].type())
{
case Node::selector_group:
throw_eval_error("selector groups may not be extended", expr[0].path(), expr[0].line());
break;
case Node::selector:
throw_eval_error("nested selectors may not be extended", expr[0].path(), expr[0].line());
break;
default:
break;
}
ctx.extensions.insert(pair<Node, Node>(expr[0], prefix)); ctx.extensions.insert(pair<Node, Node>(expr[0], prefix));
ctx.has_extensions = true; ctx.has_extensions = true;
} break; } break;
...@@ -277,7 +314,8 @@ namespace Sass { ...@@ -277,7 +314,8 @@ namespace Sass {
} }
} }
void expand_list(Node list, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx) { void expand_list(Node list, Node prefix, Environment& env, map<string, Function>& f_env, Node_Factory& new_Node, Context& ctx)
{
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);
...@@ -935,7 +973,7 @@ namespace Sass { ...@@ -935,7 +973,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.type() == Node::none) return sel; if (pre.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)) {
...@@ -1041,7 +1079,10 @@ namespace Sass { ...@@ -1041,7 +1079,10 @@ namespace Sass {
return Node(); return Node();
} }
// Resolve selector extensions. // Resolve selector extensions. Walk through the document tree and check each
// selector to see whether it's the base of an extension. Needs to be a
// separate pass after evaluation because extension requests may be located
// within mixins, and their targets may be interpolated.
void extend_selectors(vector<pair<Node, Node> >& pending, multimap<Node, Node>& extension_table, Node_Factory& new_Node) void extend_selectors(vector<pair<Node, Node> >& pending, multimap<Node, Node>& extension_table, Node_Factory& new_Node)
{ {
for (size_t i = 0, S = pending.size(); i < S; ++i) { for (size_t i = 0, S = pending.size(); i < S; ++i) {
...@@ -1095,122 +1136,6 @@ namespace Sass { ...@@ -1095,122 +1136,6 @@ namespace Sass {
} }
ruleset_to_extend[2] = extended_group; ruleset_to_extend[2] = extended_group;
} }
// if (extendee.type() != Node::selector_group && extender.type() != Node::selector_group) {
// Node ext(generate_extension(extendee, extender, new_Node));
// ext.push_front(extendee);
// ruleset_to_extend[2] = ext;
// }
// else if (extendee.type() == Node::selector_group && extender.type() != Node::selector_group) {
// cerr << "extending a group with a singleton!" << endl;
// Node new_group(new_Node(Node::selector_group, extendee.path(), extendee.line(), extendee.size()));
// for (size_t i = 0, S = extendee.size(); i < S; ++i) {
// new_group << extendee[i];
// if (extension_table.count(extendee[i])) {
// new_group << generate_extension(extendee[i], extender, new_Node);
// }
// }
// ruleset_to_extend[2] = new_group;
// }
// else if (extendee.type() != Node::selector_group && extender.type() == Node::selector_group) {
// cerr << "extending a singleton with a group!" << endl;
// }
// else {
// cerr << "skipping this for now!" << endl;
// }
// if (selector_to_extend.type() != Node::selector_group) {
// Node ext(generate_extension(selector_to_extend, extender, new_Node));
// ext.push_front(selector_to_extend);
// ruleset_to_extend[2] = ext;
// }
// else {
// cerr << "possibly extending a selector in a group: " << selector_to_extend.to_string() << endl;
// Node new_group(new_Node(Node::selector_group,
// selector_to_extend.path(),
// selector_to_extend.line(),
// selector_to_extend.size()));
// for (size_t i = 0, S = selector_to_extend.size(); i < S; ++i) {
// Node sel_i(selector_to_extend[i]);
// Node sel_ib(selector_base(sel_i));
// if (extension_table.count(sel_ib)) {
// for (multimap<Node, Node>::iterator i = extension_table.lower_bound(sel_ib); i != extension_table.upper_bound(sel_ib); ++i) {
// if (i->second.is(original_extender)) {
// new_group << sel_i;
// new_group += generate_extension(sel_i, extender, new_Node);
// }
// else {
// cerr << "not what you think is happening!" << endl;
// }
// }
// }
// }
// ruleset_to_extend[2] = new_group;
// }
// if (selector_to_extend.type() != Node::selector) {
// switch (extender.type())
// {
// case Node::simple_selector:
// case Node::attribute_selector:
// case Node::simple_selector_sequence:
// case Node::selector: {
// cerr << "EXTENDING " << selector_to_extend.to_string() << " WITH " << extender.to_string() << endl;
// if (selector_to_extend.type() == Node::selector_group) {
// selector_to_extend << extender;
// }
// else {
// Node new_group(new_Node(Node::selector_group, selector_to_extend.path(), selector_to_extend.line(), 2));
// new_group << selector_to_extend << extender;
// ruleset_to_extend[2] = new_group;
// }
// } break;
// default: {
// // handle the other cases later
// }
// }
// }
// else {
// switch (extender.type())
// {
// case Node::simple_selector:
// case Node::attribute_selector:
// case Node::simple_selector_sequence: {
// Node new_ext(new_Node(selector_to_extend));
// new_ext.back() = extender;
// if (selector_to_extend.type() == Node::selector_group) {
// selector_to_extend << new_ext;
// }
// else {
// Node new_group(new_Node(Node::selector_group, selector_to_extend.path(), selector_to_extend.line(), 2));
// new_group << selector_to_extend << new_ext;
// ruleset_to_extend[2] = new_group;
// }
// } break;
// case Node::selector: {
// Node new_ext1(new_Node(Node::selector, selector_to_extend.path(), selector_to_extend.line(), selector_to_extend.size() + extender.size() - 1));
// Node new_ext2(new_Node(Node::selector, selector_to_extend.path(), selector_to_extend.line(), selector_to_extend.size() + extender.size() - 1));
// new_ext1 += selector_prefix(selector_to_extend, new_Node);
// new_ext1 += extender;
// new_ext2 += selector_prefix(extender, new_Node);
// new_ext2 += selector_prefix(selector_to_extend, new_Node);
// new_ext2 << extender.back();
// if (selector_to_extend.type() == Node::selector_group) {
// selector_to_extend << new_ext1 << new_ext2;
// }
// else {
// Node new_group(new_Node(Node::selector_group, selector_to_extend.path(), selector_to_extend.line(), 2));
// new_group << selector_to_extend << new_ext1 << new_ext2;
// ruleset_to_extend[2] = new_group;
// }
// } break;
// default: {
// // something
// } break;
// }
// }
} }
} }
......
...@@ -42,7 +42,7 @@ extern "C" { ...@@ -42,7 +42,7 @@ extern "C" {
using namespace Sass; using namespace Sass;
doc.parse_scss(); doc.parse_scss();
expand(doc.root, expand(doc.root,
doc.context.new_Node(Node::none, doc.path, doc.line, 0), 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,
......
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