From cd18ae88b7553c12a31b4a7367f698d5af781b10 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sun, 10 May 2015 17:06:44 +0200 Subject: [PATCH] Improve ie property and expression parsing Fixes https://github.com/sass/libsass/issues/1188 Fixes https://github.com/sass/libsass/issues/1102 Fixes https://github.com/sass/libsass/issues/931 --- debugger.hpp | 4 ++++ eval.cpp | 3 +-- parser.cpp | 32 +++++++++++++++----------------- prelexer.cpp | 40 +++++++++++++++++++++++++++++++++++----- prelexer.hpp | 20 +++++++++++++++++--- util.cpp | 12 +++--------- 6 files changed, 75 insertions(+), 36 deletions(-) diff --git a/debugger.hpp b/debugger.hpp index aaaa914f8..7b0c59965 100644 --- a/debugger.hpp +++ b/debugger.hpp @@ -450,6 +450,10 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0) cerr << ind << "Map " << expression; cerr << " (" << pstate_source_position(node) << ")"; cerr << " [Hashed]" << endl; + // for (auto i : expression->elements()) { + // debug_ast(i.first, ind + " key: "); + // debug_ast(i.second, ind + " val: "); + // } } else if (dynamic_cast(node)) { List* expression = dynamic_cast(node); cerr << ind << "List " << expression; diff --git a/eval.cpp b/eval.cpp index f63f711e5..2bd9bac14 100644 --- a/eval.cpp +++ b/eval.cpp @@ -1018,8 +1018,7 @@ namespace Sass { is_rest_argument = false; is_keyword_argument = true; } - else - if(val->concrete_type() != Expression::LIST) { + else if(val->concrete_type() != Expression::LIST) { List* wrapper = new (ctx.mem) List(val->pstate(), 0, List::COMMA, diff --git a/parser.cpp b/parser.cpp index 2332bf830..10ba51cce 100644 --- a/parser.cpp +++ b/parser.cpp @@ -1458,28 +1458,23 @@ namespace Sass { Token str(lexed); const char* i = str.begin; // see if there any interpolants - const char* q; const char* p = find_first_in_interval< exactly >(str.begin, str.end); if (!p) { String_Constant* str_node = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(str.begin, str.end))); str_node->is_delayed(true); + str_node->quote_mark('*'); return str_node; } String_Schema* schema = new (ctx.mem) String_Schema(pstate); while (i < str.end) { - q = find_first_in_interval< alternatives< exactly<'"'>, exactly<'\''> > >(i, str.end); p = find_first_in_interval< exactly >(i, str.end); - if (q && (!p || p > q)) { - if (i < q) { - (*schema) << new (ctx.mem) String_Constant(pstate, string(i, q)); // accumulate the preceding segment if it's nonempty - } - (*schema) << new (ctx.mem) String_Constant(pstate, string(q, q+1)); // capture the quote mark separately - i = q+1; - } - else if (p) { + if (p) { if (i < p) { - (*schema) << new (ctx.mem) String_Constant(pstate, string(i, p)); // accumulate the preceding segment if it's nonempty + String_Constant* part = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(i, p))); // accumulate the preceding segment if it's nonempty + part->is_delayed(true); + part->quote_mark('*'); // avoid unquote in interpolation + (*schema) << part; } if (peek < sequence < optional_spaces, exactly > >(p+2)) { position = p+2; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); @@ -1498,7 +1493,12 @@ namespace Sass { } } else { // no interpolants left; add the last segment if nonempty - if (i < str.end) (*schema) << new (ctx.mem) String_Constant(pstate, string(i, str.end)); + if (i < str.end) { + String_Constant* part = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(i, str.end))); + part->is_delayed(true); + part->quote_mark('*'); // avoid unquote in interpolation + (*schema) << part; + } break; } } @@ -1512,15 +1512,13 @@ namespace Sass { *kwd_arg << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed)); } else { lex< alternatives< identifier_schema, identifier > >(); - *kwd_arg << new (ctx.mem) String_Quoted(pstate, lexed); + *kwd_arg << new (ctx.mem) String_Constant(pstate, lexed); } lex< exactly<'='> >(); - *kwd_arg << new (ctx.mem) String_Quoted(pstate, lexed); + *kwd_arg << new (ctx.mem) String_Constant(pstate, lexed); if (peek< variable >()) *kwd_arg << parse_list(); else if (lex< number >()) *kwd_arg << new (ctx.mem) Textual(pstate, Textual::NUMBER, Util::normalize_decimals(lexed)); - else if (lex< alternatives< identifier_schema, identifier, number, hexa, hex > >()) { - *kwd_arg << new (ctx.mem) String_Quoted(pstate, lexed); - } + else if (peek < ie_keyword_arg_value >()) { *kwd_arg << parse_list(); } return kwd_arg; } diff --git a/prelexer.cpp b/prelexer.cpp index e13c56d85..c9146981a 100644 --- a/prelexer.cpp +++ b/prelexer.cpp @@ -34,7 +34,12 @@ namespace Sass { { return sequence< zero_plus < space >, - delimited_by >(src); + delimited_by< + slash_star, + star_slash, + false + > + >(src); } /* not use anymore - remove? const char* block_comment_prefix(const char* src) { @@ -606,7 +611,7 @@ namespace Sass { >(src); } const char* ie_expression(const char* src) { - return sequence < word, delimited_by< '(', ')', true> >(src); + return sequence < word, exactly<'('>, skip_over_scopes< exactly<'('>, exactly<')'> > >(src); } const char* ie_property(const char* src) { return alternatives < ie_expression, ie_progid >(src); @@ -617,13 +622,38 @@ namespace Sass { // zero_plus< sequence< optional_css_whitespace, exactly<','>, optional_css_whitespace, alternatives< ie_keyword_arg, value_schema, quoted_string, interpolant, number, identifier, delimited_by<'(', ')', true> > > > >(src); // } + const char* ie_keyword_arg_property(const char* src) { + return alternatives < + variable, + identifier_schema, + identifier + >(src); + } + const char* ie_keyword_arg_value(const char* src) { + return alternatives < + variable, + identifier_schema, + identifier, + quoted_string, + number, + hexa, + sequence < + exactly < '(' >, + skip_over_scopes < + exactly < '(' >, + exactly < ')' > + > + > + >(src); + } + const char* ie_keyword_arg(const char* src) { - return sequence< - alternatives< variable, identifier_schema, identifier >, + return sequence < + ie_keyword_arg_property, optional_css_whitespace, exactly<'='>, optional_css_whitespace, - alternatives< variable, identifier_schema, identifier, quoted_string, number, hexa > + ie_keyword_arg_value >(src); } diff --git a/prelexer.hpp b/prelexer.hpp index 2f7e15a3c..a04cfe61b 100644 --- a/prelexer.hpp +++ b/prelexer.hpp @@ -58,7 +58,7 @@ namespace Sass { // recursive skip stuff delimited by start/stop // first start/opener must be consumed already! template - const char* skip_over_scopes(const char* src, const char* end = 0) { + const char* skip_over_scopes(const char* src, const char* end) { size_t level = 0; bool in_squote = false; @@ -85,8 +85,9 @@ namespace Sass { } // find another opener inside? - else if (start(src)) { - ++ level; // increase counter + else if (const char* pos = start(src)) { + ++ level; // increase stack counter + src = pos - 1; // advance position } // look for the closer (maybe final, maybe not) @@ -96,6 +97,8 @@ namespace Sass { // return position at end of stop // delimiter may be multiple chars else return final; + // advance position + src = final - 1; } // next @@ -105,6 +108,15 @@ namespace Sass { return 0; } + // skip to delimiter (mx) inside given range + // this will savely skip over all quoted strings + // recursive skip stuff delimited by start/stop + // first start/opener must be consumed already! + template + const char* skip_over_scopes(const char* src) { + return skip_over_scopes(src, 0); + } + // Match a sequence of characters delimited by the supplied chars. template const char* recursive_scopes(const char* src) { @@ -296,6 +308,8 @@ namespace Sass { const char* ie_expression(const char* src); const char* ie_property(const char* src); const char* ie_keyword_arg(const char* src); + const char* ie_keyword_arg_value(const char* src); + const char* ie_keyword_arg_property(const char* src); // match urls const char* url(const char* src); diff --git a/util.cpp b/util.cpp index 7714f6569..fc7e06ec3 100644 --- a/util.cpp +++ b/util.cpp @@ -262,21 +262,15 @@ namespace Sass { { bool ws = false; bool esc = false; - char inside_str = 0; string text = ""; - for(auto i : str) { + for(const char& i : str) { if (!esc && i == '\\') { esc = true; + ws = false; text += i; } else if (esc) { esc = false; - text += i; - } else if (!inside_str && (i == '"' || i == '\'')) { - inside_str = i; - text += i; - } else if (inside_str) { - if (i == inside_str) - inside_str = false; + ws = false; text += i; } else if ( i == ' ' ||