Mapytex/mapytex/calculus/API/tokens/__init__.py

212 lines
5.6 KiB
Python
Raw Permalink Normal View History

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2017 lafrite <lafrite@Poivre>
#
# Distributed under terms of the MIT license.
"""
Tokens represents MathObject at API level
"""
2019-07-17 07:25:12 +00:00
from ...core.MO import MO, MOnumber, MOstr, moify
from ...core.MO.fraction import MOFraction
from ...core.MO.monomial import MOstrPower, MOMonomial
from ...core.MO.polynomial import MOpolynomial
from decimal import Decimal as _Decimal
from functools import wraps
2019-07-17 07:25:12 +00:00
from .token import Token
__all__ = ["factory"]
2019-07-17 07:25:12 +00:00
def tokenify(mo, name="", ancestor=None):
""" Transform a MO or a python builtin to the appropriate token
2019-05-14 04:55:56 +00:00
2019-07-17 07:25:12 +00:00
:param mo: the thing to turn into a Token
:param name: a virtual name of the toke
:param ancestor: its ancestor
2018-12-07 10:23:05 +00:00
:example:
2019-07-17 07:25:12 +00:00
>>> a = MOnumber(2)
>>> tokenify(a)
2018-12-07 10:23:05 +00:00
<Integer 2>
2019-07-17 07:25:12 +00:00
>>> tokenify(2)
<Integer 2>
>>> tokenify("x")
<Linear x>
>>> tokenify(_Decimal("2.2"))
<Decimal 2.2>
>>> tokenify("2.2")
<Decimal 2.2>
>>> tokenify(2.2)
<Decimal 2.20000000000000017763568394002504646778106689453125>
tokenify is idempotent on "mo" parameter
>>> a = MOnumber(2)
>>> ta = tokenify(a)
>>> ta == tokenify(ta)
True
"""
if isinstance(mo, MO):
return _tokenify(mo, name, ancestor)
if isinstance(mo, Token):
if name == "":
_name = mo.name
else:
_name = name
if ancestor is None:
_ancestor = mo._ancestor
else:
_ancestor = ancestor
return _tokenify(mo._mo, _name, _ancestor)
2019-07-17 07:25:12 +00:00
return _tokenify(moify(mo), name, ancestor)
def to_be_token(func):
""" Decorator to ensure that the return value is a Token """
@wraps(func)
def wrapped(*args, **kwds):
ans = func(*args, **kwds)
try:
return [tokenify(t) for t in ans]
except TypeError:
return tokenify(ans)
return wrapped
2019-07-17 07:25:12 +00:00
def _tokenify(mo, name="", ancestor=None):
""" Transform a MO (from core) to the appropriate token (from API)
:example:
>>> a = MOnumber(2)
>>> _tokenify(a)
<Integer 2>
>>> a = MOnumber(2.5)
>>> _tokenify(a)
2018-12-07 10:23:05 +00:00
<Decimal 2.5>
2019-07-17 07:25:12 +00:00
>>> a = MOFraction(2, 5)
>>> _tokenify(a)
2018-12-07 10:23:05 +00:00
<Fraction 2 / 5>
2019-07-17 07:25:12 +00:00
>>> a = MOstr('x')
>>> _tokenify(a)
2018-12-07 10:23:05 +00:00
<Linear x>
2019-07-17 07:25:12 +00:00
>>> a = MOstrPower('x', 2)
>>> _tokenify(a)
2018-12-07 10:23:05 +00:00
<Quadratic x^2>
2019-07-17 07:25:12 +00:00
>>> a = MOstrPower('x', 3)
>>> _tokenify(a)
2018-12-07 10:23:05 +00:00
<Polynomial x^3>
2019-07-17 07:25:12 +00:00
>>> a = MOMonomial(3, 'x', 1)
>>> _tokenify(a)
2018-12-07 10:23:05 +00:00
<Linear 3x>
2019-07-17 07:25:12 +00:00
>>> a = MOMonomial(3, 'x', 2)
>>> _tokenify(a)
2018-12-07 10:23:05 +00:00
<Quadratic 3x^2>
2019-07-17 07:25:12 +00:00
>>> a = MOMonomial(3, 'x', 3)
>>> _tokenify(a)
2018-12-07 10:23:05 +00:00
<Polynomial 3x^3>
"""
if isinstance(mo, MOnumber):
if isinstance(mo.value, int):
2019-07-15 09:09:59 +00:00
from .number import Integer
return Integer.from_mo(mo, name, ancestor)
elif isinstance(mo.value, _Decimal):
2019-07-15 09:09:59 +00:00
from .number import Decimal
return Decimal.from_mo(mo, name, ancestor)
2018-12-07 10:23:05 +00:00
raise TypeError(f"Can't build from MOnumber ({mo}) neither int nor decimal")
if isinstance(mo, MOFraction):
2019-05-14 04:55:56 +00:00
if isinstance(mo._denominator, MOnumber) and isinstance(
mo._numerator, MOnumber
):
2019-07-15 09:09:59 +00:00
from .number import Fraction
return Fraction.from_mo(mo, name, ancestor)
2018-12-07 10:23:05 +00:00
2019-05-14 04:55:56 +00:00
raise TypeError(
f"Can't build from MOFraction ({mo}) numerator and denominator are not MOnumber"
)
if isinstance(mo, (MOstr, MOstrPower, MOMonomial, MOpolynomial)):
2018-12-07 10:23:05 +00:00
if not isinstance(mo._variable, (MOstr, str)):
2019-05-14 04:55:56 +00:00
raise TypeError(
f"Can't build Polynom over something else than a letter (got {mo._variable})"
)
if (
isinstance(mo, MOstr)
or (isinstance(mo, MOMonomial) and mo.power.value == 1)
or (isinstance(mo, MOpolynomial) and mo.power.value == 1)
):
2019-07-15 09:09:59 +00:00
from .polynomial import Linear
return Linear.from_mo(mo, name, ancestor)
2019-05-14 04:55:56 +00:00
elif (
(isinstance(mo, MOstrPower) and mo.power.value == 2)
or (isinstance(mo, MOMonomial) and mo.power.value == 2)
or (isinstance(mo, MOpolynomial) and mo.power.value == 2)
):
2019-07-15 09:09:59 +00:00
from .polynomial import Quadratic
return Quadratic.from_mo(mo, name, ancestor)
2018-12-07 10:23:05 +00:00
else:
2019-07-15 09:09:59 +00:00
from .polynomial import Polynomial
2018-12-07 10:23:05 +00:00
return Polynomial.from_mo(mo, name, ancestor)
raise TypeError(f"{type(mo)} is unknown MathObject")
2019-05-14 04:55:56 +00:00
2019-07-17 07:25:12 +00:00
def factory(exp, name="", ancestor=None):
""" Transform a Expression with on single MathObject (from core) to a appropriate token (from API)
:example:
>>> from ..expression import Expression
>>> a = Expression(MOnumber(2))
>>> factory(a)
<Integer 2>
>>> a = Expression(MOnumber(2.5))
>>> factory(a)
<Decimal 2.5>
>>> a = Expression(MOFraction(2, 5))
>>> factory(a)
<Fraction 2 / 5>
>>> a = Expression(MOstr('x'))
>>> factory(a)
<Linear x>
>>> a = Expression(MOstrPower('x', 2))
>>> factory(a)
<Quadratic x^2>
>>> a = Expression(MOstrPower('x', 3))
>>> factory(a)
<Polynomial x^3>
>>> a = Expression(MOMonomial(3, 'x', 1))
>>> factory(a)
<Linear 3x>
>>> a = Expression(MOMonomial(3, 'x', 2))
>>> factory(a)
<Quadratic 3x^2>
>>> a = Expression(MOMonomial(3, 'x', 3))
>>> factory(a)
<Polynomial 3x^3>
"""
mo = exp._tree
if not isinstance(mo, MO):
raise TypeError(f"Can't build Token from not computed Expression (got {mo})")
return _tokenify(mo, name, ancestor)
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del