summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEevee (Alex Munroe) <eevee.git@veekun.com>2014-08-29 20:15:43 -0700
committerEevee (Alex Munroe) <eevee.git@veekun.com>2014-08-29 20:15:43 -0700
commite7b247756035edda4619ac05388b2198c4db6857 (patch)
tree6c1830fa26043fdbaff8841de4888432afa5ad64
parent3b39aeba342974a7e87a00da7928401e4a82ddb6 (diff)
downloadpyscss-e7b247756035edda4619ac05388b2198c4db6857.tar.gz
Understand (and correctly ignore) IE's expression() function.
-rw-r--r--scss/ast.py11
-rw-r--r--scss/grammar/expression.g24
-rw-r--r--scss/grammar/expression.py70
3 files changed, 80 insertions, 25 deletions
diff --git a/scss/ast.py b/scss/ast.py
index b1bc77d..fb64f85 100644
--- a/scss/ast.py
+++ b/scss/ast.py
@@ -214,24 +214,25 @@ class Interpolation(Expression):
foo#{...}bar#{...}baz
"""
- def __init__(self, parts, quotes=None, type=String):
+ def __init__(self, parts, quotes=None, type=String, **kwargs):
self.parts = parts
self.quotes = quotes
self.type = type
+ self.kwargs = kwargs
@classmethod
- def maybe(cls, parts, quotes=None, type=String):
+ def maybe(cls, parts, quotes=None, type=String, **kwargs):
"""Returns an interpolation if there are multiple parts, otherwise a
plain Literal. This keeps the AST somewhat simpler, but also is the
only way `Literal.from_bareword` gets called.
"""
if len(parts) > 1:
- return cls(parts, quotes=quotes, type=type)
+ return cls(parts, quotes=quotes, type=type, **kwargs)
if quotes is None and type is String:
return Literal.from_bareword(parts[0])
- return Literal(type(parts[0], quotes=quotes))
+ return Literal(type(parts[0], quotes=quotes, **kwargs))
def _render_interpolated(self, value):
"""Return the result of interpolating `value`, which is slightly
@@ -263,7 +264,7 @@ class Interpolation(Expression):
value = part.evaluate(calculator, divide)
result.append(self._render_interpolated(value))
- return self.type(''.join(result), quotes=self.quotes)
+ return self.type(''.join(result), quotes=self.quotes, **self.kwargs)
diff --git a/scss/grammar/expression.g b/scss/grammar/expression.g
index 9574f28..17eb810 100644
--- a/scss/grammar/expression.g
+++ b/scss/grammar/expression.g
@@ -26,6 +26,7 @@ from scss.ast import MapLiteral
from scss.ast import ArgspecLiteral
from scss.cssdefs import unescape
from scss.types import Color
+from scss.types import Function
from scss.types import Number
from scss.types import String
from scss.types import Url
@@ -78,6 +79,7 @@ parser SassExpression:
token INTERP_START: "#[{]"
token INTERP_END: "[}]"
token INTERP_ANYTHING: "([^#]|#(?![{]))*"
+ token INTERP_NO_PARENS: "([^#()]|#(?![{]))*"
# http://dev.w3.org/csswg/css-syntax-3/#consume-a-url-token0
# Bare URLs may not contain quotes, parentheses, or unprintables. Quoted
# URLs may, of course, contain whatever they like.
@@ -210,8 +212,11 @@ parser SassExpression:
# regular function rule, which makes this not quite LL -- but they're
# different tokens so yapps can't tell, and it resolves the conflict by
# picking the first one.
+ # TODO Ruby sass somehow allows a full expression in here too
| "url" LPAR interpolated_url RPAR
- {{ print("url!"); return interpolated_url }}
+ {{ return interpolated_url }}
+ | "expression" LPAR interpolated_function RPAR
+ {{ return Interpolation.maybe(interpolated_function, type=Function, function_name='expression') }}
| FNCT LPAR argspec RPAR {{ return CallOp(FNCT, argspec) }}
| BANG_IMPORTANT {{ return Literal(String(BANG_IMPORTANT, quotes=None)) }}
| interpolated_bareword {{ return Interpolation.maybe(interpolated_bareword) }}
@@ -279,6 +284,23 @@ parser SassExpression:
BAREWORD {{ parts.append(BAREWORD) }}
)* {{ return parts }}
+ rule interpolated_function:
+ # Completely arbitrary text, but with balanced parentheses.
+ interpolated_function_parens {{ parts = interpolated_function_parens }}
+ (
+ interpolation {{ parts.append(interpolation) }}
+ interpolated_function_parens {{ parts.extend(interpolated_function_parens) }}
+ )* {{ return parts }}
+
+ rule interpolated_function_parens:
+ INTERP_NO_PARENS {{ parts = [INTERP_NO_PARENS] }}
+ (
+ LPAR
+ interpolated_function {{ parts = parts[:-1] + [parts[-1] + LPAR + interpolated_function[0]] + interpolated_function[0:] }}
+ RPAR
+ INTERP_NO_PARENS {{ parts[-1] += RPAR + INTERP_NO_PARENS }}
+ )* {{ return parts }}
+
rule goal_interpolated_anything:
# This isn't part of the grammar, but rather a separate goal, used for
diff --git a/scss/grammar/expression.py b/scss/grammar/expression.py
index 38f90e3..6caf785 100644
--- a/scss/grammar/expression.py
+++ b/scss/grammar/expression.py
@@ -26,6 +26,7 @@ from scss.ast import MapLiteral
from scss.ast import ArgspecLiteral
from scss.cssdefs import unescape
from scss.types import Color
+from scss.types import Function
from scss.types import Number
from scss.types import String
from scss.types import Url
@@ -39,6 +40,7 @@ from scss.grammar import Scanner
class SassExpressionScanner(Scanner):
patterns = None
_patterns = [
+ ('"expression"', 'expression'),
('"url"', 'url'),
('":"', ':'),
('","', ','),
@@ -80,6 +82,7 @@ class SassExpressionScanner(Scanner):
('INTERP_START', '#[{]'),
('INTERP_END', '[}]'),
('INTERP_ANYTHING', '([^#]|#(?![{]))*'),
+ ('INTERP_NO_PARENS', '([^#()]|#(?![{]))*'),
('BAREURL', '(?:[\\\\].|[^#$\'"()\\x00-\\x08\\x0b\\x0e-\\x1f\\x7f]|#(?![{]))*'),
]
@@ -319,7 +322,13 @@ class SassExpression(Parser):
LPAR = self._scan('LPAR')
interpolated_url = self.interpolated_url()
RPAR = self._scan('RPAR')
- print("url!"); return interpolated_url
+ return interpolated_url
+ elif _token_ == '"expression"':
+ self._scan('"expression"')
+ LPAR = self._scan('LPAR')
+ interpolated_function = self.interpolated_function()
+ RPAR = self._scan('RPAR')
+ return Interpolation.maybe(interpolated_function, type=Function, function_name='expression')
elif _token_ == 'FNCT':
FNCT = self._scan('FNCT')
LPAR = self._scan('LPAR')
@@ -419,6 +428,28 @@ class SassExpression(Parser):
parts.append(BAREWORD)
return parts
+ def interpolated_function(self):
+ interpolated_function_parens = self.interpolated_function_parens()
+ parts = interpolated_function_parens
+ while self._peek(self.interpolated_bare_url_rsts) == 'INTERP_START':
+ interpolation = self.interpolation()
+ parts.append(interpolation)
+ interpolated_function_parens = self.interpolated_function_parens()
+ parts.extend(interpolated_function_parens)
+ return parts
+
+ def interpolated_function_parens(self):
+ INTERP_NO_PARENS = self._scan('INTERP_NO_PARENS')
+ parts = [INTERP_NO_PARENS]
+ while self._peek(self.interpolated_function_parens_rsts) == 'LPAR':
+ LPAR = self._scan('LPAR')
+ interpolated_function = self.interpolated_function()
+ parts = parts[:-1] + [parts[-1] + LPAR + interpolated_function[0]] + interpolated_function[0:]
+ RPAR = self._scan('RPAR')
+ INTERP_NO_PARENS = self._scan('INTERP_NO_PARENS')
+ parts[-1] += RPAR + INTERP_NO_PARENS
+ return parts
+
def goal_interpolated_anything(self):
INTERP_ANYTHING = self._scan('INTERP_ANYTHING')
parts = [INTERP_ANYTHING]
@@ -430,40 +461,41 @@ class SassExpression(Parser):
END = self._scan('END')
return Interpolation.maybe(parts)
- expr_map_or_list_rsts__ = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'COLOR', 'RPAR', 'BAREWORD', 'NUM', 'FNCT', 'VAR', 'BANG_IMPORTANT', 'SINGLE_QUOTE', '","'])
- u_expr_chks = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'COLOR', 'BAREWORD', 'NUM', 'FNCT', 'VAR', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
- m_expr_rsts = set(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'RPAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'BAREWORD', '"url"', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
+ expr_map_or_list_rsts__ = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'COLOR', '"expression"', 'RPAR', 'BAREWORD', 'NUM', 'FNCT', 'VAR', 'BANG_IMPORTANT', 'SINGLE_QUOTE', '","'])
+ u_expr_chks = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'COLOR', '"expression"', 'BAREWORD', 'NUM', 'FNCT', 'VAR', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
+ m_expr_rsts = set(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'RPAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'COLOR', '"expression"', 'NE', 'LT', 'NUM', '":"', 'BAREWORD', '"url"', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
argspec_items_rsts = set(['RPAR', 'END', '","'])
expr_slst_chks = set(['INTERP_END', 'RPAR', 'END', '":"', '","'])
expr_lst_rsts = set(['INTERP_END', 'END', '","'])
expr_map_or_list_rsts = set(['RPAR', '":"', '","'])
- argspec_item_chks = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'BAREWORD', 'COLOR', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'NOT', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
+ argspec_item_chks = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'BAREWORD', 'COLOR', '"expression"', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'NOT', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
a_expr_chks = set(['ADD', 'SUB'])
- expr_slst_rsts = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'BAREWORD', 'END', 'COLOR', 'FNCT', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', '":"', 'NOT', 'INTERP_END', 'BANG_IMPORTANT', 'SINGLE_QUOTE', '","'])
- interpolated_bareword_rsts = set(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'RPAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'BAREWORD', '"url"', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
- or_expr_rsts = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'BAREWORD', 'END', 'SINGLE_QUOTE', 'COLOR', 'FNCT', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', '":"', 'NOT', 'INTERP_END', 'BANG_IMPORTANT', 'OR', '","'])
+ interpolated_function_parens_rsts = set(['LPAR', 'RPAR', 'INTERP_START'])
+ expr_slst_rsts = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'BAREWORD', 'END', 'COLOR', '"expression"', 'FNCT', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', '":"', 'NOT', 'INTERP_END', 'BANG_IMPORTANT', 'SINGLE_QUOTE', '","'])
+ interpolated_bareword_rsts = set(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'RPAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'INTERP_START', 'COLOR', '"expression"', 'NE', 'LT', 'NUM', '":"', 'BAREWORD', '"url"', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
+ or_expr_rsts = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'BAREWORD', 'END', 'SINGLE_QUOTE', 'COLOR', '"expression"', 'FNCT', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', '":"', 'NOT', 'INTERP_END', 'BANG_IMPORTANT', 'OR', '","'])
argspec_chks_ = set(['END', 'RPAR'])
interpolated_string_single_rsts = set(['SINGLE_QUOTE', 'INTERP_START'])
- and_expr_rsts = set(['AND', 'LPAR', 'RPAR', 'BAREWORD', 'END', 'SINGLE_QUOTE', 'COLOR', 'DOUBLE_QUOTE', 'FNCT', 'SIGN', 'VAR', 'ADD', 'NUM', '"url"', '":"', 'NOT', 'INTERP_END', 'BANG_IMPORTANT', 'OR', '","'])
- comparison_rsts = set(['LPAR', 'DOUBLE_QUOTE', 'RPAR', 'INTERP_END', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'BAREWORD', '"url"', 'GT', 'END', 'SIGN', 'ADD', 'FNCT', 'VAR', 'EQ', 'AND', 'GE', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
+ and_expr_rsts = set(['AND', 'LPAR', 'RPAR', 'BAREWORD', 'END', 'SINGLE_QUOTE', 'COLOR', '"expression"', 'DOUBLE_QUOTE', 'FNCT', 'SIGN', 'VAR', 'ADD', 'NUM', '"url"', '":"', 'NOT', 'INTERP_END', 'BANG_IMPORTANT', 'OR', '","'])
+ comparison_rsts = set(['LPAR', 'DOUBLE_QUOTE', 'RPAR', 'INTERP_END', 'BANG_IMPORTANT', 'LE', 'COLOR', '"expression"', 'NE', 'LT', 'NUM', '":"', 'BAREWORD', '"url"', 'GT', 'END', 'SIGN', 'ADD', 'FNCT', 'VAR', 'EQ', 'AND', 'GE', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
argspec_chks = set(['DOTDOTDOT', 'SLURPYVAR'])
- atom_rsts_ = set(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'RPAR', 'VAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'BAREWORD', '"url"', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'UNITS', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
+ atom_rsts_ = set(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'RPAR', 'VAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'COLOR', '"expression"', 'NE', 'LT', 'NUM', '":"', 'BAREWORD', '"url"', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'UNITS', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
interpolated_string_double_rsts = set(['DOUBLE_QUOTE', 'INTERP_START'])
expr_map_or_list_rsts_ = set(['RPAR', '","'])
- u_expr_rsts = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'COLOR', 'SIGN', 'BAREWORD', 'ADD', 'NUM', 'FNCT', 'VAR', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
+ u_expr_rsts = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'COLOR', '"expression"', 'SIGN', 'BAREWORD', 'ADD', 'NUM', 'FNCT', 'VAR', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
atom_chks = set(['COLOR', 'VAR'])
interpolated_url_rsts = set(['DOUBLE_QUOTE', 'BAREURL', 'SINGLE_QUOTE'])
comparison_chks = set(['GT', 'GE', 'NE', 'LT', 'LE', 'EQ'])
- argspec_items_rsts_ = set(['KWVAR', 'LPAR', 'RPAR', 'BAREWORD', 'END', 'SLURPYVAR', 'COLOR', 'DOTDOTDOT', 'DOUBLE_QUOTE', 'SIGN', 'VAR', 'ADD', 'NUM', '"url"', 'FNCT', 'NOT', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
- a_expr_rsts = set(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'RPAR', 'INTERP_END', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'BAREWORD', '"url"', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
+ argspec_items_rsts_ = set(['KWVAR', 'LPAR', 'RPAR', 'BAREWORD', 'END', 'SLURPYVAR', 'COLOR', 'DOTDOTDOT', 'DOUBLE_QUOTE', 'SIGN', 'VAR', 'ADD', 'NUM', '"url"', 'FNCT', 'NOT', 'SINGLE_QUOTE', 'BANG_IMPORTANT', '"expression"'])
+ a_expr_rsts = set(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'RPAR', 'INTERP_END', 'BANG_IMPORTANT', 'LE', 'COLOR', '"expression"', 'NE', 'LT', 'NUM', '":"', 'BAREWORD', '"url"', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
interpolated_string_rsts = set(['DOUBLE_QUOTE', 'SINGLE_QUOTE'])
m_expr_chks = set(['MUL', 'DIV'])
goal_interpolated_anything_rsts = set(['END', 'INTERP_START'])
interpolated_bare_url_rsts = set(['RPAR', 'INTERP_START'])
- argspec_items_chks = set(['KWVAR', '"url"', 'DOUBLE_QUOTE', 'BAREWORD', 'LPAR', 'COLOR', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'NOT', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
- argspec_rsts = set(['KWVAR', 'LPAR', 'DOUBLE_QUOTE', 'BANG_IMPORTANT', 'END', 'SLURPYVAR', 'COLOR', 'BAREWORD', 'DOTDOTDOT', 'RPAR', 'VAR', 'ADD', 'NUM', '"url"', 'FNCT', 'NOT', 'SIGN', 'SINGLE_QUOTE'])
- atom_rsts = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'BANG_IMPORTANT', 'COLOR', 'BAREWORD', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'NOT', 'RPAR', 'SINGLE_QUOTE'])
- argspec_items_rsts__ = set(['KWVAR', 'LPAR', 'DOUBLE_QUOTE', 'BAREWORD', 'SLURPYVAR', 'COLOR', 'DOTDOTDOT', 'SIGN', 'VAR', 'ADD', 'NUM', '"url"', 'FNCT', 'NOT', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
- argspec_rsts_ = set(['KWVAR', 'LPAR', 'DOUBLE_QUOTE', 'BANG_IMPORTANT', 'END', 'COLOR', 'BAREWORD', 'SIGN', 'VAR', 'ADD', 'NUM', '"url"', 'FNCT', 'NOT', 'RPAR', 'SINGLE_QUOTE'])
+ argspec_items_chks = set(['KWVAR', '"url"', 'DOUBLE_QUOTE', 'BAREWORD', 'LPAR', 'COLOR', '"expression"', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'NOT', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
+ argspec_rsts = set(['KWVAR', 'LPAR', 'DOUBLE_QUOTE', 'BANG_IMPORTANT', 'END', 'SLURPYVAR', 'COLOR', 'BAREWORD', 'DOTDOTDOT', 'RPAR', 'VAR', 'ADD', 'NUM', '"url"', 'FNCT', 'NOT', 'SINGLE_QUOTE', 'SIGN', '"expression"'])
+ atom_rsts = set(['"url"', 'LPAR', 'DOUBLE_QUOTE', 'BANG_IMPORTANT', 'COLOR', 'BAREWORD', '"expression"', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'NOT', 'RPAR', 'SINGLE_QUOTE'])
+ argspec_items_rsts__ = set(['KWVAR', 'LPAR', 'DOUBLE_QUOTE', 'BAREWORD', 'SLURPYVAR', 'COLOR', 'DOTDOTDOT', 'SIGN', 'VAR', 'ADD', 'NUM', '"url"', 'FNCT', 'NOT', 'SINGLE_QUOTE', 'BANG_IMPORTANT', '"expression"'])
+ argspec_rsts_ = set(['KWVAR', 'LPAR', 'DOUBLE_QUOTE', 'BANG_IMPORTANT', 'END', 'COLOR', 'BAREWORD', '"expression"', 'SIGN', 'VAR', 'ADD', 'NUM', '"url"', 'FNCT', 'NOT', 'RPAR', 'SINGLE_QUOTE'])