summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--scss/__init__.py91
-rw-r--r--scss/expression.py102
-rw-r--r--scss/src/grammar/grammar.g2
-rw-r--r--scss/src/grammar/grammar.py2
4 files changed, 101 insertions, 96 deletions
diff --git a/scss/__init__.py b/scss/__init__.py
index a07a023..cffac18 100644
--- a/scss/__init__.py
+++ b/scss/__init__.py
@@ -58,15 +58,14 @@ from scss.cssdefs import (
SEPARATOR,
_colors,
_short_color_re, _reverse_colors, _reverse_colors_re, _colors_re,
- _expr_glob_re,
_ml_comment_re, _sl_comment_re,
_zero_units_re, _zero_re,
- _escape_chars_re, _interpolate_re,
+ _escape_chars_re,
_spaces_re, _expand_rules_space_re, _collapse_properties_space_re,
_undefined_re,
_strings_re, _prop_split_re,
)
-from scss.expression import eval_expr, interpolate
+from scss.expression import Calculator, interpolate
from scss.functions import ALL_BUILTINS_LIBRARY
from scss.functions.compass.sprites import sprite_map
from scss.rule import UnparsedBlock, SassRule
@@ -1443,89 +1442,3 @@ class Scss(object):
# remove zeros before decimal point (i.e. 0.3 -> .3)
cont = _zero_re.sub('.', cont)
return cont
-
-
-class Calculator(object):
- def __init__(self, library):
- # TODO the library should really be part of the rule
- self.library = library
-
- def _calculate_expr(self, rule, context, options):
- def __calculate_expr(result):
- _group0 = result.group(1)
- _base_str = _group0
- better_expr_str = eval_expr(_base_str, rule, self.library)
-
- if better_expr_str is None:
- better_expr_str = self.apply_vars(_base_str, rule, context, options)
- else:
- better_expr_str = dequote(str(better_expr_str))
-
- return better_expr_str
- return __calculate_expr
-
- def do_glob_math(self, cont, rule, context=None, options=None):
- """Performs #{}-interpolation. The result is always treated as a fixed
- syntactic unit and will not be re-evaluated.
- """
- cont = str(cont)
- if '#{' not in cont:
- return cont
- cont = _expr_glob_re.sub(self._calculate_expr(rule, context, options), cont)
- return cont
-
- def apply_vars(self, cont, rule, context=None, options=None):
- if context is not None and isinstance(cont, basestring) and '$' in cont:
- if cont in context:
- # Optimization: the full cont is a variable in the context,
- # flatten the interpolation and use it:
- while isinstance(cont, basestring) and cont in context:
- _cont = context[cont]
- if _cont == cont:
- break
- cont = _cont
- else:
- # Flatten the context (no variables mapping to variables)
- flat_context = {}
- for k, v in context.items():
- while isinstance(v, basestring) and v in context:
- _v = context[v]
- if _v == v:
- break
- v = _v
- flat_context[k] = v
-
- # Interpolate variables:
- def _av(m):
- v = flat_context.get(normalize_var(m.group(2)))
- if v:
- v = to_str(v)
- if m.group(1):
- v = dequote(v)
- elif v is not None:
- v = to_str(v)
- else:
- v = m.group(0)
- return v
-
- cont = _interpolate_re.sub(_av, cont)
- if options is not None:
- # ...apply math:
- cont = self.do_glob_math(cont, rule, context, options)
- return cont
-
- def calculate(self, _base_str, rule, context=None, options=None):
- better_expr_str = _base_str
-
- rule = rule.copy()
- rule.context = context
- rule.options = options
-
- better_expr_str = self.do_glob_math(better_expr_str, rule, context, options)
-
- better_expr_str = eval_expr(better_expr_str, rule, self.library, True)
-
- if better_expr_str is None:
- better_expr_str = self.apply_vars(_base_str, rule, context, options)
-
- return better_expr_str
diff --git a/scss/expression.py b/scss/expression.py
index 6f85087..e16d70c 100644
--- a/scss/expression.py
+++ b/scss/expression.py
@@ -6,9 +6,9 @@ import operator
import re
import scss.config as config
-from scss.cssdefs import is_builtin_css_function, _undefined_re, _units, _variable_re
+from scss.cssdefs import is_builtin_css_function, _expr_glob_re, _interpolate_re, _units, _variable_re
from scss.types import BooleanValue, ColorValue, ListValue, NumberValue, ParserValue, QuotedStringValue, StringValue
-from scss.util import normalize_var, to_str
+from scss.util import dequote, normalize_var, to_str
################################################################################
# Load C acceleration modules
@@ -23,6 +23,97 @@ log = logging.getLogger(__name__)
ast_cache = {}
+class Calculator(object):
+ """Expression evaluator."""
+
+ def __init__(self, library):
+ # TODO the library should really be part of the rule
+ self.library = library
+
+ def _calculate_expr(self, rule, context, options):
+ def __calculate_expr(result):
+ _group0 = result.group(1)
+ _base_str = _group0
+ better_expr_str = eval_expr(_base_str, rule, self.library)
+
+ if better_expr_str is None:
+ better_expr_str = self.apply_vars(_base_str, rule, context, options)
+ else:
+ better_expr_str = dequote(str(better_expr_str))
+
+ return better_expr_str
+ return __calculate_expr
+
+ def do_glob_math(self, cont, rule, context=None, options=None):
+ """Performs #{}-interpolation. The result is always treated as a fixed
+ syntactic unit and will not be re-evaluated.
+ """
+ # TODO this should really accept and/or parse an *expression* and
+ # return a type :|
+ cont = str(cont)
+ if '#{' not in cont:
+ return cont
+ cont = _expr_glob_re.sub(self._calculate_expr(rule, context, options), cont)
+ return cont
+
+ def apply_vars(self, cont, rule, context=None, options=None):
+ if context is not None and isinstance(cont, basestring) and '$' in cont:
+ if cont in context:
+ # Optimization: the full cont is a variable in the context,
+ # flatten the interpolation and use it:
+ while isinstance(cont, basestring) and cont in context:
+ _cont = context[cont]
+ if _cont == cont:
+ break
+ cont = _cont
+ else:
+ # Flatten the context (no variables mapping to variables)
+ flat_context = {}
+ for k, v in context.items():
+ while isinstance(v, basestring) and v in context:
+ _v = context[v]
+ if _v == v:
+ break
+ v = _v
+ flat_context[k] = v
+
+ # Interpolate variables:
+ def _av(m):
+ v = flat_context.get(normalize_var(m.group(2)))
+ if v:
+ v = to_str(v)
+ # TODO this used to test for _dequote
+ if m.group(1):
+ v = dequote(v)
+ elif v is not None:
+ v = to_str(v)
+ else:
+ v = m.group(0)
+ return v
+
+ cont = _interpolate_re.sub(_av, cont)
+ if options is not None:
+ # ...apply math:
+ cont = self.do_glob_math(cont, rule, context, options)
+ return cont
+
+ def calculate(self, _base_str, rule, context=None, options=None):
+ better_expr_str = _base_str
+
+ rule = rule.copy()
+ rule.context = context
+ rule.options = options
+
+ better_expr_str = self.do_glob_math(better_expr_str, rule, context, options)
+
+ better_expr_str = eval_expr(better_expr_str, rule, self.library, True)
+
+ if better_expr_str is None:
+ better_expr_str = self.apply_vars(_base_str, rule, context, options)
+
+ return better_expr_str
+
+
def interpolate(var, rule, library):
value = rule.context.get(normalize_var(var), var)
if var != value and isinstance(value, basestring):
@@ -60,7 +151,7 @@ def eval_expr(expr, rule, library, raw=False):
results = ast.evaluate(rule, library)
else:
try:
- P = Calculator(CalculatorScanner())
+ P = CalculatorParser(CalculatorScanner())
P.reset(expr)
ast = P.goal()
except SyntaxError:
@@ -82,6 +173,7 @@ def eval_expr(expr, rule, library, raw=False):
return results
+# ------------------------------------------------------------------------------
# Expression classes -- the AST resulting from a parse
class Expression(object):
@@ -311,7 +403,7 @@ class CalculatorScanner(Scanner):
super(CalculatorScanner, self).__init__(None, ['[ \r\t\n]+'], input)
-class Calculator(Parser):
+class CalculatorParser(Parser):
def goal(self):
expr_lst = self.expr_lst()
v = expr_lst
@@ -524,4 +616,4 @@ class Calculator(Parser):
### Grammar ends.
################################################################################
-__all__ = ('interpolate', 'call', 'eval_expr', 'Calculator')
+__all__ = ('interpolate', 'eval_expr', 'Calculator')
diff --git a/scss/src/grammar/grammar.g b/scss/src/grammar/grammar.g
index 0057dce..ef0f79c 100644
--- a/scss/src/grammar/grammar.g
+++ b/scss/src/grammar/grammar.g
@@ -4,7 +4,7 @@
#'(?<!\\s)(?:' + '|'.join(_units) + ')(?![-\\w])'
## Grammar compiled using Yapps:
%%
-parser Calculator:
+parser CalculatorParser:
ignore: "[ \r\t\n]+"
token COMMA: ","
token LPAR: "\\(|\\["
diff --git a/scss/src/grammar/grammar.py b/scss/src/grammar/grammar.py
index f03f727..2afd4d5 100644
--- a/scss/src/grammar/grammar.py
+++ b/scss/src/grammar/grammar.py
@@ -54,7 +54,7 @@ class CalculatorScanner(Scanner):
super(CalculatorScanner, self).__init__(None, ['[ \r\t\n]+'], input)
-class Calculator(Parser):
+class CalculatorParser(Parser):
def goal(self):
expr_lst = self.expr_lst()
v = expr_lst