Blame src/parser.hpp

Packit Service 7770af
#ifndef SASS_PARSER_H
Packit Service 7770af
#define SASS_PARSER_H
Packit Service 7770af
Packit Service 7770af
#include <string>
Packit Service 7770af
#include <vector>
Packit Service 7770af
Packit Service 7770af
#include "ast.hpp"
Packit Service 7770af
#include "position.hpp"
Packit Service 7770af
#include "context.hpp"
Packit Service 7770af
#include "position.hpp"
Packit Service 7770af
#include "prelexer.hpp"
Packit Service 7770af
Packit Service 7770af
struct Lookahead {
Packit Service 7770af
  const char* found;
Packit Service 7770af
  const char* error;
Packit Service 7770af
  const char* position;
Packit Service 7770af
  bool parsable;
Packit Service 7770af
  bool has_interpolants;
Packit Service 7770af
};
Packit Service 7770af
Packit Service 7770af
namespace Sass {
Packit Service 7770af
Packit Service 7770af
  class Parser : public ParserState {
Packit Service 7770af
  public:
Packit Service 7770af
Packit Service 7770af
    enum Scope { Root, Mixin, Function, Media, Control, Properties, Rules };
Packit Service 7770af
Packit Service 7770af
    Context& ctx;
Packit Service 7770af
    std::vector<Block_Obj> block_stack;
Packit Service 7770af
    std::vector<Scope> stack;
Packit Service 7770af
    Media_Block_Ptr last_media_block;
Packit Service 7770af
    const char* source;
Packit Service 7770af
    const char* position;
Packit Service 7770af
    const char* end;
Packit Service 7770af
    Position before_token;
Packit Service 7770af
    Position after_token;
Packit Service 7770af
    ParserState pstate;
Packit Service 7770af
    int indentation;
Packit Service 7770af
Packit Service 7770af
Packit Service 7770af
    Token lexed;
Packit Service 7770af
Packit Service 7770af
    Parser(Context& ctx, const ParserState& pstate)
Packit Service 7770af
    : ParserState(pstate), ctx(ctx), block_stack(), stack(0), last_media_block(),
Packit Service 7770af
      source(0), position(0), end(0), before_token(pstate), after_token(pstate), pstate(pstate), indentation(0)
Packit Service 7770af
    { stack.push_back(Scope::Root); }
Packit Service 7770af
Packit Service 7770af
    // static Parser from_string(const std::string& src, Context& ctx, ParserState pstate = ParserState("[STRING]"));
Packit Service 7770af
    static Parser from_c_str(const char* src, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);
Packit Service 7770af
    static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);
Packit Service 7770af
    static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"), const char* source = 0);
Packit Service 7770af
    // special static parsers to convert strings into certain selectors
Packit Service 7770af
    static Selector_List_Obj parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0);
Packit Service 7770af
Packit Service 7770af
#ifdef __clang__
Packit Service 7770af
Packit Service 7770af
    // lex and peak uses the template parameter to branch on the action, which
Packit Service 7770af
    // triggers clangs tautological comparison on the single-comparison
Packit Service 7770af
    // branches. This is not a bug, just a merging of behaviour into
Packit Service 7770af
    // one function
Packit Service 7770af
Packit Service 7770af
#pragma clang diagnostic push
Packit Service 7770af
#pragma clang diagnostic ignored "-Wtautological-compare"
Packit Service 7770af
Packit Service 7770af
#endif
Packit Service 7770af
Packit Service 7770af
Packit Service 7770af
    // skip current token and next whitespace
Packit Service 7770af
    // moves ParserState right before next token
Packit Service 7770af
    void advanceToNextToken();
Packit Service 7770af
Packit Service 7770af
    bool peek_newline(const char* start = 0);
Packit Service 7770af
Packit Service 7770af
    // skip over spaces, tabs and line comments
Packit Service 7770af
    template <Prelexer::prelexer mx>
Packit Service 7770af
    const char* sneak(const char* start = 0)
