def test_expression_evalutate(self): # Need to handle cases like ((1*1)*(1)) # Stack is empty after we evaulate (1) so we see # the last ')', and that triggers a pop on empty stack exp = Expression('(((2.05+20)x(100÷2))x((5-2)+2))') self.assertTrue(5512.5 == exp.evaluate()) del exp exp = Expression('((2.55x6.1)÷2)') self.assertTrue(7.777499999999999 == exp.evaluate()) del exp exp = Expression('(((2.21x4.1)÷2)x0)') self.assertTrue(0 == exp.evaluate()) del exp exp = Expression('(((1x1)x1)x1)') self.assertTrue(1 == exp.evaluate()) del exp exp = Expression('(5+10)') self.assertTrue(15 == exp.evaluate()) del exp exp = Expression('(((5+10)x10)÷10)') self.assertTrue(15 == exp.evaluate()) del exp exp = Expression('((((5+10)x10)÷10)-(200.2x2))') self.assertTrue(-385.4 == exp.evaluate()) del exp
def __handle_expression(self, expr): if len(expr) > 0: e = Expression() try: result = e.evaluate(expr, self.vars_dict) if len(result) > 0: print(result) except ValueError as e: print(str(e))
def testMock1(self): expression = Expression(self.expr1) expression.rpn.evaluate = Mock(return_value=0) print('\nmock test 1') expression.print() self.assertEqual(expression.evaluate(math.pi), 0) expression.rpn.evaluate.assert_called_once() expression.rpn.evaluate.assert_called_with(x=math.pi)
def part1(lines): sum = 0 for line in lines: expr = Expression(line) expr.parse() sum += expr.evaluate() return sum
def testMock3(self): expression = Expression(self.expr2) get_rpn = Mock() get_rpn.side_effect = self.log_eval expression.rpn = get_rpn(math.pi) print('\nmock test 3') expression.print() self.assertEqual(expression.evaluate(math.pi), 0) get_rpn.assert_called_once_with(math.pi)
def __init__(self, ast_node, func): self.func = func self.ast_node = ast_node self.asm = "" # Assignment if ast_node.token.text == "ASSIGN": # Get the variable and expression var, expr = ast_node.children var = func.getVar(var.text) # Calculate the answer expr = Expression(expr, func) factor = expr.evaluate() # Add the calculations to the function body self.asm += str(expr) self.asm += self.header() self.asm += var.assign(factor, func.prog.next_loop.next()) if ast_node.token.text == "IF": cond, true_body, false_body = ast_node.children idnum = self.func.prog.next_loop.next() self.true = ".truebranch%s" % idnum self.false = ".falsebranch%s" % idnum self.asm += IF_ASM.format( cond=Condition(cond, self), id=idnum, true_body=''.join( map(str, [Statement(s, self.func) for s in true_body.children])), false_body=''.join( map(str, [Statement(s, self.func) for s in false_body.children]))) if ast_node.token.text == "WHILE": cond, body = ast_node.children idnum = self.func.prog.next_loop.next() self.true = ".loopbegin%s" % idnum self.false = ".loopexit%s" % idnum self.asm += WHILE_ASM.format( id=idnum, cond=Condition(cond, self), body=''.join( map(str, [Statement(s, self.func) for s in body.children])))
def testExpressionEvaluate1(self): expression = Expression(self.expr2) tokens = expression.rpn.tokens self.assertEqual(tokens, [ '2', '*', 'sin', '(', '1', '/', '(', 'exp', '(', '3', '*', 'x', ')', '+', '1', ')', '-', 'tg', '(', 'x', '+', 'PI', '/', '2', ')', ')' ]) notation = expression.rpn.notation self.assertEqual(notation, [ '2', '1', '3', 'x', '*', 'exp', '1', '+', '/', 'x', 'PI', '2', '/', '+', 'tg', '-', 'sin', '*' ]) self.assertEqual(expression.evaluate(math.pi), 1.630394220705093)
def __init__(self, ast_node, func): self.func = func self.ast_node = ast_node self.asm = "" # Assignment if ast_node.token.text == "ASSIGN": # Get the variable and expression var, expr = ast_node.children var = func.getVar(var.text) # Calculate the answer expr = Expression(expr, func) factor = expr.evaluate() # Add the calculations to the function body self.asm += str(expr) self.asm += self.header() self.asm += var.assign(factor, func.prog.next_loop.next()) if ast_node.token.text == "IF": cond, true_body, false_body = ast_node.children idnum = self.func.prog.next_loop.next() self.true = ".truebranch%s" % idnum self.false = ".falsebranch%s" % idnum self.asm += IF_ASM.format( cond = Condition(cond, self), id = idnum, true_body = ''.join(map(str, [Statement(s, self.func) for s in true_body.children])), false_body = ''.join(map(str, [Statement(s, self.func) for s in false_body.children])) ) if ast_node.token.text == "WHILE": cond, body = ast_node.children idnum = self.func.prog.next_loop.next() self.true = ".loopbegin%s" % idnum self.false = ".loopexit%s" % idnum self.asm += WHILE_ASM.format( id = idnum, cond = Condition(cond, self), body = ''.join(map(str, [Statement(s, self.func) for s in body.children])) )
def test_equality_false(self): expr = Expression('x == y') self.assertEqual(False, expr.evaluate(self.data), 'Should be equal')
def test_two_properties_equal(self): expr = Expression('x == z') self.assertEqual(True, expr.evaluate(self.data), 'Should be equal')
def test_gt_true(self): expr = Expression('y > x') self.assertEqual(True, expr.evaluate(self.data), 'Should be equal')
def test_equality_true(self): expr = Expression('x == z') self.assertEqual(True, expr.evaluate(self.data), 'Should be equal')
class Tree: BOOLEAN = Lark(""" start: orexpr ?orexpr: (orexpr ("+" | "|" ~ 1..2 | "or" | "OR"))? andexpr ?andexpr: (andexpr ("*" | "&" ~ 1..2 | "and" | "AND"))? xorexpr ?xorexpr: (xorexpr ("^" | "xor" | "XOR"))? xnorexpr ?xnorexpr: (xnorexpr ("-^" | "xnor" | "XNOR"))? norexpr ?norexpr: (norexpr ("-+" | "nor" | "NOR"))? nandexpr ?nandexpr: (nandexpr ("-*" | "nand" | "NAND"))? term ?term: nexpr | pexpr | IDENT ?nexpr: TILDE orexpr ?pexpr: "(" orexpr ")" | "[" orexpr "]" TILDE: "~" | "!" | "not" | "NOT" %import common.CNAME -> IDENT %import common.WS %ignore WS """) @staticmethod def __create_dict(parse_tree) -> tuple: """Creates a parsable JSON object that can be parsed through LogicNode and LogicVar objects to be used to represent a boolean expression :param parse_tree: A Lark grammar tree object to parse through :type parse_tree: lark.tree.Tree :return: A tuple containing the parsed expression as a JSON object and a list of variables the expression :rtype: tuple """ variables = [] # Check if the parse_tree is a Tree if isinstance(parse_tree, LarkTree): # Check if the expression is an orexpr (OR) or andexpr (AND) if parse_tree.data != "nexpr": # Get the left and right expression along with any new variables that may exist left, variables_new_left = Tree.__create_dict( parse_tree.children[0]) right, variables_new_right = Tree.__create_dict( parse_tree.children[1]) for variable in variables_new_left + variables_new_right: if variable not in variables: variables.append(variable) # Return the expression and the variables return { "left": left, "operator": parse_tree.data[:parse_tree.data.find("expr")].upper(), "right": right, "has_not": False }, variables # Check if the expression is an nexpr (NOT) else: # Get the expression along with any new variables that may exist expression, variables_new = Tree.__create_dict( parse_tree.children[1]) expression["has_not"] = not expression["has_not"] for variable in variables_new: if variable not in variables: variables.append(variable) # Return the expression and the variables return expression, variables # The parse_tree is an ident (Variable) return { "value": parse_tree.value, "has_not": False }, [parse_tree.value] def __init__(self, expr: str): # Try to parse the expression try: self.__root, self.__variables = Tree.__create_dict( Tree.BOOLEAN.parse(expr).children[ 0] # This ignores the "start" Tree ) self.__variables.sort() # Check if the expression is a LogicNode or LogicVar if "value" in self.__root: # noinspection PyTypeChecker self.__root = Variable(json=self.__root) else: # noinspection PyTypeChecker self.__root = Expression(json=self.__root) # If parsing the expression fails, the boolean expression is invalid except Exception: raise ValueError("The expression given is invalid") def __str__(self): if isinstance(self.__root, Expression): return str(self.__root)[1:-1] return str(self.__root) # # # # # # # # # # # # # # # # # # # # # Getters # # # # # # # # # # # # # # # # # # # # def get_variables(self) -> list: """Returns a list of variables used in this Tree""" return self.__variables def get_table(self, as_list: bool = False) -> Union[str, list]: """Returns a truth table of the root expression of this Tree :param as_list: Whether or not to return the truth table as a list of lines :type as_list: bool """ # Create the header row which holds the variables and result like the following: # | a | b | c | (a * b) + c | header = "| {} | {} |".format(" | ".join(self.get_variables()), str(self)) # Create the separator row which uses symbols to look like the following: # +---+---+---+-------------+ separator = "+-{}-+-{}-+".format( "-+-".join(["-" * len(var) for var in self.get_variables()]), "-" * len(str(self))) # Create the truth values and their evaluations which just consists of 1's and 0's # to look like the following: # | 0 | 1 | 0 | 0 | evaluations = self.evaluate() values = "\n".join([ "| {} | {} |".format( " | ".join([ ("1" if evaluation["truth_values"][value] else "0").center( len(value)) for value in evaluation["truth_values"] ]), ("1" if evaluation["truth_value"] else "0").center( len(str(self)))) for evaluation in evaluations ]) if as_list: return [header, separator, values] return f"{header}\n{separator}\n{values}" # # # # # # # # # # # # # # # # # # # # # Evaluation Methods # # # # # # # # # # # # # # # # # # # # def evaluate(self) -> list: """Evaluates the root of this Tree to get boolean values where the root expression is 1 or 0 :return: A list of evaluations and their truth values that make up the evaluation """ # Iterate through all the integer values from 2 ** len(variables) evaluations = [] for binary in range(2**len(self.get_variables())): # Create a JSON object for each variable and whether or not this variable # is true at the current binary value truth_values = { self.get_variables()[i]: binary & (1 << (len(self.get_variables()) - 1 - i)) != 0 for i in range(len(self.get_variables())) } # Add the evaluation for this binary value to the list of evaluations evaluations.append({ "truth_values": truth_values, "truth_value": self.__root.evaluate(truth_values) }) return evaluations def simplify(self, get_minterm: bool = None) -> 'Tree': """Simplifies the boolean expression at the root and returns the most simplified expression obtained from either minterm or maxterm evaluation. :param get_minterm: Whether to get the minterm expression or maxterm expression. By default, the function returns the shortest of the two :type get_minterm: bool :return: The simplified boolean expression inside a Tree :rtype: Tree """ # Get the minterm and maxterm true-at values # Note that a minterm expression is true where the expression evaluates # to true (1) and a maxterm expresion is true where the expression evaluates # to false (0) evaluations = self.evaluate() true_at_minterms = [ decimal for decimal in range(len(evaluations)) if evaluations[decimal]["truth_value"] ] true_at_maxterms = [ decimal for decimal in range(len(evaluations)) if not evaluations[decimal]["truth_value"] ] minterm_qm = QM(self.get_variables(), true_at_minterms).get_function() maxterm_qm = QM(self.get_variables(), true_at_maxterms, is_maxterm=True).get_function() tree_minterm = Tree( minterm_qm) if minterm_qm not in "01" else minterm_qm tree_maxterm = Tree( maxterm_qm) if maxterm_qm not in "01" else maxterm_qm if get_minterm is not None: if get_minterm: return tree_minterm return tree_maxterm return min(tree_minterm, tree_maxterm, key=lambda qm: len(str(qm))) def functional(self) -> str: """Returns this Tree object in a functional notation For example: - ``a or b and c`` is functionally equivalent to ``and(or(a, b), c)`` """ return self.__root.functional()
def question1(expr, x): expr = Expression(expr) return expr.evaluate(x)
class GUI: def __init__(self, title="Python Calculator", size="290x230"): self._gui = Tk() self._title = title self._size = size placeholder = StringVar().set("Enter your expression") self._field = Entry(self._gui, textvariable=placeholder) self._expression = None self._positions = { ' ( ': (2, 0), ' ) ': (2, 1), ' CE ': (2, 2), ' - ': (2, 3), ' 7 ': (3, 0), ' 8 ': (3, 1), ' 9 ': (3, 2), ' ÷ ': (3, 3), ' 4 ': (4, 0), ' 5 ': (4, 1), ' 6 ': (4, 2), ' x ': (4, 3), ' 1 ': (5, 0), ' 2 ': (5, 1), ' 3 ': (5, 2), ' + ': (5, 3), ' 0 ': (6, 0), ' . ': (6, 2), ' = ': (6, 3), } def create(self): self._configure() self._create_input_field() self._create_buttons() self._gui.mainloop() def _configure(self): self._gui.configure(background="black") self._gui.title(self._title) self._gui.geometry(self._size) def _create_input_field(self): self._field.grid(rowspan=2, columnspan=4, ipadx=49, ipady=12) def _create_buttons(self): for buttonText in self._positions.keys(): button = Button( self._gui, text=buttonText, height=2, width=1, command=lambda action=buttonText: self._button_action(action)) colspan = 2 if buttonText == " 0 " else 1 # sticky removes extra space btwn buttons button.grid(row=self._positions[buttonText][0], column=self._positions[buttonText][1], columnspan=colspan, sticky="nsew") def _button_action(self, action): action = action.strip() if action == '=': final_expression = self._field.get() if final_expression == '' or '(' not in final_expression or ')' not in final_expression: self._field.delete(0, END) self._field.insert(0, 'Invalid Expression') else: self._expression = Expression(final_expression) self._field.delete(0, END) self._field.insert(0, self._expression.evaluate()) del self._expression elif action == 'CE': current = str(self._field.get())[:-1] self._field.delete(0, END) self._field.insert(0, str(current)) else: current = self._field.get() self._field.delete(0, END) self._field.insert(0, str(current) + str(action))