Commit edf5bf06 by Aaron Leung

Implemented media queries.

parent fcae17e8
...@@ -145,7 +145,7 @@ namespace Sass { ...@@ -145,7 +145,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(Node surrounding_rulesetbool, Node::Type inside_of = Node::none); Node parse_block(Node surrounding_ruleset, Node::Type inside_of = Node::none);
Node parse_rule(); Node parse_rule();
Node parse_values(); Node parse_values();
Node parse_list(); Node parse_list();
...@@ -165,6 +165,8 @@ namespace Sass { ...@@ -165,6 +165,8 @@ namespace Sass {
Node parse_for_directive(Node surrounding_ruleset, Node::Type inside_of = Node::none); Node parse_for_directive(Node surrounding_ruleset, Node::Type inside_of = Node::none);
Node parse_each_directive(Node surrounding_ruleset, Node::Type inside_of = Node::none); Node parse_each_directive(Node surrounding_ruleset, Node::Type inside_of = Node::none);
Node parse_while_directive(Node surrounding_ruleset, Node::Type inside_of = Node::none); Node parse_while_directive(Node surrounding_ruleset, Node::Type inside_of = Node::none);
Node parse_media_query(Node::Type inside_of = Node::none);
Node parse_media_expression();
Selector_Lookahead lookahead_for_selector(const char* start = 0); Selector_Lookahead lookahead_for_selector(const char* start = 0);
......
...@@ -51,6 +51,9 @@ namespace Sass { ...@@ -51,6 +51,9 @@ namespace Sass {
else if (peek< while_directive >()) { else if (peek< while_directive >()) {
root << parse_while_directive(Node(), Node::none); root << parse_while_directive(Node(), Node::none);
} }
else if (peek< media >()) {
root << parse_media_query(Node::none);
}
else { else {
lex< spaces_and_comments >(); lex< spaces_and_comments >();
throw_syntax_error("invalid top-level expression"); throw_syntax_error("invalid top-level expression");
...@@ -535,6 +538,9 @@ namespace Sass { ...@@ -535,6 +538,9 @@ namespace Sass {
context.has_extensions = true; context.has_extensions = true;
semicolon = true; semicolon = true;
} }
else if (peek< media >()) {
block << parse_media_query(inside_of);
}
else if (!peek< exactly<';'> >()) { else if (!peek< exactly<';'> >()) {
Node rule(parse_rule()); Node rule(parse_rule());
// check for lbrace; if it's there, we have a namespace property with a value // check for lbrace; if it's there, we have a namespace property with a value
...@@ -991,7 +997,62 @@ namespace Sass { ...@@ -991,7 +997,62 @@ namespace Sass {
loop << predicate << body; loop << predicate << body;
return loop; return loop;
} }
Node Document::parse_media_query(Node::Type inside_of)
{
lex< media >();
Node media_query(context.new_Node(Node::media_query, path, line, 2));
Node media_expr(parse_media_expression());
if (peek< exactly<'{'> >()) {
media_query << media_expr;
}
else if (peek< exactly<','> >()) {
Node media_expr_group(context.new_Node(Node::media_expression_group, path, line, 2));
media_expr_group << media_expr;
while (lex< exactly<','> >()) {
media_expr_group << parse_media_expression();
}
media_query << media_expr_group;
}
else {
throw_syntax_error("expected '{' in media query");
}
media_query << parse_block(Node(), inside_of);
return media_query;
}
// extern const char not_kwd[] = "not";
extern const char only_kwd[] = "only";
Node Document::parse_media_expression()
{
Node media_expr(context.new_Node(Node::media_expression, path, line, 1));
// if the query begins with 'not' or 'only', then a media type is required
if (lex< not_kwd >() || lex< exactly<only_kwd> >()) {
media_expr << context.new_Node(Node::identifier, path, line, lexed);
if (!lex< identifier >()) throw_syntax_error("media type expected in media query");
media_expr << context.new_Node(Node::identifier, path, line, lexed);
}
// otherwise, the media type is optional
else if (lex< identifier >()) {
media_expr << context.new_Node(Node::identifier, path, line, lexed);
}
// if no media type was present, then require a parenthesized property
if (media_expr.empty()) {
if (!lex< exactly<'('> >()) throw_syntax_error("invalid media query");
media_expr << parse_rule();
if (!lex< exactly<')'> >()) throw_syntax_error("unclosed parenthesis");
}
// parse the rest of the properties for this disjunct
while (!peek< exactly<','> >() && !peek< exactly<'{'> >()) {
if (!lex< and_kwd >()) throw_syntax_error("invalid media query");
media_expr << context.new_Node(Node::identifier, path, line, lexed);
if (!lex< exactly<'('> >()) throw_syntax_error("invalid media query");
media_expr << parse_rule();
if (!lex< exactly<')'> >()) throw_syntax_error("unclosed parenthesis");
}
return media_expr;
}
Selector_Lookahead Document::lookahead_for_selector(const char* start) Selector_Lookahead Document::lookahead_for_selector(const char* start)
{ {
const char* p = start ? start : position; const char* p = start ? start : position;
......
...@@ -51,7 +51,6 @@ namespace Sass { ...@@ -51,7 +51,6 @@ namespace Sass {
} break; } break;
case Node::ruleset: { case Node::ruleset: {
// if the selector contains interpolants, eval it and re-parse // if the selector contains interpolants, eval it and re-parse
if (expr[0].type() == Node::selector_schema) { if (expr[0].type() == Node::selector_schema) {
expr[0] = eval(expr[0], prefix, env, f_env, new_Node, ctx); expr[0] = eval(expr[0], prefix, env, f_env, new_Node, ctx);
...@@ -59,7 +58,6 @@ namespace Sass { ...@@ -59,7 +58,6 @@ namespace Sass {
// expand the selector with the prefix and save it in expr[2] // expand the selector with the prefix and save it in expr[2]
expr << expand_selector(expr[0], prefix, new_Node); expr << expand_selector(expr[0], prefix, new_Node);
// gather selector extensions into a pending queue // gather selector extensions into a pending queue
if (ctx.has_extensions) { if (ctx.has_extensions) {
Node sel(selector_base(expr.back())); Node sel(selector_base(expr.back()));
...@@ -77,6 +75,14 @@ namespace Sass { ...@@ -77,6 +75,14 @@ namespace Sass {
return expr; return expr;
} break; } break;
case Node::media_query: {
Node block(expr[1]);
Node new_ruleset(new_Node(Node::ruleset, expr.path(), expr.line(), 3));
new_ruleset << prefix << block << prefix;
expr[1] = eval(new_ruleset, new_Node(Node::none, expr.path(), expr.line(), 0), env, f_env, new_Node, ctx);
return expr;
} break;
case Node::selector_schema: { case Node::selector_schema: {
string expansion; string expansion;
for (size_t i = 0, S = expr.size(); i < S; ++i) { for (size_t i = 0, S = expr.size(); i < S; ++i) {
......
...@@ -74,6 +74,7 @@ namespace Sass { ...@@ -74,6 +74,7 @@ namespace Sass {
root, root,
ruleset, ruleset,
propset, propset,
media_query,
selector_group, selector_group,
selector, selector,
...@@ -90,6 +91,9 @@ namespace Sass { ...@@ -90,6 +91,9 @@ namespace Sass {
attribute_selector, attribute_selector,
selector_schema, selector_schema,
media_expression_group,
media_expression,
block, block,
rule, rule,
property, property,
...@@ -212,7 +216,7 @@ namespace Sass { ...@@ -212,7 +216,7 @@ namespace Sass {
bool operator>=(Node rhs) const; bool operator>=(Node rhs) const;
string to_string() const; string to_string() const;
void emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel = false); void emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel = false, bool in_media_query = false);
void emit_propset(stringstream& buf, size_t depth, const string& prefix); void emit_propset(stringstream& buf, size_t depth, const string& prefix);
void echo(stringstream& buf, size_t depth = 0); void echo(stringstream& buf, size_t depth = 0);
void emit_expanded_css(stringstream& buf, const string& prefix); void emit_expanded_css(stringstream& buf, const string& prefix);
...@@ -286,6 +290,7 @@ namespace Sass { ...@@ -286,6 +290,7 @@ namespace Sass {
case Node::rule: case Node::rule:
case Node::propset: has_statements = true; break; case Node::propset: has_statements = true; break;
case Node::media_query:
case Node::ruleset: has_blocks = true; break; case Node::ruleset: has_blocks = true; break;
case Node::if_directive: case Node::if_directive:
...@@ -312,9 +317,19 @@ namespace Sass { ...@@ -312,9 +317,19 @@ namespace Sass {
case Node::css_import: case Node::css_import:
case Node::rule: case Node::rule:
case Node::propset: has_statements = true; break; case Node::propset: has_statements = true; break;
case Node::media_query:
case Node::ruleset: has_blocks = 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::expansion: has_expansions = true; break;
case Node::backref: has_backref = true; break; case Node::backref: has_backref = true; break;
default: break; default: break;
} }
if (n.has_backref()) has_backref = true; if (n.has_backref()) has_backref = true;
......
...@@ -19,7 +19,8 @@ namespace Sass { ...@@ -19,7 +19,8 @@ namespace Sass {
{ {
switch (type()) switch (type())
{ {
case selector_group: { // really only needed for arg to :not case selector_group:
case media_expression_group: { // really only needed for arg to :not
string result(at(0).to_string()); string result(at(0).to_string());
for (size_t i = 1, S = size(); i < S; ++i) { for (size_t i = 1, S = size(); i < S; ++i) {
result += ", "; result += ", ";
...@@ -27,6 +28,30 @@ namespace Sass { ...@@ -27,6 +28,30 @@ namespace Sass {
} }
return result; return result;
} break; } break;
case media_expression: {
string result;
if (at(0).type() == rule) {
result += "(";
result += at(0).to_string();
result += ")";
}
else {
result += at(0).to_string();
}
for (size_t i = 1, S = size(); i < S; ++i) {
result += " ";
if (at(i).type() == rule) {
result += "(";
result += at(i).to_string();
result += ")";
}
else {
result += at(i).to_string();
}
}
return result;
} break;
case selector: { case selector: {
string result; string result;
...@@ -84,6 +109,13 @@ namespace Sass { ...@@ -84,6 +109,13 @@ namespace Sass {
return result; return result;
} break; } break;
case rule: {
string result(at(0).to_string());
result += ": ";
result += at(1).to_string();
return result;
} break;
case comma_list: { case comma_list: {
string result(at(0).to_string()); string result(at(0).to_string());
for (size_t i = 1, S = size(); i < S; ++i) { for (size_t i = 1, S = size(); i < S; ++i) {
...@@ -295,16 +327,16 @@ namespace Sass { ...@@ -295,16 +327,16 @@ namespace Sass {
} }
} }
void Node::emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel) void Node::emit_nested_css(stringstream& buf, size_t depth, bool at_toplevel, bool in_media_query)
{ {
switch (type()) switch (type())
{ {
case root: case root: {
if (has_expansions()) flatten(); if (has_expansions()) flatten();
for (size_t i = 0, S = size(); i < S; ++i) { for (size_t i = 0, S = size(); i < S; ++i) {
at(i).emit_nested_css(buf, depth, true); at(i).emit_nested_css(buf, depth, true);
} }
break; } break;
case ruleset: { case ruleset: {
Node sel_group(at(2)); Node sel_group(at(2));
...@@ -321,18 +353,26 @@ namespace Sass { ...@@ -321,18 +353,26 @@ namespace Sass {
block[i].emit_nested_css(buf, depth+1); block[i].emit_nested_css(buf, depth+1);
} }
} }
buf << " }" << endl; buf << " }";
if (!in_media_query || (in_media_query && block.has_blocks())) buf << endl;
++depth; // if we printed content at this level, we need to indent any nested rulesets ++depth; // if we printed content at this level, we need to indent any nested rulesets
} }
if (block.has_blocks()) { if (block.has_blocks()) {
for (size_t i = 0, S = block.size(); i < S; ++i) { for (size_t i = 0, S = block.size(); i < S; ++i) {
if (block[i].type() == ruleset) { if (block[i].type() == ruleset || block[i].type() == media_query) {
block[i].emit_nested_css(buf, depth); block[i].emit_nested_css(buf, depth, false, in_media_query);
} }
} }
} }
if (block.has_statements()) --depth; // see previous comment if (block.has_statements()) --depth; // see previous comment
if ((depth == 0) && at_toplevel) buf << endl; if ((depth == 0) && at_toplevel && !in_media_query) buf << endl;
} break;
case media_query: {
buf << string(2*depth, ' ');
buf << "@media " << at(0).to_string() << " {" << endl;
at(1).emit_nested_css(buf, depth+1, false, true);
buf << " }" << endl;
} break; } break;
case propset: { case propset: {
......
...@@ -103,6 +103,10 @@ namespace Sass { ...@@ -103,6 +103,10 @@ namespace Sass {
const char* import(const char* src) { const char* import(const char* src) {
return exactly<import_kwd>(src); return exactly<import_kwd>(src);
} }
extern const char media_kwd[] = "@media";
const char* media(const char* src) {
return exactly<media_kwd>(src);
}
extern const char mixin_kwd[] = "@mixin"; extern const char mixin_kwd[] = "@mixin";
const char* mixin(const char* src) { const char* mixin(const char* src) {
return exactly<mixin_kwd>(src); return exactly<mixin_kwd>(src);
......
...@@ -308,6 +308,7 @@ namespace Sass { ...@@ -308,6 +308,7 @@ namespace Sass {
// Match CSS '@' keywords. // Match CSS '@' keywords.
const char* at_keyword(const char* src); const char* at_keyword(const char* src);
const char* import(const char* src); const char* import(const char* src);
const char* media(const char* src);
const char* mixin(const char* src); const char* mixin(const char* src);
const char* function(const char* src); const char* function(const char* src);
const char* return_directive(const char* src); const char* return_directive(const char* src);
......
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