From 20c74b6796a244f118c2dcb4cf6b7b1e05b6789d Mon Sep 17 00:00:00 2001 From: "Eevee (Alex Munroe)" Date: Wed, 10 Dec 2014 14:39:35 -0800 Subject: Fix parsing of `@if(...)`, where there's no literal space. --- scss/cssdefs.py | 10 ++++++---- scss/rule.py | 23 ++++++++++++----------- scss/tests/files/bugs/if-with-parentheses.css | 3 +++ scss/tests/files/bugs/if-with-parentheses.scss | 7 +++++++ 4 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 scss/tests/files/bugs/if-with-parentheses.css create mode 100644 scss/tests/files/bugs/if-with-parentheses.scss diff --git a/scss/cssdefs.py b/scss/cssdefs.py index d94206b..46b6877 100644 --- a/scss/cssdefs.py +++ b/scss/cssdefs.py @@ -428,7 +428,7 @@ def determine_encoding(buf): # ------------------------------------------------------------------------------ -# Bits and pieces of grammar, mostly as regexen +# Bits and pieces of the official CSS grammar # These are the only pseudo-elements allowed to be specified with a single # colon, for backwards compatibility @@ -443,8 +443,7 @@ CSS2_PSEUDO_ELEMENTS = frozenset(( # or a backslash followed by one to six hex digits and a single optional # whitespace. Escaped newlines become nothing. # Ref: http://dev.w3.org/csswg/css-syntax-3/#consume-an-escaped-code-point -unescape_rx = re.compile( - r"\\([0-9a-fA-F]{1,6})[\n\t ]?|\\(.)|\\\n", re.DOTALL) +escape_rx = re.compile(r"(?s)\\([0-9a-fA-F]{1,6})[\n\t ]?|\\(.)|\\\n") def _unescape_one(match): @@ -460,9 +459,12 @@ def unescape(string): """Given a raw CSS string (i.e. taken directly from CSS source with no processing), eliminate all backslash escapes. """ - return unescape_rx.sub(_unescape_one, string) + return escape_rx.sub(_unescape_one, string) +# ------------------------------------------------------------------------------ +# Ad-hoc regexes specific to pyscss + _expr_glob_re = re.compile(r''' \#\{(.*?)\} # Global Interpolation only ''', re.VERBOSE) diff --git a/scss/rule.py b/scss/rule.py index 712f4ba..b8bf8c9 100644 --- a/scss/rule.py +++ b/scss/rule.py @@ -2,6 +2,7 @@ from __future__ import absolute_import from __future__ import print_function import logging +import re from scss.namespace import Namespace @@ -239,17 +240,17 @@ class BlockHeader(object): # Minor parsing if prop.startswith('@'): - if prop.lower().startswith('@else if '): - directive = '@else if' - argument = prop[9:] - else: - chunks = prop.split(None, 1) - if len(chunks) == 2: - directive, argument = chunks - else: - directive, argument = prop, None - directive = directive.lower() - + # This pattern MUST NOT BE ABLE TO FAIL! + # This is slightly more lax than the CSS syntax technically allows, + # e.g. identifiers aren't supposed to begin with three hyphens. + # But we don't care, and will just spit it back out anyway. + m = re.match( + u'@(else if|[-_a-zA-Z0-9\U00000080-\U0010FFFF]*)\\b', + prop, re.I) + directive = m.group(0).lower() + argument = prop[len(directive):].strip() + if not argument: + argument = None return BlockAtRuleHeader(directive, argument, num_lines) elif prop.split(None, 1)[0].endswith(':'): # Syntax is ": [prop]" -- if the optional prop exists, it diff --git a/scss/tests/files/bugs/if-with-parentheses.css b/scss/tests/files/bugs/if-with-parentheses.css new file mode 100644 index 0000000..c342002 --- /dev/null +++ b/scss/tests/files/bugs/if-with-parentheses.css @@ -0,0 +1,3 @@ +a:hover { + text-decoration: underline; +} diff --git a/scss/tests/files/bugs/if-with-parentheses.scss b/scss/tests/files/bugs/if-with-parentheses.scss new file mode 100644 index 0000000..44bf82e --- /dev/null +++ b/scss/tests/files/bugs/if-with-parentheses.scss @@ -0,0 +1,7 @@ +a { + @if(true) { + &:hover { + text-decoration: underline; + } + } +} -- cgit v1.2.1