summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEevee <eevee.github@veekun.com>2014-12-09 14:34:18 -0800
committerEevee <eevee.github@veekun.com>2014-12-09 14:34:18 -0800
commit604ad49bc14de31a4e39c64c58b1cb04bf43f8ee (patch)
tree3ddae1f6fb28f0cedb88d2c474dfe291891e12c1
parentfee56ccb6ec85b611b4fa392ba5b9dd3053423f6 (diff)
parentd804c22987c20f1f607bda933a2b906f21dd315b (diff)
downloadpyscss-604ad49bc14de31a4e39c64c58b1cb04bf43f8ee.tar.gz
Merge pull request #314 from xen0n/modulo-expression
Support for modulo expressions
-rw-r--r--scss/grammar/expression.g2
-rw-r--r--scss/grammar/expression.py19
-rw-r--r--scss/tests/test_expression.py5
-rw-r--r--scss/types.py18
4 files changed, 37 insertions, 7 deletions
diff --git a/scss/grammar/expression.g b/scss/grammar/expression.g
index 454e112..8f4e5ad 100644
--- a/scss/grammar/expression.g
+++ b/scss/grammar/expression.g
@@ -61,6 +61,7 @@ parser SassExpression:
token END: "$"
token MUL: "[*]"
token DIV: "/"
+ token MOD: "(?<=\s)%"
token ADD: "[+]"
token SUB: "-\s"
token SIGN: "-(?![a-zA-Z_])"
@@ -218,6 +219,7 @@ parser SassExpression:
(
MUL u_expr {{ v = BinaryOp(operator.mul, v, u_expr) }}
| DIV u_expr {{ v = BinaryOp(operator.truediv, v, u_expr) }}
+ | MOD u_expr {{ v = BinaryOp(operator.mod, v, u_expr) }}
)* {{ return v }}
rule u_expr:
diff --git a/scss/grammar/expression.py b/scss/grammar/expression.py
index b2ba877..a808eae 100644
--- a/scss/grammar/expression.py
+++ b/scss/grammar/expression.py
@@ -58,6 +58,7 @@ class SassExpressionScanner(Scanner):
('END', '$'),
('MUL', '[*]'),
('DIV', '/'),
+ ('MOD', '(?<=\\s)%'),
('ADD', '[+]'),
('SUB', '-\\s'),
('SIGN', '-(?![a-zA-Z_])'),
@@ -291,10 +292,14 @@ class SassExpression(Parser):
MUL = self._scan('MUL')
u_expr = self.u_expr()
v = BinaryOp(operator.mul, v, u_expr)
- else: # == 'DIV'
+ elif _token_ == 'DIV':
DIV = self._scan('DIV')
u_expr = self.u_expr()
v = BinaryOp(operator.truediv, v, u_expr)
+ else: # == 'MOD'
+ MOD = self._scan('MOD')
+ u_expr = self.u_expr()
+ v = BinaryOp(operator.mod, v, u_expr)
return v
def u_expr(self):
@@ -517,7 +522,7 @@ class SassExpression(Parser):
atom_chks_ = frozenset(['BAREWORD', 'INTERP_START'])
expr_map_or_list_rsts__ = frozenset(['LPAR', 'DOUBLE_QUOTE', 'VAR', 'URL_FUNCTION', 'BAREWORD', 'COLOR', 'ALPHA_FUNCTION', 'INTERP_START', 'SIGN', 'LITERAL_FUNCTION', 'ADD', 'NUM', 'RPAR', 'FNCT', 'NOT', 'BANG_IMPORTANT', 'SINGLE_QUOTE', '","'])
u_expr_chks = frozenset(['LPAR', 'DOUBLE_QUOTE', 'BAREWORD', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'ALPHA_FUNCTION', 'VAR', 'NUM', 'FNCT', 'LITERAL_FUNCTION', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
- m_expr_rsts = frozenset(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'ALPHA_FUNCTION', 'RPAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'LITERAL_FUNCTION', 'GT', 'END', 'SIGN', 'BAREWORD', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
+ m_expr_rsts = frozenset(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'ALPHA_FUNCTION', 'RPAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'LITERAL_FUNCTION', 'GT', 'END', 'SIGN', 'BAREWORD', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'MOD', 'OR', '","'])
interpolated_bare_url_rsts_ = frozenset(['RPAR', 'INTERP_START', 'BAREURL', 'SPACE'])
argspec_items_rsts = frozenset(['RPAR', 'END', '","'])
expr_slst_chks = frozenset(['INTERP_END', 'RPAR', 'END', '":"', '","'])
@@ -527,12 +532,12 @@ class SassExpression(Parser):
a_expr_chks = frozenset(['ADD', 'SUB'])
interpolated_function_parens_rsts = frozenset(['LPAR', 'RPAR', 'INTERP_START'])
expr_slst_rsts = frozenset(['LPAR', 'DOUBLE_QUOTE', 'VAR', 'END', 'URL_FUNCTION', 'BAREWORD', 'COLOR', 'ALPHA_FUNCTION', 'INTERP_START', 'FNCT', 'SIGN', 'LITERAL_FUNCTION', 'ADD', 'NUM', 'RPAR', '":"', 'NOT', 'INTERP_END', 'BANG_IMPORTANT', 'SINGLE_QUOTE', '","'])
- interpolated_bareword_rsts = frozenset(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'ALPHA_FUNCTION', 'RPAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'LITERAL_FUNCTION', 'GT', 'END', 'SPACE', 'SIGN', 'BAREWORD', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
- atom_rsts__ = frozenset(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'ALPHA_FUNCTION', 'RPAR', 'VAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'LITERAL_FUNCTION', 'GT', 'END', 'SIGN', 'BAREWORD', 'GE', 'FNCT', 'UNITS', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
+ interpolated_bareword_rsts = frozenset(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'ALPHA_FUNCTION', 'RPAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'LITERAL_FUNCTION', 'GT', 'END', 'SPACE', 'SIGN', 'BAREWORD', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'MOD', 'OR', '","'])
+ atom_rsts__ = frozenset(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'ALPHA_FUNCTION', 'RPAR', 'VAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'LITERAL_FUNCTION', 'GT', 'END', 'SIGN', 'BAREWORD', 'GE', 'FNCT', 'UNITS', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'MOD', 'OR', '","'])
or_expr_rsts = frozenset(['LPAR', 'DOUBLE_QUOTE', 'ALPHA_FUNCTION', 'RPAR', 'INTERP_END', 'BANG_IMPORTANT', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NUM', '":"', 'BAREWORD', 'END', 'SIGN', 'LITERAL_FUNCTION', 'ADD', 'FNCT', 'VAR', 'OR', 'NOT', 'SINGLE_QUOTE', '","'])
argspec_chks_ = frozenset(['END', 'RPAR'])
interpolated_string_single_rsts = frozenset(['SINGLE_QUOTE', 'INTERP_START'])
- interpolated_bareword_rsts_ = frozenset(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'ALPHA_FUNCTION', 'RPAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'BAREWORD', 'GT', 'END', 'SPACE', 'SIGN', 'LITERAL_FUNCTION', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
+ interpolated_bareword_rsts_ = frozenset(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'ALPHA_FUNCTION', 'RPAR', 'MUL', 'DIV', 'BANG_IMPORTANT', 'INTERP_END', 'LE', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'BAREWORD', 'GT', 'END', 'SPACE', 'SIGN', 'LITERAL_FUNCTION', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'MOD', 'OR', '","'])
and_expr_rsts = frozenset(['LPAR', 'DOUBLE_QUOTE', 'ALPHA_FUNCTION', 'RPAR', 'INTERP_END', 'BANG_IMPORTANT', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NUM', '":"', 'BAREWORD', 'END', 'SIGN', 'LITERAL_FUNCTION', 'ADD', 'FNCT', 'VAR', 'AND', 'OR', 'NOT', 'SINGLE_QUOTE', '","'])
comparison_rsts = frozenset(['LPAR', 'DOUBLE_QUOTE', 'ALPHA_FUNCTION', 'RPAR', 'INTERP_END', 'BANG_IMPORTANT', 'LE', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'LITERAL_FUNCTION', 'GT', 'END', 'SIGN', 'BAREWORD', 'ADD', 'FNCT', 'VAR', 'EQ', 'AND', 'GE', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
argspec_chks = frozenset(['DOTDOTDOT', 'SLURPYVAR'])
@@ -548,8 +553,8 @@ class SassExpression(Parser):
argspec_items_rsts_ = frozenset(['KWVAR', 'LPAR', 'DOUBLE_QUOTE', 'VAR', 'END', 'SLURPYVAR', 'URL_FUNCTION', 'BAREWORD', 'COLOR', 'ALPHA_FUNCTION', 'DOTDOTDOT', 'INTERP_START', 'SIGN', 'LITERAL_FUNCTION', 'ADD', 'NUM', 'RPAR', 'FNCT', 'NOT', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
a_expr_rsts = frozenset(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'ALPHA_FUNCTION', 'RPAR', 'INTERP_END', 'BANG_IMPORTANT', 'LE', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'LITERAL_FUNCTION', 'GT', 'END', 'SIGN', 'BAREWORD', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
interpolated_string_rsts = frozenset(['DOUBLE_QUOTE', 'SINGLE_QUOTE'])
- interpolated_bareword_rsts__ = frozenset(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'ALPHA_FUNCTION', 'RPAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'LITERAL_FUNCTION', 'GT', 'END', 'SIGN', 'BAREWORD', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'OR', '","'])
- m_expr_chks = frozenset(['MUL', 'DIV'])
+ interpolated_bareword_rsts__ = frozenset(['LPAR', 'DOUBLE_QUOTE', 'SUB', 'ALPHA_FUNCTION', 'RPAR', 'MUL', 'INTERP_END', 'BANG_IMPORTANT', 'DIV', 'LE', 'URL_FUNCTION', 'INTERP_START', 'COLOR', 'NE', 'LT', 'NUM', '":"', 'LITERAL_FUNCTION', 'GT', 'END', 'SIGN', 'BAREWORD', 'GE', 'FNCT', 'VAR', 'EQ', 'AND', 'ADD', 'SINGLE_QUOTE', 'NOT', 'MOD', 'OR', '","'])
+ m_expr_chks = frozenset(['MUL', 'DIV', 'MOD'])
goal_interpolated_anything_rsts = frozenset(['END', 'INTERP_START'])
interpolated_bare_url_rsts = frozenset(['RPAR', 'INTERP_START'])
argspec_items_chks = frozenset(['KWVAR', 'LPAR', 'DOUBLE_QUOTE', 'VAR', 'URL_FUNCTION', 'BAREWORD', 'COLOR', 'ALPHA_FUNCTION', 'INTERP_START', 'SIGN', 'LITERAL_FUNCTION', 'ADD', 'NUM', 'FNCT', 'NOT', 'BANG_IMPORTANT', 'SINGLE_QUOTE'])
diff --git a/scss/tests/test_expression.py b/scss/tests/test_expression.py
index d895948..33adfe2 100644
--- a/scss/tests/test_expression.py
+++ b/scss/tests/test_expression.py
@@ -55,6 +55,11 @@ def test_reference_operations():
assert calc('#{$font-size}/#{$line-height}') == String('12px / 30px')
# uses #{}; does no division
+ # Modulo
+ assert calc('29 % 12') == Number(5)
+ assert calc('29px % 12') == Number(5, 'px')
+ assert calc('29px % 12px') == Number(5, 'px')
+
# Color operations
ns.set_variable('$translucent-red', Color.from_rgb(1, 0, 0, 0.5))
ns.set_variable('$green', Color.from_name('lime'))
diff --git a/scss/types.py b/scss/types.py
index 092f93b..54345da 100644
--- a/scss/types.py
+++ b/scss/types.py
@@ -396,6 +396,24 @@ class Number(Value):
return Number(amount, unit_numer=numer, unit_denom=denom)
+ def __mod__(self, other):
+ if not isinstance(other, Number):
+ return NotImplemented
+
+ amount = self.value % other.value
+
+ if self.is_unitless:
+ return Number(amount)
+
+ if not other.is_unitless:
+ left = self.to_base_units()
+ right = other.to_base_units()
+
+ if left.unit_numer != right.unit_numer or left.unit_denom != right.unit_denom:
+ raise ValueError("Can't reconcile units: %r and %r" % (self, other))
+
+ return Number(amount, unit_numer=self.unit_numer, unit_denom=self.unit_denom)
+
def __add__(self, other):
# Numbers auto-cast to strings when added to other strings
if isinstance(other, String):