#! /usr/bin/env python # -*- coding: utf-8 -*- # vim:fenc=utf-8 # # Copyright © 2017 lafrite # # Distributed under terms of the MIT license. from ..coroutine import coroutine, STOOOP from ..renders import tree2txt, tree2tex from decimal import Decimal __all__ = ["moify", "MO", "MOstr"] class MOError(Exception): pass @coroutine def moify(target): """ Coroutine which try to convert everything into an MO """ try: target_ = target() except TypeError: target_ = target try: while True: tok = yield try: target_.send(MOnumber(tok)) except MOError: try: target_.send(MOstr(tok)) except MOError: target_.send(tok) except STOOOP as err: yield target_.throw(err) class MO(object): """MO for math object This base class is representing int and Decimal. It stocks its value in self.value and it """ def __init__(self, value): """ Initiate the MO It should be idempotent. >>> a = MO(3) >>> a >>> a = MO(a) >>> a """ try: self.value = value.value except AttributeError: self.value = value self.is_scalar = True @classmethod def factory(cls, value): """ Factory to ensure that a value is a MO before using it """ if isinstance(value, MO): return value elif isinstance(value, str): return MOstr(value) return MO(value) def __repr__(self): return f"<{self.__class__.__name__} {self.__txt__}>" def __str__(self): return str(self.value) @property def __txt__(self): try: return tree2txt(self.value) except AttributeError: return str(self.value) @property def __tex__(self): try: return tree2tex(self.value) except AttributeError: return str(self.value) def __add__(self, other): """ Overload + for MOs >>> from decimal import Decimal >>> a = MO(4) >>> b = MO(Decimal("1.2")) >>> a + b >>> b + a """ return MO.factory(self.value + other.value) def __mul__(self, other): """ Overload * for MOs >>> from decimal import Decimal >>> a = MO(4) >>> b = MO(Decimal("1.2")) >>> a * b >>> b * a """ return MO.factory(self.value * other.value) def __truediv__(self, other): """ Overload / for MOs >>> from decimal import Decimal >>> a = MO(4) >>> b = MO(Decimal("1.4")) >>> c = b / a >>> c >>> type(c.value) >>> c = a / b >>> c >>> type(c.value) """ return MO.factory(self.value / other.value) def __neg__(self): """ Overload + for MOs >>> from decimal import Decimal >>> a = MO(4) >>> - a >>> b = MO(Decimal("1.2")) >>> - b """ return MO.factory(-self.value) class MOnumber(MO): """ Base number math object (int or Decimal) """ def __init__(self, value, negative=False): """ Initiate a number MO >>> MOnumber(23) >>> MOnumber(-23) >>> MOnumber(23.3) >>> MOnumber(Decimal("23.3")) >>> MOnumber(Decimal("-23.3")) >>> a = MOnumber(23) >>> MOnumber(a) >>> MOnumber("a") Traceback (most recent call last): ... mapytex.calculus.core.MO.mo.MOError: The value of an MOnumber need to be a int, a float or a Decimal """ try: val = value.value except AttributeError: val = value if isinstance(val, int) or isinstance(val, Decimal): MO.__init__(self, value) elif isinstance(val, float): MO.__init__(self, Decimal(val)) else: raise MOError("The value of an MOnumber need to be a int, a float or a Decimal") @property def __txt__(self): if self.value > 0: return str(self.value) return f"- {abs(self.value)}" @property def __tex__(self): if self.value > 0: return str(self.value) return f"- {abs(self.value)}" class MOstr(MO): """ Unknown math object like x or n""" def __init__(self, value): """ Initiate a string MO >>> a = MOstr("x") >>> a >>> b = MOstr(a) >>> b >>> a = MOstr("+") Traceback (most recent call last): ... mapytex.calculus.core.MO.mo.MOError: An MOstr should be initiate with a alpha string, got + >>> MOstr("ui") Traceback (most recent call last): ... mapytex.calculus.core.MO.mo.MOError: An MOstr should be initiate with a single caracter string, got ui >>> MOstr(2) Traceback (most recent call last): ... mapytex.calculus.core.MO.mo.MOError: An MOstr should be initiate with a string - the unknown, got 2 """ try: val = value.value except AttributeError: val = value if not isinstance(val, str): raise MOError(f"An MOstr should be initiate with a string - the unknown, got {val}") if len(val) != 1: raise MOError(f"An MOstr should be initiate with a single caracter string, got {val}") if not val.isalpha(): raise MOError(f"An MOstr should be initiate with a alpha string, got {val}") MO.__init__(self, value) self.is_scalar = False def __add__(self, other): raise NotImplemented def __radd__(self, other): raise NotImplemented def __mul__(self, other): if other.is_scalar: raise NotImplemented raise NotImplemented def __rmul__(self, other): if other.is_scalar: raise NotImplemented raise NotImplemented def __truediv__(self, other): if other.is_scalar: raise NotImplemented raise NotImplemented def __rtruediv__(self, other): if other.is_scalar: raise NotImplemented raise NotImplemented def __neg__(self): pass # ----------------------------- # Reglages pour 'vim' # vim:set autoindent expandtab tabstop=4 shiftwidth=4: # cursor: 16 del