Import work from other repo

This commit is contained in:
Bertrand Benjamin 2018-01-21 11:26:34 +03:00
parent 500426bf82
commit a751e346d3
7 changed files with 1331 additions and 8 deletions

View File

@ -1,12 +1,16 @@
#!/usr/bin/env python
# encoding: utf-8
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2017 lafrite <lafrite@Poivre>
#
# Distributed under terms of the MIT license.
from .expression import Expression
from .polynom import Polynom
from .fraction import Fraction
from .equation import Equation
from .random_expression import random_str
from .render import txt
"""
Abstracts tools for calculs manipulations
"""
__all__ = []
# -----------------------------

View File

@ -0,0 +1,21 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2017 lafrite <lafrite@Poivre>
#
# Distributed under terms of the MIT license.
"""
Abstracts tools for calculs manipulations
"""
__all__ = ["Tree"]
from .tree import Tree
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del

View File

@ -0,0 +1,33 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2017 lafrite <lafrite@Poivre>
#
# Distributed under terms of the MIT license.
"""
Coroutine and sink tools
"""
from functools import wraps
__all__ = ["coroutine", "STOOOP", "RESTAAART"]
def coroutine(func):
@wraps(func)
def start(*args,**kwargs):
cr = func(*args,**kwargs)
next(cr)
return cr
return start
class STOOOP(BaseException): pass
class RESTAAART(BaseException): pass
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del

View File

@ -0,0 +1,22 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2017 lafrite <lafrite@Poivre>
#
# Distributed under terms of the MIT license.
OPERATORS = {
"+": {"priority":0},
"-": {"priority":1},
"*": {"priority":2},
"/": {"priority":3},
"^": {"priority":4},
}
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del

View File

