#!/usr/bin/env python # encoding: utf-8 from .generic import Stack, isNumber, isPolynom from .operator import op from decimal import Decimal import logging # logging.basicConfig(filename='str2tokens_debug.log',level=logging.DEBUG) def str2tokens(exp): """ Parse the string into tokens then turn it into postfix form >>> str2tokens('2+3*4') [2, 3, 4, *, +] >>> str2tokens('2*3+4') [2, 3, *, 4, +] >>> str2tokens('2x+4') [2, < Polynom x [0, 1]>, *, 4, +] """ in_tokens = str2in_tokens(exp) post_tokens = in2post_fix(in_tokens) return post_tokens def str2in_tokens(exp): """ Parse the expression, ie tranform a string into a list of tokens :param exp: The expression (a string) :returns: list of token >>> str2in_tokens('2+3*4') [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') [< Polynom a [0, 1]>, '*', 3, '+', 4] """ tokens = ['', ''] for character in exp: if character.isdigit(): tokens += feed_digit(character, tokens.pop(), tokens[-1]) elif character == "(": tokens += hidden_meaning_time(tokens[-1]) tokens.append("(") elif character in op.available_op(): tokens.append(character) elif character == ")": tokens.append(character) elif character.isalpha(): tokens += hidden_meaning_time(tokens[-1]) tokens.append(feed_alpha(character)) elif character == ".": tokens.append(feed_dot(tokens.pop())) elif character != " ": raise ValueError("{} is an unvalid character".format(character)) 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 mapytex.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 mapytex.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 mapytex.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): """ From the infix_tokens list compute the corresponding postfix_tokens list @param infix_tokens: the infix list of tokens to transform into postfix form. @return: the corresponding postfix list of tokens. >>> in2post_fix(['(', 2, '+', 5, '-', 1, ')', '/', '(', 3, '*', 4, ')']) [2, 5, 1, -, +, 3, 4, *, /] >>> in2post_fix(['-', '(', '-', 2, ')']) [2, -, -] >>> in2post_fix(['-', '(', '-', 2, '+', 3, '*', 4, ')']) [2, -, 3, 4, *, +, -] """ logging.debug("New start with {}".format(infix_tokens)) # Stack where operator will be stocked opStack = Stack() # final postfix list of tokens postfix_tokens = [] # stack with the nbr of tokens still to compute in postfix_tokens arity_Stack = Stack() arity_Stack.push(0) for (pos_token, token) in enumerate(infix_tokens): logging.debug(str(postfix_tokens) + " | " + str(opStack) + " | " + str(infix_tokens[(pos_token+1):]) + " | " + str(arity_Stack) ) if token == ")": next_op = opStack.pop() while next_op != op.par: postfix_tokens.append(next_op) next_op = opStack.pop() # Go back to old arity arity_Stack.pop() # Raise the arity arity = arity_Stack.pop() arity_Stack.push(arity + 1) elif op.can_be_operator(token): if token == "(": opStack.push(op.get_op(token, 0)) # Set next arity counter arity_Stack.push(0) else: arity = arity_Stack.pop() token_op = op.get_op(token, arity + 1) # Reset arity to 0 in case there is other operators (the real # operation would be "-op.arity + 1") arity_Stack.push(0) while (not opStack.isEmpty()) and opStack.peek( ).priority >= token_op.priority: next_op = opStack.pop() postfix_tokens.append(next_op) opStack.push(token_op) logging.debug("--" + token + " -> " + str(arity + 1)) else: postfix_tokens.append(token) arity = arity_Stack.pop() arity_Stack.push(arity + 1) logging.debug(str(postfix_tokens) + " | " + str(opStack) + " | " + str(infix_tokens[(pos_token+1):]) + " | " + str(arity_Stack) ) while not opStack.isEmpty(): next_op = opStack.pop() postfix_tokens.append(next_op) logging.debug(str(postfix_tokens) + " | " + str(opStack) + " | " + str(infix_tokens[(pos_token+1):]) + " | " + str(arity_Stack) ) if arity_Stack.peek() != 1: raise ValueError( "Unvalid expression. The arity Stack is ", str(arity_Stack)) logging.debug("Fini!") return postfix_tokens # ----------------------------- # Reglages pour 'vim' # vim:set autoindent expandtab tabstop=4 shiftwidth=4: # cursor: 16 del