Text-Sass-XS

 view release on metacpan or  search on metacpan

libsass/old/document_parser.cpp  view on Meta::CPAN

#include <cstdlib>
#include <iostream>
#include "document.hpp"
#include "constants.hpp"
#include "error.hpp"

#ifndef SASS_PRELEXER
#include "prelexer.hpp"
#endif

namespace Sass {
  using namespace std;
  using namespace Constants;

  void Document::parse_scss()
  {
    read_bom();
    lex< optional_spaces >();
    Selector_Lookahead lookahead_result;
    while (position < end) {
      if (lex< block_comment >()) {
        root << context.new_Node(Node::comment, path, line, lexed);
      }
      else if (peek< import >()) {
        Node importee(parse_import());
        if (importee.type() == Node::css_import) root << importee;
        else                                     root += importee;
        if (!lex< exactly<';'> >()) throw_syntax_error("top-level @import directive must be terminated by ';'");
      }
      else if (peek< mixin >() /* || peek< exactly<'='> >() */) {
        root << parse_mixin_definition();
      }
      else if (peek< function >()) {
        root << parse_function_definition();
      }
      else if (peek< variable >()) {
        root << parse_assignment();
        if (!lex< exactly<';'> >()) throw_syntax_error("top-level variable binding must be terminated by ';'");
      }
      else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
        root << parse_propset();
      }
      else if ((lookahead_result = lookahead_for_selector(position)).found) {
        root << parse_ruleset(lookahead_result);
      }
      else if (peek< include >() /* || peek< exactly<'+'> >() */) {
        Node mixin_call(parse_mixin_call());
        root << mixin_call;
        if (mixin_call.size() < 3 && !lex< exactly<';'> >()) throw_syntax_error("top-level @include directive must be terminated by ';'");
      }
      else if (peek< if_directive >()) {
        root << parse_if_directive(Node(), Node::none);
      }
      else if (peek< for_directive >()) {
        root << parse_for_directive(Node(), Node::none);
      }
      else if (peek< each_directive >()) {
        root << parse_each_directive(Node(), Node::none);
      }
      else if (peek< while_directive >()) {
        root << parse_while_directive(Node(), Node::none);
      }
      else if (peek< media >()) {
        root << parse_media_query(Node::none);
      }
      else if (peek< keyframes >()) {
        root << parse_keyframes(Node::none);
      }
      else if (peek< warn >()) {
        root << parse_warning();
        if (!lex< exactly<';'> >()) throw_syntax_error("top-level @warn directive must be terminated by ';'");
      }
      // ignore the @charset directive for now
      else if (lex< exactly< charset_kwd > >()) {
        lex< string_constant >();
        lex< exactly<';'> >();
      }
      else if (peek< directive >()) {
        Node dir(parse_directive(Node(), Node::none));
        if (dir.type() == Node::blockless_directive) {
          if (!lex< exactly<';'> >()) throw_syntax_error("top-level blockless directive must be terminated by ';'");
        }
        root << dir;
      }
      else {
        lex< spaces_and_comments >();
        if (position >= end) break;
        throw_syntax_error("invalid top-level expression");
      }
      lex< optional_spaces >();
    }
  }

  Node Document::parse_import()
  {
    lex< import >();
    if (lex< uri_prefix >())
    {
      if (peek< string_constant >()) {
        Node schema(parse_string());
        Node importee(context.new_Node(Node::css_import, path, line, 1));

libsass/old/document_parser.cpp  view on Meta::CPAN

    return args;
  }

  Node Document::parse_argument(Node::Type arg_type)
  {
    // if arg_type is assignment, only accept keyword args from here onwards
    if (arg_type == Node::assignment) {
      if (peek< sequence < variable, spaces_and_comments, exactly<':'> > >()) {
        lex< variable >();
        Node var(context.new_Node(Node::variable, path, line, lexed));
        lex< exactly<':'> >();
        Node val(parse_space_list());
        val.should_eval() = true;
        Node assn(context.new_Node(Node::assignment, path, line, 2));
        assn << var << val;
        return assn;
      }
      else {
        throw_syntax_error("ordinal arguments must precede keyword arguments");
      }
    }
    // otherwise accept either, and let the caller set the arg_type flag
    else if (arg_type == Node::none &&
             peek< sequence < variable, spaces_and_comments, exactly<':'> > >()) {
      lex< variable >();
      Node var(context.new_Node(Node::variable, path, line, lexed));
      lex< exactly<':'> >();
      Node val(parse_space_list());
      val.should_eval() = true;
      Node assn(context.new_Node(Node::assignment, path, line, 2));
      assn << var << val;
      return assn;
    }
    // else if (arg_type == Node::none &&
    //          peek< sequence < variable, spaces_and_comments, exactly< ellipsis > > >()) {
    //   lex< variable >();
    //   lex< exactly< ellipsis > >();
    //   return context.new_Node(Node::rest, path, line, lexed);
    // }
    Node val(parse_space_list());
    val.should_eval() = true;
    if (lex< exactly< ellipsis > >()) {
      val.is_arglist() = true;
      val.is_splat() = true;
    }
    return val;
  }

  Node Document::parse_assignment()
  {
    lex< variable >();
    Node var(context.new_Node(Node::variable, path, line, lexed));
    if (!lex< exactly<':'> >()) throw_syntax_error("expected ':' after " + lexed.to_string() + " in assignment statement");
    Node val(parse_list());
    Node assn(context.new_Node(Node::assignment, path, line, 2));
    assn << var << val;
    if (lex< default_flag >()) assn << context.new_Node(Node::none, path, line, 0);
    return assn;
  }

  Node Document::parse_propset()
  {
    Node property_segment;
    if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
      property_segment = parse_identifier_schema();
    }
    else {
      lex< sequence< optional< exactly<'*'> >, identifier > >();
      property_segment = context.new_Node(Node::identifier, path, line, lexed);
    }
    lex< exactly<':'> >();
    Node block(parse_block(Node()));
    // lex< exactly<'{'> >();
    // Node block(context.new_Node(Node::block, path, line, 1));
    // while (!lex< exactly<'}'> >()) {
    //   if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
    //     block << parse_propset();
    //   }
    //   else {
    //     block << parse_rule();
    //     lex< exactly<';'> >();
    //   }
    // }
    if (block.empty()) throw_syntax_error("namespaced property cannot be empty");
    Node propset(context.new_Node(Node::propset, path, line, 2));
    propset << property_segment;
    propset << block;
    return propset;
  }

  Node Document::parse_ruleset(Selector_Lookahead lookahead, Node::Type inside_of)
  {
    Node ruleset(context.new_Node(Node::ruleset, path, line, 3));
    if (lookahead.has_interpolants) {
      ruleset << parse_selector_schema(lookahead.found);
    }
    else {
      ruleset << parse_selector_group();
    }
    if (!peek< exactly<'{'> >()) throw_syntax_error("expected a '{' after the selector");
    ruleset << parse_block(ruleset, inside_of);
    return ruleset;
  }

  Node Document::parse_selector_schema(const char* end_of_selector)
  {
    const char* i = position;
    const char* p;
    Node schema(context.new_Node(Node::selector_schema, path, line, 1));

    while (i < end_of_selector) {
      p = find_first_in_interval< exactly<hash_lbrace> >(i, end_of_selector);
      if (p) {
        // accumulate the preceding segment if there is one
        if (i < p) schema << context.new_Node(Node::identifier, path, line, Token::make(i, p));
        // find the end of the interpolant and parse it
        const char* j = find_first_in_interval< exactly<rbrace> >(p, end_of_selector);
        Node interp_node(Document::make_from_token(context, Token::make(p+2, j), path, line).parse_list());
        interp_node.should_eval() = true;
        schema << interp_node;
        i = j + 1;
      }
      else { // no interpolants left; add the last segment if there is one
        if (i < end_of_selector) schema << context.new_Node(Node::identifier, path, line, Token::make(i, end_of_selector));
        break;
      }
    }
    position = end_of_selector;
    return schema;
  }

  Node Document::parse_selector_group()
  {
    Node sel1(parse_selector());
    if (!peek< exactly<','> >()) return sel1;

    Node group(context.new_Node(Node::selector_group, path, line, 2));
    group << sel1;
    while (lex< exactly<','> >()) group << parse_selector();
    return group;
  }

  Node Document::parse_selector()
  {
    Node seq1(parse_simple_selector_sequence());
    if (peek< exactly<','> >() ||
        peek< exactly<')'> >() ||
        peek< exactly<'{'> >() ||

libsass/old/document_parser.cpp  view on Meta::CPAN

      else if (peek< import >(position)) {
        if (inside_of == Node::mixin || inside_of == Node::function) {
          lex< import >(); // to adjust the line number
          throw_syntax_error("@import directive not allowed inside definition of mixin or function");
        }
        Node imported_tree(parse_import());
        if (imported_tree.type() == Node::css_import) {
          block << imported_tree;
        }
        else {
          for (size_t i = 0, S = imported_tree.size(); i < S; ++i) {
            block << imported_tree[i];
          }
          semicolon = true;
        }
      }
      else if (lex< variable >()) {
        block << parse_assignment();
        semicolon = true;
      }
      else if (peek< if_directive >()) {
        block << parse_if_directive(surrounding_ruleset, inside_of);
      }
      else if (peek< for_directive >()) {
        block << parse_for_directive(surrounding_ruleset, inside_of);
      }
      else if (peek< each_directive >()) {
        block << parse_each_directive(surrounding_ruleset, inside_of);
      }
      else if (peek < while_directive >()) {
        block << parse_while_directive(surrounding_ruleset, inside_of);
      }
      else if (lex < return_directive >()) {
        Node ret_expr(context.new_Node(Node::return_directive, path, line, 1));
        ret_expr << parse_list();
        ret_expr.should_eval() = true;
        block << ret_expr;
        semicolon = true;
      }
      else if (peek< warn >()) {
        block << parse_warning();
        semicolon = true;
      }
      else if (inside_of == Node::function) {
        throw_syntax_error("only variable declarations and control directives are allowed inside functions");
      }
      else if (peek< include >(position)) {
        Node the_call = parse_mixin_call(inside_of);
        block << the_call;
        // don't need a semicolon after a content block
        semicolon = (the_call.size() == 3) ? false : true;
      }
      else if (lex< content >()) {
        if (inside_of != Node::mixin) {
          throw_syntax_error("@content may only be used within a mixin");
        }
        block << context.new_Node(Node::mixin_content, path, line, 0); // just an expansion stub
        semicolon = true;
      }
      else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
        block << parse_propset();
      }
      else if (peek < keyframes >()) {
        block << parse_keyframes(inside_of);
      }
      else if (peek< sequence< keyf, optional_spaces, exactly<'{'> > >()) {
        block << parse_keyframe(inside_of);
      }
      else if ((lookahead_result = lookahead_for_selector(position)).found) {
        block << parse_ruleset(lookahead_result, inside_of);
      }
      /*
      else if (peek< exactly<'+'> >()) {
        block << parse_mixin_call();
        semicolon = true;
      }
      */
      else if (lex< extend >()) {
        Node request(context.new_Node(Node::extend_directive, path, line, 1));
        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;
      }
      else if (peek< media >()) {
        block << parse_media_query(inside_of);
      }
      // ignore the @charset directive for now
      else if (lex< exactly< charset_kwd > >()) {
        lex< string_constant >();
        lex< exactly<';'> >();
      }
      else if (peek< directive >()) {
        Node dir(parse_directive(surrounding_ruleset, inside_of));
        if (dir.type() == Node::blockless_directive) semicolon = true;
        block << dir;
      }
      else if (peek< percentage >() ){
        lex< percentage >();
        block << context.new_Node(path, line, atof(lexed.begin), Node::numeric_percentage);
        if (peek< exactly<'{'> >()) {
          Node inner(parse_block(Node()));
          block << inner;
        }
      }
      else if (!peek< exactly<';'> >()) {
        Node rule(parse_rule());
        // check for lbrace; if it's there, we have a namespace property with a value
        if (peek< exactly<'{'> >()) {
          Node inner(parse_block(Node()));
          Node propset(context.new_Node(Node::propset, path, line, 2));
          propset << rule[0];
          rule[0] = context.new_Node(Node::property, path, line, Token::make());
          inner.push_front(rule);
          propset << inner;
          block << propset;
        }
        else {
          block << rule;
          semicolon = true;
        }
      }
      else lex< exactly<';'> >();
      while (lex< block_comment >()) {
        block << context.new_Node(Node::comment, path, line, lexed);
      }
    }
    return block;
  }

  Node Document::parse_rule() {
    Node rule(context.new_Node(Node::rule, path, line, 2));
    if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
      rule << parse_identifier_schema();
    }
    else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) {
      rule << context.new_Node(Node::property, path, line, lexed);
    }
    else {
      throw_syntax_error("invalid property name");
    }
    if (!lex< exactly<':'> >()) throw_syntax_error("property \"" + lexed.to_string() + "\" must be followed by a ':'");
    rule << parse_list();
    return rule;
  }

  Node Document::parse_list()
  {
    return parse_comma_list();
  }

  Node Document::parse_comma_list()
  {
    if (peek< exactly<';'> >(position) ||
        peek< exactly<'}'> >(position) ||
        peek< exactly<'{'> >(position) ||
        peek< exactly<')'> >(position) ||
        peek< exactly<ellipsis> >(position))
    { return context.new_Node(Node::list, path, line, 0); }
    Node list1(parse_space_list());
    // if it's a singleton, return it directly; don't wrap it
    if (!peek< exactly<','> >(position)) return list1;

    Node comma_list(context.new_Node(Node::list, path, line, 2));
    comma_list.is_comma_separated() = true;
    comma_list << list1;
    comma_list.should_eval() |= list1.should_eval();

    while (lex< exactly<','> >())
    {
      Node list(parse_space_list());
      comma_list << list;
      comma_list.should_eval() |= list.should_eval();
    }

    return comma_list;



( run in 1.228 second using v1.01-cache-2.11-cpan-71847e10f99 )