@ -0,0 +1,507 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2017 lafrite <lafrite@Poivre>
#
# Distributed under terms of the MIT license.
"""
Converting a string with coroutines
"""
from functools import partial
from .coroutine import *
__all__ = ["str2", ]
def maybe_it_is(cara):
""" Return a function which return
Maybe if cara startwith the argument and True if it is cara
:exemple:
>>> it_is_pipo = maybe_it_is("pipo")
>>> it_is_pipo("pi")
'maybe'
>>> it_is_pipo("pipo")
True
>>> it_is_pipo("uo")
False
>>> it_is_iuo = maybe_it_is("iuo")
>>> it_is_iuo("pi")
False
>>> it_is_iuo("iuo")
True
>>> it_is_iuo("uo")
False
"""
def func(c):
if c == cara:
return True
elif cara.startswith(c):
return "maybe"
else:
return False
return func
def something_in(cara_list):
""" Return a function which sais whether a caracter is in cara_list or not
"""
def func(c):
if c in cara_list:
return True
else:
return False
return func
@coroutine
def lookfor(condition, replace = lambda x:''.join(x)):
""" Sink which is looking for term and yield replace founded patern
with the lenght of the accumulation.
It can be reset by sending None
:param condition: conditional function which returns a boolean or "maybe"
:param replace: function which make a final transformation to the founded
string. Id by default.
:example:
>>> lf = lookfor(maybe_it_is("pipo"), lambda x: 'remp')
>>> acc = ""
>>> for i in 'popipo':
... a = lf.send(i)
... if a == "maybe":
... acc += i
... elif a:
... acc = ""
... print(a)
... else:
... print(acc + i)
... acc = ""
po
remp
>>> lf = lookfor(maybe_it_is("po") , lambda x: ''.join(x).upper())
>>> acc = ""
>>> for i in 'popipo':
... a = lf.send(i)
... if a == "maybe":
... acc += i
... elif a:
... acc = ""
... print(a)
... else:
... print(acc + i)
... acc = ""
PO
pi
PO
>>> lf = lookfor(maybe_it_is("pi") , lambda x: 1)
>>> acc = ""
>>> for i in 'popipop':
... a = lf.send(i)
... if a == "maybe":
... acc += i
... elif a:
... acc = ""
... print(a)
... else:
... print(acc + i)
... acc = ""
po
1
po
>>> # the p in still processing
>>> acc = ""
>>> for i in 'iop':
... a = lf.send(i)
... if a == "maybe":
... acc += i
... elif a:
... acc = ""
... print(a)
... else:
... print(acc + i)
... acc = ""
1
o
>>> # the p in still processing
>>> lf.throw(RESTAAART)
False
>>> # p has been forgot
>>> acc = ""
>>> for i in 'ipopi':
... a = lf.send(i)
... if a == "maybe":
... acc += i
... elif a:
... acc = ""
... print(a)
... else:
... print(acc + i)
... acc = ""
i
po
1
"""
acc = []
ans = False
while True:
try:
c = (yield ans)
except RESTAAART as err:
acc = []
ans = False
else:
if c is not None:
acc.append(c)
found = condition(''.join(acc))
if found == "maybe":
ans = "maybe"
elif found:
ans = replace(acc)
acc = []
else:
ans = False
acc = []
@coroutine
def remember_lookfor(lookfor):
""" Coroutine which remember sent value before the lookfor finds something
:example:
>>> lkf = lookfor(maybe_it_is("pipo"), lambda x: 'remp')
>>> rmb_lkf = remember_lookfor(lkf)
>>> for i in 'popipopi':
... print(rmb_lkf.send(i))
maybe
False
maybe
maybe
maybe
['p', 'o', 'remp']
maybe
maybe
>>> for i in 'popi':
... print(rmb_lkf.send(i))
maybe
['remp']
maybe
maybe
>>> rmb_lkf.throw(RESTAAART)
['p', 'i']
>>> for i in 'popi':
... print(rmb_lkf.send(i))
maybe
False
maybe
maybe
"""
acc = []
mb_acc = []
ans = False
while True:
try:
c = (yield ans)
except RESTAAART as err:
lookfor.throw(err)
ans = acc + mb_acc
acc = []
mb_acc = []
else:
lkf_state = lookfor.send(c)
if lkf_state == "maybe":
mb_acc.append(c)
ans = "maybe"
elif lkf_state:
ans = acc + [lkf_state]
acc = []
mb_acc = []
else:
acc += mb_acc
mb_acc = []
acc.append(c)
ans = False
@coroutine
def concurent_broadcast(target, lookfors = []):
""" Coroutine which broadcasts multiple lookfor coroutines and reinitializes
them when one found something
Same as parallelized_lookfor but coroutine version
:param target: target to send data to
:param lookfors: list of lookfor coroutines
:example:
>>> lf1 = lookfor(maybe_it_is("abc"), lambda x: "".join(x).upper())
>>> searcher = concurent_broadcast(list_sink, [lf1])
>>> for i in "azabcab":
... searcher.send(i)
>>> a = searcher.throw(STOOOP)
>>> print(a)
['a', 'z', 'ABC', 'a', 'b']
>>> lf2 = lookfor(maybe_it_is("az"))
>>> searcher = concurent_broadcast(list_sink, [lf1, lf2])
>>> for i in "azabcabazb":
... searcher.send(i)
>>> a = searcher.throw(STOOOP)
>>> print(a)
['az', 'ABC', 'a', 'b', 'az', 'b']
>>> lfop = lookfor(something_in("+-*/()"), lambda x: f"op{x}")
>>> searcher = concurent_broadcast(list_sink, [lfop])
>>> for i in '12+3+234':
... searcher.send(i)
>>> a = searcher.throw(STOOOP)
>>> print(a)
['1', '2', "op['+']", '3', "op['+']", '2', '3', '4']
>>> # need to remake a searcher otherwise it remenbers old ans
>>> searcher = concurent_broadcast(list_sink, [lfop])
>>> for i in '12*(3+234)':
... searcher.send(i)
>>> a = searcher.throw(STOOOP)
>>> print(a)
['1', '2', "op['*']", "op['(']", '3', "op['+']", '2', '3', '4', "op[')']"]
>>> lfsqrt = lookfor(maybe_it_is("sqrt"), lambda x: f"op['sqrt']")
>>> searcher = concurent_broadcast(list_sink, [lfop, lfsqrt])
>>> for i in '3+2*sqrt(3)':
... searcher.send(i)
>>> a = searcher.throw(STOOOP)
>>> print(a)
['3', "op['+']", '2', "op['*']", "op['sqrt']", "op['(']", '3', "op[')']"]
"""
try:
target_ = target()
except TypeError:
target_ = target
lookfors_ = [remember_lookfor(lkf) for lkf in lookfors]
stock = []
ans = []
try:
while True:
found = False
tok = yield
if tok is not None:
for lf in lookfors_:
lf_ans = lf.send(tok)
if lf_ans and lf_ans != "maybe":
found = lf_ans
break
if found:
for lf in lookfors_:
lf.throw(RESTAAART)
for i in found:
ans = target_.send(i)
except STOOOP as err:
for lf in lookfors_:
last = lf.throw(RESTAAART)
for i in last:
target_.send(i)
yield target_.throw(err)
@coroutine
def lookforNumbers(target):
""" Coroutine which parse numbers
:exemple:
>>> str2list = lookforNumbers(list_sink)
>>> for i in "12+1234*67":
... str2list.send(i)
>>> a = str2list.throw(STOOOP)
>>> print(a)
['12', '+', '1234', '*', '67']
>>> str2list = lookforNumbers(list_sink)
>>> for i in "1.2+12.34*67":
... str2list.send(i)
>>> a = str2list.throw(STOOOP)
>>> print(a)
['1.2', '+', '12.34', '*', '67']
>>> str2list = lookforNumbers(list_sink)
>>> for i in "12.3.4*67":
... str2list.send(i)
Traceback (most recent call last):
...
ValueError: Can't build a number with 2 dots (current is 12.3)
>>> str2list = lookforNumbers(list_sink)
>>> for i in ".34*67":
... a = str2list.send(i)
Traceback (most recent call last):
...
ValueError: Can't build a number starting with a dot
"""
try:
target_ = target()
except TypeError:
target_ = target
current = ""
ans = []
try:
while True:
tok = yield
if tok is not None:
try:
int(tok)
except ValueError:
if tok == '.':
if "." in current:
raise ValueError(f"Can't build a number with 2 dots (current is {current})")
elif len(current) == 0:
raise ValueError(f"Can't build a number starting with a dot")
else:
current += tok
else:
if current:
target_.send(current)
current = ""
target_.send(tok)
else:
current += tok
except STOOOP as err:
if current:
target_.send(current)
yield target_.throw(err)
@coroutine
def pparser(target):
""" Parenthesis parser sink
:example:
>>> pp = pparser(list_sink)
>>> for i in "buio":
... pp.send(i)
>>> a = pp.throw(STOOOP)
>>> a
['b', 'u', 'i', 'o']
>>> pp = pparser(list_sink)
>>> for i in "agg(bcz)e(i(o))":
... pp.send(i)
>>> a = pp.throw(STOOOP)
>>> a
['a', 'g', 'g', ['b', 'c', 'z'], 'e', ['i', ['o']]]
"""
target_ = target()
try:
while True:
caract = yield
if caract == "(":
a = yield from pparser(target)
target_.send(a)
elif caract == ")":
a = target_.throw(STOOOP)
return a
else:
target_.send(caract)
except STOOOP as err:
yield target_.throw(err)
@coroutine
def list_sink():
""" Testing sink for coroutines
:example:
>>> sink = list_sink()
>>> for i in '12+34 * 3':
... sink.send(i)
>>> a = sink.throw(STOOOP)
>>> a
['1', '2', '+', '3', '4', ' ', '*', ' ', '3']
"""
ans = list()
try:
while True:
c = (yield)
if c is not None:
ans.append(c)
except STOOOP:
yield ans
def str2(sink):
""" Return a pipeline which parse an expression with the sink as an endpont
:example:
>>> str2nestedlist = str2(list_sink)
>>> exp = "12+3*4"
>>> t = str2nestedlist(exp)
>>> print(t)
['12', '+', '3', '*', '4']
>>> exp = "12*3+4"
>>> t = str2nestedlist(exp)
>>> print(t)
['12', '*', '3', '+', '4']
>>> exp = "12*(3+4)"
>>> t = str2nestedlist(exp)
>>> print(t)
['12', '*', ['3', '+', '4']]
>>> from .tree import MutableTree
>>> str2tree = str2(MutableTree.sink)
>>> exp = "12+3*4"
>>> t = str2tree(exp)
>>> print(t)
+
> 12
> *
| > 3
| > 4
>>> exp = "12*3+4"
>>> t = str2tree(exp)
>>> print(t)
+
> *
| > 12
| > 3
> 4
>>> exp = "12*(3+4)"
>>> t = str2tree(exp)
>>> print(t)
*
> 12
> +
| > 3
| > 4
"""
lfop = lookfor(something_in("+-*/"))
operator_corout = partial(concurent_broadcast, lookfors = [lfop])
def pipeline(expression):
str2_corout = lookforNumbers(operator_corout(pparser(sink)))
for i in expression:
str2_corout.send(i)
a = str2_corout.throw(STOOOP)
return a
return pipeline
str2nestedlist = str2(list_sink)
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del

