From a4fd3ad386ce159d4b653c7cdd3804ac98d9fc12 Mon Sep 17 00:00:00 2001 From: Aymeric Vincent Date: Mon, 11 Dec 2017 16:56:12 +0100 Subject: Remove some unnecessary parentheses when str()'ing expressions This patch removes quite a few useless parentheses from expressions thanks to the usual priorities between operators. It is conservative in the sense that not all such parentheses are removed when we felt the priorities won't be known by most users. --- miasm2/expression/expression.py | 62 ++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 16 deletions(-) (limited to 'miasm2/expression/expression.py') diff --git a/miasm2/expression/expression.py b/miasm2/expression/expression.py index 591dc024..d31c509c 100644 --- a/miasm2/expression/expression.py +++ b/miasm2/expression/expression.py @@ -56,6 +56,38 @@ EXPRSLICE = 7 EXPRCOMPOSE = 8 +priorities_list = [ + [ '+' ], + [ '*', '/', '%' ], + [ '**' ], + [ '-' ], # Unary '-', associativity with + not handled +] + +# dictionary from 'op' to priority, derived from above +priorities = dict((op, prio) + for prio, l in enumerate(priorities_list) + for op in l) +PRIORITY_MAX = len(priorities_list) - 1 + +def should_parenthesize_child(child, parent): + if (isinstance(child, ExprId) or isinstance(child, ExprInt) or + isinstance(child, ExprCompose) or isinstance(child, ExprMem) or + isinstance(child, ExprSlice)): + return False + elif isinstance(child, ExprOp) and not child.is_infix(): + return False + elif (isinstance(child, ExprCond) or isinstance(parent, ExprSlice)): + return True + elif (isinstance(child, ExprOp) and isinstance(parent, ExprOp)): + pri_child = priorities.get(child.op, -1) + pri_parent = priorities.get(parent.op, PRIORITY_MAX + 1) + return pri_child < pri_parent + else: + return True + +def str_protected_child(child, parent): + return ("(%s)" % child) if should_parenthesize_child(child, parent) else str(child) + def visit_chk(visitor): "Function decorator launching callback on Expression visit" def wrapped(expr, callback, test_visit=lambda x: True): @@ -687,7 +719,7 @@ class ExprCond(Expr): return Expr.get_object(cls, (cond, src1, src2)) def __str__(self): - return "(%s?(%s,%s))" % (str(self._cond), str(self._src1), str(self._src2)) + return "%s?(%s,%s)" % (str_protected_child(self._cond, self), str(self._src1), str(self._src2)) def get_r(self, mem_read=False, cst_read=False): out_src1 = self.src1.get_r(mem_read, cst_read) @@ -919,20 +951,13 @@ class ExprOp(Expr): return Expr.get_object(cls, (op, args)) def __str__(self): - if self.is_associative(): - return '(' + self._op.join([str(arg) for arg in self._args]) + ')' - if (self._op.startswith('call_func_') or - self._op == 'cpuid' or - len(self._args) > 2 or - self._op in ['parity', 'segm']): - return self._op + '(' + ', '.join([str(arg) for arg in self._args]) + ')' - if len(self._args) == 2: - return ('(' + str(self._args[0]) + - ' ' + self.op + ' ' + str(self._args[1]) + ')') - else: - return reduce(lambda x, y: x + ' ' + str(y), - self._args, - '(' + str(self._op)) + ')' + if self._op == '-': # Unary minus + return '-' + str_protected_child(self._args[0], self) + if self.is_associative() or self.is_infix(): + return (' ' + self._op + ' ').join([str_protected_child(arg, self) + for arg in self._args]) + return (self._op + '(' + + ', '.join([str(arg) for arg in self._args]) + ')') def get_r(self, mem_read=False, cst_read=False): return reduce(lambda elements, arg: @@ -960,6 +985,11 @@ class ExprOp(Expr): def is_function_call(self): return self._op.startswith('call') + def is_infix(self): + return self._op in [ '-', '+', '*', '^', '&', '|', '>>', '<<', + 'a>>', '>>>', '<<<', '/', '%', '**', + '