CSS-Sass
view release on metacpan or search on metacpan
libsass/parser.cpp view on Meta::CPAN
exactly<'+'>,
exactly<'~'>,
exactly<'>'>
> >())
// no selector before the combinator
{ lhs = 0; }
else {
lhs = parse_simple_selector_sequence();
sel_source_position = before_token;
lhs->has_line_break(peek_newline());
}
Complex_Selector::Combinator cmb;
if (lex< exactly<'+'> >()) cmb = Complex_Selector::ADJACENT_TO;
else if (lex< exactly<'~'> >()) cmb = Complex_Selector::PRECEDES;
else if (lex< exactly<'>'> >()) cmb = Complex_Selector::PARENT_OF;
else cmb = Complex_Selector::ANCESTOR_OF;
bool cpx_lf = peek_newline();
Complex_Selector* rhs;
if (peek_css< alternatives <
exactly<','>,
exactly<')'>,
exactly<'{'>,
exactly<'}'>,
exactly<';'>,
optional
> >())
// no selector after the combinator
{ rhs = 0; }
else {
rhs = parse_selector_combination();
sel_source_position = before_token;
}
if (!sel_source_position.line) sel_source_position = before_token;
Complex_Selector* cpx = new (ctx.mem) Complex_Selector(ParserState(path, source, sel_source_position), cmb, lhs, rhs);
cpx->media_block(last_media_block);
cpx->last_block(block_stack.back());
if (cpx_lf) cpx->has_line_break(cpx_lf);
return cpx;
}
Compound_Selector* Parser::parse_simple_selector_sequence()
{
Compound_Selector* seq = new (ctx.mem) Compound_Selector(pstate);
seq->media_block(last_media_block);
seq->last_block(block_stack.back());
bool sawsomething = false;
if (lex< exactly<'&'> >()) {
// check if we have a parent selector on the root level block
if (block_stack.back() && block_stack.back()->is_root()) {
//error("Base-level rules cannot contain the parent-selector-referencing character '&'.", pstate);
}
(*seq) << new (ctx.mem) Selector_Reference(pstate);
sawsomething = true;
// if you see a space after a &, then you're done
if(peek< spaces >() || peek< alternatives < spaces, exactly<';'> > >()) {
return seq;
}
}
if (sawsomething && lex_css< sequence< negate< functional >, alternatives< identifier_alnums, universal, quoted_string, dimension, percentage, number > > >()) {
// saw an ampersand, then allow type selectors with arbitrary number of hyphens at the beginning
(*seq) << new (ctx.mem) Type_Selector(pstate, unquote(lexed));
} else if (lex_css< sequence< negate< functional >, alternatives< type_selector, universal, quoted_string, dimension, percentage, number > > >()) {
// if you see a type selector
(*seq) << new (ctx.mem) Type_Selector(pstate, lexed);
sawsomething = true;
}
if (!sawsomething) {
// don't blindly do this if you saw a & or selector
(*seq) << parse_simple_selector();
}
while (!peek< spaces >(position) &&
!(peek_css < alternatives <
exactly<'+'>,
exactly<'~'>,
exactly<'>'>,
exactly<','>,
exactly<')'>,
exactly<'{'>,
exactly<'}'>,
exactly<';'>
> >(position))) {
(*seq) << parse_simple_selector();
}
return seq;
}
Simple_Selector* Parser::parse_simple_selector()
{
lex < css_comments >();
if (lex< alternatives < id_name, class_name > >()) {
return new (ctx.mem) Selector_Qualifier(pstate, unquote(lexed));
}
else if (lex< quoted_string >()) {
return new (ctx.mem) Type_Selector(pstate, unquote(lexed));
}
else if (lex< alternatives < number, kwd_sel_deep > >()) {
return new (ctx.mem) Type_Selector(pstate, lexed);
}
else if (peek< pseudo_not >()) {
return parse_negated_selector();
}
else if (peek< exactly<':'> >(position) || peek< functional >()) {
return parse_pseudo_selector();
}
else if (peek< exactly<'['> >(position)) {
return parse_attribute_selector();
}
else if (lex< placeholder >()) {
Selector_Placeholder* sel = new (ctx.mem) Selector_Placeholder(pstate, unquote(lexed));
sel->media_block(last_media_block);
sel->last_block(block_stack.back());
return sel;
}
else {
error("invalid selector after " + lexed.to_string(), pstate);
}
// unreachable statement
return 0;
}
Wrapped_Selector* Parser::parse_negated_selector()
libsass/parser.cpp view on Meta::CPAN
peek< exactly< webkit_calc_kwd > >()) {
return parse_calc_function();
}
else if (peek< functional_schema >()) {
return parse_function_call_schema();
}
else if (peek< sequence< identifier_schema, negate< exactly<'%'> > > >()) {
return parse_identifier_schema();
}
else if (peek< functional >()) {
return parse_function_call();
}
else if (lex< sequence< exactly<'+'>, optional_css_whitespace, negate< number > > >()) {
return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::PLUS, parse_factor());
}
else if (lex< sequence< exactly<'-'>, optional_css_whitespace, negate< number> > >()) {
return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::MINUS, parse_factor());
}
else if (lex< sequence< kwd_not, css_whitespace > >()) {
return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::NOT, parse_factor());
}
else if (peek < sequence < one_plus < alternatives < css_whitespace, exactly<'-'>, exactly<'+'> > >, number > >()) {
if (parse_number_prefix()) return parse_value(); // prefix is positive
return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::MINUS, parse_value());
}
else {
return parse_value();
}
}
Expression* Parser::parse_value()
{
lex< css_comments >();
if (lex< ampersand >())
{
return new (ctx.mem) Parent_Selector(pstate, parse_selector_group()); }
if (lex< important >())
{ return new (ctx.mem) String_Constant(pstate, "!important"); }
const char* stop;
if ((stop = peek< value_schema >()))
{ return parse_value_schema(stop); }
if (lex< kwd_true >())
{ return new (ctx.mem) Boolean(pstate, true); }
if (lex< kwd_false >())
{ return new (ctx.mem) Boolean(pstate, false); }
if (lex< kwd_null >())
{ return new (ctx.mem) Null(pstate); }
if (lex< identifier >()) {
String_Constant* str = new (ctx.mem) String_Quoted(pstate, lexed);
// Dont' delay this string if it is a name color. Fixes #652.
str->is_delayed(ctx.names_to_colors.count(unquote(lexed)) == 0);
return str;
}
if (lex< percentage >())
{ return new (ctx.mem) Textual(pstate, Textual::PERCENTAGE, lexed); }
// match hex number first because 0x000 looks like a number followed by an indentifier
if (lex< alternatives< hex, hex0 > >())
{ return new (ctx.mem) Textual(pstate, Textual::HEX, lexed); }
// also handle the 10em- foo special case
if (lex< sequence< dimension, optional< sequence< exactly<'-'>, negate< digit > > > > >())
{ return new (ctx.mem) Textual(pstate, Textual::DIMENSION, lexed); }
if (lex< number >())
{ return new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed); }
if (peek< quoted_string >())
{ return parse_string(); }
if (lex< variable >())
{ return new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed)); }
// Special case handling for `%` proceeding an interpolant.
if (lex< sequence< exactly<'%'>, optional< percentage > > >())
{ return new (ctx.mem) String_Quoted(pstate, lexed); }
error("error reading values after " + lexed.to_string(), pstate);
// unreachable statement
return 0;
}
// this parses interpolation inside other strings
// means the result should later be quoted again
String* Parser::parse_interpolated_chunk(Token chunk, bool constant)
{
const char* i = chunk.begin;
// see if there any interpolants
const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end);
if (!p) {
String_Quoted* str_quoted = new (ctx.mem) String_Quoted(pstate, string(i, chunk.end));
if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*');
str_quoted->is_delayed(true);
return str_quoted;
}
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
while (i < chunk.end) {
p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end);
if (p) {
if (i < p) {
// accumulate the preceding segment if it's nonempty
(*schema) << new (ctx.mem) String_Quoted(pstate, string(i, p));
}
// we need to skip anything inside strings
// create a new target in parser/prelexer
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, chunk.end); // find the closing brace
if (j) { --j;
// parse the interpolant and accumulate it
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
interp_node->is_interpolant(true);
(*schema) << interp_node;
i = j;
}
else {
// throw an error if the interpolant is unterminated
error("unterminated interpolant inside string constant " + chunk.to_string(), pstate);
}
}
else { // no interpolants left; add the last segment if nonempty
// check if we need quotes here (was not sure after merge)
if (i < chunk.end) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, chunk.end));
break;
}
++ i;
}
return schema;
}
String_Constant* Parser::parse_static_value()
{
lex< static_value >();
Token str(lexed);
--str.end;
libsass/parser.cpp view on Meta::CPAN
if (i < p) {
(*schema) << new (ctx.mem) String_Constant(pstate, string(i, p)); // accumulate the preceding segment if it's nonempty
}
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, str.end); // find the closing brace
if (j) {
// parse the interpolant and accumulate it
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
interp_node->is_interpolant(true);
(*schema) << interp_node;
i = j;
}
else {
// throw an error if the interpolant is unterminated
error("unterminated interpolant inside IE function " + str.to_string(), pstate);
}
}
else { // no interpolants left; add the last segment if nonempty
if (i < str.end) (*schema) << new (ctx.mem) String_Constant(pstate, string(i, str.end));
break;
}
}
return schema;
}
String* Parser::parse_ie_keyword_arg()
{
String_Schema* kwd_arg = new (ctx.mem) String_Schema(pstate, 3);
if (lex< variable >()) {
*kwd_arg << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed));
} else {
lex< alternatives< identifier_schema, identifier > >();
*kwd_arg << new (ctx.mem) String_Quoted(pstate, lexed);
}
lex< exactly<'='> >();
*kwd_arg << new (ctx.mem) String_Quoted(pstate, lexed);
if (peek< variable >()) *kwd_arg << parse_list();
else if (lex< number >()) *kwd_arg << new (ctx.mem) Textual(pstate, Textual::NUMBER, Util::normalize_decimals(lexed));
else if (lex< alternatives< identifier_schema, identifier, number, hexa, hex > >()) {
*kwd_arg << new (ctx.mem) String_Quoted(pstate, lexed);
}
return kwd_arg;
}
String_Schema* Parser::parse_value_schema(const char* stop)
{
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
size_t num_items = 0;
while (position < stop) {
if (lex< interpolant >()) {
Token insides(Token(lexed.begin + 2, lexed.end - 1));
Expression* interp_node = Parser::from_token(insides, ctx, pstate).parse_list();
interp_node->is_interpolant(true);
(*schema) << interp_node;
}
else if (lex< exactly<'%'> >()) {
(*schema) << new (ctx.mem) String_Constant(pstate, lexed);
}
else if (lex< identifier >()) {
(*schema) << new (ctx.mem) String_Quoted(pstate, lexed);
}
else if (lex< percentage >()) {
(*schema) << new (ctx.mem) Textual(pstate, Textual::PERCENTAGE, lexed);
}
else if (lex< dimension >()) {
(*schema) << new (ctx.mem) Textual(pstate, Textual::DIMENSION, lexed);
}
else if (lex< number >()) {
(*schema) << new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed);
}
else if (lex< hex >()) {
(*schema) << new (ctx.mem) Textual(pstate, Textual::HEX, unquote(lexed));
}
else if (lex< quoted_string >()) {
(*schema) << new (ctx.mem) String_Quoted(pstate, lexed);
}
else if (lex< variable >()) {
(*schema) << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed));
}
else {
error("error parsing interpolated value", pstate);
}
++num_items;
}
return schema;
}
/* not used anymore - remove?
String_Schema* Parser::parse_url_schema()
{
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
while (position < end) {
if (position[0] == '/') {
lexed = Token(position, position+1, before_token);
(*schema) << new (ctx.mem) String_Quoted(pstate, lexed);
++position;
}
else if (lex< interpolant >()) {
Token insides(Token(lexed.begin + 2, lexed.end - 1, before_token));
Expression* interp_node = Parser::from_token(insides, ctx, pstate).parse_list();
interp_node->is_interpolant(true);
(*schema) << interp_node;
}
else if (lex< sequence< identifier, exactly<':'> > >()) {
(*schema) << new (ctx.mem) String_Quoted(pstate, lexed);
}
else if (lex< filename >()) {
(*schema) << new (ctx.mem) String_Quoted(pstate, lexed);
}
else {
error("error parsing interpolated url", pstate);
}
}
return schema;
} */
// this parses interpolation outside other strings
// means the result must not be quoted again later
String* Parser::parse_identifier_schema()
{
// first lex away whatever we have found
libsass/parser.cpp view on Meta::CPAN
}
At_Rule* Parser::parse_at_rule()
{
lex<at_keyword>();
string kwd(lexed);
ParserState at_source_position = pstate;
Selector* sel = 0;
Expression* val = 0;
Selector_Lookahead lookahead = lookahead_for_extension_target(position);
if (lookahead.found) {
if (lookahead.has_interpolants) {
sel = parse_selector_schema(lookahead.found);
}
else {
sel = parse_selector_group();
}
}
else if (!(peek<exactly<'{'> >() || peek<exactly<'}'> >() || peek<exactly<';'> >())) {
val = parse_list();
}
Block* body = 0;
if (peek< exactly<'{'> >()) body = parse_block();
At_Rule* rule = new (ctx.mem) At_Rule(at_source_position, kwd, sel, body);
if (!sel) rule->value(val);
return rule;
}
Warning* Parser::parse_warning()
{
lex< kwd_warn >();
return new (ctx.mem) Warning(pstate, parse_list());
}
Error* Parser::parse_error()
{
lex< kwd_err >();
return new (ctx.mem) Error(pstate, parse_list());
}
Debug* Parser::parse_debug()
{
lex< kwd_dbg >();
return new (ctx.mem) Debug(pstate, parse_list());
}
Selector_Lookahead Parser::lookahead_for_selector(const char* start)
{
const char* p = start ? start : position;
const char* q;
bool saw_stuff = false;
bool saw_interpolant = false;
while ((q = peek< identifier >(p)) ||
(q = peek< hyphens_and_identifier >(p)) ||
(q = peek< hyphens_and_name >(p)) ||
(q = peek< type_selector >(p)) ||
(q = peek< id_name >(p)) ||
(q = peek< class_name >(p)) ||
(q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
(q = peek< percentage >(p)) ||
(q = peek< dimension >(p)) ||
(q = peek< quoted_string >(p)) ||
(q = peek< exactly<'*'> >(p)) ||
(q = peek< exactly<sel_deep_kwd> >(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)) ||
(saw_stuff && (q = peek< exactly<'-'> >(p))) ||
(q = peek< binomial >(p)) ||
(q = peek< block_comment >(p)) ||
(q = peek< sequence< optional<sign>,
zero_plus<digit>,
exactly<'n'> > >(p)) ||
(q = peek< sequence< optional<sign>,
one_plus<digit> > >(p)) ||
(q = peek< number >(p)) ||
(q = peek< sequence< exactly<'&'>,
identifier_alnums > >(p)) ||
(q = peek< exactly<'&'> >(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< one_plus< exactly<'-'> >, interpolant > >(p)) ||
(q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
(q = peek< interpolant >(p))) {
saw_stuff = true;
p = q;
if (*(p - 1) == '}') saw_interpolant = true;
}
Selector_Lookahead result;
result.found = saw_stuff && peek< exactly<'{'> >(p) ? p : 0;
result.has_interpolants = saw_interpolant;
return result;
}
Selector_Lookahead Parser::lookahead_for_extension_target(const char* start)
{
const char* p = start ? start : position;
const char* q;
bool saw_interpolant = false;
bool saw_stuff = false;
while ((q = peek< identifier >(p)) ||
(q = peek< type_selector >(p)) ||
(q = peek< id_name >(p)) ||
(q = peek< class_name >(p)) ||
(q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
(q = peek< percentage >(p)) ||
(q = peek< dimension >(p)) ||
(q = peek< quoted_string >(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)) ||
(saw_stuff && (q = peek< exactly<'-'> >(p))) ||
(q = peek< binomial >(p)) ||
(q = peek< block_comment >(p)) ||
(q = peek< sequence< optional<sign>,
zero_plus<digit>,
exactly<'n'> > >(p)) ||
(q = peek< sequence< optional<sign>,
one_plus<digit> > >(p)) ||
(q = peek< number >(p)) ||
(q = peek< sequence< exactly<'&'>,
identifier_alnums > >(p)) ||
(q = peek< exactly<'&'> >(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< one_plus< exactly<'-'> >, interpolant > >(p)) ||
(q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
(q = peek< interpolant >(p)) ||
(q = peek< optional >(p))) {
p = q;
if (*(p - 1) == '}') saw_interpolant = true;
saw_stuff = true;
}
Selector_Lookahead result;
result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0;
result.has_interpolants = saw_interpolant;
return result;
}
void Parser::read_bom()
{
size_t skip = 0;
string encoding;
bool utf_8 = false;
switch ((unsigned char) source[0]) {
case 0xEF:
skip = check_bom_chars(source, end, utf_8_bom, 3);
encoding = "UTF-8";
utf_8 = true;
break;
case 0xFE:
( run in 0.720 second using v1.01-cache-2.11-cpan-39bf76dae61 )