View File

@ -0,0 +1,644 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2017 lafrite <lafrite@Poivre>
#
# Distributed under terms of the MIT license.
"""
Tree class
"""
from .tree_tools import (to_nested_parenthesis,
infix_str_concatenate,
postfix_concatenate,
show_tree,
)
from .coroutine import *
from .str2 import str2
from .operator import OPERATORS
__all__ = ["Tree", "MutableTree"]
class Tree(object):
"""
Binary tree
This is the chosen structure to manage calculus in PyMath.
The class is not mutable to preserve its integrity.
"""
def __init__(self, node, left_value, right_value):
"""
Initiate a tree with tuple (node, (left value, right value))
:example:
>>> t = Tree("+", 1, 2)
>>> t.node
'+'
>>> t.left_value
1
>>> t.right_value
2
"""
if node is None or \
left_value is None or \
right_value is None:
raise TypeError("Tree can't have empty node or leaf")
self.node = node
self.left_value = left_value
self.right_value = right_value
@classmethod
def from_str(cls, expression):
""" Initiate a tree from an string expression
:example:
>>> t = MutableTree.fromStr("2+3*4")
>>> print(t)
+
> 2
> *
| > 3
| > 4
>>> t = MutableTree.fromStr("(2+3)*4")
>>> print(t)
*
> +
| > 2
| > 3
> 4
"""
t = MutableTree.from_str(expression)
return cls.from_mutable_tree(t)
@classmethod
def from_nested_parenthesis(cls, nested_parenthesis):
"""
Initiate recursively a tree with tuple (node, (left value, right value))
:example:
>>> nested_par = ("+", (1, 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t.node
'+'
>>> t.left_value
1
>>> t.right_value
2
>>> nested_par = ("+", (
... ("*", (3, 4)),
... 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t.node
'+'
>>> type(t.left_value)
<class 'calculus.core.tree.Tree'>
>>> t.right_value
2
"""
try:
nested_len = len(nested_parenthesis)
num_len = len(nested_parenthesis[1])
except TypeError:
raise ValueError("Nested parenthesis are not composed of lists")
if nested_len != 2 and \
num_len != 2:
raise ValueError("Nested parenthesis don't have right shape")
node = nested_parenthesis[0]
try:
left_value = Tree.from_nested_parenthesis(nested_parenthesis[1][0])
except ValueError:
left_value = nested_parenthesis[1][0]
try:
right_value = Tree.from_nested_parenthesis(nested_parenthesis[1][1])
except ValueError:
right_value = nested_parenthesis[1][1]
return cls(node, left_value, right_value)
@classmethod
def from_mutable_tree(cls, mutable_tree):
""" Initial a Tree from a MutableTree
:example:
>>> t = MutableTree("*", 1, 2)
>>> print(Tree.from_mutable_tree(t))
*
> 1
> 2
>>> t1 = MutableTree("*", 1, 2)
>>> t2 = MutableTree("*", t1, 3)
>>> print(Tree.from_mutable_tree(t2))
*
> *
| > 1
| > 2
> 3
>>> t = MutableTree("*", 1)
>>> print(t)
*
> 1
> None
>>> Tree.from_mutable_tree(t)
Traceback (most recent call last):
...
TypeError: Tree can't have empty node or leaf
"""
node = mutable_tree.node
left_value = mutable_tree.left_value
right_value = mutable_tree.right_value
if node is None or \
left_value is None or \
right_value is None:
raise TypeError("Tree can't have empty node or leaf")
try:
l_value = Tree.from_mutable_tree(left_value)
except AttributeError:
l_value = left_value
try:
r_value = Tree.from_mutable_tree(right_value)
except AttributeError:
r_value = right_value
return cls(node, l_value, r_value)
def map_leaf(self, function):
""" Map on leafs a function
:param function: take leaf value returns other value
:returns: Tree with calculated leaf
:example:
>>> nested_par = ("+", (
... ("*", (3, 4)),
... 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
"""
try:
left_applied = self.left_value.apply(function)
except AttributeError:
left_applied = function(self.left_value)
try:
right_applied = self.right_value.apply(function)
except AttributeError:
right_applied = function(self.right_value)
return Tree(self.node, left_applied, right_applied)
def apply_on_last_branches(self, function):
""" Apply the function on last branches before leaf.
:param function: (op, a, a) -> b
:returns: b if it is a 1 level Tree, Tree otherwise
"""
try:
# TODO: to change when typify is done |dim. févr. 19 14:49:06 EAT 2017
left_applied = self.left_value.apply_on_last_branches(function)
except AttributeError:
left_is_leaf = 1
try:
# TODO: to change when typify is done |dim. févr. 19 14:49:06 EAT 2017
left_applied = self.left_value.apply_on_last_branches(function)
except AttributeError:
right_is_leaf = 1
if left_is_leaf and right_is_leaf:
return function(self.node, self.left_value, self.right_value)
else:
return Tree(self.node, left_applied, right_applied)
def apply(self, function):
""" Apply the function to the whole tree
:param function: (op, a, a) -> b
:returns: b
:example:
>>> def to_nested(op, left, right):
... return (op, (left, right))
>>> nested_par = ("+", (1, 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t.apply(to_nested)
('+', (1, 2))
>>> nested_par = ("+", (
... ("*", (3, 4)),
... 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t.apply(to_nested)
('+', (('*', (3, 4)), 2))
"""
try:
left_value = self.left_value.apply(function)
except AttributeError:
left_value = self.left_value
try:
right_value = self.right_value.apply(function)
except AttributeError:
right_value = self.right_value
return function(self.node, left_value, right_value)
def to_nested_parenthesis(self):
""" Transform the Tree into its nested parenthesis description
:example:
>>> nested_par = ("+", (1, 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t.to_nested_parenthesis()
('+', (1, 2))
>>> nested_par = ("+", (
... ("*", (3, 4)),
... 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t.to_nested_parenthesis()
('+', (('*', (3, 4)), 2))
"""
return self.apply(to_nested_parenthesis)
def to_postfix(self):
""" Transform the Tree into postfix notation
:example:
>>> nested_par = ("+", (1, 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t.to_postfix()
[1, 2, '+']
>>> nested_par = ("+", (
... ("*", (3, 4)),
... 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t.to_postfix()
[3, 4, '*', 2, '+']
>>> nested_par = ("+", (
... ("*", (3, 4)),
... ("*", (5, 6)),
... ))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t.to_postfix()
[3, 4, '*', 5, 6, '*', '+']
"""
return self.apply(postfix_concatenate)
def __str__(self):
""" Overload str method
:example:
>>> nested_par = ("+", (1, 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> print(t)
+
> 1
> 2
>>> nested_par = ("+", (
... ("*", (3, 4)),
... 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> print(t)
+
> *
| > 3
| > 4
> 2
>>> nested_par = ("+", (
... ("*", (1, 2)),
... ("*", (3, 4)),
... ))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> print(t)
+
> *
| > 1
| > 2
> *
| > 3
| > 4
"""
return self.apply(show_tree)
class MutableTree(Tree):
"""
Mutable Tree.
It is used to build a new tree before fixing it into a Tree
"""
def __init__(self,
node = None,
left_value = None,
right_value = None):
"""
Initiate a tree with potentialy None values
:Example:
>>> t = MutableTree()
>>> t.node, t.left_value, t.right_value
(None, None, None)
>>> t = MutableTree('*', 1)
>>> t.node, t.left_value, t.right_value
('*', 1, None)
"""
self.node = node
self.left_value = left_value
self.right_value = right_value
@classmethod
def fromStr(cls, expression):
""" Initiate the MutableTree
:example:
>>> t = MutableTree.fromStr("2+3*4")
>>> print(t)
+
> 2
> *
| > 3
| > 4
>>> t = MutableTree.fromStr("(2+3)*4")
>>> print(t)
*
> +
| > 2
| > 3
> 4
>>> t = MutableTree.fromStr("4*(-2+3)")
>>> print(t)
*
> 4
> +
| > -
| | > None
| | > 2
| > 3
"""
str2mutTree = str2(cls.sink)
return str2mutTree(expression)
@classmethod
@coroutine
def sink(cls):
""" Sink which build a build a mutabletree
Returns the built tree when STOOOP is thrown
:example:
>>> sink = MutableTree.sink()
>>> for i in ["1", "+", "2", "*", "3"]:
... sink.send(i)
>>> a = sink.throw(STOOOP)
>>> print(a)
+
> 1
> *
| > 2
| > 3
>>> sink = MutableTree.sink()
>>> for i in ["1", "*", "2", "+", "3"]:
... sink.send(i)
>>> a = sink.throw(STOOOP)
>>> print(a)
+
> *
| > 1
| > 2
> 3
>>> sink = MutableTree.sink()
>>> for i in ["-", "1", "+", "2"]:
... sink.send(i)
>>> a = sink.throw(STOOOP)
>>> print(a)
+
> -
| > None
| > 1
> 2
"""
ans = cls()
try:
while True:
c = yield
if c is not None:
if c in OPERATORS.keys():
try:
ans.set_node(c)
except ValueError:
if OPERATORS[c]["priority"] > OPERATORS[ans.node]["priority"]:
ans.append_bot(c)
else:
ans.append_top(c)
else:
ans.append(c)
except STOOOP:
yield ans
def set_node(self, value):
""" Set the node value.
Once it has been changed it can't be changed again.
:example:
>>> t = MutableTree()
>>> t.node
>>> t.set_node("*")
>>> t.node
'*'
>>> t.set_node("+")
Traceback (most recent call last):
...
ValueError: The node of the tree is already set
"""
if self.node is None:
self.node = value
else:
raise ValueError("The node of the tree is already set")
def set_left_value(self, value):
""" Set the left value.
Once it has been changed it can't be changed again.
:example:
>>> t = MutableTree()
>>> t.left_value
>>> t.set_left_value(1)
>>> t.left_value
1
>>> t.set_left_value(2)
Traceback (most recent call last):
...
ValueError: The left branch is full, use set_right_value
"""
if self.left_value is None:
self.left_value = value
else:
try:
self.left_value.append(value)
except AttributeError:
raise ValueError("The left branch is full, use set_right_value")
def set_right_value(self, value):
""" Set the right value.
Once it has been changed it can't be changed again.
:example:
>>> t = MutableTree()
>>> t.right_value
>>> t.set_right_value(2)
>>> t.right_value
2
>>> t.set_right_value(3)
Traceback (most recent call last):
...
ValueError: The right branch is full, use append_top or append_bot
>>> # potentielle source de problèmes??
>>> t = MutableTree()
>>> t.set_right_value([1])
>>> t.right_value
[1]
>>> t.set_right_value(3)
"""
if self.right_value is None:
self.right_value = value
else:
try:
self.right_value.append(value)
except AttributeError:
raise ValueError("The right branch is full, use append_top or append_bot")
def append(self, value):
""" Append the value at the bottom of the tree.
In order to enable operator with arity 1, left value can be set only
before the node is set, assuming that those operator are placed before
operand.
It tries to add it on left branch first.
If it fails, the value is appened on right branch.
:example:
>>> t = MutableTree()
>>> t.append(1)
>>> t.set_node("*")
>>> t.append(2)
>>> print(t)
*
> 1
> 2
>>> t.append(3)
Traceback (most recent call last):
...
ValueError: The right branch is full, use append_top or append_bot
>>> t1 = MutableTree()
>>> t1.append(1)
>>> t1.set_node("*")
>>> t2 = MutableTree()
>>> t1.append(t2)
>>> t1.append(2)
>>> t2.set_node("+")
>>> t1.append(3)
>>> print(t1)
*
> 1
> +
| > 2
| > 3
>>> t1 = MutableTree()
>>> t1.set_node("-")
>>> t1.append(1)
>>> print(t1)
-
> None
> 1
"""
if self.node is None:
try:
self.set_left_value(value)
except ValueError:
self.set_right_value(value)
else:
self.set_right_value(value)
def append_top(self, value):
""" Append node into the tree at the top
:example:
>>> t = MutableTree("*", 1, 2)
>>> t.append_top("+")
>>> print(t)
+
> *
| > 1
| > 2
> None
"""
self_cp = MutableTree()
self_cp.set_node(self.node)
self_cp.set_left_value(self.left_value)
self_cp.set_right_value(self.right_value)
self.left_value, self.node, self.right_value = self_cp, value, None
def append_bot(self, value):
""" Append node into the tree at the bottom right_value
:example:
>>> t = MutableTree("*", 1, 2)
>>> t.append_bot("+")
>>> print(t)
*
> 1
> +
| > 2
| > None
"""
rv = self.right_value
nright_value = MutableTree()
nright_value.set_node(value)
nright_value.set_left_value(rv)
self.right_value = nright_value
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del

