Sascha Peilicke d8cd1fa968 Don't switch operands for non-implemented operators.
Bot __truediv__(int, float) and __add__(int, float) raise
NotImplemented, but switching only helps in the case of addition.
Division isn't commutative so simply cast the int to float. Due to this,
lesscpy now generates the same result as lessc in the modified test
case.
2013-08-08 18:25:22 +02:00

164 lines
4.8 KiB
Python

# -*- coding: utf8 -*-
"""
.. module:: lesscpy.plib.expression
:synopsis: Expression node.
Copyright (c)
See LICENSE for details.
.. moduleauthor:: Johann T. Mariusson <jtm@robot.is>
"""
import sys
from .node import Node
from lesscpy.lessc import utility
from lesscpy.lessc import color
class Expression(Node):
"""Expression node. Parses all expression except
color expressions, (handled in the color class)
"""
def parse(self, scope):
""" Parse Node
args:
scope (Scope): Scope object
raises:
SyntaxError
returns:
str
"""
assert(len(self.tokens) == 3)
expr = self.process(self.tokens, scope)
expr = [self.neg(t, scope) for t in expr]
A, O, B = [e[0]
if isinstance(e, tuple)
else e
for e in expr
if str(e).strip()]
try:
a, ua = utility.analyze_number(A, 'Illegal element in expression')
b, ub = utility.analyze_number(B, 'Illegal element in expression')
except SyntaxError:
return ' '.join([str(A), str(O), str(B)])
if(a is False or b is False):
return ' '.join([str(A), str(O), str(B)])
if ua == 'color' or ub == 'color':
return color.Color().process((A, O, B))
out = self.operate(a, b, O)
if isinstance(out, bool):
return out
return self.with_units(out, ua, ub)
def neg(self, value, scope):
"""Apply negativity.
args:
value (mixed): test value
scope (Scope): Current scope
raises:
SyntaxError
returns:
str
"""
if value and isinstance(value, list) and value[0] == '-':
val = value[1]
if len(value) > 1 and hasattr(value[1], 'parse'):
val = value[1].parse(scope)
if isinstance(val, str):
return '-' + val
return -val
return value
def with_units(self, val, ua, ub):
"""Return value with unit.
args:
val (mixed): result
ua (str): 1st unit
ub (str): 2nd unit
raises:
SyntaxError
returns:
str
"""
if not val:
return str(val)
if ua or ub:
if ua and ub:
if ua == ub:
return str(val) + ua
else:
# Nodejs version does not seem to mind mismatched
# units within expressions. So we choose the first
# as they do
# raise SyntaxError("Error in expression %s != %s" % (ua, ub))
return str(val) + ua
elif ua:
return str(val) + ua
elif ub:
return str(val) + ub
return str(val)
def operate(self, vala, valb, oper):
"""Perform operation
args:
vala (mixed): 1st value
valb (mixed): 2nd value
oper (str): operation
returns:
mixed
"""
operation = {
'+': '__add__',
'-': '__sub__',
'*': '__mul__',
'/': '__truediv__',
'=': '__eq__',
'>': '__gt__',
'<': '__lt__',
'>=': '__ge__',
'<=': '__le__',
'!=': '__ne__',
'<>': '__ne__',
}.get(oper)
if sys.version_info[0] < 3:
ret = self.py2op(vala, operation, valb)
else:
ret = getattr(vala, operation)(valb)
if ret is NotImplemented:
# __truediv__(int, float) isn't implemented, but __truediv__(float, float) is.
# __add__(int, float) is similar. Simply cast vala to float:
ret = getattr(float(vala), operation)(valb)
if oper in '+-*/':
try:
if int(ret) == ret:
return int(ret)
except ValueError:
pass
return ret
def py2op(self, vala, operation, valb):
""" Python2 operators
"""
if operation == '__lt__':
ret = (vala < valb)
elif operation == '__gt__':
ret = (vala > valb)
elif operation == '__eq__':
ret = (vala == valb)
elif operation == '__ge__':
ret = (vala >= valb)
elif operation == '__le__':
ret = (vala <= valb)
elif operation == '__ne__':
ret = (vala != valb)
else:
ret = getattr(vala, operation)(valb)
return ret
def expression(self):
"""Return str representation of expression
returns:
str
"""
return utility.flatten(self.tokens)