#! /usr/bin/env python # -*- coding: utf-8 -*- # vim:fenc=utf-8 # # Copyright © 2017 lafrite # # Distributed under terms of the MIT license. from decimal import Decimal from ..coroutine import coroutine, STOOOP from ..renders import tree2txt, tree2tex __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, str): return MOstr(value) elif isinstance(value, int) \ or isinstance(value, Decimal) \ or isinstance(value, float): return MOnumber(value) elif isinstance(value, MO): return 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) class MOnumber(MO): """ Base number math object (int or Decimal) """ def __init__(self, value): """ 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, 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 # ----------------------------- # Reglages pour 'vim' # vim:set autoindent expandtab tabstop=4 shiftwidth=4: # cursor: 16 del