1
from __future__ import absolute_import
2
from __future__ import print_function
4
from functools import partial
11
import scss.config as config
12
from scss.cssdefs import COLOR_NAMES, is_builtin_css_function, _expr_glob_re, _interpolate_re
13
from scss.errors import SassError, SassEvaluationError, SassParseError
14
from scss.rule import Namespace
15
from scss.types import Boolean, Color, List, Map, Null, Number, String, Undefined, Value
16
from scss.util import dequote, normalize_var
18
################################################################################
19
# Load C acceleration modules
22
from scss._speedups import Scanner
24
from scss._native import Scanner
26
log = logging.getLogger(__name__)
29
class Calculator(object):
30
"""Expression evaluator."""
34
def __init__(self, namespace=None):
36
self.namespace = Namespace()
38
self.namespace = namespace
40
def _pound_substitute(self, result):
41
expr = result.group(1)
42
value = self.evaluate_expression(expr)
45
return self.apply_vars(expr)
49
return dequote(value.render())
51
def do_glob_math(self, cont):
52
"""Performs #{}-interpolation. The result is always treated as a fixed
53
syntactic unit and will not be re-evaluated.
55
# TODO that's a lie! this should be in the parser for most cases.
59
cont = _expr_glob_re.sub(self._pound_substitute, cont)
62
def apply_vars(self, cont):
63
# TODO this is very complicated. it should go away once everything
64
# valid is actually parseable.
65
if isinstance(cont, six.string_types) and '$' in cont:
67
# Optimization: the full cont is a variable in the context,
68
cont = self.namespace.variable(cont)
70
# Interpolate variables:
75
v = self.namespace.variable(n)
77
if config.FATAL_UNDEFINED:
78
raise SyntaxError("Undefined variable: '%s'." % n)
80
if config.VERBOSITY > 1:
81
log.error("Undefined variable '%s'", n, extra={'stack': True})
85
if not isinstance(v, six.string_types):
87
# TODO this used to test for _dequote
94
cont = _interpolate_re.sub(_av, cont)
95
# TODO this is surprising and shouldn't be here
96
cont = self.do_glob_math(cont)
99
def calculate(self, _base_str, divide=False):
100
better_expr_str = _base_str
102
better_expr_str = self.do_glob_math(better_expr_str)
104
better_expr_str = self.evaluate_expression(better_expr_str, divide=divide)
106
if better_expr_str is None:
107
better_expr_str = String.unquoted(self.apply_vars(_base_str))
109
return better_expr_str
111
# TODO only used by magic-import...?
112
def interpolate(self, var):
113
value = self.namespace.variable(var)
114
if var != value and isinstance(value, six.string_types):
115
_vi = self.evaluate_expression(value)
120
def evaluate_expression(self, expr, divide=False):
122
ast = self.parse_expression(expr)
130
return ast.evaluate(self, divide=divide)
131
except Exception as e:
132
raise SassEvaluationError(e, expression=expr)
134
def parse_expression(self, expr, target='goal'):
135
if not isinstance(expr, six.string_types):
136
raise TypeError("Expected string, got %r" % (expr,))
139
if key in self.ast_cache:
140
return self.ast_cache[key]
143
parser = SassExpression(SassExpressionScanner(expr))
144
ast = getattr(parser, target)()
145
except SyntaxError as e:
146
raise SassParseError(e, expression=expr, expression_pos=parser._char_pos)
148
self.ast_cache[key] = ast
152
# ------------------------------------------------------------------------------
153
# Expression classes -- the AST resulting from a parse
155
class Expression(object):
157
return '<%s()>' % (self.__class__.__name__)
159
def evaluate(self, calculator, divide=False):
160
"""Evaluate this AST node, and return a Sass value.
162
`divide` indicates whether a descendant node representing a division
163
should be forcibly treated as a division. See the commentary in
166
raise NotImplementedError
169
class Parentheses(Expression):
170
"""An expression of the form `(foo)`.
172
Only exists to force a slash to be interpreted as division when contained
176
return '<%s(%s)>' % (self.__class__.__name__, repr(self.contents))
178
def __init__(self, contents):
179
self.contents = contents
181
def evaluate(self, calculator, divide=False):
182
return self.contents.evaluate(calculator, divide=True)
185
class UnaryOp(Expression):
187
return '<%s(%s, %s)>' % (self.__class__.__name__, repr(self.op), repr(self.operand))
189
def __init__(self, op, operand):
191
self.operand = operand
193
def evaluate(self, calculator, divide=False):
194
return self.op(self.operand.evaluate(calculator, divide=True))
197
class BinaryOp(Expression):
199
return '<%s(%s, %s, %s)>' % (self.__class__.__name__, repr(self.op), repr(self.left), repr(self.right))
201
def __init__(self, op, left, right):
206
def evaluate(self, calculator, divide=False):
207
left = self.left.evaluate(calculator, divide=True)
208
right = self.right.evaluate(calculator, divide=True)
210
# Special handling of division: treat it as a literal slash if both
211
# operands are literals, there are parentheses, or this is part of a
213
# The first condition is covered by the type check. The other two are
214
# covered by the `divide` argument: other nodes that perform arithmetic
215
# will pass in True, indicating that this should always be a division.
217
self.op is operator.truediv
219
and isinstance(self.left, Literal)
220
and isinstance(self.right, Literal)
222
return String(left.render() + ' / ' + right.render(), quotes=None)
224
return self.op(left, right)
227
class AnyOp(Expression):
229
return '<%s(*%s)>' % (self.__class__.__name__, repr(self.op), repr(self.operands))
231
def __init__(self, *operands):
232
self.operands = operands
234
def evaluate(self, calculator, divide=False):
235
for operand in self.operands:
236
value = operand.evaluate(calculator, divide=True)
242
class AllOp(Expression):
244
return '<%s(*%s)>' % (self.__class__.__name__, repr(self.operands))
246
def __init__(self, *operands):
247
self.operands = operands
249
def evaluate(self, calculator, divide=False):
250
for operand in self.operands:
251
value = operand.evaluate(calculator, divide=True)
257
class NotOp(Expression):
259
return '<%s(%s)>' % (self.__class__.__name__, repr(self.operand))
261
def __init__(self, operand):
262
self.operand = operand
264
def evaluate(self, calculator, divide=False):
265
operand = self.operand.evaluate(calculator, divide=True)
266
return Boolean(not(operand))
269
class CallOp(Expression):
271
return '<%s(%s, %s)>' % (self.__class__.__name__, repr(self.func_name), repr(self.argspec))
273
def __init__(self, func_name, argspec):
274
self.func_name = func_name
275
self.argspec = argspec
277
def evaluate(self, calculator, divide=False):
278
# TODO bake this into the context and options "dicts", plus library
279
func_name = normalize_var(self.func_name)
281
argspec_node = self.argspec
283
# Turn the pairs of arg tuples into *args and **kwargs
284
# TODO unclear whether this is correct -- how does arg, kwarg, arg
286
args, kwargs = argspec_node.evaluate_call_args(calculator)
287
argspec_len = len(args) + len(kwargs)
289
# Translate variable names to Python identifiers
290
# TODO what about duplicate kw names? should this happen in argspec?
291
# how does that affect mixins?
293
(key.lstrip('$').replace('-', '_'), value)
294
for key, value in kwargs.items())
296
# TODO merge this with the library
299
funct = calculator.namespace.function(func_name, argspec_len)
300
# @functions take a ns as first arg. TODO: Python functions possibly
302
if getattr(funct, '__name__', None) == '__call':
303
funct = partial(funct, calculator.namespace)
306
# DEVIATION: Fall back to single parameter
307
funct = calculator.namespace.function(func_name, 1)
308
args = [List(args, use_comma=True)]
310
if not is_builtin_css_function(func_name):
311
log.error("Function not found: %s:%s", func_name, argspec_len, extra={'stack': True})
314
ret = funct(*args, **kwargs)
315
if not isinstance(ret, Value):
316
raise TypeError("Expected Sass type as return value, got %r" % (ret,))
319
# No matching function found, so render the computed values as a CSS
320
# function call. Slurpy arguments are expanded and named arguments are
323
raise TypeError("The CSS function %s doesn't support keyword arguments." % (func_name,))
325
# TODO another candidate for a "function call" sass type
326
rendered_args = [arg.render() for arg in args]
329
u"%s(%s)" % (func_name, u", ".join(rendered_args)),
333
class Literal(Expression):
335
return '<%s(%s)>' % (self.__class__.__name__, repr(self.value))
337
def __init__(self, value):
338
if isinstance(value, Undefined) and config.FATAL_UNDEFINED:
339
raise SyntaxError("Undefined literal.")
343
def evaluate(self, calculator, divide=False):
347
class Variable(Expression):
349
return '<%s(%s)>' % (self.__class__.__name__, repr(self.name))
351
def __init__(self, name):
354
def evaluate(self, calculator, divide=False):
356
value = calculator.namespace.variable(self.name)
358
if config.FATAL_UNDEFINED:
359
raise SyntaxError("Undefined variable: '%s'." % self.name)
361
if config.VERBOSITY > 1:
362
log.error("Undefined variable '%s'", self.name, extra={'stack': True})
365
if isinstance(value, six.string_types):
366
evald = calculator.evaluate_expression(value)
367
if evald is not None:
372
class ListLiteral(Expression):
374
return '<%s(%s, comma=%s)>' % (self.__class__.__name__, repr(self.items), repr(self.comma))
376
def __init__(self, items, comma=True):
380
def evaluate(self, calculator, divide=False):
381
items = [item.evaluate(calculator, divide=divide) for item in self.items]
383
# Whether this is a "plain" literal matters for null removal: nulls are
384
# left alone if this is a completely vanilla CSS property
387
# TODO sort of overloading "divide" here... rename i think
389
elif not all(isinstance(item, Literal) for item in self.items):
392
return List(items, use_comma=self.comma, is_literal=is_literal)
395
class MapLiteral(Expression):
397
return '<%s(%s)>' % (self.__class__.__name__, repr(self.pairs))
399
def __init__(self, pairs):
400
self.pairs = tuple((var, value) for var, value in pairs if value is not None)
402
def evaluate(self, calculator, divide=False):
404
for key, value in self.pairs:
406
key.evaluate(calculator),
407
value.evaluate(calculator),
410
return Map(scss_pairs)
413
class ArgspecLiteral(Expression):
414
"""Contains pairs of argument names and values, as parsed from a function
415
definition or function call.
417
Note that the semantics are somewhat ambiguous. Consider parsing:
421
If this appeared in a function call, $foo would refer to a value; if it
422
appeared in a function definition, $foo would refer to an existing
423
variable. This it's up to the caller to use the right iteration function.
426
return '<%s(%s)>' % (self.__class__.__name__, repr(self.argpairs))
428
def __init__(self, argpairs, slurp=None):
429
# argpairs is a list of 2-tuples, parsed as though this were a function
430
# call, so (variable name as string or None, default value as AST
432
# slurp is the name of a variable to receive slurpy arguments.
433
self.argpairs = tuple(argpairs)
435
# DEVIATION: special syntax to allow injecting arbitrary arguments
436
# from the caller to the callee
441
self.slurp = Variable(slurp)
446
def iter_list_argspec(self):
447
yield None, ListLiteral(zip(*self.argpairs)[1])
449
def iter_def_argspec(self):
450
"""Interpreting this literal as a function definition, yields pairs of
451
(variable name as a string, default value as an AST node or None).
453
started_kwargs = False
456
for var, value in self.argpairs:
458
# value is actually the name
464
"Required argument %r must precede optional arguments"
468
started_kwargs = True
470
if not isinstance(var, Variable):
471
raise SyntaxError("Expected variable name, got %r" % (var,))
473
if var.name in seen_vars:
474
raise SyntaxError("Duplicate argument %r" % (var.name,))
475
seen_vars.add(var.name)
477
yield var.name, value
479
def evaluate_call_args(self, calculator):
480
"""Interpreting this literal as a function call, return a 2-tuple of
485
for var_node, value_node in self.argpairs:
486
value = value_node.evaluate(calculator, divide=True)
492
if not isinstance(var_node, Variable):
493
raise SyntaxError("Expected variable name, got %r" % (var_node,))
494
kwargs[var_node.name] = value
496
# Slurpy arguments go on the end of the args
498
args.extend(self.slurp.evaluate(calculator, divide=True))
503
def parse_bareword(word):
504
if word in COLOR_NAMES:
505
return Color.from_name(word)
508
elif word == 'undefined':
512
elif word == 'false':
513
return Boolean(False)
515
return String(word, quotes=None)
518
class Parser(object):
519
def __init__(self, scanner):
520
self._scanner = scanner
524
def reset(self, input):
525
self._scanner.reset(input)
529
def _peek(self, types):
531
Returns the token type for lookahead; if there are any args
532
then the list of args is the set of token types to allow
535
tok = self._scanner.token(self._pos, types)
540
def _scan(self, type):
542
Returns the matched text, and moves to the next token
544
tok = self._scanner.token(self._pos, set([type]))
545
self._char_pos = tok[0]
547
raise SyntaxError("SyntaxError[@ char %s: %s]" % (repr(tok[0]), "Trying to find " + type))
552
################################################################################
553
## Grammar compiled using Yapps:
555
class SassExpressionScanner(Scanner):
560
('[ \r\t\n]+', '[ \r\t\n]+'),
568
('SIGN', '-(?![a-zA-Z_])'),
569
('AND', '(?<![-\\w])and(?![-\\w])'),
570
('OR', '(?<![-\\w])or(?![-\\w])'),
571
('NOT', '(?<![-\\w])not(?![-\\w])'),
579
('DOTDOTDOT', '[.]{3}'),
580
('KWSTR', "'[^']*'(?=\\s*:)"),
582
('KWQSTR', '"[^"]*"(?=\\s*:)'),
584
('UNITS', '(?<!\\s)(?:[a-zA-Z]+|%)(?![-\\w])'),
585
('KWNUM', '(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?=\\s*:)'),
586
('NUM', '(?:\\d+(?:\\.\\d*)?|\\.\\d+)'),
587
('KWCOLOR', '#(?:[a-fA-F0-9]{6}|[a-fA-F0-9]{3})(?![a-fA-F0-9])(?=\\s*:)'),
588
('COLOR', '#(?:[a-fA-F0-9]{6}|[a-fA-F0-9]{3})(?![a-fA-F0-9])'),
589
('KWVAR', '\\$[-a-zA-Z0-9_]+(?=\\s*:)'),
590
('SLURPYVAR', '\\$[-a-zA-Z0-9_]+(?=[.][.][.])'),
591
('VAR', '\\$[-a-zA-Z0-9_]+'),
592
('FNCT', '[-a-zA-Z_][-a-zA-Z0-9_]*(?=\\()'),
593
('KWID', '[-a-zA-Z_][-a-zA-Z0-9_]*(?=\\s*:)'),
594
('ID', '[-a-zA-Z_][-a-zA-Z0-9_]*'),
595
('BANG_IMPORTANT', '!important'),
598
def __init__(self, input=None):
599
if hasattr(self, 'setup_patterns'):
600
self.setup_patterns(self._patterns)
601
elif self.patterns is None:
602
self.__class__.patterns = []
603
for t, p in self._patterns:
604
self.patterns.append((t, re.compile(p)))
605
super(SassExpressionScanner, self).__init__(None, ['[ \r\t\n]+'], input)
608
class SassExpression(Parser):
610
expr_lst = self.expr_lst()
611
END = self._scan('END')
614
def goal_argspec(self):
615
argspec = self.argspec()
616
END = self._scan('END')
620
_token_ = self._peek(self.argspec_rsts)
621
if _token_ not in self.argspec_chks:
622
if self._peek(self.argspec_rsts_) not in self.argspec_chks_:
623
argspec_items = self.argspec_items()
624
args, slurpy = argspec_items
625
return ArgspecLiteral(args, slurp=slurpy)
626
return ArgspecLiteral([])
627
elif _token_ == 'SLURPYVAR':
628
SLURPYVAR = self._scan('SLURPYVAR')
629
DOTDOTDOT = self._scan('DOTDOTDOT')
630
return ArgspecLiteral([], slurp=SLURPYVAR)
631
else: # == 'DOTDOTDOT'
632
DOTDOTDOT = self._scan('DOTDOTDOT')
633
return ArgspecLiteral([], slurp=all)
635
def argspec_items(self):
637
argspec_item = self.argspec_item()
638
args = [argspec_item]
639
if self._peek(self.argspec_items_rsts) == '","':
641
if self._peek(self.argspec_items_rsts_) not in self.argspec_chks_:
642
_token_ = self._peek(self.argspec_items_rsts__)
643
if _token_ == 'SLURPYVAR':
644
SLURPYVAR = self._scan('SLURPYVAR')
645
DOTDOTDOT = self._scan('DOTDOTDOT')
647
elif _token_ == 'DOTDOTDOT':
648
DOTDOTDOT = self._scan('DOTDOTDOT')
650
else: # in self.argspec_items_chks
651
argspec_items = self.argspec_items()
652
more_args, slurpy = argspec_items
653
args.extend(more_args)
656
def argspec_item(self):
657
_token_ = self._peek(self.argspec_items_chks)
658
if _token_ == 'KWVAR':
659
KWVAR = self._scan('KWVAR')
661
expr_slst = self.expr_slst()
662
return (Variable(KWVAR), expr_slst)
663
else: # in self.argspec_item_chks
664
expr_slst = self.expr_slst()
665
return (None, expr_slst)
668
map_item = self.map_item()
670
while self._peek(self.expr_map_rsts) == '","':
672
map_item = (None, None)
673
if self._peek(self.expr_map_rsts_) not in self.expr_map_rsts:
674
map_item = self.map_item()
675
pairs.append(map_item)
676
return MapLiteral(pairs)
679
kwatom = self.kwatom()
681
expr_slst = self.expr_slst()
682
return (kwatom, expr_slst)
685
expr_slst = self.expr_slst()
687
while self._peek(self.argspec_items_rsts) == '","':
689
expr_slst = self.expr_slst()
691
return ListLiteral(v) if len(v) > 1 else v[0]
694
or_expr = self.or_expr()
696
while self._peek(self.expr_slst_rsts) not in self.argspec_items_rsts:
697
or_expr = self.or_expr()
699
return ListLiteral(v, comma=False) if len(v) > 1 else v[0]
702
and_expr = self.and_expr()
704
while self._peek(self.or_expr_rsts) == 'OR':
705
OR = self._scan('OR')
706
and_expr = self.and_expr()
707
v = AnyOp(v, and_expr)
711
not_expr = self.not_expr()
713
while self._peek(self.and_expr_rsts) == 'AND':
714
AND = self._scan('AND')
715
not_expr = self.not_expr()
716
v = AllOp(v, not_expr)
720
_token_ = self._peek(self.argspec_item_chks)
722
comparison = self.comparison()
725
NOT = self._scan('NOT')
726
not_expr = self.not_expr()
727
return NotOp(not_expr)
729
def comparison(self):
730
a_expr = self.a_expr()
732
while self._peek(self.comparison_rsts) in self.comparison_chks:
733
_token_ = self._peek(self.comparison_chks)
735
LT = self._scan('LT')
736
a_expr = self.a_expr()
737
v = BinaryOp(operator.lt, v, a_expr)
738
elif _token_ == 'GT':
739
GT = self._scan('GT')
740
a_expr = self.a_expr()
741
v = BinaryOp(operator.gt, v, a_expr)
742
elif _token_ == 'LE':
743
LE = self._scan('LE')
744
a_expr = self.a_expr()
745
v = BinaryOp(operator.le, v, a_expr)
746
elif _token_ == 'GE':
747
GE = self._scan('GE')
748
a_expr = self.a_expr()
749
v = BinaryOp(operator.ge, v, a_expr)
750
elif _token_ == 'EQ':
751
EQ = self._scan('EQ')
752
a_expr = self.a_expr()
753
v = BinaryOp(operator.eq, v, a_expr)
755
NE = self._scan('NE')
756
a_expr = self.a_expr()
757
v = BinaryOp(operator.ne, v, a_expr)
761
m_expr = self.m_expr()
763
while self._peek(self.a_expr_rsts) in self.a_expr_chks:
764
_token_ = self._peek(self.a_expr_chks)
766
ADD = self._scan('ADD')
767
m_expr = self.m_expr()
768
v = BinaryOp(operator.add, v, m_expr)
770
SUB = self._scan('SUB')
771
m_expr = self.m_expr()
772
v = BinaryOp(operator.sub, v, m_expr)
776
u_expr = self.u_expr()
778
while self._peek(self.m_expr_rsts) in self.m_expr_chks:
779
_token_ = self._peek(self.m_expr_chks)
781
MUL = self._scan('MUL')
782
u_expr = self.u_expr()
783
v = BinaryOp(operator.mul, v, u_expr)
785
DIV = self._scan('DIV')
786
u_expr = self.u_expr()
787
v = BinaryOp(operator.truediv, v, u_expr)
791
_token_ = self._peek(self.u_expr_rsts)
792
if _token_ == 'SIGN':
793
SIGN = self._scan('SIGN')
794
u_expr = self.u_expr()
795
return UnaryOp(operator.neg, u_expr)
796
elif _token_ == 'ADD':
797
ADD = self._scan('ADD')
798
u_expr = self.u_expr()
799
return UnaryOp(operator.pos, u_expr)
800
else: # in self.u_expr_chks
805
_token_ = self._peek(self.u_expr_chks)
806
if _token_ == 'LPAR':
807
LPAR = self._scan('LPAR')
808
_token_ = self._peek(self.atom_rsts)
809
if _token_ not in self.argspec_item_chks:
810
expr_map = self.expr_map()
812
else: # in self.argspec_item_chks
813
expr_lst = self.expr_lst()
815
RPAR = self._scan('RPAR')
816
return Parentheses(v)
817
elif _token_ == 'FNCT':
818
FNCT = self._scan('FNCT')
819
LPAR = self._scan('LPAR')
820
argspec = self.argspec()
821
RPAR = self._scan('RPAR')
822
return CallOp(FNCT, argspec)
823
elif _token_ == 'BANG_IMPORTANT':
824
BANG_IMPORTANT = self._scan('BANG_IMPORTANT')
825
return Literal(String(BANG_IMPORTANT, quotes=None))
826
elif _token_ == 'ID':
827
ID = self._scan('ID')
828
return Literal(parse_bareword(ID))
829
elif _token_ == 'NUM':
830
NUM = self._scan('NUM')
832
if self._peek(self.atom_rsts_) == 'UNITS':
833
UNITS = self._scan('UNITS')
834
return Literal(Number(float(NUM), unit=UNITS))
835
elif _token_ == 'STR':
836
STR = self._scan('STR')
837
return Literal(String(STR[1:-1], quotes="'"))
838
elif _token_ == 'QSTR':
839
QSTR = self._scan('QSTR')
840
return Literal(String(QSTR[1:-1], quotes='"'))
841
elif _token_ == 'COLOR':
842
COLOR = self._scan('COLOR')
843
return Literal(Color.from_hex(COLOR, literal=True))
845
VAR = self._scan('VAR')
849
_token_ = self._peek(self.kwatom_rsts)
852
elif _token_ == 'KWID':
853
KWID = self._scan('KWID')
854
return Literal(parse_bareword(KWID))
855
elif _token_ == 'KWNUM':
856
KWNUM = self._scan('KWNUM')
858
if self._peek(self.kwatom_rsts_) == 'UNITS':
859
UNITS = self._scan('UNITS')
860
return Literal(Number(float(KWNUM), unit=UNITS))
861
elif _token_ == 'KWSTR':
862
KWSTR = self._scan('KWSTR')
863
return Literal(String(KWSTR[1:-1], quotes="'"))
864
elif _token_ == 'KWQSTR':
865
KWQSTR = self._scan('KWQSTR')
866
return Literal(String(KWQSTR[1:-1], quotes='"'))
867
elif _token_ == 'KWCOLOR':
868
KWCOLOR = self._scan('KWCOLOR')
869
return Literal(Color.from_hex(COLOR, literal=True))
871
KWVAR = self._scan('KWVAR')
872
return Variable(KWVAR)
874
u_expr_chks = set(['LPAR', 'COLOR', 'QSTR', 'NUM', 'FNCT', 'STR', 'VAR', 'BANG_IMPORTANT', 'ID'])
875
m_expr_rsts = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'MUL', 'DIV', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR', '","'])
876
argspec_items_rsts = set(['RPAR', 'END', '","'])
877
expr_map_rsts = set(['RPAR', '","'])
878
argspec_items_rsts__ = set(['KWVAR', 'LPAR', 'QSTR', 'SLURPYVAR', 'COLOR', 'DOTDOTDOT', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID'])
879
kwatom_rsts = set(['KWVAR', 'KWID', 'KWSTR', 'KWQSTR', 'KWCOLOR', '":"', 'KWNUM'])
880
argspec_item_chks = set(['LPAR', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID'])
881
a_expr_chks = set(['ADD', 'SUB'])
882
expr_slst_rsts = set(['LPAR', 'END', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', 'FNCT', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID', '","'])
883
or_expr_rsts = set(['LPAR', 'END', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', 'FNCT', 'STR', 'NOT', 'ID', 'BANG_IMPORTANT', 'OR', '","'])
884
and_expr_rsts = set(['AND', 'LPAR', 'END', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', 'FNCT', 'STR', 'NOT', 'ID', 'BANG_IMPORTANT', 'OR', '","'])
885
comparison_rsts = set(['LPAR', 'QSTR', 'RPAR', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'GT', 'END', 'SIGN', 'ADD', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'GE', 'NOT', 'OR', '","'])
886
argspec_chks = set(['DOTDOTDOT', 'SLURPYVAR'])
887
atom_rsts_ = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'VAR', 'MUL', 'DIV', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'UNITS', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR', '","'])
888
expr_map_rsts_ = set(['KWVAR', 'KWID', 'KWSTR', 'KWQSTR', 'RPAR', 'KWCOLOR', '":"', 'KWNUM', '","'])
889
u_expr_rsts = set(['LPAR', 'COLOR', 'QSTR', 'SIGN', 'ADD', 'NUM', 'FNCT', 'STR', 'VAR', 'BANG_IMPORTANT', 'ID'])
890
comparison_chks = set(['GT', 'GE', 'NE', 'LT', 'LE', 'EQ'])
891
argspec_items_rsts_ = set(['KWVAR', 'LPAR', 'QSTR', 'END', 'SLURPYVAR', 'COLOR', 'DOTDOTDOT', 'SIGN', 'VAR', 'ADD', 'NUM', 'RPAR', 'FNCT', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID'])
892
a_expr_rsts = set(['LPAR', 'SUB', 'QSTR', 'RPAR', 'BANG_IMPORTANT', 'LE', 'COLOR', 'NE', 'LT', 'NUM', 'GT', 'END', 'SIGN', 'GE', 'FNCT', 'STR', 'VAR', 'EQ', 'ID', 'AND', 'ADD', 'NOT', 'OR', '","'])
893
m_expr_chks = set(['MUL', 'DIV'])
894
kwatom_rsts_ = set(['UNITS', '":"'])
895
argspec_items_chks = set(['KWVAR', 'LPAR', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'STR', 'NOT', 'BANG_IMPORTANT', 'ID'])
896
argspec_rsts = set(['KWVAR', 'LPAR', 'BANG_IMPORTANT', 'END', 'SLURPYVAR', 'COLOR', 'DOTDOTDOT', 'RPAR', 'VAR', 'ADD', 'NUM', 'FNCT', 'STR', 'NOT', 'QSTR', 'SIGN', 'ID'])
897
atom_rsts = set(['KWVAR', 'KWID', 'KWSTR', 'BANG_IMPORTANT', 'LPAR', 'COLOR', 'KWQSTR', 'SIGN', 'KWCOLOR', 'VAR', 'ADD', 'NUM', '":"', 'STR', 'NOT', 'QSTR', 'KWNUM', 'ID', 'FNCT'])
898
argspec_chks_ = set(['END', 'RPAR'])
899
argspec_rsts_ = set(['KWVAR', 'LPAR', 'BANG_IMPORTANT', 'END', 'COLOR', 'QSTR', 'SIGN', 'VAR', 'ADD', 'NUM', 'FNCT', 'STR', 'NOT', 'RPAR', 'ID'])
903
################################################################################
905
__all__ = ('Calculator',)