Mapytex/mapytex/calculus/operator/operator.py

235 lines
6.8 KiB
Python

#!/usr/bin/env python
# encoding: utf-8
# from debug.tools import report
from ..generic import flatten_list
class Operator():
"""The operator class, is a string (representation of the operator) with its arity"""
def __init__(self, operator="", name="", priority=0, actions=("", ""), txt="", tex="", arity=2, **kwrds):
""" Create an Operator """
self.operator = operator
self.name = name
self.arity = arity
self.priority = priority
self.actions = actions
self.txt = txt
self.tex = tex
self.isOperator = 1
def __call__(self, *args):
""" Calling this operator performs the rigth calculus """
if self.arity == 1:
return getattr(args[0], self.actions)()
elif self.arity == 2:
if issubclass(type(args[1]), int):
return getattr(args[0], self.actions[0])(args[1])
else:
return getattr(args[1], self.actions[1])(args[0])
def _render(self, link, *args):
"""Global step for __txt__ and __tex__
:param link: the link between operators
:param *args: the operands
:returns: the string with operator and operands
"""
if self.arity == 1:
op1 = self.l_parenthesis(args[0], True)
ans = link.format(op1=op1)
elif self.arity == 2:
op1 = self.l_parenthesis(args[0], True)
op2 = self.r_parenthesis(args[1], True)
ans = link.format(op1=op1, op2=op2)
ans = save_mainOp(ans, self)
return ans
def __repr__(self):
return str(self.operator)
def __str__(self):
return str(self.operator)
def __txt__(self, *args):
"""Txt rendering for the operator
:*args: Operands for this operation
:returns: String with operator and his operands
>>> from .mul import Mul
>>> mul= Mul()
>>> from .add import Add
>>> add = Add()
>>> from .sub import Sub
>>> sub = Sub()
>>> from .sub1 import Sub1
>>> sub1 = Sub1()
>>> mul.__txt__('1','2')
'1 * 2'
>>> add.__txt__('1','2')
'1 + 2'
>>> f = save_mainOp('2 + 3',add)
>>> mul.__txt__(f, '4')
'( 2 + 3 ) * 4'
>>> f = save_mainOp('-3',sub1)
>>> sub1.__txt__(f)
'- ( -3 )'
>>> sub1.__txt__('-3')
'- ( -3 )'
>>> f = save_mainOp('2 + 3',add)
>>> sub1.__txt__(f)
'- ( 2 + 3 )'
"""
return self._render(self.txt, *args)
def __tex__(self, *args):
"""Tex rendering for the operator
:*args: Operands for this operation
:returns: String with operator and his operands
>>> from .mul import Mul
>>> mul= Mul()
>>> from .add import Add
>>> add = Add()
>>> from .sub import Sub
>>> sub = Sub()
>>> from .sub1 import Sub1
>>> sub1 = Sub1()
>>> mul.__tex__('1','2')
'1 \\\\times 2'
>>> add.__tex__('1','2')
'1 + 2'
>>> f = save_mainOp('2 + 3',add)
>>> mul.__tex__(f, '4')
'( 2 + 3 ) \\\\times 4'
>>> f = save_mainOp('-3',sub1)
>>> sub1.__tex__(f)
'- ( -3 )'
>>> sub1.__tex__('-3')
'- ( -3 )'
>>> f = save_mainOp('2 + 3',add)
>>> sub1.__tex__(f)
'- ( 2 + 3 )'
"""
return self._render(self.tex, *args)
def __p2i__(self, *args):
"""Fix list transformation for the operator
:*args: Operands for this operation
:returns: list with the operator surrounded by operands
>>> from .mul import Mul
>>> mul= Mul()
>>> from .add import Add
>>> add = Add()
>>> from .sub import Sub
>>> sub = Sub()
>>> from .sub1 import Sub1
>>> sub1 = Sub1()
>>> mul.__p2i__(1,2)
[1, *, 2]
>>> f = save_mainOp([2, add, 3],add)
>>> mul.__p2i__(f, 4)
['(', 2, +, 3, ')', *, 4]
>>> f = save_mainOp([sub1, 3],sub1)
>>> sub1.__p2i__(f)
[-, '(', -, 3, ')']
>>> sub1.__p2i__(-3)
[-, '(', -3, ')']
>>> f = save_mainOp([2, add, 3],add)
>>> sub1.__p2i__(f)
[-, '(', 2, +, 3, ')']
"""
if self.arity == 1:
op1 = self.l_parenthesis(args[0])
ans = flatten_list([self, op1])
elif self.arity == 2:
op1 = self.l_parenthesis(args[0])
op2 = self.r_parenthesis(args[1])
ans = flatten_list([op1, self, op2])
ans = save_mainOp(ans, self)
return ans
def l_parenthesis(self, opl, str_join=False):
""" Add parenthesis for left operand if necessary """
ans = opl
try:
if opl.mainOp.name == "sub1":
ans = opl
elif opl.mainOp.priority < self.priority:
ans = flatten_list(["(", opl, ")"])
except AttributeError:
# op has not the attribute priority
pass
ans = flatten_list([ans])
if str_join:
ans = ' '.join([str(i) for i in ans])
return ans
def r_parenthesis(self, opr, str_join=False):
""" Add parenthesis for rigth operand if necessary """
ans = opr
try:
if opr.mainOp.priority < self.priority or \
(opr.mainOp.name == 'mul' and opr[0] == '-'):
ans = flatten_list(["(", ans, ")"])
except AttributeError:
# op has not the attribute priority
try:
if int(ans) < 0:
ans = ['(', ans, ')']
except ValueError:
pass
ans = flatten_list([ans])
if str_join:
ans = ' '.join([str(i) for i in ans])
return ans
def uniq_desc(self):
""" Return the uniq description of the operator
:returns: (self.name, self.arity)
"""
return (self.operator, self.arity)
def __eq__(self, op2):
""" op1 == op2 """
try:
return self.name == op2.name and self.arity == op2.arity
except AttributeError:
return False
def save_mainOp(obj, mainOp):
"""Create a temporary class build over built-in type to stock the main operation of a calculus
:obj: the object to add the attribute
:mainOp: the main operator
:returns: the same object with the main operation attribute
"""
# TODO: À mettre avec render? |mar. févr. 23 09:45:22 EAT 2016
# TODO: On pourrait mettre un try avant de créer une nouvelle classe |mar. févr. 23 09:44:45 EAT 2016
Fake = type('fake_'+str(type(obj)), (type(obj),), {'mainOp': mainOp})
return Fake(obj)
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del