summaryrefslogtreecommitdiff
path: root/mesonbuild/ast
diff options
context:
space:
mode:
authorVolker Weißmann <volker.weissmann@gmx.de>2025-03-02 10:03:17 +0100
committerDylan Baker <dylan@pnwbakers.com>2025-05-29 09:20:27 -0700
commit4bd3d638afe55f86633a7781cfc6cb68191906f0 (patch)
tree80b34737600b314dfb4c5aabbae4a3db164aed36 /mesonbuild/ast
parent2e68735349ebf4dcf11071a8f25e526f0ebd120f (diff)
downloadmeson-4bd3d638afe55f86633a7781cfc6cb68191906f0.tar.gz
AstPrinter: Add parenthesis where neccessary
Diffstat (limited to 'mesonbuild/ast')
-rw-r--r--mesonbuild/ast/printer.py46
1 files changed, 44 insertions, 2 deletions
diff --git a/mesonbuild/ast/printer.py b/mesonbuild/ast/printer.py
index 10a03bf77..0d0c821d4 100644
--- a/mesonbuild/ast/printer.py
+++ b/mesonbuild/ast/printer.py
@@ -7,11 +7,43 @@ from __future__ import annotations
from .. import mparser
from .visitor import AstVisitor, FullAstVisitor
+from ..mesonlib import MesonBugException
import re
import typing as T
+# Also known as "order of operations" or "binding power".
+# This is the counterpart to Parser.e1, Parser.e2, Parser.e3, Parser.e4, Parser.e5, Parser.e6, Parser.e7, Parser.e8, Parser.e9, Parser.e10
+def precedence_level(node: mparser.BaseNode) -> int:
+ if isinstance(node, (mparser.PlusAssignmentNode, mparser.AssignmentNode, mparser.TernaryNode)):
+ return 1
+ elif isinstance(node, mparser.OrNode):
+ return 2
+ elif isinstance(node, mparser.AndNode):
+ return 3
+ elif isinstance(node, mparser.ComparisonNode):
+ return 4
+ elif isinstance(node, mparser.ArithmeticNode):
+ if node.operation in {'add', 'sub'}:
+ return 5
+ elif node.operation in {'mod', 'mul', 'div'}:
+ return 6
+ elif isinstance(node, (mparser.NotNode, mparser.UMinusNode)):
+ return 7
+ elif isinstance(node, mparser.FunctionNode):
+ return 8
+ elif isinstance(node, (mparser.ArrayNode, mparser.DictNode)):
+ return 9
+ elif isinstance(node, (mparser.BooleanNode, mparser.IdNode, mparser.NumberNode, mparser.StringNode, mparser.EmptyNode)):
+ return 10
+ elif isinstance(node, mparser.ParenthesizedNode):
+ # Parenthesize have the highest binding power, but since the AstPrinter
+ # ignores ParanthesizedNode, the binding power of the inner node is
+ # relevant.
+ return precedence_level(node.inner)
+ raise MesonBugException('Unhandled node type')
+
class AstPrinter(AstVisitor):
escape_trans: T.Dict[int, str] = str.maketrans({'\\': '\\\\', "'": "\'"})
@@ -110,11 +142,21 @@ class AstPrinter(AstVisitor):
node.lineno = self.curr_line or node.lineno
node.right.accept(self)
+ def maybe_parentheses(self, outer: mparser.BaseNode, inner: mparser.BaseNode, parens: bool) -> None:
+ if parens:
+ self.append('(', inner)
+ inner.accept(self)
+ if parens:
+ self.append(')', inner)
+
def visit_ArithmeticNode(self, node: mparser.ArithmeticNode) -> None:
- node.left.accept(self)
+ prec = precedence_level(node)
+ prec_left = precedence_level(node.left)
+ prec_right = precedence_level(node.right)
+ self.maybe_parentheses(node, node.left, prec > prec_left)
self.append_padded(node.operator.value, node)
node.lineno = self.curr_line or node.lineno
- node.right.accept(self)
+ self.maybe_parentheses(node, node.right, prec > prec_right or (prec == prec_right and node.operation in {'sub', 'div', 'mod'}))
def visit_NotNode(self, node: mparser.NotNode) -> None:
node.lineno = self.curr_line or node.lineno