Import work from other repo
This commit is contained in:
parent
500426bf82
commit
a751e346d3
@ -1,12 +1,16 @@
|
|||||||
#!/usr/bin/env python
|
#! /usr/bin/env python
|
||||||
# encoding: utf-8
|
# -*- 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
|
Abstracts tools for calculs manipulations
|
||||||
from .fraction import Fraction
|
"""
|
||||||
from .equation import Equation
|
|
||||||
from .random_expression import random_str
|
__all__ = []
|
||||||
from .render import txt
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
|
21
mapytex/calculus/core/__init__.py
Normal file
21
mapytex/calculus/core/__init__.py
Normal 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
|
33
mapytex/calculus/core/coroutine.py
Normal file
33
mapytex/calculus/core/coroutine.py
Normal 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
|
22
mapytex/calculus/core/operator.py
Normal file
22
mapytex/calculus/core/operator.py
Normal 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
|
507
mapytex/calculus/core/str2.py
Normal file
507
mapytex/calculus/core/str2.py
Normal 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
|
644
mapytex/calculus/core/tree.py
Normal file
644
mapytex/calculus/core/tree.py
Normal 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
|
92
mapytex/calculus/core/tree_tools.py
Normal file
92
mapytex/calculus/core/tree_tools.py
Normal 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
|
Loading…
Reference in New Issue
Block a user