#!/usr/bin/env python # -*- coding: utf-8 -*- # I, Danny Milosavljevic, place this file in the public domain. # TODO unary operators. import sys import data import parsers operator_precedence = parsers.operator_precedence operator_to_precedence = {} for index, level_operators in enumerate(operator_precedence): for operator in level_operators: operator_to_precedence[operator] = index class NullFormatter(object): def format(self, expression, IO): IO.write(repr(expression)) class Formatter(object): def precedence(self, expression): """ given an operation, returns its precedence index """ operator_symbol = self.operator_symbol(expression) assert(operator_symbol) precedence = operator_to_precedence[operator_symbol] return precedence def child_precedence(self, operand): """ given an operand(!), retrns its precedence, if it has its own. """ if data.node_P(operand) and not data.reciprocation_P(operand) and not data.function_application_P(operand): child_precedence = self.precedence(operand) else: # FIXME why is this special case here? child_precedence = None return child_precedence def operator_symbol(self, expression): """ returns the operator symbol used for precedence lookup (as opposed to display) """ if data.node_P(expression): return str(expression.__class__.name) else: return None # if an operator has a given precedence x and, if a term must be presented as an operand, a bracket is output if the child-binary operator has a lower precedence. # (think a⋅(b+c)) # TODO use roots, divisions, subtractions. def format_infix(self, expression, IO): count = len(expression.operands) symbol = self.operator_symbol(expression) assert(symbol) precedence = operator_to_precedence[symbol] for index, operand in enumerate(expression.operands): self.format_operand(operand, precedence, IO) if index < count - 1: self.format_symbol(symbol, IO) # IO.write(symbol) def format_opening_brace(self, IO): IO.write("(") # TODO different parentheses? def format_closing_brace(self, IO): IO.write(")") def format_operand(self, operand, precedence, IO): """ given an operand and it's parent's precedence level, outputs braces if neccessary and then outputs the operand """ child_precedence = self.child_precedence(operand) or precedence #FIXME if (data.inner_product_P(expression) or data.convolution_P(expression)) and data.operation_P(operand): # almost nobody in the world knows the precedence levels of these, just put braces around the operands :P # child_precedence = 999 if child_precedence > precedence: self.format_opening_brace(IO) self.format(operand, IO) if child_precedence > precedence: self.format_closing_brace(IO) def format_function_application(self, expression, IO): self.format_symbol(expression.operands[0].decode("utf-8"), IO) assert(len(expression.operands) == 2) IO.write("(") self.format(expression.operands[1], IO) IO.write(")") def format_gradient(self, expression, IO): IO.write("∇(") self.format(expression.operands[0], IO) IO.write(")") def format_antiderivative(self, expression, IO): #IO.write("(") # TODO remove? symbol = self.operator_symbol(expression) self.format_symbol(symbol, IO) self.format(expression.operands[0], IO) #IO.write(")") def format_reciprocation(self, expression, IO): precedence = self.precedence(expression) IO.write("(1/") # TODO just use division if possible. self.format_operand(expression.operands[0], precedence, IO) IO.write(")") def format_negation(self, expression, IO): precedence = self.precedence(expression) IO.write("(-") # TODO just use subtraction if possible. TODO is it safe to remove the braces here? self.format_operand(expression.operands[0], precedence, IO) IO.write(")") def format_factorial(self, expression, IO): precedence = self.precedence(expression) self.format_operand(expression.operands[0], precedence, IO) IO.write("!") def format_symbol(self, expression, IO): name = str(expression) IO.write(name) def format(self, expression, IO): if data.relation_P(expression) or data.equation_P(expression) or data.addition_P(expression) or data.power_P(expression) or data.product_P(expression) or data.cross_product_P(expression) or data.inner_product_P(expression) or data.convolution_P(expression): self.format_infix(expression, IO) elif data.reciprocation_P(expression): self.format_reciprocation(expression, IO) elif data.negation_P(expression): self.format_negation(expression, IO) elif data.function_application_P(expression): self.format_function_application(expression, IO) elif data.antiderivative_P(expression): self.format_antiderivative(expression, IO) elif data.gradient_P(expression): self.format_gradient(expression, IO) elif data.factorial_P(expression): self.format_factorial(expression, IO) else: #IO.write(expression) #print "=>", type(expression), expression self.format_symbol(expression, IO) if __name__ == "__main__": import StringIO import parsers import codecs def parse_expression(text): text = text.replace("²", "^2") text = text.replace("³", "^3") expression = parsers.ExpressionParser(StringIO.StringIO(text)).parse() return expression def test_expression(expression_text, expected_text = None): expression = parse_expression(expression_text) IO = StringIO.StringIO() formatter_1 = Formatter() formatter_1.format(expression, IO) text = IO.getvalue() if not expected_text: expected_text = expression_text assert(text == expected_text) def print_expression(expression): formatter_1 = Formatter() formatter_1.format(expression, sys.stdout) sys.stdout.write("\n") sys.stdout.flush() print_expression(parse_expression("5!")) test_expression("5!") test_expression("5+3⋅2") test_expression("5⋅3+2") test_expression("5⋅(3+2)") test_expression("5+3⋅2≤0") print parse_expression("2^3!") #sys.stdout = codecs.open("/dev/stdout", "w", "UTF-8") expression = parse_expression("5+3⋅2≤0") print_expression(parse_expression("-(5+2)")) print_expression(parse_expression("-1")) print_expression(parse_expression("∇A")) print_expression(parse_expression("∇⨯A")) print_expression(parse_expression("∇∙A")) print_expression(parse_expression("∫f(x)⋅dx")) print_expression(parse_expression("√1⋅√2")) print_expression(parse_expression("sin ω⋅t+2⋅3⋅f(x)/2+√5")) print(parse_expression("2^3^5")) print_expression(parse_expression("2^3^5"))