diff --git a/pymath/calculus/operator/operator_set.py b/pymath/calculus/operator/operator_set.py index 7a69c97..38af650 100644 --- a/pymath/calculus/operator/operator_set.py +++ b/pymath/calculus/operator/operator_set.py @@ -54,6 +54,23 @@ class Operator_set(object): except KeyError: raise KeyError("{theOp} (arity: {arity}) is not available".format(theOp = op, arity = arity)) + def available_op(self): + """ Get the set of available operators strings + + >>> op = Operator_set() + >>> op.available_op() + set() + >>> op.store_operator(Add()) + >>> '+' in op.available_op() + True + >>> '*' in op.available_op() + False + >>> op.store_operator(Mul()) + >>> '*' in op.available_op() + True + """ + return set([i[0] for i in self._operators]) + def can_be_operator(cls, symbole): r""" Tell if the symbole can be an operator diff --git a/pymath/calculus/str2tokens.py b/pymath/calculus/str2tokens.py index a02ed12..747dafa 100644 --- a/pymath/calculus/str2tokens.py +++ b/pymath/calculus/str2tokens.py @@ -28,8 +28,6 @@ def str2tokens(exp): def str2in_tokens(exp): """ Parse the expression, ie tranform a string into a list of tokens - /!\ float are not availiable yet! - :param exp: The expression (a string) :returns: list of token @@ -37,67 +35,147 @@ def str2in_tokens(exp): [2, '+', 3, '*', 4] >>> str2in_tokens('2*3+4') [2, '*', 3, '+', 4] + >>> str2in_tokens('12*3+4') + [12, '*', 3, '+', 4] + >>> str2in_tokens('2.3*3+4') + [Decimal('2.3'), '*', 3, '+', 4] + >>> str2in_tokens('a*3+4') + [< [0, 1]>, '*', 3, '+', 4] """ - tokens = [''] + tokens = ['', ''] for character in exp: if character.isdigit(): - # for "big" numbers (like 2345) - if isinstance(tokens[-1], int): - if tokens[-1] > 0: - tokens[-1] = tokens[-1] * 10 + int(character) - else: - tokens[-1] = tokens[-1] * 10 - int(character) + tokens += feed_digit(character, tokens.pop(), tokens[-1]) - elif isinstance(tokens[-1], Decimal): - after_coma += 1 - if tokens[-1] >= 0: - tokens[-1] = tokens[-1] + int(character) * Decimal('0.1') ** after_coma - else: - tokens[-1] = tokens[-1]- int(character) * Decimal('0.1') ** after_coma + elif character == "(": + tokens += hidden_meaning_time(tokens[-1]) + tokens.append("(") - # Special case for "-" at the begining of an expression or before - # "(" - elif tokens[-1] == "-" and \ - str(tokens[-2]) in " (+-*/:": - tokens[-1] = - int(character) - else: - tokens.append(int(character)) - - elif character in "+-*/:^": + elif character in op.available_op(): tokens.append(character) elif character == ")": tokens.append(character) - elif character == "(": - # If "3(", ")(", "x(" - if isNumber(tokens[-1]) \ - or tokens[-1] == ")" \ - or isPolynom(tokens[-1]): - tokens.append("*") - tokens.append(character) - elif character.isalpha(): - # If "3x", ")x", "xy" - if isNumber(tokens[-1]) \ - or tokens[-1] == ")" \ - or isPolynom(tokens[-1]): - tokens.append("*") - from pymath.calculus.polynom import Polynom - tokens.append(Polynom([0, 1], letter=character)) + tokens += hidden_meaning_time(tokens[-1]) + tokens.append(feed_alpha(character)) elif character == ".": - if isinstance(tokens[-1], Decimal): - raise ValueError("A number has 2 points...!") - else: - tokens[-1] = Decimal(tokens[-1]) - after_coma = 0 + tokens.append(feed_dot(tokens.pop())) elif character != " ": raise ValueError("{} is an unvalid character".format(character)) - return tokens[1:] + return tokens[2:] + +def feed_digit(character, tok_b, tok_bb): + """ Feed token when a digit is detected + + :param character: the character + :param tok_b: the token before + :param tok_bb: the token before before + :returns: list of token to replace + + >>> feed_digit(1, '-', 2) + ['-', 1] + >>> feed_digit(1, '', '') + ['', 1] + >>> feed_digit(1, 2, '') + [21] + >>> feed_digit(1, Decimal(2), '') + [Decimal('2.1')] + >>> feed_digit(1, Decimal('2.3'), '') + [Decimal('2.31')] + >>> feed_digit(1, -2, '') + [-21] + >>> feed_digit(1, Decimal('-2'), '') + [Decimal('-2.1')] + >>> feed_digit(1, '-', '') + [-1] + >>> feed_digit(1, '-', '+') + [-1] + >>> feed_digit(1, '-', '(') + [-1] + >>> feed_digit(1, '-', 2) + ['-', 1] + >>> from pymath.calculus.polynom import Polynom + >>> feed_digit(1, '-', Polynom([0,1])) + ['-', 1] + + """ + if isinstance(tok_b, int): + return [tok_b * 10 + int(tok_b/abs(tok_b)) * int(character)] + + elif isinstance(tok_b, Decimal): + return [tok_b + int(tok_b/abs(tok_b)) * int(character) * Decimal('10') ** (tok_b.as_tuple().exponent - 1)] + + # TODO: WTF!!! |sam. févr. 27 17:11:53 EAT 2016 + elif tok_b == "-" and (str(tok_bb) in op.available_op() or str(tok_bb) == ""): + return [- int(character)] + else: + return [tok_b, int(character)] + +def hidden_meaning_time(tok_b): + """ Return a "*" character if it is hidden meaning + + :param tok_b: the token before + + >>> hidden_meaning_time(4) + ['*'] + >>> hidden_meaning_time(')') + ['*'] + >>> from pymath.calculus.polynom import Polynom + >>> hidden_meaning_time(Polynom([0,1])) + ['*'] + >>> hidden_meaning_time("+") + [] + >>> hidden_meaning_time("*") + [] + + """ + if isNumber(tok_b) \ + or tok_b == ")" \ + or isPolynom(tok_b): + return ["*"] + return [] + +def feed_alpha(character): + """ Feed token when an alpha character is detected + + :param character: the alpha character + :param tok_b: the token before + :returns: tokens to add + + """ + from pymath.calculus.polynom import Polynom + return Polynom([0, 1], letter=character) + +def feed_dot(tok_b): + r""" Build Decimal with the previous token + + :param tok_b: the previous token + :returns: the Decimal + + >>> feed_dot(2) + Decimal('2') + >>> feed_dot(Decimal('2.3')) + Traceback (most recent call last): + ... + ValueError: A number has 2 points...! We have 2.3 before the dot + >>> feed_dot('+') + Traceback (most recent call last): + ... + ValueError: Try to make decimal but + before the dot + + """ + if isinstance(tok_b, int): + return Decimal(tok_b) + elif isinstance(tok_b, Decimal): + raise ValueError("A number has 2 points...! We have {} before the dot".format(tok_b)) + else: + raise ValueError("Try to make decimal but {} before the dot".format(tok_b)) def in2post_fix(infix_tokens):