diff --git a/pymath/expression.py b/pymath/expression.py index 57e764b..f4ce7e4 100644 --- a/pymath/expression.py +++ b/pymath/expression.py @@ -1,9 +1,9 @@ #!/usr/bin/env python # encoding: utf-8 -from .generic import Stack, flatten_list, expand_list +from .generic import Stack, flatten_list, expand_list, isNumber, isOperator from .renders import txt, post2in_fix, tex -from .str2tokens import str2tokens, isNumber, isOperator +from .str2tokens import str2tokens __all__ = ['Expression'] diff --git a/pymath/fraction.py b/pymath/fraction.py index 3cff715..72ad861 100644 --- a/pymath/fraction.py +++ b/pymath/fraction.py @@ -60,6 +60,15 @@ class Fraction(object): def __repr__(self): return "< Fraction " + self.__str__() + ">" + def __txt__(self): + return str(self) + + def __tex__(self): + if self._denom == 1: + return str(self._num) + else: + return "\\frac{{ {a} }}{{ {b} }}".format(a = self._num, b = self._denom) + def __float__(self): return self._num / self._denom diff --git a/pymath/generic.py b/pymath/generic.py index 959c66a..8980977 100644 --- a/pymath/generic.py +++ b/pymath/generic.py @@ -243,6 +243,37 @@ def convolution_dict(D1, D2, op = lambda x,y:x*y,\ return new_dict +def isOperator(exp): + """Check if the expression is an opération in "+-*/:^" + + :param exp: an expression + :returns: boolean + + """ + + #return (type(exp) == str and exp in "+-*/:^") + try: + exp.isOperator + except AttributeError: + return 0 + return 1 + +def isNumber(exp): + """Check if the expression can be a number + + :param exp: an expression + :returns: True if the expression can be a number and false otherwise + + """ + try: + exp.isNumber + except AttributeError: + if type(exp) == int: + return 1 + else: + return 0 + return 1 + if __name__ == '__main__': import doctest doctest.testmod() diff --git a/pymath/operator.py b/pymath/operator.py index a8a0fc7..dbe95d4 100644 --- a/pymath/operator.py +++ b/pymath/operator.py @@ -3,7 +3,7 @@ from .fraction import Fraction -from .generic import flatten_list +from .generic import flatten_list, isNumber class Operator(str): @@ -75,25 +75,23 @@ class Operator(str): >>> add = Operator("+", 2) >>> sub1 = Operator("-", 1) >>> div = Operator("/", 1) - >>> mul.__txt__(1,2) + >>> mul.__txt__('1','2') '1 * 2' - >>> add.__txt__(1,2) + >>> add.__txt__('1','2') '1 + 2' >>> f = save_mainOp('2 + 3',add) - >>> mul.__txt__(f, 4) + >>> mul.__txt__(f, '4') '( 2 + 3 ) * 4' >>> f = save_mainOp('-3',sub1) >>> sub1.__txt__(f) '- ( -3 )' - >>> sub1.__txt__(-3) + >>> sub1.__txt__('-3') '- ( -3 )' >>> f = save_mainOp('2 + 3',add) >>> sub1.__txt__(f) '- ( 2 + 3 )' """ - - #vive le inline? ... - replacement = {"op"+str(i+1): ' '.join([str(o) if type(o)==int else o for o in self.add_parenthesis(op)]) for (i,op) in enumerate(args)} + replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)} ans = self._txt.format(**replacement) ans = save_mainOp(ans, self) @@ -109,23 +107,23 @@ class Operator(str): >>> add = Operator("+", 2) >>> sub1 = Operator("-", 1) >>> div = Operator("/", 1) - >>> mul.__tex__(1,2) + >>> mul.__tex__('1','2') '1 \\\\times 2' - >>> add.__tex__(1,2) + >>> add.__tex__('1','2') '1 + 2' >>> f = save_mainOp('2 + 3',add) - >>> mul.__tex__(f, 4) + >>> mul.__tex__(f, '4') '( 2 + 3 ) \\\\times 4' >>> f = save_mainOp('-3',sub1) >>> sub1.__tex__(f) '- ( -3 )' - >>> sub1.__tex__(-3) + >>> sub1.__tex__('-3') '- ( -3 )' >>> f = save_mainOp('2 + 3',add) >>> sub1.__tex__(f) '- ( 2 + 3 )' """ - replacement = {"op"+str(i+1): ' '.join([str(o) if type(o)==int else o for o in self.add_parenthesis(op)]) for (i,op) in enumerate(args)} + replacement = {"op"+str(i+1): ' '.join(self.add_parenthesis(op)) for (i,op) in enumerate(args)} ans = self._tex.format(**replacement) ans = save_mainOp(ans, self) @@ -174,13 +172,15 @@ class Operator(str): if op.mainOp.priority < self.priority: op = flatten_list(["("] + [op] + [")"]) except AttributeError: - if type(op) == int and op < 0: - op = ['(', op, ')'] + try: + if int(op) < 0: + op = ['(', op, ')'] + except ValueError: + pass return flatten_list([op]) - def save_mainOp(obj, mainOp): """Create a temporary class build over built-in type to stock the main operation of a calculus @@ -198,21 +198,27 @@ def save_mainOp(obj, mainOp): return Fake(obj) if __name__ == '__main__': - #op = Operator("+", 2) - #print(op.__txt__(1,2)) - #mul = Operator("*", 2) - #add = Operator("+", 2) - #sub1 = Operator("-", 1) - #div = Operator("/", 1) - #print(mul.__txt__(1,2)) - #print(add.__txt__(1,2)) - #f = save_mainOp('2 + 3',add) - #print(mul.__txt__(f, 4)) - #f = save_mainOp('-3',sub1) - #print(sub1.__txt__(f)) - #print(sub1.__txt__(-3)) - #f = save_mainOp('2 + 3',add) - #print(sub1.__txt__(f)) + op = Operator("+", 2) + print(op.__txt__('1','2')) + mul = Operator("*", 2) + add = Operator("+", 2) + sub1 = Operator("-", 1) + div = Operator("/", 1) + print(mul.__txt__('1','2')) + print(add.__txt__('1','2')) + f = save_mainOp('2 + 3',add) + print(mul.__txt__(f, '4')) + f = save_mainOp('-3',sub1) + print(sub1.__txt__(f)) + print(sub1.__txt__('-3')) + f = save_mainOp('2 + 3',add) + print(sub1.__txt__(f)) + + from .fraction import Fraction + f = Fraction(1, 2) + print(add.__txt__(f.__txt__(),'2')) + print(add.__tex__(f.__tex__(),'2')) + import doctest doctest.testmod() diff --git a/pymath/render.py b/pymath/render.py index 2ad50da..394cb34 100644 --- a/pymath/render.py +++ b/pymath/render.py @@ -38,13 +38,36 @@ class Render(object): # Switch op1 and op2 to respect order operandeStack.push(self.render(token)(op2, op1)) else: - operandeStack.push(token) + operandeStack.push(self.render(token)()) return operandeStack.pop() -txt = Render(lambda x:x.__txt__) -tex = Render(lambda x:x.__tex__) -p2i = Render(lambda x:x.__p2i__) +def txt_render(token): + def render(*args): + try: + return getattr(token, '__txt__')(*args) + except AttributeError: + return str(token) + return render + +txt = Render(txt_render) +def tex_render(token): + def render(*args): + try: + return getattr(token, '__tex__')(*args) + except AttributeError: + return str(token) + return render +tex = Render(tex_render) + +def p2i_render(token): + def render(*args): + try: + return getattr(token, '__p2i__')(*args) + except AttributeError: + return list(token) + return render +p2i = Render(p2i_render) if __name__ == '__main__': from .operator import Operator diff --git a/pymath/renders.py b/pymath/renders.py deleted file mode 100644 index 5eaa635..0000000 --- a/pymath/renders.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -from .render import Render -from .fraction import Fraction -from .generic import first_elem, last_elem - -__all__ = ['post2in_fix', 'tex', 'txt'] - -# ------------------------ -# A infix to postfix list convertor - -p2i_infix = {"+": "+", "-": "-", "*": "*", "/" : "/", ":": ":", "^":"^"} -p2i_postfix = {} -p2i_other = {"(": "(", ")": ")"} - -post2in_fix = Render(p2i_infix, p2i_postfix, p2i_other, join = False) - -# ------------------------ -# A console render - -def txtMult(op1,op2): - """ Tex render for * - Cases where \\times won't be displayed - * nbr letter - * nbr ( - * )( - """ - first_nbr = type(op1) in [int, Fraction] - seg_letter = type(op2) == str and op2.isalpha() - first_par = (first_elem(op2) == "(") - seg_par = (last_elem(op1) == ")") - - if (first_nbr and (seg_letter or seg_par)) \ - or (first_par and seg_par): - return [op1, op2] - else: - return [op1, "*", op2] - -txt_infix = {"+": "+", "-": "-", "*": txtMult, "/" : "/", ":":":", "^":"^"} -txt_postfix = {} -txt_other = {"(": "(", ")": ")"} - -txt = Render(txt_infix, txt_postfix, txt_other) - -# ------------------------ -# A latex render - -def texSlash(op1, op2): - """ Tex render for / """ - if not Render.isNumerande(op1) and op1[0] == "(" and op1[-1] == ")": - op1 = op1[1:-1] - if not Render.isNumerande(op2) and op2[0] == "(" and op2[-1] == ")": - op2 = op2[1:-1] - return ["\\frac{" , op1 , "}{" , op2 , "}"] - -def texFrac(frac): - """ Tex render for Fractions""" - return ["\\frac{" , str(frac._num) , "}{" , str(frac._denom) , "}"] - -def texMult(op1,op2): - """ Tex render for * - Cases where \\times won't be displayed - * nbr letter - * nbr ( - * )( - """ - first_nbr = type(op1) in [int, Fraction] - seg_letter = type(op2) == str and op2.isalpha() - first_par = (first_elem(op2) == "(") - seg_par = (last_elem(op1) == ")") - - if (first_nbr and (seg_letter or seg_par)) \ - or (first_par and seg_par): - return [op1, op2] - else: - return [op1, "\\times", op2] - -tex_infix = {"+": " + ", "-": " - ", "*": texMult , ":": ":", "^":"^"} -tex_postfix = {"/": texSlash} -tex_other = {"(": "(", ")": ")"} -tex_type_render = {str:str, int: str, Fraction: texFrac} - -tex = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render) - - - -if __name__ == '__main__': - #exp = [2, 5, '^', 1, '-', 3, 4, '*', ':'] - #print(txt(exp)) - #exp = [2, 5, '^', 1, '-', 3, 4, '*', '/', 3, 5, '/', ':'] - exp = [2, -3, "*"] - print(tex(exp)) - #exp = [2, 5, '^', 1, '-', 3, 4, '*', '/', 3, '+'] - #print(post2in_fix(exp)) - - - - -# ----------------------------- -# Reglages pour 'vim' -# vim:set autoindent expandtab tabstop=4 shiftwidth=4: -# cursor: 16 del diff --git a/pymath/str2tokens.py b/pymath/str2tokens.py index d770ab5..f0eff71 100644 --- a/pymath/str2tokens.py +++ b/pymath/str2tokens.py @@ -2,7 +2,7 @@ # encoding: utf-8 from .operator import Operator -from .generic import Stack +from .generic import Stack, isOperator, isNumber def str2tokens(exp): """ Parse the string into tokens then turn it into postfix form @@ -147,36 +147,6 @@ def in2post_fix(infix_tokens): return postfix_tokens -def isOperator(exp): - """Check if the expression is an opération in "+-*/:^" - - :param exp: an expression - :returns: boolean - - """ - - #return (type(exp) == str and exp in "+-*/:^") - try: - exp.isOperator - except AttributeError: - return 0 - return 1 - -def isNumber(exp): - """Check if the expression can be a number - - :param exp: an expression - :returns: True if the expression can be a number and false otherwise - - """ - try: - exp.isNumber - except AttributeError: - if type(exp) == int: - return 1 - else: - return 0 - return 1 if __name__ == '__main__': #a, s, m, d, p = Operator("+"), Operator("-"), Operator("*"), Operator("/"), Operator("^") diff --git a/test/test_renders.py b/test/test_render.py similarity index 73% rename from test/test_renders.py rename to test/test_render.py index 6453dff..aa22126 100644 --- a/test/test_renders.py +++ b/test/test_render.py @@ -4,7 +4,7 @@ import unittest -from pymath.renders import tex, txt +from pymath.render import tex, txt,p2i from pymath.fraction import Fraction from pymath.operator import Operator @@ -43,8 +43,24 @@ class TestTexRender(unittest.TestCase): rend = tex(e) self.assertEqual(rend, wanted_render[i]) - def test_mult_exp(self): - pass + def test_parentheses(self): + mul = Operator("*", 2) + add = Operator("+", 2) + exps = [\ + [ 2, 3, add, 4, mul],\ + [ 2, 3, mul, 4, add],\ + [ 2, 3, 4, mul, add],\ + [ 2, 3, 4, add, add],\ + ] + wanted_render = [\ + '( 2 + 3 ) \\times 4',\ + '2 \\times 3 + 4',\ + '2 + 3 \\times 4',\ + '2 + 3 + 4',\ + ] + for (i,e) in enumerate(exps): + rend = tex(e) + self.assertEqual(rend, wanted_render[i]) def test_slash(self): pass @@ -91,8 +107,24 @@ class TesttxtRender(unittest.TestCase): rend = txt(e) self.assertEqual(rend, wanted_render[i]) - def test_mult_exp(self): - pass + def test_parentheses(self): + mul = Operator("*", 2) + add = Operator("+", 2) + exps = [\ + [ 2, 3, add, 4, mul],\ + [ 2, 3, mul, 4, add],\ + [ 2, 3, 4, mul, add],\ + [ 2, 3, 4, add, add],\ + ] + wanted_render = [\ + '( 2 + 3 ) * 4',\ + '2 * 3 + 4',\ + '2 + 3 * 4',\ + '2 + 3 + 4',\ + ] + for (i,e) in enumerate(exps): + rend = txt(e) + self.assertEqual(rend, wanted_render[i]) def test_slash(self): pass