summaryrefslogtreecommitdiff
path: root/scss/expression.py
diff options
context:
space:
mode:
authorEevee (Alex Munroe) <eevee.git@veekun.com>2014-09-05 15:00:16 -0700
committerEevee (Alex Munroe) <eevee.git@veekun.com>2014-09-05 15:00:16 -0700
commitd9c1b07f94d3d5bf6a7ed07e5254a313b6c991fc (patch)
treeb8a817c96a92b1848ed6970c72b95f195b7ff253 /scss/expression.py
parent9b1ec745356183305f8e2385b78b77b959885867 (diff)
downloadpyscss-d9c1b07f94d3d5bf6a7ed07e5254a313b6c991fc.tar.gz
scss.expression -> scss.calculator
Diffstat (limited to 'scss/expression.py')
-rw-r--r--scss/expression.py184
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',)