Commit 766673fc by Aaron Leung

Re-doing the selector inheritance infrastructure.

parent 247e5ed3
......@@ -175,7 +175,8 @@ namespace Sass {
Node parse_warning();
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_read_error(string message, size_t ln = 0);
......
......@@ -355,12 +355,15 @@ namespace Sass {
Node seq1(parse_simple_selector_sequence());
if (peek< exactly<','> >() ||
peek< exactly<')'> >() ||
peek< exactly<'{'> >()) return seq1;
peek< exactly<'{'> >() ||
peek< exactly<';'> >()) return seq1;
Node selector(context.new_Node(Node::selector, path, line, 2));
selector << seq1;
while (!peek< exactly<'{'> >() && !peek< exactly<','> >()) {
while (!peek< exactly<'{'> >() &&
!peek< exactly<','> >() &&
!peek< exactly<';'> >()) {
selector << parse_simple_selector_sequence();
}
return selector;
......@@ -588,12 +591,14 @@ namespace Sass {
semicolon = true;
}
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));
request << extendee;
// context.has_extensions = true;
Selector_Lookahead lookahead = lookahead_for_extension_target(position);
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;
block << request;
}
......@@ -1338,5 +1343,56 @@ namespace Sass {
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 {
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());
......@@ -166,6 +167,42 @@ namespace Sass {
case Node::extend_directive: {
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.has_extensions = true;
} break;
......@@ -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) {
list[i].should_eval() = true;
list[i] = eval(list[i], prefix, env, f_env, new_Node, ctx);
......@@ -935,7 +973,7 @@ namespace Sass {
// combine the various kinds of selectors.
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 ((pre.type() == Node::selector_group) && (sel.type() == Node::selector_group)) {
......@@ -1041,7 +1079,10 @@ namespace Sass {
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)
{
for (size_t i = 0, S = pending.size(); i < S; ++i) {
......@@ -1095,122 +1136,6 @@ namespace Sass {
}
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" {
using namespace Sass;
doc.parse_scss();
expand(doc.root,
doc.context.new_Node(Node::none, doc.path, doc.line, 0),
Node(),
doc.context.global_env,
doc.context.function_env,
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