Merge branch 'arity' into rewrite
Conflicts: pymath/expression.py
This commit is contained in:
commit
a32fc4b892
@ -2,6 +2,7 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
from .generic import Stack, flatten_list, expand_list
|
from .generic import Stack, flatten_list, expand_list
|
||||||
|
from .operator import Operator
|
||||||
from .fraction import Fraction
|
from .fraction import Fraction
|
||||||
from .renders import txt, post2in_fix, tex
|
from .renders import txt, post2in_fix, tex
|
||||||
|
|
||||||
@ -81,20 +82,36 @@ class Expression(object):
|
|||||||
tmpTokenList = []
|
tmpTokenList = []
|
||||||
|
|
||||||
while len(tokenList) > 2:
|
while len(tokenList) > 2:
|
||||||
# on va chercher les motifs du genre A B + pour les calculer
|
# on va chercher les motifs du genre A B +, quad l'operateur est d'arité 2, pour les calculer
|
||||||
if self.isNumber(tokenList[0]) and self.isNumber(tokenList[1]) and self.isOperator(tokenList[2]):
|
if self.isNumber(tokenList[0]) and self.isNumber(tokenList[1]) \
|
||||||
|
and type(tokenList[2]) == Operator and tokenList[2].arity == 2 :
|
||||||
|
|
||||||
# S'il y a une opération à faire
|
# S'il y a une opération à faire
|
||||||
op1 = tokenList[0]
|
op1 = tokenList[0]
|
||||||
op2 = tokenList[1]
|
op2 = tokenList[1]
|
||||||
token = tokenList[2]
|
operator = tokenList[2]
|
||||||
|
|
||||||
res = self.doMath(token, op1, op2)
|
res = operator(op1, op2)
|
||||||
|
|
||||||
tmpTokenList.append(res)
|
tmpTokenList.append(res)
|
||||||
|
|
||||||
# Comme on vient de faire le calcul, on peut détruire aussi les deux prochains termes
|
# Comme on vient de faire le calcul, on peut détruire aussi les deux prochains termes
|
||||||
del tokenList[0:3]
|
del tokenList[0:3]
|
||||||
|
|
||||||
|
elif self.isNumber(tokenList[0]) \
|
||||||
|
and type(tokenList[1]) == Operator and tokenList[1].arity == 1 :
|
||||||
|
|
||||||
|
# S'il y a une opération à faire
|
||||||
|
op1 = tokenList[0]
|
||||||
|
operator = tokenList[1]
|
||||||
|
|
||||||
|
res = operator(op1)
|
||||||
|
|
||||||
|
tmpTokenList.append(res)
|
||||||
|
|
||||||
|
# Comme on vient de faire le calcul, on peut détruire aussi les deux prochains termes
|
||||||
|
del tokenList[0:2]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
tmpTokenList.append(tokenList[0])
|
tmpTokenList.append(tokenList[0])
|
||||||
|
|
||||||
@ -190,31 +207,73 @@ class Expression(object):
|
|||||||
# "fix" tranformations
|
# "fix" tranformations
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
## ---------------------
|
def in2post_fix(cls, infix_tokens):
|
||||||
## Computing the expression
|
""" From the infix_tokens list compute the corresponding postfix_tokens list
|
||||||
|
|
||||||
@staticmethod
|
@param infix_tokens: the infix list of tokens to transform into postfix form.
|
||||||
def doMath(op, op1, op2):
|
@return: the corresponding postfix list of tokens.
|
||||||
"""Compute "op1 op op2" or create a fraction
|
|
||||||
|
|
||||||
:param op: operator
|
|
||||||
:param op1: first operande
|
|
||||||
:param op2: second operande
|
|
||||||
:returns: string representing the result
|
|
||||||
|
|
||||||
|
>>> Expression.in2post_fix(['(', 2, '+', 5, '-', 1, ')', '/', '(', 3, '*', 4, ')'])
|
||||||
|
[2, 5, '+', 1, '-', 3, 4, '*', '/']
|
||||||
|
>>> Expression.in2post_fix(['-', '(', '-', 2, ')'])
|
||||||
|
[2, '-', '-']
|
||||||
|
>>> Expression.in2post_fix(['-', '(', '-', 2, '+', 3, "*", 4, ')'])
|
||||||
|
[2, '-', 3, 4, '*', '+', '-']
|
||||||
"""
|
"""
|
||||||
if op == "/":
|
# Stack where operator will be stocked
|
||||||
ans = [Fraction(op1, op2)]
|
opStack = Stack()
|
||||||
ans += ans[0].simplify()
|
# final postfix list of tokens
|
||||||
return ans
|
postfix_tokens = []
|
||||||
else:
|
# stack with the nbr of tokens still to compute in postfix_tokens
|
||||||
if type(op2) != int:
|
arity_Stack = Stack()
|
||||||
operations = {"+": "__radd__", "-": "__rsub__", "*": "__rmul__"}
|
arity_Stack.push(0)
|
||||||
return getattr(op2,operations[op])(op1)
|
|
||||||
else:
|
|
||||||
operations = {"+": "__add__", "-": "__sub__", "*": "__mul__", "^": "__pow__"}
|
|
||||||
return getattr(op1,operations[op])(op2)
|
|
||||||
|
|
||||||
|
for (pos_token,token) in enumerate(infix_tokens):
|
||||||
|
|
||||||
|
# # Pour voir ce qu'il se passe dans cette procédure
|
||||||
|
# print(str(postfix_tokens), " | ", str(opStack), " | ", str(infix_tokens[(pos_token+1):]), " | ", str(arity_Stack))
|
||||||
|
if token == "(":
|
||||||
|
opStack.push(token)
|
||||||
|
# Set next arity counter
|
||||||
|
arity_Stack.push(0)
|
||||||
|
elif token == ")":
|
||||||
|
op = opStack.pop()
|
||||||
|
while op != "(":
|
||||||
|
postfix_tokens.append(op)
|
||||||
|
op = opStack.pop()
|
||||||
|
|
||||||
|
# Go back to old arity
|
||||||
|
arity_Stack.pop()
|
||||||
|
# Raise the arity
|
||||||
|
arity = arity_Stack.pop()
|
||||||
|
arity_Stack.push(arity + 1)
|
||||||
|
|
||||||
|
elif cls.isOperator(token):
|
||||||
|
while (not opStack.isEmpty()) and (cls.PRIORITY[opStack.peek()] >= cls.PRIORITY[token]):
|
||||||
|
op = opStack.pop()
|
||||||
|
postfix_tokens.append(op)
|
||||||
|
|
||||||
|
arity = arity_Stack.pop()
|
||||||
|
opStack.push(Operator(token, arity + 1))
|
||||||
|
# print("--", token, " -> ", str(arity + 1))
|
||||||
|
# Reset arity to 0 in case there is other operators (the real operation would be "-op.arity + 1")
|
||||||
|
arity_Stack.push(0)
|
||||||
|
else:
|
||||||
|
postfix_tokens.append(token)
|
||||||
|
arity = arity_Stack.pop()
|
||||||
|
arity_Stack.push(arity + 1)
|
||||||
|
|
||||||
|
while not opStack.isEmpty():
|
||||||
|
op = opStack.pop()
|
||||||
|
postfix_tokens.append(op)
|
||||||
|
|
||||||
|
# # Pour voir ce qu'il se passe dans cette procédure
|
||||||
|
# print(str(postfix_tokens), " | ", str(opStack), " | ", str(infix_tokens[(pos_token+1):]), " | ", str(arity_Stack))
|
||||||
|
|
||||||
|
if arity_Stack.peek() != 1:
|
||||||
|
raise ValueError("Unvalid expression. The arity Stack is ", str(arity_Stack))
|
||||||
|
|
||||||
|
return postfix_tokens
|
||||||
|
|
||||||
## ---------------------
|
## ---------------------
|
||||||
## Recognize numbers and operators
|
## Recognize numbers and operators
|
||||||
@ -250,11 +309,11 @@ def test(exp):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
Expression.STR_RENDER = txt
|
Expression.STR_RENDER = txt
|
||||||
#exp = "2 ^ 3 * 5"
|
exp = "2 ^ 3 * 5"
|
||||||
#test(exp)
|
test(exp)
|
||||||
|
|
||||||
#exp = "1 + 3 * 5"
|
exp = "1 + 3 * 5"
|
||||||
#test(exp)
|
test(exp)
|
||||||
|
|
||||||
#exp = "2 * 3 * 3 * 5"
|
#exp = "2 * 3 * 3 * 5"
|
||||||
#test(exp)
|
#test(exp)
|
||||||
|
@ -57,7 +57,6 @@ class Stack(object):
|
|||||||
def __add__(self, addList):
|
def __add__(self, addList):
|
||||||
return self.items + addList
|
return self.items + addList
|
||||||
|
|
||||||
|
|
||||||
def flatten_list(a, result=None):
|
def flatten_list(a, result=None):
|
||||||
"""Flattens a nested list.
|
"""Flattens a nested list.
|
||||||
|
|
||||||
|
52
pymath/operator.py
Normal file
52
pymath/operator.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
|
||||||
|
from .fraction import Fraction
|
||||||
|
|
||||||
|
class Operator(str):
|
||||||
|
|
||||||
|
"""The operator class, is a string (representation of the operator) with its arity"""
|
||||||
|
|
||||||
|
OPERATORS = { \
|
||||||
|
"+": ["", "", ("__add__","__radd__")],\
|
||||||
|
"-": ["", "__neg__", ("__sub__", "__rsub__")], \
|
||||||
|
"*": ["", "", ("__mul__", "__rmul__")], \
|
||||||
|
"/": ["", "", ("__div__","__rdiv__")], \
|
||||||
|
"^": ["", "", ("__pow__", "")] \
|
||||||
|
}
|
||||||
|
|
||||||
|
def __new__(cls, operator, arity = 2):
|
||||||
|
op = str.__new__(cls, operator)
|
||||||
|
op.arity = arity
|
||||||
|
|
||||||
|
op.actions = cls.OPERATORS[operator][arity]
|
||||||
|
|
||||||
|
return op
|
||||||
|
|
||||||
|
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:
|
||||||
|
# C'est moche mais je veux que ça marche...
|
||||||
|
if str(self) == "/":
|
||||||
|
ans = [Fraction(args[0], args[1])]
|
||||||
|
ans += ans[0].simplify()
|
||||||
|
return ans
|
||||||
|
else:
|
||||||
|
if type(args[1]) == int:
|
||||||
|
return getattr(args[0], self.actions[0])(args[1])
|
||||||
|
else:
|
||||||
|
return getattr(args[1], self.actions[1])(args[0])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Reglages pour 'vim'
|
||||||
|
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
|
||||||
|
# cursor: 16 del
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
from .generic import Stack,flatten_list
|
from .generic import Stack,flatten_list
|
||||||
from .fraction import Fraction
|
from .fraction import Fraction
|
||||||
|
from .operator import Operator
|
||||||
|
|
||||||
__all__ = ['Render']
|
__all__ = ['Render']
|
||||||
|
|
||||||
@ -195,7 +196,7 @@ class Render(object):
|
|||||||
:returns: boolean
|
:returns: boolean
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return (type(exp) == str and exp in self.operators)
|
return (type(exp) == Operator and str(exp) in self.operators)
|
||||||
|
|
||||||
class flist(list):
|
class flist(list):
|
||||||
"""Fake list- they are used to stock the main operation of an rendered expression"""
|
"""Fake list- they are used to stock the main operation of an rendered expression"""
|
||||||
|
@ -56,18 +56,6 @@ class TestExpression(unittest.TestCase):
|
|||||||
exp = "1 + $"
|
exp = "1 + $"
|
||||||
self.assertRaises(ValueError, Expression.str2tokens, exp)
|
self.assertRaises(ValueError, Expression.str2tokens, exp)
|
||||||
|
|
||||||
def test_doMath(self):
|
|
||||||
ops = [\
|
|
||||||
{"op": ("+", 1 , 2), "res" : 3}, \
|
|
||||||
{"op": ("-", 1 , 2), "res" : -1}, \
|
|
||||||
{"op": ("*", 1 , 2), "res" : 2}, \
|
|
||||||
{"op": ("/", 1 , 2), "res" : Fraction(1,2)}, \
|
|
||||||
{"op": ("^", 1 , 2), "res" : 1}, \
|
|
||||||
]
|
|
||||||
for op in ops:
|
|
||||||
res = first_elem(Expression.doMath(*op["op"]))
|
|
||||||
self.assertAlmostEqual(res, op["res"])
|
|
||||||
|
|
||||||
def test_isNumber(self):
|
def test_isNumber(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import unittest
|
|||||||
|
|
||||||
from pymath.renders import tex, txt
|
from pymath.renders import tex, txt
|
||||||
from pymath.fraction import Fraction
|
from pymath.fraction import Fraction
|
||||||
|
from pymath.operator import Operator
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -22,21 +23,21 @@ class TestTexRender(unittest.TestCase):
|
|||||||
self.assertEqual(tex([Fraction(1,2)]), "\\frac{ 1 }{ 2 }")
|
self.assertEqual(tex([Fraction(1,2)]), "\\frac{ 1 }{ 2 }")
|
||||||
|
|
||||||
def test_mult_interger(self):
|
def test_mult_interger(self):
|
||||||
exps = [ [2, 3, "*"], [2, -3, "*"], [-2, 3, "*"]]
|
exps = [ [2, 3, Operator("*", 2)], [2, -3, Operator("*", 2)], [-2, 3, Operator("*", 2)]]
|
||||||
wanted_render = [ "2 \\times 3", "2 \\times ( -3 )", "-2 \\times 3"]
|
wanted_render = [ "2 \\times 3", "2 \\times ( -3 )", "-2 \\times 3"]
|
||||||
for (i,e) in enumerate(exps):
|
for (i,e) in enumerate(exps):
|
||||||
rend = tex(e)
|
rend = tex(e)
|
||||||
self.assertEqual(rend, wanted_render[i])
|
self.assertEqual(rend, wanted_render[i])
|
||||||
|
|
||||||
def test_mult_letter(self):
|
def test_mult_letter(self):
|
||||||
exps = [ [2, "a", "*"], ["a", 3, "*"], [-2, "a", "*"], ["a", -2, "*"]]
|
exps = [ [2, "a", Operator("*", 2)], ["a", 3, Operator("*", 2)], [-2, "a", Operator("*", 2)], ["a", -2, Operator("*", 2)]]
|
||||||
wanted_render = [ "2 a", "a \\times 3", "-2 a", "a \\times ( -2 )"]
|
wanted_render = [ "2 a", "a \\times 3", "-2 a", "a \\times ( -2 )"]
|
||||||
for (i,e) in enumerate(exps):
|
for (i,e) in enumerate(exps):
|
||||||
rend = tex(e)
|
rend = tex(e)
|
||||||
self.assertEqual(rend, wanted_render[i])
|
self.assertEqual(rend, wanted_render[i])
|
||||||
|
|
||||||
def test_mult_fraction(self):
|
def test_mult_fraction(self):
|
||||||
exps = [ [2, Fraction(1,2), "*"], [Fraction(1,2), 3, "*"]]
|
exps = [ [2, Fraction(1,2), Operator("*", 2)], [Fraction(1,2), 3, Operator("*", 2)]]
|
||||||
wanted_render = [ "2 \\times \\frac{ 1 }{ 2 }", "\\frac{ 1 }{ 2 } \\times 3"]
|
wanted_render = [ "2 \\times \\frac{ 1 }{ 2 }", "\\frac{ 1 }{ 2 } \\times 3"]
|
||||||
for (i,e) in enumerate(exps):
|
for (i,e) in enumerate(exps):
|
||||||
rend = tex(e)
|
rend = tex(e)
|
||||||
@ -64,21 +65,27 @@ class TesttxtRender(unittest.TestCase):
|
|||||||
self.assertEqual(txt([Fraction(1,2)]), "1 / 2")
|
self.assertEqual(txt([Fraction(1,2)]), "1 / 2")
|
||||||
|
|
||||||
def test_mult_interger(self):
|
def test_mult_interger(self):
|
||||||
exps = [ [2, 3, "*"], [2, -3, "*"], [-2, 3, "*"]]
|
exps = [ [2, 3, Operator("*", 2)], \
|
||||||
|
[2, -3, Operator("*", 2)], \
|
||||||
|
[-2, 3, Operator("*", 2)]]
|
||||||
wanted_render = [ "2 * 3", "2 * ( -3 )", "-2 * 3"]
|
wanted_render = [ "2 * 3", "2 * ( -3 )", "-2 * 3"]
|
||||||
for (i,e) in enumerate(exps):
|
for (i,e) in enumerate(exps):
|
||||||
rend = txt(e)
|
rend = txt(e)
|
||||||
self.assertEqual(rend, wanted_render[i])
|
self.assertEqual(rend, wanted_render[i])
|
||||||
|
|
||||||
def test_mult_letter(self):
|
def test_mult_letter(self):
|
||||||
exps = [ [2, "a", "*"], ["a", 3, "*"], [-2, "a", "*"], ["a", -2, "*"]]
|
exps = [ [2, "a", Operator("*", 2)], \
|
||||||
|
["a", 3, Operator("*", 2)], \
|
||||||
|
[-2, "a", Operator("*", 2)], \
|
||||||
|
["a", -2, Operator("*", 2)]]
|
||||||
wanted_render = [ "2 a", "a * 3", "-2 a", "a * ( -2 )"]
|
wanted_render = [ "2 a", "a * 3", "-2 a", "a * ( -2 )"]
|
||||||
for (i,e) in enumerate(exps):
|
for (i,e) in enumerate(exps):
|
||||||
rend = txt(e)
|
rend = txt(e)
|
||||||
self.assertEqual(rend, wanted_render[i])
|
self.assertEqual(rend, wanted_render[i])
|
||||||
|
|
||||||
def test_mult_fraction(self):
|
def test_mult_fraction(self):
|
||||||
exps = [ [2, Fraction(1,2), "*"], [Fraction(1,2), 3, "*"]]
|
exps = [ [2, Fraction(1,2), Operator("*", 2)], \
|
||||||
|
[Fraction(1,2), 3, Operator("*", 2)]]
|
||||||
wanted_render = [ "2 * 1 / 2", "1 / 2 * 3"]
|
wanted_render = [ "2 * 1 / 2", "1 / 2 * 3"]
|
||||||
for (i,e) in enumerate(exps):
|
for (i,e) in enumerate(exps):
|
||||||
rend = txt(e)
|
rend = txt(e)
|
||||||
|
Loading…
Reference in New Issue
Block a user