View File

@ -0,0 +1,92 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2017 lafrite <lafrite@Poivre>
#
# Distributed under terms of the MIT license.
"""
Functions to manipulate trees
"""
__all__ = []
# Functions on leaf
# Functions on (op, left, right)
def to_nested_parenthesis(op, left, right):
""" Get nested form for arguments
:exemple:
>>> to_nested_parenthesis('+', 3, 4)
('+', (3, 4))
"""
return (op, (left, right))
def infix_str_concatenate(op, left, right):
""" Concatenate arguments placing op on the middle.
:example:
>>> infix_str_concatenate('+', 1, 2)
'1 + 2'
"""
return f"{left} {op} {right}"
def postfix_concatenate(op, left, right):
""" Concatenate arguments placing op on the middle.
:example:
>>> postfix_concatenate('+', 1, 2)
[1, 2, '+']
>>> p = postfix_concatenate('+', 1, 2)
>>> postfix_concatenate('*', p, 3)
[1, 2, '+', 3, '*']
>>> postfix_concatenate('*', 3, p)
[3, 1, 2, '+', '*']
"""
if isinstance(left, list):
left_tokens = left
else:
left_tokens = [left]
if isinstance(right, list):
right_tokens = right
else:
right_tokens = [right]
return left_tokens + right_tokens + [op]
def show_tree(op, left, right, sep = "|", node_caracter = ">"):
""" Shape argument to make nice Tree display
:example:
>>> print(show_tree("+", 1, 2))
+
> 1
> 2
>>> t1 = show_tree("*", 1, 2)
>>> print(show_tree("+", t1, 3))
+
> *
| > 1
| > 2
> 3
"""
node_suffix = f"\n {node_caracter} "
leaf_suffix = f"\n {sep}"
left_slided = leaf_suffix.join(str(left).splitlines())
right_slided = leaf_suffix.join(str(right).splitlines())
return node_suffix.join([op, left_slided, right_slided])
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del