Packit Service 7770af
    {
Packit Service 7770af
      using namespace Prelexer;
Packit Service 7770af
Packit Service 7770af
      // maybe use optional start position from arguments?
Packit Service 7770af
      const char* it_position = start ? start : position;
Packit Service 7770af
Packit Service 7770af
      // skip white-space?
Packit Service 7770af
      if (mx == spaces ||
Packit Service 7770af
          mx == no_spaces ||
Packit Service 7770af
          mx == css_comments ||
Packit Service 7770af
          mx == css_whitespace ||
Packit Service 7770af
          mx == optional_spaces ||
Packit Service 7770af
          mx == optional_css_comments ||
Packit Service 7770af
          mx == optional_css_whitespace
Packit Service 7770af
      ) {
Packit Service 7770af
        return it_position;
Packit Service 7770af
      }
Packit Service 7770af
Packit Service 7770af
      // skip over spaces, tabs and sass line comments
Packit Service 7770af
      const char* pos = optional_css_whitespace(it_position);
Packit Service 7770af
      // always return a valid position
Packit Service 7770af
      return pos ? pos : it_position;
Packit Service 7770af
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    // peek will only skip over space, tabs and line comment
Packit Service 7770af
    // return the position where the lexer match will occur
Packit Service 7770af
    template <Prelexer::prelexer mx>
Packit Service 7770af
    const char* match(const char* start = 0)
Packit Service 7770af
    {
Packit Service 7770af
      // match the given prelexer
Packit Service 7770af
      return mx(position);
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    // peek will only skip over space, tabs and line comment
Packit Service 7770af
    // return the position where the lexer match will occur
Packit Service 7770af
    template <Prelexer::prelexer mx>
Packit Service 7770af
    const char* peek(const char* start = 0)
Packit Service 7770af
    {
Packit Service 7770af
Packit Service 7770af
      // sneak up to the actual token we want to lex
Packit Service 7770af
      // this should skip over white-space if desired
Packit Service 7770af
      const char* it_before_token = sneak < mx >(start);
Packit Service 7770af
Packit Service 7770af
      // match the given prelexer
Packit Service 7770af
      const char* match = mx(it_before_token);
Packit Service 7770af
Packit Service 7770af
      // check if match is in valid range
Packit Service 7770af
      return match <= end ? match : 0;
Packit Service 7770af
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    // white-space handling is built into the lexer
Packit Service 7770af
    // this way you do not need to parse it yourself
Packit Service 7770af
    // some matchers don't accept certain white-space
Packit Service 7770af
    // we do not support start arg, since we manipulate
Packit Service 7770af
    // sourcemap offset and we modify the position pointer!
Packit Service 7770af
    // lex will only skip over space, tabs and line comment
Packit Service 7770af
    template <Prelexer::prelexer mx>
Packit Service 7770af
    const char* lex(bool lazy = true, bool force = false)
Packit Service 7770af
    {
Packit Service 7770af
Packit Service 7770af
      if (*position == 0) return 0;
Packit Service 7770af
Packit Service 7770af
      // position considered before lexed token
Packit Service 7770af
      // we can skip whitespace or comments for
Packit Service 7770af
      // lazy developers (but we need control)
Packit Service 7770af
      const char* it_before_token = position;
Packit Service 7770af
Packit Service 7770af
      // sneak up to the actual token we want to lex
Packit Service 7770af
      // this should skip over white-space if desired
Packit Service 7770af
      if (lazy) it_before_token = sneak < mx >(position);
Packit Service 7770af
Packit Service 7770af
      // now call matcher to get position after token
Packit Service 7770af
      const char* it_after_token = mx(it_before_token);
Packit Service 7770af
Packit Service 7770af
      // check if match is in valid range
Packit Service 7770af
      if (it_after_token > end) return 0;
Packit Service 7770af
Packit Service 7770af
      // maybe we want to update the parser state anyway?
Packit Service 7770af
      if (force == false) {
Packit Service 7770af
        // assertion that we got a valid match
Packit Service 7770af
        if (it_after_token == 0) return 0;
Packit Service 7770af
        // assertion that we actually lexed something
Packit Service 7770af
        if (it_after_token == it_before_token) return 0;
Packit Service 7770af
      }
Packit Service 7770af
Packit Service 7770af
      // create new lexed token object (holds the parse results)
Packit Service 7770af
      lexed = Token(position, it_before_token, it_after_token);
Packit Service 7770af
Packit Service 7770af
      // advance position (add whitespace before current token)
Packit Service 7770af
      before_token = after_token.add(position, it_before_token);
Packit Service 7770af
Packit Service 7770af
      // update after_token position for current token
Packit Service 7770af
      after_token.add(it_before_token, it_after_token);
Packit Service 7770af
Packit Service 7770af
      // ToDo: could probably do this incremetal on original object (API wants offset?)
Packit Service 7770af
      pstate = ParserState(path, source, lexed, before_token, after_token - before_token);
Packit Service 7770af
Packit Service 7770af
      // advance internal char iterator
Packit Service 7770af
      return position = it_after_token;
Packit Service 7770af
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    // lex_css skips over space, tabs, line and block comment
Packit Service 7770af
    // all block comments will be consumed and thrown away
Packit Service 7770af
    // source-map position will point to token after the comment
Packit Service 7770af
    template <Prelexer::prelexer mx>
Packit Service 7770af
    const char* lex_css()
Packit Service 7770af
    {
Packit Service 7770af
      // copy old token
Packit Service 7770af
      Token prev = lexed;
Packit Service 7770af
      // store previous pointer
Packit Service 7770af
      const char* oldpos = position;
Packit Service 7770af
      Position bt = before_token;
Packit Service 7770af
      Position at = after_token;
Packit Service 7770af
      ParserState op = pstate;
Packit Service 7770af
      // throw away comments
Packit Service 7770af
      // update srcmap position
Packit Service 7770af
      lex < Prelexer::css_comments >();
Packit Service 7770af
      // now lex a new token
Packit Service 7770af
      const char* pos = lex< mx >();
Packit Service 7770af
      // maybe restore prev state
Packit Service 7770af
      if (pos == 0) {
Packit Service 7770af
        pstate = op;
Packit Service 7770af
        lexed = prev;
Packit Service 7770af
        position = oldpos;
Packit Service 7770af
        after_token = at;
Packit Service 7770af
        before_token = bt;
Packit Service 7770af
      }
Packit Service 7770af
      // return match
Packit Service 7770af
      return pos;
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    // all block comments will be skipped and thrown away
Packit Service 7770af
    template <Prelexer::prelexer mx>
Packit Service 7770af
    const char* peek_css(const char* start = 0)
Packit Service 7770af
    {
Packit Service 7770af
      // now peek a token (skip comments first)
Packit Service 7770af
      return peek< mx >(peek < Prelexer::css_comments >(start));
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
#ifdef __clang__
Packit Service 7770af
Packit Service 7770af
#pragma clang diagnostic pop
Packit Service 7770af
Packit Service 7770af
#endif
Packit Service 7770af
Packit Service 7770af
    void error(std::string msg, Position pos);
Packit Service 7770af
    // generate message with given and expected sample
Packit Service 7770af
    // text before and in the middle are configurable
Packit Service 7770af
    void css_error(const std::string& msg,
Packit Service 7770af
                   const std::string& prefix = " after ",
Packit Service 7770af
                   const std::string& middle = ", was: ",
Packit Service 7770af
                   const bool trim = true);
Packit Service 7770af
    void read_bom();
Packit Service 7770af
Packit Service 7770af
    Block_Obj parse();
Packit Service 7770af
    Import_Obj parse_import();
Packit Service 7770af
    Definition_Obj parse_definition(Definition::Type which_type);
Packit Service 7770af
    Parameters_Obj parse_parameters();
Packit Service 7770af
    Parameter_Obj parse_parameter();
Packit Service 7770af
    Mixin_Call_Obj parse_include_directive();
Packit Service 7770af
    Arguments_Obj parse_arguments();
Packit Service 7770af
    Argument_Obj parse_argument();
Packit Service 7770af
    Assignment_Obj parse_assignment();
Packit Service 7770af
    Ruleset_Obj parse_ruleset(Lookahead lookahead);
Packit Service 7770af
    Selector_List_Obj parse_selector_list(bool chroot);
Packit Service 7770af
    Complex_Selector_Obj parse_complex_selector(bool chroot);
Packit Service 7770af
    Selector_Schema_Obj parse_selector_schema(const char* end_of_selector, bool chroot);
Packit Service 7770af
    Compound_Selector_Obj parse_compound_selector();
Packit Service 7770af
    Simple_Selector_Obj parse_simple_selector();
Packit Service 7770af
    Wrapped_Selector_Obj parse_negated_selector();
Packit Service 7770af
    Simple_Selector_Obj parse_pseudo_selector();
Packit Service 7770af
    Attribute_Selector_Obj parse_attribute_selector();
Packit Service 7770af
    Block_Obj parse_block(bool is_root = false);
Packit Service 7770af
    Block_Obj parse_css_block(bool is_root = false);
Packit Service 7770af
    bool parse_block_nodes(bool is_root = false);
Packit Service 7770af
    bool parse_block_node(bool is_root = false);
Packit Service 7770af
Packit Service 7770af
    bool parse_number_prefix();
Packit Service 7770af
    Declaration_Obj parse_declaration();
Packit Service 7770af
    Expression_Obj parse_map();
Packit Service 7770af
    Expression_Obj parse_list(bool delayed = false);
Packit Service 7770af
    Expression_Obj parse_comma_list(bool delayed = false);
Packit Service 7770af
    Expression_Obj parse_space_list();
Packit Service 7770af
    Expression_Obj parse_disjunction();
Packit Service 7770af
    Expression_Obj parse_conjunction();
Packit Service 7770af
    Expression_Obj parse_relation();
Packit Service 7770af
    Expression_Obj parse_expression();
Packit Service 7770af
    Expression_Obj parse_operators();
Packit Service 7770af
    Expression_Obj parse_factor();
Packit Service 7770af
    Expression_Obj parse_value();
Packit Service 7770af
    Function_Call_Obj parse_calc_function();
Packit Service 7770af
    Function_Call_Obj parse_function_call();
Packit Service 7770af
    Function_Call_Schema_Obj parse_function_call_schema();
Packit Service 7770af
    String_Obj parse_url_function_string();
Packit Service 7770af
    String_Obj parse_url_function_argument();
Packit Service 7770af
    String_Obj parse_interpolated_chunk(Token, bool constant = false);
Packit Service 7770af
    String_Obj parse_string();
Packit Service 7770af
    String_Constant_Obj parse_static_value();
Packit Service 7770af
    String_Obj parse_ie_property();
Packit Service 7770af
    String_Obj parse_ie_keyword_arg();
Packit Service 7770af
    String_Schema_Obj parse_value_schema(const char* stop);
Packit Service 7770af
    String_Obj parse_identifier_schema();
Packit Service 7770af
    If_Obj parse_if_directive(bool else_if = false);
Packit Service 7770af
    For_Obj parse_for_directive();
Packit Service 7770af
    Each_Obj parse_each_directive();
Packit Service 7770af
    While_Obj parse_while_directive();
Packit Service 7770af
    Return_Obj parse_return_directive();
Packit Service 7770af
    Content_Obj parse_content_directive();
Packit Service 7770af
    void parse_charset_directive();
Packit Service 7770af
    Media_Block_Obj parse_media_block();
Packit Service 7770af
    List_Obj parse_media_queries();
Packit Service 7770af
    Media_Query_Obj parse_media_query();
Packit Service 7770af
    Media_Query_Expression_Obj parse_media_expression();
Packit Service 7770af
    Supports_Block_Obj parse_supports_directive();
Packit Service 7770af
    Supports_Condition_Obj parse_supports_condition();
Packit Service 7770af
    Supports_Condition_Obj parse_supports_negation();
Packit Service 7770af
    Supports_Condition_Obj parse_supports_operator();
Packit Service 7770af
    Supports_Condition_Obj parse_supports_interpolation();
Packit Service 7770af
    Supports_Condition_Obj parse_supports_declaration();
Packit Service 7770af
    Supports_Condition_Obj parse_supports_condition_in_parens();
Packit Service 7770af
    At_Root_Block_Obj parse_at_root_block();
Packit Service 7770af
    At_Root_Query_Obj parse_at_root_query();
Packit Service 7770af
    String_Schema_Obj parse_almost_any_value();
Packit Service 7770af
    Directive_Obj parse_special_directive();
Packit Service 7770af
    Directive_Obj parse_prefixed_directive();
Packit Service 7770af
    Directive_Obj parse_directive();
Packit Service 7770af
    Warning_Obj parse_warning();
Packit Service 7770af
    Error_Obj parse_error();
Packit Service 7770af
    Debug_Obj parse_debug();
Packit Service 7770af
Packit Service 7770af
    // be more like ruby sass
Packit Service 7770af
    Expression_Obj lex_almost_any_value_token();
Packit Service 7770af
    Expression_Obj lex_almost_any_value_chars();
Packit Service 7770af
    Expression_Obj lex_interp_string();
Packit Service 7770af
    Expression_Obj lex_interp_uri();
Packit Service 7770af
    Expression_Obj lex_interpolation();
Packit Service 7770af
Packit Service 7770af
    // these will throw errors
Packit Service 7770af
    Token lex_variable();
Packit Service 7770af
    Token lex_identifier();
Packit Service 7770af
Packit Service 7770af
    void parse_block_comments();
Packit Service 7770af
Packit Service 7770af
    Lookahead lookahead_for_value(const char* start = 0);
Packit Service 7770af
    Lookahead lookahead_for_selector(const char* start = 0);
Packit Service 7770af
    Lookahead lookahead_for_include(const char* start = 0);
Packit Service 7770af
Packit Service 7770af
    Expression_Obj fold_operands(Expression_Obj base, std::vector<Expression_Obj>& operands, Operand op);
Packit Service 7770af
    Expression_Obj fold_operands(Expression_Obj base, std::vector<Expression_Obj>& operands, std::vector<Operand>& ops, size_t i = 0);
Packit Service 7770af
Packit Service 7770af
    void throw_syntax_error(std::string message, size_t ln = 0);
Packit Service 7770af
    void throw_read_error(std::string message, size_t ln = 0);
Packit Service 7770af
Packit Service 7770af
Packit Service 7770af
    template <Prelexer::prelexer open, Prelexer::prelexer close>
Packit Service 7770af
    Expression_Obj lex_interp()
Packit Service 7770af
    {
Packit Service 7770af
      if (lex < open >(false)) {
Packit Service 7770af
        String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
Packit Service 7770af
        // std::cerr << "LEX [[" << std::string(lexed) << "]]\n";
Packit Service 7770af
        schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));
Packit Service 7770af
        if (position[0] == '#' && position[1] == '{') {
Packit Service 7770af
          Expression_Obj itpl = lex_interpolation();
Packit Service 7770af
          if (!itpl.isNull()) schema->append(itpl);
Packit Service 7770af
          while (lex < close >(false)) {
Packit Service 7770af
            // std::cerr << "LEX [[" << std::string(lexed) << "]]\n";
Packit Service 7770af
            schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));
Packit Service 7770af
            if (position[0] == '#' && position[1] == '{') {
Packit Service 7770af
              Expression_Obj itpl = lex_interpolation();
Packit Service 7770af
              if (!itpl.isNull()) schema->append(itpl);
Packit Service 7770af
            } else {
Packit Service 7770af
              return schema;
Packit Service 7770af
            }
Packit Service 7770af
          }
Packit Service 7770af
        } else {
Packit Service 7770af
          return SASS_MEMORY_NEW(String_Constant, pstate, lexed);
Packit Service 7770af
        }
Packit Service 7770af
      }
Packit Service 7770af
      return 0;
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
  public:
Packit Service 7770af
    static Number_Ptr lexed_number(const ParserState& pstate, const std::string& parsed);
Packit Service 7770af
    static Number_Ptr lexed_dimension(const ParserState& pstate, const std::string& parsed);
Packit Service 7770af
    static Number_Ptr lexed_percentage(const ParserState& pstate, const std::string& parsed);
Packit Service 7770af
    static Expression_Ptr lexed_hex_color(const ParserState& pstate, const std::string& parsed);
Packit Service 7770af
  private:
Packit Service 7770af
    Number_Ptr lexed_number(const std::string& parsed) { return lexed_number(pstate, parsed); };
Packit Service 7770af
    Number_Ptr lexed_dimension(const std::string& parsed) { return lexed_dimension(pstate, parsed); };
Packit Service 7770af
    Number_Ptr lexed_percentage(const std::string& parsed) { return lexed_percentage(pstate, parsed); };
Packit Service 7770af
    Expression_Ptr lexed_hex_color(const std::string& parsed) { return lexed_hex_color(pstate, parsed); };
Packit Service 7770af
Packit Service 7770af
  };
Packit Service 7770af
Packit Service 7770af
  size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len);
Packit Service 7770af
}
Packit Service 7770af
Packit Service 7770af
#endif