diff options
author | Eevee (Alex Munroe) <eevee.git@veekun.com> | 2014-09-05 15:00:16 -0700 |
---|---|---|
committer | Eevee (Alex Munroe) <eevee.git@veekun.com> | 2014-09-05 15:00:16 -0700 |
commit | d9c1b07f94d3d5bf6a7ed07e5254a313b6c991fc (patch) | |
tree | b8a817c96a92b1848ed6970c72b95f195b7ff253 /scss/expression.py | |
parent | 9b1ec745356183305f8e2385b78b77b959885867 (diff) | |
download | pyscss-d9c1b07f94d3d5bf6a7ed07e5254a313b6c991fc.tar.gz |
scss.expression -> scss.calculator
Diffstat (limited to 'scss/expression.py')
-rw-r--r-- | scss/expression.py | 184 |
1 files changed, 0 insertions, 184 deletions
diff --git a/scss/expression.py b/scss/expression.py deleted file mode 100644 index 39eb623..0000000 --- a/scss/expression.py +++ /dev/null @@ -1,184 +0,0 @@ -from __future__ import absolute_import -from __future__ import print_function -from __future__ import unicode_literals - -import sys -import logging -from warnings import warn - -import six - -from scss.ast import Literal -from scss.cssdefs import _expr_glob_re, _interpolate_re -from scss.errors import SassError, SassEvaluationError, SassParseError -from scss.grammar.expression import SassExpression, SassExpressionScanner -from scss.rule import Namespace -from scss.types import String -from scss.types import Value -from scss.util import dequote - - -log = logging.getLogger(__name__) - - -class Calculator(object): - """Expression evaluator.""" - - ast_cache = {} - - def __init__( - self, namespace=None, - ignore_parse_errors=False, - undefined_variables_fatal=True, - ): - if namespace is None: - self.namespace = Namespace() - else: - self.namespace = namespace - - self.ignore_parse_errors = ignore_parse_errors - self.undefined_variables_fatal = undefined_variables_fatal - - def _pound_substitute(self, result): - expr = result.group(1) - value = self.evaluate_expression(expr) - - if value is None: - return self.apply_vars(expr) - elif value.is_null: - return "" - else: - return dequote(value.render()) - - def do_glob_math(self, cont): - """Performs #{}-interpolation. The result is always treated as a fixed - syntactic unit and will not be re-evaluated. - """ - # TODO that's a lie! this should be in the parser for most cases. - if not isinstance(cont, six.string_types): - warn(FutureWarning( - "do_glob_math was passed a non-string {0!r} " - "-- this will no longer be supported in pyScss 2.0" - .format(cont) - )) - cont = six.text_type(cont) - if '#{' not in cont: - return cont - cont = _expr_glob_re.sub(self._pound_substitute, cont) - return cont - - def apply_vars(self, cont): - # TODO this is very complicated. it should go away once everything - # valid is actually parseable. - if isinstance(cont, six.string_types) and '$' in cont: - try: - # Optimization: the full cont is a variable in the context, - cont = self.namespace.variable(cont) - except KeyError: - # Interpolate variables: - def _av(m): - v = None - n = m.group(2) - try: - v = self.namespace.variable(n) - except KeyError: - if self.undefined_variables_fatal: - raise SyntaxError("Undefined variable: '%s'." % n) - else: - log.error("Undefined variable '%s'", n, extra={'stack': True}) - return n - else: - if v: - if not isinstance(v, Value): - raise TypeError( - "Somehow got a variable {0!r} " - "with a non-Sass value: {1!r}" - .format(n, v) - ) - v = v.render() - # TODO this used to test for _dequote - if m.group(1): - v = dequote(v) - else: - v = m.group(0) - return v - - cont = _interpolate_re.sub(_av, cont) - - else: - # Variable succeeded, so we need to render it - cont = cont.render() - # TODO this is surprising and shouldn't be here - cont = self.do_glob_math(cont) - return cont - - def calculate(self, expression, divide=False): - expression = self.evaluate_expression(expression, divide=divide) - - if expression is None: - return String.unquoted(self.apply_vars(expression)) - - return expression - - # TODO only used by magic-import...? - def interpolate(self, var): - value = self.namespace.variable(var) - if var != value and isinstance(value, six.string_types): - _vi = self.evaluate_expression(value) - if _vi is not None: - value = _vi - return value - - def evaluate_expression(self, expr, divide=False): - try: - ast = self.parse_expression(expr) - except SassError as e: - if self.ignore_parse_errors: - return None - raise - - try: - return ast.evaluate(self, divide=divide) - except Exception as e: - six.reraise(SassEvaluationError, SassEvaluationError(e, expression=expr), sys.exc_info()[2]) - - def parse_expression(self, expr, target='goal'): - if isinstance(expr, six.text_type): - # OK - pass - elif isinstance(expr, six.binary_type): - # Dubious - warn(FutureWarning( - "parse_expression was passed binary data {0!r} " - "-- this will no longer be supported in pyScss 2.0" - .format(expr) - )) - # Don't guess an encoding; you reap what you sow - expr = six.text_type(expr) - else: - raise TypeError("Expected string, got %r" % (expr,)) - - key = (target, expr) - if key in self.ast_cache: - return self.ast_cache[key] - - try: - parser = SassExpression(SassExpressionScanner(expr)) - ast = getattr(parser, target)() - except SyntaxError as e: - raise SassParseError(e, expression=expr, expression_pos=parser._char_pos) - - self.ast_cache[key] = ast - return ast - - def parse_interpolations(self, string): - """Parse a string for interpolations, but don't treat anything else as - Sass syntax. Returns an AST node. - """ - # Shortcut: if there are no #s in the string in the first place, it - # must not have any interpolations, right? - if '#' not in string: - return Literal(String.unquoted(string)) - return self.parse_expression(string, 'goal_interpolated_anything') - -__all__ = ('Calculator',) |