2018-01-21 09:21:13 +00:00
|
|
|
#! /usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# vim:fenc=utf-8
|
|
|
|
#
|
|
|
|
# Copyright © 2017 lafrite <lafrite@Poivre>
|
|
|
|
#
|
|
|
|
# Distributed under terms of the MIT license.
|
|
|
|
|
|
|
|
"""
|
|
|
|
Expression
|
|
|
|
|
|
|
|
"""
|
2018-11-19 11:23:37 +00:00
|
|
|
from ..core import AssocialTree, Tree, compute, typing, TypingError
|
2018-09-17 16:18:29 +00:00
|
|
|
from .renders import renders
|
2018-01-21 09:21:13 +00:00
|
|
|
|
|
|
|
class Expression(object):
|
|
|
|
|
|
|
|
"""
|
|
|
|
Expression class
|
|
|
|
|
2018-09-17 14:29:00 +00:00
|
|
|
:example:
|
|
|
|
|
|
|
|
>>> e = Expression.from_str("2+3*4")
|
|
|
|
>>> e2 = e.simplify()
|
|
|
|
>>> print(e2)
|
|
|
|
14
|
2018-09-17 16:18:29 +00:00
|
|
|
>>> for s in e2.explain():
|
|
|
|
... print(s)
|
|
|
|
2 + 3 * 4
|
|
|
|
2 + 12
|
|
|
|
14
|
2018-01-21 09:21:13 +00:00
|
|
|
"""
|
|
|
|
|
2018-09-17 16:18:29 +00:00
|
|
|
RENDER = 'txt'
|
2018-09-17 14:29:00 +00:00
|
|
|
|
2018-09-17 16:18:29 +00:00
|
|
|
def __init__(self, tree, ancestor=None):
|
2018-01-21 09:21:13 +00:00
|
|
|
"""
|
|
|
|
"""
|
2018-09-17 14:29:00 +00:00
|
|
|
self._tree = tree
|
2018-09-17 16:18:29 +00:00
|
|
|
self._ancestor = ancestor
|
2018-01-21 09:21:13 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_str(cls, string):
|
|
|
|
""" Initiate the expression from a string
|
|
|
|
|
2018-09-20 16:27:40 +00:00
|
|
|
:param string: String to parse to generate the Expression
|
|
|
|
:returns: the expression
|
2018-01-21 09:21:13 +00:00
|
|
|
|
|
|
|
"""
|
2018-09-17 14:29:00 +00:00
|
|
|
t = Tree.from_str(string)
|
|
|
|
return cls(t)
|
2018-01-21 09:21:13 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def random(self, template, conditions = [], shuffle = False):
|
|
|
|
""" Initiate randomly the expression
|
|
|
|
|
|
|
|
:param template: the template of the expression
|
|
|
|
:param conditions: conditions on randomly generate variable
|
|
|
|
:param shuffle: allowing to shuffle the tree
|
|
|
|
:returns: TODO
|
|
|
|
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
2018-09-17 14:29:00 +00:00
|
|
|
@classmethod
|
|
|
|
def set_render(cls, render):
|
|
|
|
""" Define default render function
|
|
|
|
|
|
|
|
:param render: render function Tree -> str
|
|
|
|
|
|
|
|
:example:
|
2018-09-17 16:18:29 +00:00
|
|
|
>>> Expression.RENDER
|
|
|
|
'txt'
|
2018-09-17 14:29:00 +00:00
|
|
|
>>> e = Expression.from_str("2+3*4")
|
|
|
|
>>> print(e)
|
|
|
|
2 + 3 * 4
|
2018-09-17 16:18:29 +00:00
|
|
|
>>> Expression.set_render('tex')
|
|
|
|
>>> Expression.RENDER
|
|
|
|
'tex'
|
2018-09-17 14:29:00 +00:00
|
|
|
>>> print(e)
|
|
|
|
2 + 3 \\times 4
|
|
|
|
"""
|
|
|
|
cls.RENDER = render
|
|
|
|
|
|
|
|
def __str__(self):
|
2018-09-17 16:18:29 +00:00
|
|
|
return renders[self.RENDER](self._tree)
|
2018-09-17 14:29:00 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
2018-09-17 16:18:29 +00:00
|
|
|
return f"<Exp: {renders[self.RENDER](self._tree)}>"
|
|
|
|
|
2018-10-01 14:14:04 +00:00
|
|
|
def simplify(self, optimize=True):
|
2018-09-17 16:18:29 +00:00
|
|
|
""" Compute as much as possible the expression
|
|
|
|
|
2018-10-01 14:14:04 +00:00
|
|
|
:param optimize: bool to optimize tree when it's possible
|
2018-09-17 16:18:29 +00:00
|
|
|
:return: an expression
|
|
|
|
|
|
|
|
:example:
|
|
|
|
>>> e = Expression.from_str("2+3*4")
|
|
|
|
>>> e
|
|
|
|
<Exp: 2 + 3 \\times 4>
|
|
|
|
>>> f = e.simplify()
|
|
|
|
>>> f
|
|
|
|
<Exp: 14>
|
|
|
|
>>> f._ancestor
|
|
|
|
<Exp: 2 + 12>
|
|
|
|
"""
|
2018-11-19 11:23:37 +00:00
|
|
|
t = self._tree
|
2018-10-01 14:14:04 +00:00
|
|
|
if optimize:
|
|
|
|
try:
|
2018-10-05 06:52:04 +00:00
|
|
|
# TODO: need to test exclude_nodes |ven. oct. 5 08:51:02 CEST 2018
|
2018-11-19 11:23:37 +00:00
|
|
|
t = t.balance(
|
2018-10-05 06:52:04 +00:00
|
|
|
exclude_nodes=["\\", "**"]
|
|
|
|
)
|
2018-10-01 14:14:04 +00:00
|
|
|
except AttributeError:
|
|
|
|
pass
|
2018-11-19 11:23:37 +00:00
|
|
|
|
|
|
|
more_typing = 1
|
|
|
|
while more_typing:
|
|
|
|
try:
|
|
|
|
t = t.apply_on_last_level(typing)
|
|
|
|
except TypingError:
|
|
|
|
more_typing = 0
|
|
|
|
except NotImplementedError:
|
|
|
|
more_typing = 0
|
|
|
|
except AttributeError:
|
|
|
|
more_typing = 0
|
|
|
|
|
2018-09-17 16:18:29 +00:00
|
|
|
try:
|
2018-11-19 11:23:37 +00:00
|
|
|
t = t.apply_on_last_level(compute)
|
2018-09-17 16:18:29 +00:00
|
|
|
except AttributeError:
|
|
|
|
return self
|
|
|
|
else:
|
|
|
|
e = Expression(t, ancestor=self)
|
2018-10-01 14:14:04 +00:00
|
|
|
return e.simplify(optimize=optimize)
|
2018-09-17 16:18:29 +00:00
|
|
|
|
|
|
|
def explain(self):
|
|
|
|
""" Yield every calculus step which have lead to self
|
|
|
|
|
|
|
|
:example:
|
|
|
|
>>> e = Expression.from_str("2+3*4")
|
|
|
|
>>> f = e.simplify()
|
|
|
|
>>> for s in f.explain():
|
|
|
|
... print(s)
|
|
|
|
2 + 3 * 4
|
|
|
|
2 + 12
|
|
|
|
14
|
2018-10-01 14:14:04 +00:00
|
|
|
>>> e = Expression.from_str("1+2+3+4+5+6+7+8+9")
|
|
|
|
>>> f = e.simplify()
|
|
|
|
>>> for s in f.explain():
|
|
|
|
... print(s)
|
|
|
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
|
2018-10-04 06:16:59 +00:00
|
|
|
3 + 3 + 9 + 6 + 7 + 17
|
|
|
|
6 + 9 + 6 + 24
|
|
|
|
15 + 30
|
2018-10-01 14:14:04 +00:00
|
|
|
45
|
|
|
|
>>> e = Expression.from_str("1+2+3+4+5+6+7+8+9")
|
|
|
|
>>> f_no_balance = e.simplify(optimize=False)
|
|
|
|
>>> for s in f_no_balance.explain():
|
|
|
|
... print(s)
|
|
|
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
|
|
|
|
3 + 3 + 4 + 5 + 6 + 7 + 8 + 9
|
|
|
|
6 + 4 + 5 + 6 + 7 + 8 + 9
|
|
|
|
10 + 5 + 6 + 7 + 8 + 9
|
|
|
|
15 + 6 + 7 + 8 + 9
|
|
|
|
21 + 7 + 8 + 9
|
|
|
|
28 + 8 + 9
|
|
|
|
36 + 9
|
|
|
|
45
|
|
|
|
>>> e = Expression.from_str("1+2+3+4+5*6*7*8*9")
|
|
|
|
>>> f = e.simplify()
|
|
|
|
>>> for s in f.explain():
|
|
|
|
... print(s)
|
|
|
|
1 + 2 + 3 + 4 + 5 * 6 * 7 * 8 * 9
|
|
|
|
3 + 3 + 4 + 30 * 7 * 72
|
2018-10-04 06:16:59 +00:00
|
|
|
6 + 4 + 210 * 72
|
|
|
|
10 + 15120
|
2018-10-01 14:14:04 +00:00
|
|
|
15130
|
|
|
|
>>> e = Expression.from_str("1+2+3+4+5*6*7*8*9")
|
|
|
|
>>> f_no_balance = e.simplify(optimize=False)
|
|
|
|
>>> for s in f_no_balance.explain():
|
|
|
|
... print(s)
|
|
|
|
1 + 2 + 3 + 4 + 5 * 6 * 7 * 8 * 9
|
|
|
|
3 + 3 + 4 + 30 * 7 * 8 * 9
|
|
|
|
6 + 4 + 210 * 8 * 9
|
|
|
|
10 + 1680 * 9
|
|
|
|
10 + 15120
|
|
|
|
15130
|
2018-10-05 09:18:09 +00:00
|
|
|
>>> e = Expression.from_str("1+2/3/4/5")
|
|
|
|
>>> f = e.simplify()
|
|
|
|
>>> for s in f.explain():
|
|
|
|
... print(s)
|
|
|
|
1 + 2 / 3 / 4 / 5
|
|
|
|
1 + (2 / 3 * 1 / 4) / 5
|
|
|
|
1 + (2 * 1) / (3 * 4) / 5
|
|
|
|
1 + 2 / 12 / 5
|
|
|
|
1 + 2 / 12 * 1 / 5
|
|
|
|
1 + (2 * 1) / (12 * 5)
|
|
|
|
1 + 2 / 60
|
|
|
|
1 / 1 + 2 / 60
|
|
|
|
(1 * 60) / (1 * 60) + 2 / 60
|
|
|
|
60 / 60 + 2 / 60
|
|
|
|
(60 + 2) / 60
|
|
|
|
62 / 60
|
2018-09-17 16:18:29 +00:00
|
|
|
"""
|
|
|
|
try:
|
|
|
|
yield from self._ancestor.explain()
|
|
|
|
except AttributeError:
|
|
|
|
yield self
|
|
|
|
else:
|
|
|
|
yield self
|
2018-01-21 09:21:13 +00:00
|
|
|
|
|
|
|
# -----------------------------
|
|
|
|
# Reglages pour 'vim'
|
|
|
|
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
|
|
|
|
# cursor: 16 del
|