From d977f420671ee1005c4e48564fa357d15c3e3e8f Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sat, 23 Jan 2016 15:47:20 +0100 Subject: [PATCH 1/2] Preparations for directive refactoring --- src/ast.cpp | 4 +- src/ast.hpp | 16 +++---- src/ast_factory.hpp | 2 +- src/ast_fwd_decl.hpp | 4 +- src/cssize.cpp | 26 ++++++++---- src/cssize.hpp | 7 +++- src/debugger.hpp | 6 +-- src/eval.cpp | 8 ++-- src/eval.hpp | 2 +- src/expand.cpp | 8 ++-- src/expand.hpp | 2 +- src/extend.cpp | 2 +- src/extend.hpp | 2 +- src/functions.cpp | 2 +- src/inspect.cpp | 26 ++++++------ src/inspect.hpp | 4 +- src/lexer.cpp | 3 +- src/lexer.hpp | 19 +++++++++ src/operation.hpp | 8 ++-- src/output.cpp | 2 +- src/output.hpp | 2 +- src/parser.cpp | 83 ++++--------------------------------- src/parser.hpp | 4 +- src/prelexer.cpp | 79 ++++++++++++++++++++++++++++++++++- src/prelexer.hpp | 3 +- src/remove_placeholders.cpp | 2 +- src/remove_placeholders.hpp | 2 +- src/util.cpp | 8 ++-- 28 files changed, 191 insertions(+), 145 deletions(-) diff --git a/src/ast.cpp b/src/ast.cpp index 2c202f6b25..64a7b61368 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -60,7 +60,7 @@ namespace Sass { bool Compound_Selector::has_parent_ref() { for (Simple_Selector* s : *this) { - if (s->has_parent_ref()) return true; + if (s && s->has_parent_ref()) return true; } return false; } @@ -1293,7 +1293,7 @@ namespace Sass { bool Selector_List::has_parent_ref() { for (Complex_Selector* s : *this) { - if (s->has_parent_ref()) return true; + if (s && s->has_parent_ref()) return true; } return false; } diff --git a/src/ast.hpp b/src/ast.hpp index 78f5dfe9e7..892af02cfc 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -524,12 +524,12 @@ namespace Sass { // At-rules -- arbitrary directives beginning with "@" that may have an // optional statement block. /////////////////////////////////////////////////////////////////////// - class At_Rule : public Has_Block { + class Directive : public Has_Block { ADD_PROPERTY(std::string, keyword) ADD_PROPERTY(Selector*, selector) ADD_PROPERTY(Expression*, value) public: - At_Rule(ParserState pstate, std::string kwd, Selector* sel = 0, Block* b = 0, Expression* val = 0) + Directive(ParserState pstate, std::string kwd, Selector* sel = 0, Block* b = 0, Expression* val = 0) : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed { statement_type(DIRECTIVE); } bool bubbles() { return is_keyframes() || is_media(); } @@ -1696,13 +1696,13 @@ namespace Sass { ///////////////////////////////////////////////// // At root expressions (for use inside @at-root). ///////////////////////////////////////////////// - class At_Root_Expression : public Expression { + class At_Root_Query : public Expression { private: ADD_PROPERTY(String*, feature) ADD_PROPERTY(Expression*, value) ADD_PROPERTY(bool, is_interpolated) public: - At_Root_Expression(ParserState pstate, String* f = 0, Expression* v = 0, bool i = false) + At_Root_Query(ParserState pstate, String* f = 0, Expression* v = 0, bool i = false) : Expression(pstate), feature_(f), value_(v), is_interpolated_(i) { } bool exclude(std::string str) @@ -1739,9 +1739,9 @@ namespace Sass { // At-root. /////////// class At_Root_Block : public Has_Block { - ADD_PROPERTY(At_Root_Expression*, expression) + ADD_PROPERTY(At_Root_Query*, expression) public: - At_Root_Block(ParserState pstate, Block* b = 0, At_Root_Expression* e = 0) + At_Root_Block(ParserState pstate, Block* b = 0, At_Root_Query* e = 0) : Has_Block(pstate, b), expression_(e) { statement_type(ATROOT); } bool is_hoistable() { return true; } @@ -1749,7 +1749,7 @@ namespace Sass { bool exclude_node(Statement* s) { if (s->statement_type() == Statement::DIRECTIVE) { - return expression()->exclude(static_cast(s)->keyword().erase(0, 1)); + return expression()->exclude(static_cast(s)->keyword().erase(0, 1)); } if (s->statement_type() == Statement::MEDIA) { @@ -1763,7 +1763,7 @@ namespace Sass { { return expression()->exclude("supports"); } - if (static_cast(s)->is_keyframes()) + if (static_cast(s)->is_keyframes()) { return expression()->exclude("keyframes"); } diff --git a/src/ast_factory.hpp b/src/ast_factory.hpp index c2e1830ff8..9a0a9b68e4 100644 --- a/src/ast_factory.hpp +++ b/src/ast_factory.hpp @@ -17,7 +17,7 @@ namespace Sass { Supports_Query* new_Supports_Query(std::string p, size_t l, Supports_Query* f, Block* b); Media_Query* new_Media_Query(std::string p, size_t l, List* q, Block* b); At_Root_Block* new_At_Root_Block(std::string p, size_t l, Selector* sel, Block* b); - At_Rule* new_At_Rule(std::string p, size_t l, std::string kwd, Selector* sel, Block* b); + Directive* new_At_Rule(std::string p, size_t l, std::string kwd, Selector* sel, Block* b); Keyframe_Rule* new_Keyframe_Rule(std::string p, size_t l, Block* b); Declaration* new_Declaration(std::string p, size_t l, String* prop, List* vals); Assignment* new_Assignment(std::string p, size_t l, std::string var, Expression* val, bool guarded = false); diff --git a/src/ast_fwd_decl.hpp b/src/ast_fwd_decl.hpp index 7d4db19e55..9082ebed9d 100644 --- a/src/ast_fwd_decl.hpp +++ b/src/ast_fwd_decl.hpp @@ -15,7 +15,7 @@ namespace Sass { class Bubble; class Media_Block; class Supports_Block; - class At_Rule; + class Directive; class Keyframe_Rule; class At_Root_Block; class Declaration; @@ -62,7 +62,7 @@ namespace Sass { class Supports_Negation; class Supports_Declaration; class Supports_Interpolation; - class At_Root_Expression; + class At_Root_Query; class Null; class Parent_Selector; // parameters and arguments diff --git a/src/cssize.cpp b/src/cssize.cpp index d96344136d..5a563be887 100644 --- a/src/cssize.cpp +++ b/src/cssize.cpp @@ -13,14 +13,22 @@ namespace Sass { : ctx(ctx), block_stack(std::vector()), p_stack(std::vector()), + s_stack(std::vector()), backtrace(bt) - { } + { + s_stack.push_back(NULL); + } Statement* Cssize::parent() { return p_stack.size() ? p_stack.back() : block_stack.front(); } + Selector_List* Cssize::selector() + { + return s_stack.size() ? s_stack.back() : NULL; + } + Statement* Cssize::operator()(Block* b) { Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, b->pstate(), b->length(), b->is_root()); @@ -31,7 +39,7 @@ namespace Sass { return bb; } - Statement* Cssize::operator()(At_Rule* r) + Statement* Cssize::operator()(Directive* r) { if (!r->block() || !r->block()->length()) return r; @@ -41,7 +49,7 @@ namespace Sass { } p_stack.push_back(r); - At_Rule* rr = SASS_MEMORY_NEW(ctx.mem, At_Rule, + Directive* rr = SASS_MEMORY_NEW(ctx.mem, Directive, r->pstate(), r->keyword(), r->selector(), @@ -57,7 +65,7 @@ namespace Sass { else { s = static_cast(s)->node(); if (s->statement_type() != Statement::DIRECTIVE) directive_exists = false; - else directive_exists = (static_cast(s)->keyword() == rr->keyword()); + else directive_exists = (static_cast(s)->keyword() == rr->keyword()); } } @@ -65,7 +73,7 @@ namespace Sass { Block* result = SASS_MEMORY_NEW(ctx.mem, Block, rr->pstate()); if (!(directive_exists || rr->is_keyframes())) { - At_Rule* empty_node = static_cast(rr); + Directive* empty_node = static_cast(rr); empty_node->block(SASS_MEMORY_NEW(ctx.mem, Block, rr->block() ? rr->block()->pstate() : rr->pstate())); *result << empty_node; } @@ -93,12 +101,14 @@ namespace Sass { Statement* Cssize::operator()(Ruleset* r) { p_stack.push_back(r); + s_stack.push_back(dynamic_cast(r->selector())); Ruleset* rr = SASS_MEMORY_NEW(ctx.mem, Ruleset, r->pstate(), r->selector(), r->block()->perform(this)->block()); rr->is_root(r->is_root()); // rr->tabs(r->block()->tabs()); + s_stack.pop_back(); p_stack.pop_back(); if (!rr->block()) { @@ -214,7 +224,7 @@ namespace Sass { return bubble(m); } - Statement* Cssize::bubble(At_Rule* m) + Statement* Cssize::bubble(Directive* m) { Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, this->parent()->pstate()); Has_Block* new_rule = static_cast(shallow_copy(this->parent())); @@ -228,7 +238,7 @@ namespace Sass { Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block() ? m->block()->pstate() : m->pstate()); *wrapper_block << new_rule; - At_Rule* mm = SASS_MEMORY_NEW(ctx.mem, At_Rule, + Directive* mm = SASS_MEMORY_NEW(ctx.mem, Directive, m->pstate(), m->keyword(), m->selector(), @@ -375,7 +385,7 @@ namespace Sass { case Statement::BUBBLE: return SASS_MEMORY_NEW(ctx.mem, Bubble, *static_cast(s)); case Statement::DIRECTIVE: - return SASS_MEMORY_NEW(ctx.mem, At_Rule, *static_cast(s)); + return SASS_MEMORY_NEW(ctx.mem, Directive, *static_cast(s)); case Statement::SUPPORTS: return SASS_MEMORY_NEW(ctx.mem, Supports_Block, *static_cast(s)); case Statement::ATROOT: diff --git a/src/cssize.hpp b/src/cssize.hpp index 9d5a34ad5d..2ad9fcf9c8 100644 --- a/src/cssize.hpp +++ b/src/cssize.hpp @@ -16,6 +16,7 @@ namespace Sass { Context& ctx; std::vector block_stack; std::vector p_stack; + std::vector s_stack; Backtrace* backtrace; Statement* fallback_impl(AST_Node* n); @@ -24,6 +25,8 @@ namespace Sass { Cssize(Context&, Backtrace*); ~Cssize() { } + Selector_List* selector(); + Statement* operator()(Block*); Statement* operator()(Ruleset*); // Statement* operator()(Propset*); @@ -31,7 +34,7 @@ namespace Sass { Statement* operator()(Media_Block*); Statement* operator()(Supports_Block*); Statement* operator()(At_Root_Block*); - Statement* operator()(At_Rule*); + Statement* operator()(Directive*); Statement* operator()(Keyframe_Rule*); // Statement* operator()(Declaration*); // Statement* operator()(Assignment*); @@ -53,7 +56,7 @@ namespace Sass { Statement* parent(); std::vector> slice_by_bubble(Statement*); - Statement* bubble(At_Rule*); + Statement* bubble(Directive*); Statement* bubble(At_Root_Block*); Statement* bubble(Media_Block*); Statement* bubble(Supports_Block*); diff --git a/src/debugger.hpp b/src/debugger.hpp index 28bcd47593..15170f4561 100644 --- a/src/debugger.hpp +++ b/src/debugger.hpp @@ -388,9 +388,9 @@ inline void debug_ast(AST_Node* node, std::string ind, Env* env) std::cerr << " " << has_block->tabs() << std::endl; if (has_block->selector()) debug_ast(has_block->selector(), ind + "@"); if (has_block->block()) for(auto i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); } - } else if (dynamic_cast(node)) { - At_Rule* block = dynamic_cast(node); - std::cerr << ind << "At_Rule " << block; + } else if (dynamic_cast(node)) { + Directive* block = dynamic_cast(node); + std::cerr << ind << "Directive " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << block->keyword() << "] " << block->tabs() << std::endl; debug_ast(block->selector(), ind + "~", env); diff --git a/src/eval.cpp b/src/eval.cpp index 29b8e71bdb..923818ffdb 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -1280,17 +1280,16 @@ namespace Sass { return cc; } - Expression* Eval::operator()(At_Root_Expression* e) + Expression* Eval::operator()(At_Root_Query* e) { Expression* feature = e->feature(); feature = (feature ? feature->perform(this) : 0); Expression* value = e->value(); value = (value ? value->perform(this) : 0); - Expression* ee = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression, + Expression* ee = SASS_MEMORY_NEW(ctx.mem, At_Root_Query, e->pstate(), static_cast(feature), - value, - e->is_interpolated()); + value); return ee; } @@ -1682,6 +1681,7 @@ namespace Sass { { std::vector rv; Selector_List* sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, s->pstate()); + sl->is_optional(s->is_optional()); sl->media_block(s->media_block()); sl->is_optional(s->is_optional()); for (size_t i = 0, iL = s->length(); i < iL; ++i) { diff --git a/src/eval.hpp b/src/eval.hpp index 08b8724a70..5ba9f8a34a 100644 --- a/src/eval.hpp +++ b/src/eval.hpp @@ -57,7 +57,7 @@ namespace Sass { // Expression* operator()(Selector_List*); Expression* operator()(Media_Query*); Expression* operator()(Media_Query_Expression*); - Expression* operator()(At_Root_Expression*); + Expression* operator()(At_Root_Query*); Expression* operator()(Supports_Operator*); Expression* operator()(Supports_Negation*); Expression* operator()(Supports_Declaration*); diff --git a/src/expand.cpp b/src/expand.cpp index 9ae56f8e82..2515483bb1 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -222,17 +222,17 @@ namespace Sass { // if (ab) ab->is_root(true); Expression* ae = a->expression(); if (ae) ae = ae->perform(&eval); - else ae = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression, a->pstate()); + else ae = SASS_MEMORY_NEW(ctx.mem, At_Root_Query, a->pstate()); Block* bb = ab ? ab->perform(this)->block() : 0; At_Root_Block* aa = SASS_MEMORY_NEW(ctx.mem, At_Root_Block, a->pstate(), bb, - static_cast(ae)); + static_cast(ae)); // aa->block()->is_root(true); return aa; } - Statement* Expand::operator()(At_Rule* a) + Statement* Expand::operator()(Directive* a) { LOCAL_FLAG(in_keyframes, a->is_keyframes()); Block* ab = a->block(); @@ -243,7 +243,7 @@ namespace Sass { if (as) as = dynamic_cast(as->perform(&eval)); selector_stack.pop_back(); Block* bb = ab ? ab->perform(this)->block() : 0; - At_Rule* aa = SASS_MEMORY_NEW(ctx.mem, At_Rule, + Directive* aa = SASS_MEMORY_NEW(ctx.mem, Directive, a->pstate(), a->keyword(), as, diff --git a/src/expand.hpp b/src/expand.hpp index dc22546115..b787193c6d 100644 --- a/src/expand.hpp +++ b/src/expand.hpp @@ -52,7 +52,7 @@ namespace Sass { Statement* operator()(Media_Block*); Statement* operator()(Supports_Block*); Statement* operator()(At_Root_Block*); - Statement* operator()(At_Rule*); + Statement* operator()(Directive*); Statement* operator()(Declaration*); Statement* operator()(Assignment*); Statement* operator()(Import*); diff --git a/src/extend.cpp b/src/extend.cpp index 604f6af12b..bd972ba534 100644 --- a/src/extend.cpp +++ b/src/extend.cpp @@ -2069,7 +2069,7 @@ namespace Sass { pMediaBlock->block()->perform(this); } - void Extend::operator()(At_Rule* a) + void Extend::operator()(Directive* a) { // Selector_List* ls = dynamic_cast(a->selector()); // selector_stack.push_back(ls); diff --git a/src/extend.hpp b/src/extend.hpp index 7061b3fada..0ace05a72c 100644 --- a/src/extend.hpp +++ b/src/extend.hpp @@ -31,7 +31,7 @@ namespace Sass { void operator()(Ruleset*); void operator()(Supports_Block*); void operator()(Media_Block*); - void operator()(At_Rule*); + void operator()(Directive*); template void fallback(U x) { return fallback_impl(x); } diff --git a/src/functions.cpp b/src/functions.cpp index 8078255611..1fb00db8f2 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -1822,7 +1822,7 @@ namespace Sass { } // Cannot be a Universal selector - Type_Selector* pType = dynamic_cast(base->head()->first()); + Type_Selector* pType = dynamic_cast(childSeq->head()->first()); if(pType && pType->name() == "*") { std::string msg("Can't append `"); msg += childSeq->to_string(); diff --git a/src/inspect.cpp b/src/inspect.cpp index 1b9f55dd7c..55a3b12cc2 100644 --- a/src/inspect.cpp +++ b/src/inspect.cpp @@ -96,7 +96,7 @@ namespace Sass { at_root_block->block()->perform(this); } - void Inspect::operator()(At_Rule* at_rule) + void Inspect::operator()(Directive* at_rule) { append_indentation(); append_token(at_rule->keyword(), at_rule); @@ -812,20 +812,15 @@ namespace Sass { } } - void Inspect::operator()(At_Root_Expression* ae) + void Inspect::operator()(At_Root_Query* ae) { - if (ae->is_interpolated()) { - ae->feature()->perform(this); - } - else { - append_string("("); - ae->feature()->perform(this); - if (ae->value()) { - append_colon_separator(); - ae->value()->perform(this); - } - append_string(")"); + append_string("("); + ae->feature()->perform(this); + if (ae->value()) { + append_colon_separator(); + ae->value()->perform(this); } + append_string(")"); } void Inspect::operator()(Null* n) @@ -980,6 +975,11 @@ namespace Sass { Complex_Selector* tail = c->tail(); Complex_Selector::Combinator comb = c->combinator(); + if (comb == Complex_Selector::ANCESTOR_OF && (!head || head->empty())) { + if (tail) tail->perform(this); + return; + } + if (c->has_line_feed()) { if (!(c->has_parent_ref())) { append_optional_linefeed(); diff --git a/src/inspect.hpp b/src/inspect.hpp index 873faf40a5..fdbd96e5bf 100644 --- a/src/inspect.hpp +++ b/src/inspect.hpp @@ -28,7 +28,7 @@ namespace Sass { virtual void operator()(Supports_Block*); virtual void operator()(Media_Block*); virtual void operator()(At_Root_Block*); - virtual void operator()(At_Rule*); + virtual void operator()(Directive*); virtual void operator()(Keyframe_Rule*); virtual void operator()(Declaration*); virtual void operator()(Assignment*); @@ -72,7 +72,7 @@ namespace Sass { virtual void operator()(Supports_Interpolation*); virtual void operator()(Media_Query*); virtual void operator()(Media_Query_Expression*); - virtual void operator()(At_Root_Expression*); + virtual void operator()(At_Root_Query*); virtual void operator()(Null*); virtual void operator()(Parent_Selector* p); // parameters and arguments diff --git a/src/lexer.cpp b/src/lexer.cpp index b55d3e2497..f9ae07c7c4 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -91,7 +91,8 @@ namespace Sass { // valid in a uri (copied from Ruby Sass) bool is_uri_character(const char& chr) { - return unsigned(chr) > 41 && unsigned(chr) < 127; + return (unsigned(chr) > 41 && unsigned(chr) < 127) || + unsigned(chr) == ':' || unsigned(chr) == '/'; } // check if char is within a reduced ascii range diff --git a/src/lexer.hpp b/src/lexer.hpp index c8c84adf16..5356c4f9ea 100644 --- a/src/lexer.hpp +++ b/src/lexer.hpp @@ -144,6 +144,25 @@ namespace Sass { return p == src ? 0 : p; } + // Match for members of char class. + // Regex equivalent: /[^axy]/ + template + const char* neg_class_char(const char* src) { + if (*src == 0) return 0; + const char* cc = neg_char_class; + while (*cc && *src != *cc) ++cc; + return *cc ? 0 : src + 1; + } + + // Match for members of char class. + // Regex equivalent: /[^axy]+/ + template + const char* neg_class_chars(const char* src) { + const char* p = src; + while (neg_class_char(p)) ++p; + return p == src ? 0 : p; + } + // Match all except the supplied one. // Regex equivalent: /[^x]/ template diff --git a/src/operation.hpp b/src/operation.hpp index 86d61f9928..74121b1b24 100644 --- a/src/operation.hpp +++ b/src/operation.hpp @@ -18,7 +18,7 @@ namespace Sass { virtual T operator()(Supports_Block* x) = 0; virtual T operator()(Media_Block* x) = 0; virtual T operator()(At_Root_Block* x) = 0; - virtual T operator()(At_Rule* x) = 0; + virtual T operator()(Directive* x) = 0; virtual T operator()(Keyframe_Rule* x) = 0; virtual T operator()(Declaration* x) = 0; virtual T operator()(Assignment* x) = 0; @@ -61,7 +61,7 @@ namespace Sass { virtual T operator()(Supports_Interpolation* x) = 0; virtual T operator()(Media_Query* x) = 0; virtual T operator()(Media_Query_Expression* x) = 0; - virtual T operator()(At_Root_Expression* x) = 0; + virtual T operator()(At_Root_Query* x) = 0; virtual T operator()(Null* x) = 0; virtual T operator()(Parent_Selector* x) = 0; // parameters and arguments @@ -99,7 +99,7 @@ namespace Sass { T operator()(Supports_Block* x) { return static_cast(this)->fallback(x); } T operator()(Media_Block* x) { return static_cast(this)->fallback(x); } T operator()(At_Root_Block* x) { return static_cast(this)->fallback(x); } - T operator()(At_Rule* x) { return static_cast(this)->fallback(x); } + T operator()(Directive* x) { return static_cast(this)->fallback(x); } T operator()(Keyframe_Rule* x) { return static_cast(this)->fallback(x); } T operator()(Declaration* x) { return static_cast(this)->fallback(x); } T operator()(Assignment* x) { return static_cast(this)->fallback(x); } @@ -142,7 +142,7 @@ namespace Sass { T operator()(Supports_Interpolation* x) { return static_cast(this)->fallback(x); } T operator()(Media_Query* x) { return static_cast(this)->fallback(x); } T operator()(Media_Query_Expression* x) { return static_cast(this)->fallback(x); } - T operator()(At_Root_Expression* x) { return static_cast(this)->fallback(x); } + T operator()(At_Root_Query* x) { return static_cast(this)->fallback(x); } T operator()(Null* x) { return static_cast(this)->fallback(x); } T operator()(Parent_Selector* x) { return static_cast(this)->fallback(x); } // parameters and arguments diff --git a/src/output.cpp b/src/output.cpp index 70fb998a5b..71f7e465ef 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -310,7 +310,7 @@ namespace Sass { append_scope_closer(); } - void Output::operator()(At_Rule* a) + void Output::operator()(Directive* a) { std::string kwd = a->keyword(); Selector* s = a->selector(); diff --git a/src/output.hpp b/src/output.hpp index 8dd150b59d..a6bad52f1d 100644 --- a/src/output.hpp +++ b/src/output.hpp @@ -38,7 +38,7 @@ namespace Sass { // virtual void operator()(Propset*); virtual void operator()(Supports_Block*); virtual void operator()(Media_Block*); - virtual void operator()(At_Rule*); + virtual void operator()(Directive*); virtual void operator()(Keyframe_Rule*); virtual void operator()(Import*); virtual void operator()(Comment*); diff --git a/src/parser.cpp b/src/parser.cpp index f75ae23998..1d99040ef1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -250,7 +250,7 @@ namespace Sass { else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); } // generic at keyword (keep last) - else if (lex< at_keyword >(true)) { (*block) << parse_at_rule(); } + else if (lex< re_special_directive >(true)) { (*block) << parse_special_directive(); } else if (block->is_root()) { lex< css_whitespace >(); @@ -2140,11 +2140,11 @@ namespace Sass { { ParserState at_source_position = pstate; Block* body = 0; - At_Root_Expression* expr = 0; + At_Root_Query* expr = 0; Lookahead lookahead_result; LOCAL_FLAG(in_at_root, true); if (lex< exactly<'('> >()) { - expr = parse_at_root_expression(); + expr = parse_at_root_query(); } if (peek < exactly<'{'> >()) { body = parse_block(true); @@ -2159,7 +2159,7 @@ namespace Sass { return at_root; } - At_Root_Expression* Parser::parse_at_root_expression() + At_Root_Query* Parser::parse_at_root_query() { if (peek< exactly<')'> >()) error("at-root feature required in at-root expression", pstate); @@ -2175,7 +2175,7 @@ namespace Sass { } else *value << declaration->value(); - At_Root_Expression* cond = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression, + At_Root_Query* cond = SASS_MEMORY_NEW(ctx.mem, At_Root_Query, declaration->pstate(), declaration->property(), value); @@ -2183,13 +2183,13 @@ namespace Sass { return cond; } - At_Rule* Parser::parse_at_rule() + Directive* Parser::parse_special_directive() { std::string kwd(lexed); if (lexed == "@else") error("Invalid CSS: @else must come after @if", pstate); - At_Rule* at_rule = SASS_MEMORY_NEW(ctx.mem, At_Rule, pstate, kwd); + Directive* at_rule = SASS_MEMORY_NEW(ctx.mem, Directive, pstate, kwd); Lookahead lookahead = lookahead_for_include(position); if (lookahead.found && !lookahead.has_interpolants) { at_rule->selector(parse_selector_list(true)); @@ -2266,72 +2266,7 @@ namespace Sass { rv.error = p; if (const char* q = peek < - alternatives < - // partial BEM selector - sequence < - ampersand, - one_plus < - exactly < '-' > - >, - word_boundary - >, - // main selector matching - one_plus < - alternatives < - // consume whitespace and comments - spaces, block_comment, line_comment, - // match `/deep/` selector (pass-trough) - // there is no functionality for it yet - schema_reference_combinator, - // match selector ops /[*&%,()\[\]]/ - class_char < selector_lookahead_ops >, - // match selector combinators /[>+~]/ - class_char < selector_combinator_ops >, - // match attribute compare operators - alternatives < - exact_match, class_match, dash_match, - prefix_match, suffix_match, substring_match - >, - // main selector match - sequence < - // allow namespace prefix - optional < namespace_schema >, - // modifiers prefixes - alternatives < - sequence < - exactly <'#'>, - // not for interpolation - negate < exactly <'{'> > - >, - // class match - exactly <'.'>, - // single or double colon - optional < pseudo_prefix > - >, - // accept hyphens in token - one_plus < sequence < - // can start with hyphens - zero_plus < exactly<'-'> >, - // now the main token - alternatives < - kwd_optional, - exactly <'*'>, - quoted_string, - interpolant, - identifier, - variable, - percentage, - binomial, - dimension, - alnum - > - > >, - // can also end with hyphens - zero_plus < exactly<'-'> > - > - > - > - > + re_selector_list >(p) ) { while (p < q) { @@ -2364,7 +2299,7 @@ namespace Sass { } // EO lookahead_for_selector - // used in parse_block_nodes and parse_at_rule + // used in parse_block_nodes and parse_special_directive // ToDo: actual usage is still not really clear to me? Lookahead Parser::lookahead_for_include(const char* start) { diff --git a/src/parser.hpp b/src/parser.hpp index 6cf9e2cf53..db4dc84f9b 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -298,8 +298,8 @@ namespace Sass { Supports_Condition* parse_supports_declaration(); Supports_Condition* parse_supports_condition_in_parens(); At_Root_Block* parse_at_root_block(); - At_Root_Expression* parse_at_root_expression(); - At_Rule* parse_at_rule(); + At_Root_Query* parse_at_root_query(); + Directive* parse_special_directive(); Warning* parse_warning(); Error* parse_error(); Debug* parse_debug(); diff --git a/src/prelexer.cpp b/src/prelexer.cpp index dc6bf0719b..9c4ff81e7d 100644 --- a/src/prelexer.cpp +++ b/src/prelexer.cpp @@ -366,7 +366,7 @@ namespace Sass { } // Match CSS '@' keywords. - const char* at_keyword(const char* src) { + const char* re_special_directive(const char* src) { return sequence, identifier>(src); } @@ -1183,6 +1183,83 @@ namespace Sass { >(src); } + const char* re_selector_list(const char* src) { + return alternatives < + // partial bem selector + sequence < + ampersand, + one_plus < + exactly < '-' > + >, + word_boundary, + optional_spaces + >, + // main selector matching + one_plus < + alternatives < + // consume whitespace and comments + spaces, block_comment, line_comment, + // match `/deep/` selector (pass-trough) + // there is no functionality for it yet + schema_reference_combinator, + // match selector ops /[*&%,\[\]]/ + class_char < selector_lookahead_ops >, + // match selector combinators /[>+~]/ + class_char < selector_combinator_ops >, + // match attribute compare operators + sequence < + exactly <'('>, + optional_spaces, + optional , + optional_spaces, + exactly <')'> + >, + alternatives < + exact_match, class_match, dash_match, + prefix_match, suffix_match, substring_match + >, + // main selector match + sequence < + // allow namespace prefix + optional < namespace_schema >, + // modifiers prefixes + alternatives < + sequence < + exactly <'#'>, + // not for interpolation + negate < exactly <'{'> > + >, + // class match + exactly <'.'>, + // single or double colon + optional < pseudo_prefix > + >, + // accept hypens in token + one_plus < sequence < + // can start with hyphens + zero_plus < exactly<'-'> >, + // now the main token + alternatives < + kwd_optional, + exactly <'*'>, + quoted_string, + interpolant, + identifier, + variable, + percentage, + binomial, + dimension, + alnum + > + > >, + // can also end with hyphens + zero_plus < exactly<'-'> > + > + > + > + >(src); + } + const char* type_selector(const char* src) { return sequence< optional, identifier>(src); } diff --git a/src/prelexer.hpp b/src/prelexer.hpp index e1cd7d7142..13bc2e21dd 100644 --- a/src/prelexer.hpp +++ b/src/prelexer.hpp @@ -217,7 +217,7 @@ namespace Sass { // const char* url_value(const char* src); const char* vendor_prefix(const char* src); // Match CSS '@' keywords. - const char* at_keyword(const char* src); + const char* re_special_directive(const char* src); const char* kwd_import(const char* src); const char* kwd_at_root(const char* src); const char* kwd_with_directive(const char* src); @@ -261,6 +261,7 @@ namespace Sass { const char* kwd_null(const char* src); + const char* re_selector_list(const char* src); const char* re_type_selector(const char* src); const char* re_static_expression(const char* src); diff --git a/src/remove_placeholders.cpp b/src/remove_placeholders.cpp index 40312a9794..3f9dbd7758 100644 --- a/src/remove_placeholders.cpp +++ b/src/remove_placeholders.cpp @@ -74,7 +74,7 @@ namespace Sass { operator()(m->block()); } - void Remove_Placeholders::operator()(At_Rule* a) { + void Remove_Placeholders::operator()(Directive* a) { if (a->block()) a->block()->perform(this); } diff --git a/src/remove_placeholders.hpp b/src/remove_placeholders.hpp index 762dd2344c..5825e8c940 100644 --- a/src/remove_placeholders.hpp +++ b/src/remove_placeholders.hpp @@ -28,7 +28,7 @@ namespace Sass { void operator()(Ruleset*); void operator()(Media_Block*); void operator()(Supports_Block*); - void operator()(At_Rule*); + void operator()(Directive*); template void fallback(U x) { return fallback_impl(x); } diff --git a/src/util.cpp b/src/util.cpp index 0b6b079488..426e10d079 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -461,7 +461,7 @@ namespace Sass { bool hasPrintableChildBlocks = false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; - if (dynamic_cast(stm)) { + if (dynamic_cast(stm)) { return true; } else if (dynamic_cast(stm)) { Block* pChildBlock = ((Has_Block*)stm)->block(); @@ -528,7 +528,7 @@ namespace Sass { // is NULL, then that means there was never a wrapping selector and it is printable (think of a top level media block with // a declaration in it). } - else if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) { + else if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(Directive)) { hasDeclarations = true; } else if (dynamic_cast(stm)) { @@ -553,7 +553,7 @@ namespace Sass { if (b == 0) return false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; - if (typeid(*stm) == typeid(At_Rule)) return true; + if (typeid(*stm) == typeid(Directive)) return true; else if (typeid(*stm) == typeid(Declaration)) return true; else if (typeid(*stm) == typeid(Comment)) { Comment* c = (Comment*) stm; @@ -607,7 +607,7 @@ namespace Sass { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; - if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) { + if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(Directive)) { return true; } else if (typeid(*stm) == typeid(Comment)) { From 83d401b19ae6dc61968aaf833513594c57e05a6c Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Fri, 15 Apr 2016 15:23:33 +0200 Subject: [PATCH 2/2] Refactor directive (i.e. At_Rule) parsing --- .gitignore | 3 + src/ast.cpp | 79 +++++++++- src/ast.hpp | 44 ++---- src/constants.cpp | 2 + src/constants.hpp | 3 + src/eval.cpp | 6 +- src/parser.cpp | 188 +++++++++++++++++++++-- src/parser.hpp | 40 +++++ src/prelexer.cpp | 371 +++++++++++++++++++++++++++++++++++++++++++++- src/prelexer.hpp | 15 +- 10 files changed, 704 insertions(+), 47 deletions(-) diff --git a/.gitignore b/.gitignore index f8a9d8f934..af37864181 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # Miscellaneous stuff +/sassc +/sass-spec + VERSION .DS_Store .sass-cache diff --git a/src/ast.cpp b/src/ast.cpp index 64a7b61368..b0f05ab9d8 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -7,8 +7,11 @@ #include "color_maps.hpp" #include #include -#include #include +#include +#include +#include +#include namespace Sass { @@ -25,6 +28,80 @@ namespace Sass { dynamic_cast(cond); } + std::string & str_ltrim(std::string & str) + { + auto it2 = std::find_if( str.begin() , str.end() , [](char ch){ return !std::isspace(ch , std::locale::classic() ) ; } ); + str.erase( str.begin() , it2); + return str; + } + + std::string & str_rtrim(std::string & str) + { + auto it1 = std::find_if( str.rbegin() , str.rend() , [](char ch){ return !std::isspace(ch , std::locale::classic() ) ; } ); + str.erase( it1.base() , str.end() ); + return str; + } + + void String_Constant::rtrim() + { + value_ = str_rtrim(value_); + } + void String_Constant::ltrim() + { + value_ = str_ltrim(value_); + } + void String_Constant::trim() + { + rtrim(); + ltrim(); + } + + void String_Schema::rtrim() + { + if (!empty()) { + if (String* str = dynamic_cast(last())) str->rtrim(); + } + } + void String_Schema::ltrim() + { + if (!empty()) { + if (String* str = dynamic_cast(first())) str->ltrim(); + } + } + void String_Schema::trim() + { + rtrim(); + ltrim(); + } + + bool At_Root_Query::exclude(std::string str) + { + bool with = feature() && unquote(feature()->to_string()).compare("with") == 0; + List* l = static_cast(value()); + std::string v; + + if (with) + { + if (!l || l->length() == 0) return str.compare("rule") != 0; + for (size_t i = 0, L = l->length(); i < L; ++i) + { + v = unquote((*l)[i]->to_string()); + if (v.compare("all") == 0 || v == str) return false; + } + return true; + } + else + { + if (!l || !l->length()) return str.compare("rule") == 0; + for (size_t i = 0, L = l->length(); i < L; ++i) + { + v = unquote((*l)[i]->to_string()); + if (v.compare("all") == 0 || v == str) return true; + } + return false; + } + } + void AST_Node::update_pstate(const ParserState& pstate) { pstate_.offset += pstate - pstate_ + pstate.offset; diff --git a/src/ast.hpp b/src/ast.hpp index 892af02cfc..269b31fe0b 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -1471,6 +1471,9 @@ namespace Sass { { concrete_type(STRING); } static std::string type_name() { return "string"; } virtual ~String() = 0; + virtual void rtrim() = 0; + virtual void ltrim() = 0; + virtual void trim() = 0; virtual bool operator==(const Expression& rhs) const = 0; ATTACH_OPERATIONS() }; @@ -1499,6 +1502,9 @@ namespace Sass { } return false; } + virtual void rtrim(); + virtual void ltrim(); + virtual void trim(); virtual size_t hash() { @@ -1539,6 +1545,9 @@ namespace Sass { std::string type() { return "string"; } static std::string type_name() { return "string"; } virtual bool is_invisible() const; + virtual void rtrim(); + virtual void ltrim(); + virtual void trim(); virtual size_t hash() { @@ -1698,40 +1707,13 @@ namespace Sass { ///////////////////////////////////////////////// class At_Root_Query : public Expression { private: - ADD_PROPERTY(String*, feature) + ADD_PROPERTY(Expression*, feature) ADD_PROPERTY(Expression*, value) - ADD_PROPERTY(bool, is_interpolated) public: - At_Root_Query(ParserState pstate, String* f = 0, Expression* v = 0, bool i = false) - : Expression(pstate), feature_(f), value_(v), is_interpolated_(i) + At_Root_Query(ParserState pstate, Expression* f = 0, Expression* v = 0, bool i = false) + : Expression(pstate), feature_(f), value_(v) { } - bool exclude(std::string str) - { - bool with = feature() && unquote(feature()->to_string()).compare("with") == 0; - List* l = static_cast(value()); - std::string v; - - if (with) - { - if (!l || l->length() == 0) return str.compare("rule") != 0; - for (size_t i = 0, L = l->length(); i < L; ++i) - { - v = unquote((*l)[i]->to_string()); - if (v.compare("all") == 0 || v == str) return false; - } - return true; - } - else - { - if (!l || !l->length()) return str.compare("rule") == 0; - for (size_t i = 0, L = l->length(); i < L; ++i) - { - v = unquote((*l)[i]->to_string()); - if (v.compare("all") == 0 || v == str) return true; - } - return false; - } - } + bool exclude(std::string str); ATTACH_OPERATIONS() }; diff --git a/src/constants.cpp b/src/constants.cpp index cad7a91376..31b009685a 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -89,6 +89,8 @@ namespace Sass { extern const char expression_kwd[] = "expression"; extern const char calc_fn_kwd[] = "calc"; + extern const char almost_any_value_class[] = "\"'#!;{}"; + // css selector keywords extern const char sel_deep_kwd[] = "/deep/"; diff --git a/src/constants.hpp b/src/constants.hpp index bd4bae3c63..568a28f76b 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -89,6 +89,9 @@ namespace Sass { extern const char expression_kwd[]; extern const char calc_fn_kwd[]; + // char classes for "regular expressions" + extern const char almost_any_value_class[]; + // css selector keywords extern const char sel_deep_kwd[]; diff --git a/src/eval.cpp b/src/eval.cpp index 923818ffdb..5e1f11c19c 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -1186,8 +1186,10 @@ namespace Sass { if (!dynamic_cast((*s)[0]) && !dynamic_cast((*s)[L - 1])) { if (String_Constant* l = dynamic_cast((*s)[0])) { if (String_Constant* r = dynamic_cast((*s)[L - 1])) { - if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true; - if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true; + if (r->value().size() > 0) { + if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true; + if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true; + } } } } diff --git a/src/parser.cpp b/src/parser.cpp index 1d99040ef1..07d5a3cf8c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -251,6 +251,8 @@ namespace Sass { // generic at keyword (keep last) else if (lex< re_special_directive >(true)) { (*block) << parse_special_directive(); } + else if (lex< re_prefixed_directive >(true)) { (*block) << parse_prefixed_directive(); } + else if (lex< at_keyword >(true)) { (*block) << parse_directive(); } else if (block->is_root()) { lex< css_whitespace >(); @@ -2143,10 +2145,11 @@ namespace Sass { At_Root_Query* expr = 0; Lookahead lookahead_result; LOCAL_FLAG(in_at_root, true); - if (lex< exactly<'('> >()) { + if (lex_css< exactly<'('> >()) { expr = parse_at_root_query(); } - if (peek < exactly<'{'> >()) { + if (peek_css < exactly<'{'> >()) { + lex (); body = parse_block(true); } else if ((lookahead_result = lookahead_for_selector(position)).found) { @@ -2167,19 +2170,21 @@ namespace Sass { css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was "); } - Declaration* declaration = parse_declaration(); - List* value = SASS_MEMORY_NEW(ctx.mem, List, declaration->value()->pstate(), 1); + Expression* feature = parse_list(); + if (!lex_css< exactly<':'> >()) error("style declaration must contain a value", pstate); + Expression* expression = parse_list(); + List* value = SASS_MEMORY_NEW(ctx.mem, List, feature->pstate(), 1); - if (declaration->value()->concrete_type() == Expression::LIST) { - value = static_cast(declaration->value()); + if (expression->concrete_type() == Expression::LIST) { + value = static_cast(expression); } - else *value << declaration->value(); + else *value << expression; At_Root_Query* cond = SASS_MEMORY_NEW(ctx.mem, At_Root_Query, - declaration->pstate(), - declaration->property(), - value); - if (!lex< exactly<')'> >()) error("unclosed parenthesis in @at-root expression", pstate); + value->pstate(), + feature, + value); + if (!lex_css< exactly<')'> >()) error("unclosed parenthesis in @at-root expression", pstate); return cond; } @@ -2212,6 +2217,166 @@ namespace Sass { return at_rule; } + Directive* Parser::parse_prefixed_directive() + { + std::string kwd(lexed); + + if (lexed == "@else") error("Invalid CSS: @else must come after @if", pstate); + + Directive* at_rule = SASS_MEMORY_NEW(ctx.mem, Directive, pstate, kwd); + Lookahead lookahead = lookahead_for_include(position); + if (lookahead.found && !lookahead.has_interpolants) { + at_rule->selector(parse_selector_list(true)); + } + + lex < css_comments >(false); + + if (lex < static_property >()) { + at_rule->value(parse_interpolated_chunk(Token(lexed))); + } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) { + at_rule->value(parse_list()); + } + + lex < css_comments >(false); + + if (peek< exactly<'{'> >()) { + at_rule->block(parse_block()); + } + + return at_rule; + } + + + Directive* Parser::parse_directive() + { + Directive* directive = SASS_MEMORY_NEW(ctx.mem, Directive, pstate, lexed); + Expression* val = parse_almost_any_value(); + // strip left and right if they are of type string + // debug_ast(val); + // std::cerr << "HAASDASD\n"; + directive->value(val); + if (peek< exactly<'{'> >()) { + directive->block(parse_block()); + } else if (!val) { + css_error("most def"); + } + return directive; + } + + Expression* Parser::lex_interpolation() + { + if (lex < interpolant >(true) != NULL) { + return parse_interpolated_chunk(lexed, true); + } + return 0; + } + + Expression* Parser::lex_interp_uri() + { + // create a string schema by lexing optional interpolations + return lex_interp< re_string_uri_open, re_string_uri_close >(); + } + + Expression* Parser::lex_interp_string() + { + Expression* rv = 0; + if ((rv = lex_interp< re_string_double_open, re_string_double_close >()) != NULL) return rv; + if ((rv = lex_interp< re_string_single_open, re_string_single_close >()) != NULL) return rv; + return rv; + } + + Expression* Parser::lex_almost_any_value_chars() + { + const char* match = + lex < + one_plus < + alternatives < + sequence < + exactly <'\\'>, + any_char + >, + sequence < + negate < + sequence < + exactly < url_kwd >, + exactly <'('> + > + >, + neg_class_char < + almost_any_value_class + > + >, + sequence < + exactly <'/'>, + negate < + alternatives < + exactly <'/'>, + exactly <'*'> + > + > + >, + sequence < + exactly <'\\'>, + exactly <'#'>, + negate < + exactly <'{'> + > + >, + sequence < + exactly <'!'>, + negate < + alpha + > + > + > + > + >(false); + if (match) { + // std::cerr << "[[" << std::string(lexed) << "]\n"; + return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); + } + return NULL; + } + + Expression* Parser::lex_almost_any_value_token() + { + Expression* rv = 0; + if (*position == 0) return 0; + if ((rv = lex_almost_any_value_chars()) != NULL) return rv; + // if ((rv = lex_block_comment()) != NULL) return rv; + // if ((rv = lex_single_line_comment()) != NULL) return rv; + if ((rv = lex_interp_string()) != NULL) return rv; + if ((rv = lex_interp_uri()) != NULL) return rv; + if ((rv = lex_interpolation()) != NULL) return rv; + return rv; + } + + String_Schema* Parser::parse_almost_any_value() + { + + String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); + if (*position == 0) return 0; + lex < spaces >(false); + Expression* token = lex_almost_any_value_token(); + if (!token) return 0; + // std::cerr << "LEX [" << std::string(lexed) << "]\n"; + *schema << token; + if (*position == 0) { + schema->rtrim(); + return schema; + } + + while ((token = lex_almost_any_value_token())) { + *schema << token; + } + + lex < css_whitespace >(); + + schema->rtrim(); + + return schema; + } + Warning* Parser::parse_warning() { if (stack.back() != Scope::Root && @@ -2286,6 +2451,7 @@ namespace Sass { // check expected opening bracket // only after successfull matching if (peek < exactly<'{'> >(q)) rv.found = q; + else if (peek < exactly<'('> >(q)) rv.found = q; // else if (peek < exactly<';'> >(q)) rv.found = q; // else if (peek < exactly<'}'> >(q)) rv.found = q; if (rv.found || *p == 0) rv.error = 0; diff --git a/src/parser.hpp b/src/parser.hpp index db4dc84f9b..e9284978d4 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -133,6 +133,8 @@ namespace Sass { const char* lex(bool lazy = true, bool force = false) { + if (*position == 0) return 0; + // position considered before lexed token // we can skip whitespace or comments for // lazy developers (but we need control) @@ -299,11 +301,21 @@ namespace Sass { Supports_Condition* parse_supports_condition_in_parens(); At_Root_Block* parse_at_root_block(); At_Root_Query* parse_at_root_query(); + String_Schema* parse_almost_any_value(); Directive* parse_special_directive(); + Directive* parse_prefixed_directive(); + Directive* parse_directive(); Warning* parse_warning(); Error* parse_error(); Debug* parse_debug(); + // be more like ruby sass + Expression* lex_almost_any_value_token(); + Expression* lex_almost_any_value_chars(); + Expression* lex_interp_string(); + Expression* lex_interp_uri(); + Expression* lex_interpolation(); + // these will throw errors Token lex_variable(); Token lex_identifier(); @@ -319,6 +331,34 @@ namespace Sass { void throw_syntax_error(std::string message, size_t ln = 0); void throw_read_error(std::string message, size_t ln = 0); + + + template + Expression* lex_interp() + { + if (lex < open >(false)) { + String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); + // std::cerr << "LEX [[" << std::string(lexed) << "]]\n"; + *schema << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); + if (position[0] == '#' && position[1] == '{') { + Expression* itpl = lex_interpolation(); + if (itpl) *schema << itpl; + while (lex < close >(false)) { + // std::cerr << "LEX [[" << std::string(lexed) << "]]\n"; + *schema << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); + if (position[0] == '#' && position[1] == '{') { + Expression* itpl = lex_interpolation(); + if (itpl) *schema << itpl; + } else { + return schema; + } + } + } else { + return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); + } + } + return 0; + } }; size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len); diff --git a/src/prelexer.cpp b/src/prelexer.cpp index 9c4ff81e7d..269feec516 100644 --- a/src/prelexer.cpp +++ b/src/prelexer.cpp @@ -16,6 +16,248 @@ namespace Sass { namespace Prelexer { + /* + + def string_re(open, close) + /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m + end + end + + # A hash of regular expressions that are used for tokenizing strings. + # + # The key is a `[Symbol, Boolean]` pair. + # The symbol represents which style of quotation to use, + # while the boolean represents whether or not the string + # is following an interpolated segment. + STRING_REGULAR_EXPRESSIONS = { + :double => { + /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m + false => string_re('"', '"'), + true => string_re('', '"') + }, + :single => { + false => string_re("'", "'"), + true => string_re('', "'") + }, + :uri => { + false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, + true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ + }, + # Defined in https://developer.mozilla.org/en/CSS/@-moz-document as a + # non-standard version of http://www.w3.org/TR/css3-conditional/ + :url_prefix => { + false => /url-prefix\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, + true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ + }, + :domain => { + false => /domain\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, + true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ + } + } + */ + + /* + /#{open} + ( + \\. + | + \# (?!\{) + | + [^#{close}\\#] + )* + (#{close}|#\{) + /m + false => string_re('"', '"'), + true => string_re('', '"') + */ + extern const char string_double_negates[] = "\"\\#"; + const char* re_string_double_close(const char* src) + { + return sequence < + // valid chars + zero_plus < + alternatives < + // escaped char + sequence < + exactly <'\\'>, + any_char + >, + // non interpolate hash + sequence < + exactly <'#'>, + negate < + exactly <'{'> + > + >, + // other valid chars + neg_class_char < + string_double_negates + > + > + >, + // quoted string closer + // or interpolate opening + alternatives < + exactly <'"'>, + lookahead < exactly< hash_lbrace > > + > + >(src); + } + + const char* re_string_double_open(const char* src) + { + return sequence < + // quoted string opener + exactly <'"'>, + // valid chars + zero_plus < + alternatives < + // escaped char + sequence < + exactly <'\\'>, + any_char + >, + // non interpolate hash + sequence < + exactly <'#'>, + negate < + exactly <'{'> + > + >, + // other valid chars + neg_class_char < + string_double_negates + > + > + >, + // quoted string closer + // or interpolate opening + alternatives < + exactly <'"'>, + lookahead < exactly< hash_lbrace > > + > + >(src); + } + + extern const char string_single_negates[] = "'\\#"; + const char* re_string_single_close(const char* src) + { + return sequence < + // valid chars + zero_plus < + alternatives < + // escaped char + sequence < + exactly <'\\'>, + any_char + >, + // non interpolate hash + sequence < + exactly <'#'>, + negate < + exactly <'{'> + > + >, + // other valid chars + neg_class_char < + string_single_negates + > + > + >, + // quoted string closer + // or interpolate opening + alternatives < + exactly <'\''>, + lookahead < exactly< hash_lbrace > > + > + >(src); + } + + const char* re_string_single_open(const char* src) + { + return sequence < + // quoted string opener + exactly <'\''>, + // valid chars + zero_plus < + alternatives < + // escaped char + sequence < + exactly <'\\'>, + any_char + >, + // non interpolate hash + sequence < + exactly <'#'>, + negate < + exactly <'{'> + > + >, + // other valid chars + neg_class_char < + string_single_negates + > + > + >, + // quoted string closer + // or interpolate opening + alternatives < + exactly <'\''>, + lookahead < exactly< hash_lbrace > > + > + >(src); + } + + /* + :uri => { + false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, + true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ + }, + */ + const char* re_string_uri_close(const char* src) + { + return sequence < + non_greedy< + alternatives< + class_char< real_uri_chars >, + uri_character, + NONASCII, + ESCAPE + >, + alternatives< + sequence < optional < W >, exactly <')'> >, + lookahead < exactly< hash_lbrace > > + > + >, + optional < + sequence < optional < W >, exactly <')'> > + > + >(src); + } + + const char* re_string_uri_open(const char* src) + { + return sequence < + exactly <'u'>, + exactly <'r'>, + exactly <'l'>, + exactly <'('>, + W, + non_greedy< + alternatives< + class_char< real_uri_chars >, + uri_character, + NONASCII, + ESCAPE + >, + alternatives< + sequence < W, exactly <')'> >, + exactly< hash_lbrace > + > + > + >(src); + } + // Match a line comment (/.*?(?=\n|\r\n?|\Z)/. const char* line_comment(const char* src) { @@ -366,10 +608,137 @@ namespace Sass { } // Match CSS '@' keywords. - const char* re_special_directive(const char* src) { + const char* at_keyword(const char* src) { return sequence, identifier>(src); } + /* + tok(%r{ + ( + \\. + | + (?!url\() + [^"'/\#!;\{\}] # " + | + /(?![\*\/]) + | + \#(?!\{) + | + !(?![a-z]) # TODO: never consume "!" when issue 1126 is fixed. + )+ + }xi) || tok(COMMENT) || tok(SINGLE_LINE_COMMENT) || interp_string || interp_uri || + interpolation(:warn_for_color) + */ + const char* re_almost_any_value_token(const char* src) { + + return alternatives < + one_plus < + alternatives < + sequence < + exactly <'\\'>, + any_char + >, + sequence < + negate < + sequence < + exactly < url_kwd >, + exactly <'('> + > + >, + neg_class_char < + almost_any_value_class + > + >, + sequence < + exactly <'/'>, + negate < + alternatives < + exactly <'/'>, + exactly <'*'> + > + > + >, + sequence < + exactly <'\\'>, + exactly <'#'>, + negate < + exactly <'{'> + > + >, + sequence < + exactly <'!'>, + negate < + alpha + > + > + > + >, + block_comment, + line_comment, + interpolant, + space, + sequence < + exactly<'u'>, + exactly<'r'>, + exactly<'l'>, + exactly<'('>, + zero_plus < + alternatives < + class_char< real_uri_chars >, + uri_character, + NONASCII, + ESCAPE + > + >, + // false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, + // true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ + exactly<')'> + > + >(src); + } + + /* + DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for, + :each, :while, :if, :else, :extend, :import, :media, :charset, :content, + :_moz_document, :at_root, :error] + */ + const char* re_special_directive(const char* src) { + return alternatives < + word < mixin_kwd >, + word < include_kwd >, + word < function_kwd >, + word < return_kwd >, + word < debug_kwd >, + word < warn_kwd >, + word < for_kwd >, + word < each_kwd >, + word < while_kwd >, + word < if_kwd >, + word < else_kwd >, + word < extend_kwd >, + word < import_kwd >, + word < media_kwd >, + word < charset_kwd >, + word < content_kwd >, + // exactly < moz_document_kwd >, + word < at_root_kwd >, + word < error_kwd > + >(src); + } + + const char* re_prefixed_directive(const char* src) { + return sequence < + optional < + sequence < + exactly <'-'>, + one_plus < alnum >, + exactly <'-'> + > + >, + exactly < supports_kwd > + >(src); + } + const char* re_reference_combinator(const char* src) { return sequence < optional < diff --git a/src/prelexer.hpp b/src/prelexer.hpp index 13bc2e21dd..69c4043237 100644 --- a/src/prelexer.hpp +++ b/src/prelexer.hpp @@ -162,6 +162,14 @@ namespace Sass { return src; } + // equivalent of STRING_REGULAR_EXPRESSIONS + const char* re_string_double_open(const char* src); + const char* re_string_double_close(const char* src); + const char* re_string_single_open(const char* src); + const char* re_string_single_close(const char* src); + const char* re_string_uri_open(const char* src); + const char* re_string_uri_close(const char* src); + // Match a line comment. const char* line_comment(const char* src); @@ -216,8 +224,13 @@ namespace Sass { // const char* url_schema(const char* src); // const char* url_value(const char* src); const char* vendor_prefix(const char* src); - // Match CSS '@' keywords. + const char* re_special_directive(const char* src); + const char* re_prefixed_directive(const char* src); + const char* re_almost_any_value_token(const char* src); + + // Match CSS '@' keywords. + const char* at_keyword(const char* src); const char* kwd_import(const char* src); const char* kwd_at_root(const char* src); const char* kwd_with_directive(const char* src);