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 )