CSS-Sass
view release on metacpan or search on metacpan
libsass/parser.cpp view on Meta::CPAN
bool Parser::peek_newline(const char* start)
{
return peek_linefeed(start ? start : position);
}
Parser Parser::from_token(Token t, Context& ctx, ParserState pstate)
{
Parser p(ctx, pstate);
p.source = t.begin;
p.position = p.source;
p.end = t.end;
Block* root = new (ctx.mem) Block(pstate);
p.block_stack.push_back(root);
root->is_root(true);
return p;
}
Block* Parser::parse()
{
Block* root = new (ctx.mem) Block(pstate);
block_stack.push_back(root);
root->is_root(true);
read_bom();
if (ctx.queue.size() == 1) {
Import* pre = new (ctx.mem) Import(pstate);
string load_path(ctx.queue[0].load_path);
do_import(load_path, pre, ctx.c_headers, false);
ctx.head_imports = ctx.queue.size() - 1;
if (!pre->urls().empty()) (*root) << pre;
if (!pre->files().empty()) {
for (size_t i = 0, S = pre->files().size(); i < S; ++i) {
(*root) << new (ctx.mem) Import_Stub(pstate, pre->files()[i]);
}
}
}
lex< optional_spaces >();
Selector_Lookahead lookahead_result;
while (position < end) {
parse_block_comments(root);
if (peek< kwd_import >()) {
Import* imp = parse_import();
if (!imp->urls().empty()) (*root) << imp;
if (!imp->files().empty()) {
for (size_t i = 0, S = imp->files().size(); i < S; ++i) {
(*root) << new (ctx.mem) Import_Stub(pstate, imp->files()[i]);
}
}
if (!lex< one_plus< exactly<';'> > >()) error("top-level @import directive must be terminated by ';'", pstate);
}
else if (peek< kwd_mixin >() || peek< kwd_function >()) {
(*root) << parse_definition();
}
else if (peek< variable >()) {
(*root) << parse_assignment();
if (!lex< one_plus< exactly<';'> > >()) error("top-level variable binding must be terminated by ';'", pstate);
}
/*else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
(*root) << parse_propset();
}*/
else if (peek< kwd_include >() /* || peek< exactly<'+'> >() */) {
Mixin_Call* mixin_call = parse_mixin_call();
(*root) << mixin_call;
if (!mixin_call->block() && !lex< one_plus< exactly<';'> > >()) error("top-level @include directive must be terminated by ';'", pstate);
}
else if (peek< kwd_if_directive >()) {
(*root) << parse_if_directive();
}
else if (peek< kwd_for_directive >()) {
(*root) << parse_for_directive();
}
else if (peek< kwd_each_directive >()) {
(*root) << parse_each_directive();
}
else if (peek< kwd_while_directive >()) {
(*root) << parse_while_directive();
}
else if (peek< kwd_media >()) {
(*root) << parse_media_block();
}
else if (peek< kwd_at_root >()) {
(*root) << parse_at_root_block();
}
else if (peek< kwd_supports >()) {
(*root) << parse_feature_block();
}
else if (peek< kwd_warn >()) {
(*root) << parse_warning();
if (!lex< one_plus< exactly<';'> > >()) error("top-level @warn directive must be terminated by ';'", pstate);
}
else if (peek< kwd_err >()) {
(*root) << parse_error();
if (!lex< one_plus< exactly<';'> > >()) error("top-level @error directive must be terminated by ';'", pstate);
}
else if (peek< kwd_dbg >()) {
(*root) << parse_debug();
if (!lex< one_plus< exactly<';'> > >()) error("top-level @debug directive must be terminated by ';'", pstate);
}
// ignore the @charset directive for now
else if (lex< exactly< charset_kwd > >()) {
lex< quoted_string >();
lex< one_plus< exactly<';'> > >();
}
else if (peek< at_keyword >()) {
At_Rule* at_rule = parse_at_rule();
(*root) << at_rule;
if (!at_rule->block() && !lex< one_plus< exactly<';'> > >()) error("top-level directive must be terminated by ';'", pstate);
}
else if ((lookahead_result = lookahead_for_selector(position)).found) {
(*root) << parse_ruleset(lookahead_result);
}
else if (peek< exactly<';'> >()) {
lex< one_plus< exactly<';'> > >();
}
else {
lex< css_whitespace >();
if (position >= end) break;
error("invalid top-level expression", after_token);
}
libsass/parser.cpp view on Meta::CPAN
if (!peek_css< exactly<')'> >()) {
do (*args) << parse_argument(has_url);
while (lex_css< exactly<','> >());
}
if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);
}
return args;
}
Argument* Parser::parse_argument(bool has_url)
{
Argument* arg;
// some urls can look like line comments (parse literally - chunk would not work)
if (has_url && lex< sequence < uri_value, lookahead < loosely<')'> > > >(false)) {
String* the_url = parse_interpolated_chunk(lexed);
arg = new (ctx.mem) Argument(the_url->pstate(), the_url);
}
else if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) {
lex_css< variable >();
string name(Util::normalize_underscores(lexed));
ParserState p = pstate;
lex_css< exactly<':'> >();
Expression* val = parse_space_list();
val->is_delayed(false);
arg = new (ctx.mem) Argument(p, val, name);
}
else {
bool is_arglist = false;
bool is_keyword = false;
Expression* val = parse_space_list();
val->is_delayed(false);
if (lex_css< exactly< ellipsis > >()) {
if (val->concrete_type() == Expression::MAP) is_keyword = true;
else is_arglist = true;
}
arg = new (ctx.mem) Argument(pstate, val, "", is_arglist, is_keyword);
}
return arg;
}
Assignment* Parser::parse_assignment()
{
lex< variable >();
string name(Util::normalize_underscores(lexed));
ParserState var_source_position = pstate;
if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate);
Expression* val = parse_list();
val->is_delayed(false);
bool is_default = false;
bool is_global = false;
while (peek< default_flag >() || peek< global_flag >()) {
is_default = lex< default_flag >() || is_default;
is_global = lex< global_flag >() || is_global;
}
Assignment* var = new (ctx.mem) Assignment(var_source_position, name, val, is_default, is_global);
return var;
}
/* not used anymore - remove?
Propset* Parser::parse_propset()
{
String* property_segment;
if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
property_segment = parse_identifier_schema();
}
else {
lex< sequence< optional< exactly<'*'> >, identifier > >();
property_segment = new (ctx.mem) String_Quoted(pstate, lexed);
}
Propset* propset = new (ctx.mem) Propset(pstate, property_segment);
lex< exactly<':'> >();
if (!peek< exactly<'{'> >()) error("expected a '{' after namespaced property", pstate);
propset->block(parse_block());
propset->tabs(indentation);
return propset;
} */
Ruleset* Parser::parse_ruleset(Selector_Lookahead lookahead)
{
Selector* sel;
if (lookahead.has_interpolants) {
sel = parse_selector_schema(lookahead.found);
}
else {
sel = parse_selector_group();
}
bool old_in_at_root = in_at_root;
ParserState r_source_position = pstate;
lex < css_comments >();
in_at_root = false;
if (!peek< exactly<'{'> >()) error("expected a '{' after the selector", pstate);
Block* block = parse_block();
in_at_root = old_in_at_root;
old_in_at_root = false;
Ruleset* ruleset = new (ctx.mem) Ruleset(r_source_position, sel, block);
return ruleset;
}
Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector)
{
lex< optional_spaces >();
const char* i = position;
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
while (i < end_of_selector) {
// try to parse mutliple interpolants
if (const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, end_of_selector)) {
// accumulate the preceding segment if the position has advanced
if (i < p) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, p));
// skip to the delimiter by skipping occurences in quoted strings
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, end_of_selector);
Expression* interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list();
interpolant->is_interpolant(true);
(*schema) << interpolant;
i = j;
}
// no more interpolants have been found
// add the last segment if there is one
else {
if (i < end_of_selector) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, end_of_selector));
break;
}
}
position = end_of_selector;
Selector_Schema* selector_schema = new (ctx.mem) Selector_Schema(pstate, schema);
selector_schema->media_block(last_media_block);
selector_schema->last_block(block_stack.back());
return selector_schema;
}
Selector_List* Parser::parse_selector_group()
{
bool reloop = true;
To_String to_string(&ctx);
lex< css_whitespace >();
Selector_List* group = new (ctx.mem) Selector_List(pstate);
libsass/parser.cpp view on Meta::CPAN
(*block) << parse_debug();
semicolon = true;
}
else if (stack.back() == function_def) {
error("only variable declarations and control directives are allowed inside functions", pstate);
}
else if (peek< kwd_mixin >() || peek< kwd_function >()) {
(*block) << parse_definition();
}
else if (peek< kwd_include >(position)) {
Mixin_Call* the_call = parse_mixin_call();
(*block) << the_call;
// don't need a semicolon after a content block
semicolon = (the_call->block()) ? false : true;
}
else if (lex< kwd_content >()) {
if (stack.back() != mixin_def) {
error("@content may only be used within a mixin", pstate);
}
(*block) << new (ctx.mem) Content(pstate);
semicolon = true;
}
/*
else if (peek< exactly<'+'> >()) {
(*block) << parse_mixin_call();
semicolon = true;
}
*/
else if (lex< kwd_extend >()) {
Selector_Lookahead lookahead = lookahead_for_extension_target(position);
if (!lookahead.found) error("invalid selector for @extend", pstate);
Selector* target;
if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found);
else target = parse_selector_group();
(*block) << new (ctx.mem) Extension(pstate, target);
semicolon = true;
}
else if (peek< kwd_media >()) {
(*block) << parse_media_block();
}
else if (peek< kwd_supports >()) {
(*block) << parse_feature_block();
}
else if (peek< kwd_at_root >()) {
(*block) << parse_at_root_block();
}
// ignore the @charset directive for now
else if (lex< exactly< charset_kwd > >()) {
lex< quoted_string >();
lex< one_plus< exactly<';'> > >();
}
else if (peek< at_keyword >()) {
At_Rule* at_rule = parse_at_rule();
(*block) << at_rule;
if (!at_rule->block()) semicolon = true;
}
else if ((lookahead_result = lookahead_for_selector(position)).found) {
(*block) << parse_ruleset(lookahead_result);
}/* not used anymore - remove?
else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
(*block) << parse_propset();
}*/
else if (!peek< exactly<';'> >()) {
bool indent = ! peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position);
/* not used anymore - remove?
if (peek< sequence< optional< exactly<'*'> >, identifier_schema, exactly<':'>, exactly<'{'> > >()) {
(*block) << parse_propset();
}
else if (peek< sequence< optional< exactly<'*'> >, identifier, exactly<':'>, exactly<'{'> > >()) {
(*block) << parse_propset();
}
else */ {
Declaration* decl = parse_declaration();
decl->tabs(indentation);
(*block) << decl;
if (peek< exactly<'{'> >()) {
// parse a propset that rides on the declaration's property
if (indent) indentation++;
Propset* ps = new (ctx.mem) Propset(pstate, decl->property(), parse_block());
if (indent) indentation--;
(*block) << ps;
}
else {
// finish and let the semicolon get munched
semicolon = true;
}
}
}
else lex< one_plus< exactly<';'> > >();
parse_block_comments(block);
}
block_stack.pop_back();
return block;
}
Declaration* Parser::parse_declaration() {
String* prop = 0;
if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
prop = parse_identifier_schema();
}
else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) {
prop = new (ctx.mem) String_Quoted(pstate, lexed);
}
else {
error("invalid property name", pstate);
}
const string property(lexed);
if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate);
if (peek_css< exactly<';'> >()) error("style declaration must contain a value", pstate);
if (peek_css< static_value >()) {
return new (ctx.mem) Declaration(prop->pstate(), prop, parse_static_value()/*, lex<important>()*/);
}
else {
Expression* list_ex = parse_list();
if (List* list = dynamic_cast<List*>(list_ex)) {
if (list->length() == 0 && !peek< exactly <'{'> >()) {
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
}
}
return new (ctx.mem) Declaration(prop->pstate(), prop, list_ex/*, lex<important>()*/);
}
}
// parse +/- and return false if negative
bool Parser::parse_number_prefix()
{
bool positive = true;
while(true) {
if (lex < block_comment >()) continue;
if (lex < number_prefix >()) continue;
if (lex < exactly < '-' > >()) {
positive = !positive;
continue;
}
break;
}
return positive;
}
( run in 0.734 second using v1.01-cache-2.11-cpan-5511b514fd6 )