diff options
author | Julian <cod3monk@users.noreply.github.com> | 2020-10-05 15:27:24 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-05 06:27:24 -0700 |
commit | 75e875732d3c4b923ea0d1f0713b88d35f5ef612 (patch) | |
tree | a8ded3224038259d802377fbd66f8715d612f7db /pycparser | |
parent | ba80794f1460a87eed2f8f918e47868878f3a5ad (diff) | |
download | pycparser-75e875732d3c4b923ea0d1f0713b88d35f5ef612.tar.gz |
Added flattening of abundant parenthesis in CGenerator (#394)
Diffstat (limited to 'pycparser')
-rw-r--r-- | pycparser/c_generator.py | 54 | ||||
-rw-r--r-- | pycparser/c_parser.py | 1 |
2 files changed, 50 insertions, 5 deletions
diff --git a/pycparser/c_generator.py b/pycparser/c_generator.py index 53c26fd..c494176 100644 --- a/pycparser/c_generator.py +++ b/pycparser/c_generator.py @@ -14,11 +14,17 @@ class CGenerator(object): return a value from each visit method, using string accumulation in generic_visit. """ - def __init__(self): + def __init__(self, reduce_parentheses=False): + """ Constructs C-code generator + + reduce_parentheses: + if True, eliminates needless parentheses on binary operators + """ # Statements start with indentation of self.indent_level spaces, using # the _make_indent method # self.indent_level = 0 + self.reduce_parentheses = reduce_parentheses def _make_indent(self): return ' ' * self.indent_level @@ -72,11 +78,49 @@ class CGenerator(object): else: return '%s%s' % (n.op, operand) + # Precedence map of binary operators: + precedence_map = { + # Some what of a duplicate of c_paarser.CParser.precedence + # Higher numbers are stronger binding + '||': 0, # weakest binding + '&&': 1, + '|': 2, + '^': 3, + '&': 4, + '==': 5, '!=': 5, + '>': 6, '>=': 6, '<': 6, '<=': 6, + '>>': 7, '<<': 7, + '+': 8, '-': 8, + '*': 9, '/': 9, '%': 9 # strongest binding + } + def visit_BinaryOp(self, n): - lval_str = self._parenthesize_if(n.left, - lambda d: not self._is_simple_node(d)) - rval_str = self._parenthesize_if(n.right, - lambda d: not self._is_simple_node(d)) + # Note: all binary operators are left-to-right associative + # + # If `n.left.op` has a stronger or equally binding precedence in + # comparison to `n.op`, no parenthesis are needed for the left: + # e.g., `(a*b) + c` is equivelent to `a*b + c`, as well as + # `(a+b) - c` is equivelent to `a+b - c` (same precedence). + # If the left operator is weaker binding than the current, then + # parentheses are necessary: + # e.g., `(a+b) * c` is NOT equivelent to `a+b * c`. + lval_str = self._parenthesize_if( + n.left, + lambda d: not (self._is_simple_node(d) or + self.reduce_parentheses and isinstance(d, c_ast.BinaryOp) and + self.precedence_map[d.op] >= self.precedence_map[n.op])) + # If `n.right.op` has a stronger -but not equal- binding precedence, + # parenthesis can be omitted on the right: + # e.g., `a + (b*c)` is equivelent to `a + b*c`. + # If the right operator is weaker or equally binding, then parentheses + # are necessary: + # e.g., `a * (b+c)` is NOT equivelent to `a * b+c` and + # `a - (b+c)` is NOT equivelent to `a - b+c` (same precedence). + rval_str = self._parenthesize_if( + n.right, + lambda d: not (self._is_simple_node(d) or + self.reduce_parentheses and isinstance(d, c_ast.BinaryOp) and + self.precedence_map[d.op] > self.precedence_map[n.op])) return '%s %s %s' % (lval_str, n.op, rval_str) def visit_Assignment(self, n): diff --git a/pycparser/c_parser.py b/pycparser/c_parser.py index c2d82f7..0536e58 100644 --- a/pycparser/c_parser.py +++ b/pycparser/c_parser.py @@ -491,6 +491,7 @@ class CParser(PLYParser): ## ## Precedence and associativity of operators ## + # If this changes, c_generator.CGenerator.precedence_map needs to change as well precedence = ( ('left', 'LOR'), ('left', 'LAND'), |