#include #include #include #include #include "util.hpp" #include "position.hpp" #include "prelexer.hpp" #include "constants.hpp" namespace Sass { using namespace Constants; namespace Prelexer { using std::ptrdiff_t; // Matches zero characters (always succeeds without consuming input). /* not used anymore - remove? const char* epsilon(char *src) { return src; }*/ // Matches the empty string. /* not used anymore - remove? const char* empty(char *src) { return *src ? 0 : src; }*/ // Match any single character. const char* any_char(const char* src) { return *src ? src+1 : src; } // Match word boundary (look ahead) const char* word_boundary(const char* src) { return !*src || isspace(*src) || ispunct(*src) || !Sass::Util::isAscii(*src) ? src : 0 ; } // Match a single character satisfying the ctype predicates. const char* space(const char* src) { return std::isspace(*src) ? src+1 : 0; } const char* alpha(const char* src) { return std::isalpha(*src) || !Sass::Util::isAscii(*src) ? src+1 : 0; } const char* digit(const char* src) { return std::isdigit(*src) ? src+1 : 0; } const char* xdigit(const char* src) { return std::isxdigit(*src) ? src+1 : 0; } const char* alnum(const char* src) { return std::isalnum(*src) || !Sass::Util::isAscii(*src) ? src+1 : 0; } const char* punct(const char* src) { return std::ispunct(*src) ? src+1 : 0; } // Match multiple ctype characters. const char* spaces(const char* src) { return one_plus(src); } const char* alphas(const char* src) { return one_plus(src); } const char* digits(const char* src) { return one_plus(src); } const char* xdigits(const char* src) { return one_plus(src); } const char* alnums(const char* src) { return one_plus(src); } const char* puncts(const char* src) { return one_plus(src); } // Match a line comment. const char* line_comment(const char* src) { return to_endl(src); } // Match a line comment prefix. const char* line_comment_prefix(const char* src) { return exactly(src); } // Match a block comment. const char* block_comment(const char* src) { return sequence< optional_spaces, delimited_by >(src); } const char* block_comment_prefix(const char* src) { return exactly(src); } // Match either comment. const char* comment(const char* src) { return line_comment(src); } const char* wspaces(const char* src) { return alternatives< exactly<' '>, exactly<'\t'> >(src); } /* not used anymore - remove? const char* newline(const char* src) { return alternatives< exactly<'\n'>, sequence< exactly<'\r'>, exactly<'\n'> >, exactly<'\r'>, exactly<'\f'> >(src); }*/ /* not used anymore - remove? const char* whitespace(const char* src) { return spaces(src); }*/ /* not used anymore - remove? const char* escape(const char* src) { return sequence< exactly<'\\'>, any_char >(src); }*/ // Whitespace handling. const char* optional_spaces(const char* src) { return optional(src); } // const char* optional_comment(const char* src) { return optional(src); } const char* optional_spaces_and_comments(const char* src) { return zero_plus< alternatives >(src); } const char* spaces_and_comments(const char* src) { return one_plus< alternatives >(src); } const char* no_spaces(const char* src) { return negate< spaces >(src); } const char* backslash_something(const char* src) { return sequence< exactly<'\\'>, any_char >(src); } // Match CSS identifiers. const char* identifier(const char* src) { return sequence< optional< exactly<'-'> >, alternatives< alpha, exactly<'_'>, backslash_something >, zero_plus< alternatives< alnum, exactly<'-'>, exactly<'_'>, backslash_something > > >(src); } const char* identifier_fragment(const char* src) { return one_plus< alternatives< alnum, exactly<'-'>, exactly<'_'>, backslash_something > >(src); } // Match CSS selectors. /* not used anymore - remove? const char* sel_ident(const char* src) { return sequence< optional< alternatives< exactly<'-'>, exactly<'|'> > >, alternatives< alpha, exactly<'_'>, backslash_something, exactly<'|'> >, zero_plus< alternatives< alnum, exactly<'-'>, exactly<'_'>, exactly<'|'>, backslash_something > > >(src); }*/ // Match CSS css variables. const char* custom_property_name(const char* src) { return sequence< exactly<'-'>, exactly<'-'>, identifier >(src); } // Match number prefix ([\+\-]+) const char* number_prefix(const char* src) { return alternatives < exactly < '+' >, sequence < exactly < '-' >, optional_spaces_and_comments, exactly< '-' > > >(src); } // Match interpolant schemas const char* identifier_schema(const char* src) { // follows this pattern: (x*ix*)+ ... well, not quite return sequence< one_plus< sequence< zero_plus< alternatives< identifier, exactly<'-'> > >, interpolant, zero_plus< alternatives< identifier, number, exactly<'-'> > > > >, negate< exactly<'%'> > >(src); } // interpolants can be recursive/nested const char* interpolant(const char* src) { return recursive_scopes< exactly, exactly >(src); } // $re_squote = /'(?:$re_itplnt|\\.|[^'])*'/ const char* single_quoted_string(const char* src) { // match a single quoted string, while skipping interpolants return sequence < exactly <'\''>, zero_plus < alternatives < // skip all escaped chars first backslash_something, // skip interpolants interpolant, // skip non delimiters any_char_except < '\'' > > >, exactly <'\''> >(src); } // $re_dquote = /"(?:$re_itp|\\.|[^"])*"/ const char* double_quoted_string(const char* src) { // match a single quoted string, while skipping interpolants return sequence < exactly <'"'>, zero_plus < alternatives < // skip all escaped chars first backslash_something, // skip interpolants interpolant, // skip non delimiters any_char_except < '"' > > >, exactly <'"'> >(src); } // $re_quoted = /(?:$re_squote|$re_dquote)/ const char* quoted_string(const char* src) { // match a quoted string, while skipping interpolants return alternatives< single_quoted_string, double_quoted_string >(src); } const char* value_schema(const char* src) { // follows this pattern: ([xyz]*i[xyz]*)+ return one_plus< sequence< zero_plus< alternatives< identifier, percentage, dimension, hex, number, quoted_string > >, interpolant, zero_plus< alternatives< identifier, percentage, dimension, hex, number, quoted_string, exactly<'%'> > > > >(src); } /* not used anymore - remove? const char* filename_schema(const char* src) { return one_plus< sequence< zero_plus< alternatives< identifier, number, exactly<'.'>, exactly<'/'> > >, interpolant, zero_plus< alternatives< identifier, number, exactly<'.'>, exactly<'/'> > > > >(src); }*/ const char* filename(const char* src) { return one_plus< alternatives< identifier, number, exactly<'.'> > >(src); } // Match CSS '@' keywords. const char* at_keyword(const char* src) { return sequence, identifier>(src); } const char* import(const char* src) { return exactly(src); } const char* at_root(const char* src) { return exactly(src); } const char* with_directive(const char* src) { return exactly(src); } const char* without_directive(const char* src) { return exactly(src); } const char* media(const char* src) { return exactly(src); } const char* supports(const char* src) { return exactly(src); } /* not used anymore - remove? const char* keyframes(const char* src) { return sequence< exactly<'@'>, optional< vendor_prefix >, exactly< keyframes_kwd > >(src); } */ /* not used anymore - remove? const char* vendor_prefix(const char* src) { return alternatives< exactly< vendor_opera_kwd >, exactly< vendor_webkit_kwd >, exactly< vendor_mozilla_kwd >, exactly< vendor_ms_kwd >, exactly< vendor_khtml_kwd > >(src); } */ /* not used anymore - remove? const char* keyf(const char* src) { return one_plus< alternatives< to, from, percentage > >(src); } */ const char* mixin(const char* src) { return exactly(src); } const char* function(const char* src) { return exactly(src); } const char* return_directive(const char* src) { return exactly(src); } const char* include(const char* src) { return exactly(src); } const char* content(const char* src) { return exactly(src); } const char* extend(const char* src) { return sequence < exactly, word_boundary >(src); } const char* if_directive(const char* src) { return exactly(src); } const char* else_directive(const char* src) { return exactly(src); } const char* elseif_directive(const char* src) { return sequence< else_directive, optional_spaces_and_comments, exactly< if_after_else_kwd > >(src); } const char* for_directive(const char* src) { return exactly(src); } const char* from(const char* src) { return exactly(src); } const char* to(const char* src) { return exactly(src); } const char* through(const char* src) { return exactly(src); } const char* each_directive(const char* src) { return exactly(src); } const char* in(const char* src) { return exactly(src); } const char* while_directive(const char* src) { return exactly(src); } const char* name(const char* src) { return one_plus< alternatives< alnum, exactly<'-'>, exactly<'_'>, exactly<'\\'> > >(src); } const char* warn(const char* src) { return exactly(src); } const char* err(const char* src) { return exactly(src); } const char* dbg(const char* src) { return exactly(src); } /* not used anymore - remove? const char* directive(const char* src) { return sequence< exactly<'@'>, identifier >(src); } */ const char* null(const char* src) { return exactly(src); } // Match CSS type selectors const char* namespace_prefix(const char* src) { return sequence< optional< alternatives< identifier, exactly<'*'> > >, exactly<'|'> >(src); } const char* type_selector(const char* src) { return sequence< optional, identifier>(src); } const char* hyphens_and_identifier(const char* src) { return sequence< zero_plus< exactly< '-' > >, identifier >(src); } const char* hyphens_and_name(const char* src) { return sequence< zero_plus< exactly< '-' > >, name >(src); } const char* universal(const char* src) { return sequence< optional, exactly<'*'> >(src); } // Match CSS id names. const char* id_name(const char* src) { return sequence, name>(src); } // Match CSS class names. const char* class_name(const char* src) { return sequence, identifier>(src); } // Attribute name in an attribute selector. const char* attribute_name(const char* src) { return alternatives< sequence< optional, identifier>, identifier >(src); } // match placeholder selectors const char* placeholder(const char* src) { return sequence, identifier>(src); } // Match CSS numeric constants. const char* sign(const char* src) { return class_char(src); } const char* unsigned_number(const char* src) { return alternatives, exactly<'.'>, one_plus >, digits>(src); } const char* number(const char* src) { return sequence< optional, unsigned_number>(src); } const char* coefficient(const char* src) { return alternatives< sequence< optional, digits >, sign >(src); } const char* binomial(const char* src) { return sequence< optional, optional, exactly<'n'>, optional_spaces, sign, optional_spaces, digits >(src); } const char* percentage(const char* src) { return sequence< number, exactly<'%'> >(src); } /* not used anymore - remove? const char* em(const char* src) { return sequence< number, exactly >(src); } */ const char* dimension(const char* src) { return sequence >(src); } const char* hex(const char* src) { const char* p = sequence< exactly<'#'>, one_plus >(src); ptrdiff_t len = p - src; return (len != 4 && len != 7) ? 0 : p; } const char* hexa(const char* src) { const char* p = sequence< exactly<'#'>, one_plus >(src); ptrdiff_t len = p - src; return (len != 4 && len != 7 && len != 9) ? 0 : p; } const char* hex0(const char* src) { const char* p = sequence< exactly<'0'>, exactly<'x'>, one_plus >(src); ptrdiff_t len = p - src; return (len != 5 && len != 8) ? 0 : p; } /* no longer used - remove? const char* rgb_prefix(const char* src) { return exactly(src); }*/ // Match CSS uri specifiers. const char* uri_prefix(const char* src) { return exactly(src); } // TODO: rename the following two functions /* no longer used - remove? const char* uri(const char* src) { return sequence< exactly, optional, quoted_string, optional, exactly<')'> >(src); }*/ /* no longer used - remove? const char* url_value(const char* src) { return sequence< optional< sequence< identifier, exactly<':'> > >, // optional protocol one_plus< sequence< zero_plus< exactly<'/'> >, filename > >, // one or more folders and/or trailing filename optional< exactly<'/'> > >(src); }*/ /* no longer used - remove? const char* url_schema(const char* src) { return sequence< optional< sequence< identifier, exactly<':'> > >, // optional protocol filename_schema >(src); // optional trailing slash }*/ // Match CSS "!important" keyword. const char* important(const char* src) { return sequence< exactly<'!'>, optional_spaces_and_comments, exactly >(src); } // Match CSS "!optional" keyword. const char* optional(const char* src) { return sequence< exactly<'!'>, optional_spaces_and_comments, exactly >(src); } // Match Sass "!default" keyword. const char* default_flag(const char* src) { return sequence< exactly<'!'>, optional_spaces_and_comments, exactly >(src); } // Match Sass "!global" keyword. const char* global_flag(const char* src) { return sequence< exactly<'!'>, optional_spaces_and_comments, exactly >(src); } // Match CSS pseudo-class/element prefixes. const char* pseudo_prefix(const char* src) { return sequence< exactly<':'>, optional< exactly<':'> > >(src); } // Match CSS function call openers. const char* functional_schema(const char* src) { return sequence< identifier_schema, exactly<'('> >(src); } const char* functional(const char* src) { return sequence< identifier, exactly<'('> >(src); } // Match the CSS negation pseudo-class. const char* pseudo_not(const char* src) { return exactly< pseudo_not_kwd >(src); } // Match CSS 'odd' and 'even' keywords for functional pseudo-classes. const char* even(const char* src) { return exactly(src); } const char* odd(const char* src) { return exactly(src); } // Match CSS attribute-matching operators. const char* exact_match(const char* src) { return exactly<'='>(src); } const char* class_match(const char* src) { return exactly(src); } const char* dash_match(const char* src) { return exactly(src); } const char* prefix_match(const char* src) { return exactly(src); } const char* suffix_match(const char* src) { return exactly(src); } const char* substring_match(const char* src) { return exactly(src); } // Match CSS combinators. /* not used anymore - remove? const char* adjacent_to(const char* src) { return sequence< optional_spaces, exactly<'+'> >(src); } const char* precedes(const char* src) { return sequence< optional_spaces, exactly<'~'> >(src); } const char* parent_of(const char* src) { return sequence< optional_spaces, exactly<'>'> >(src); } const char* ancestor_of(const char* src) { return sequence< spaces, negate< exactly<'{'> > >(src); }*/ // Match SCSS variable names. const char* variable(const char* src) { return sequence, identifier>(src); } // Match Sass boolean keywords. const char* true_val(const char* src) { return exactly(src); } const char* false_val(const char* src) { return exactly(src); } const char* and_op(const char* src) { return exactly(src); } const char* or_op(const char* src) { return exactly(src); } const char* not_op(const char* src) { return exactly(src); } const char* eq_op(const char* src) { return exactly(src); } const char* neq_op(const char* src) { return exactly(src); } const char* gt_op(const char* src) { return exactly(src); } const char* gte_op(const char* src) { return exactly(src); } const char* lt_op(const char* src) { return exactly(src); } const char* lte_op(const char* src) { return exactly(src); } // match specific IE syntax const char* ie_progid(const char* src) { return sequence < exactly, exactly<':'>, alternatives< identifier_schema, identifier >, zero_plus< sequence< exactly<'.'>, alternatives< identifier_schema, identifier > > >, zero_plus < sequence< exactly<'('>, optional_spaces_and_comments, optional < sequence< alternatives< variable, identifier_schema, identifier >, optional_spaces_and_comments, exactly<'='>, optional_spaces_and_comments, alternatives< variable, identifier_schema, identifier, quoted_string, number, hexa >, zero_plus< sequence< optional_spaces_and_comments, exactly<','>, optional_spaces_and_comments, sequence< alternatives< variable, identifier_schema, identifier >, optional_spaces_and_comments, exactly<'='>, optional_spaces_and_comments, alternatives< variable, identifier_schema, identifier, quoted_string, number, hexa > > > > > >, optional_spaces_and_comments, exactly<')'> > > >(src); } const char* ie_expression(const char* src) { return sequence < exactly, delimited_by< '(', ')', true> >(src); } const char* ie_property(const char* src) { return alternatives < ie_expression, ie_progid >(src); } // const char* ie_args(const char* src) { // return sequence< alternatives< ie_keyword_arg, value_schema, quoted_string, interpolant, number, identifier, delimited_by< '(', ')', true> >, // zero_plus< sequence< optional_spaces_and_comments, exactly<','>, optional_spaces_and_comments, alternatives< ie_keyword_arg, value_schema, quoted_string, interpolant, number, identifier, delimited_by<'(', ')', true> > > > >(src); // } const char* ie_keyword_arg(const char* src) { return sequence< alternatives< variable, identifier_schema, identifier >, optional_spaces_and_comments, exactly<'='>, optional_spaces_and_comments, alternatives< variable, identifier_schema, identifier, quoted_string, number, hexa > >(src); } // Path matching functions. /* not used anymore - remove? const char* folder(const char* src) { return sequence< zero_plus< any_char_except<'/'> >, exactly<'/'> >(src); } const char* folders(const char* src) { return zero_plus< folder >(src); }*/ const char* chunk(const char* src) { char inside_str = 0; const char* p = src; size_t depth = 0; while (true) { if (!*p) { return 0; } else if (!inside_str && (*p == '"' || *p == '\'')) { inside_str = *p; } else if (*p == inside_str && *(p-1) != '\\') { inside_str = 0; } else if (*p == '(' && !inside_str) { ++depth; } else if (*p == ')' && !inside_str) { if (depth == 0) return p; else --depth; } ++p; } // unreachable return 0; } // follow the CSS spec more closely and see if this helps us scan URLs correctly /* not used anymore - remove? const char* NL(const char* src) { return alternatives< exactly<'\n'>, sequence< exactly<'\r'>, exactly<'\n'> >, exactly<'\r'>, exactly<'\f'> >(src); }*/ /* not used anymore - remove? const char* H(const char* src) { return std::isxdigit(*src) ? src+1 : 0; }*/ /* not used anymore - remove? const char* unicode(const char* src) { return sequence< exactly<'\\'>, between, optional< class_char > >(src); }*/ /* not used anymore - remove? const char* ESCAPE(const char* src) { return alternatives< unicode, class_char >(src); }*/ const char* url(const char* src) { return chunk(src); } const char* static_string(const char* src) { const char* pos = src; const char * s = quoted_string(pos); Token t(pos, s, Position(0, 0)); const unsigned int p = count_interval< interpolant >(t.begin, t.end); return (p == 0) ? t.end : 0; } const char* static_component(const char* src) { return alternatives< identifier, static_string, percentage, hex, number, sequence< exactly<'!'>, exactly > >(src); } const char* static_value(const char* src) { return sequence< static_component, zero_plus < sequence< alternatives< sequence< optional_spaces, alternatives< exactly < '/' >, exactly < ',' >, exactly < ' ' > >, optional_spaces >, spaces >, static_component > >, alternatives< exactly<';'>, exactly<'}'> > >(src); } } }