aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2022-02-24 16:30:22 -0800
committerXavier Claessens <xclaesse@gmail.com>2023-06-01 15:32:18 -0400
commit1ed1c8e2b0fa639a4a5ee74a2984ff7b03526028 (patch)
tree25b261d28a30edb3c46c9c497f9990e571e97d2b /mesonbuild
parentcb0b6352b5b3fe548f2148999b4072eac76ff465 (diff)
downloadmeson-1ed1c8e2b0fa639a4a5ee74a2984ff7b03526028.zip
meson-1ed1c8e2b0fa639a4a5ee74a2984ff7b03526028.tar.gz
meson-1ed1c8e2b0fa639a4a5ee74a2984ff7b03526028.tar.bz2
mparser: add equality operators to nodes
This makes use of dataclasses, but without a dataclass generated initializer. This means that we get nice `__repr__` and `__eq__` methods without having to type them by hand. Pylance understands `dataclass(init=False)`, but mypy doesn't. https://github.com/microsoft/pyright/issues/1753 https://github.com/python/mypy/issues/10309
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/mparser.py215
1 files changed, 161 insertions, 54 deletions
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
index fb0ec95..15e62e0 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -12,12 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
-
-from dataclasses import dataclass
+from dataclasses import dataclass, field
import re
import codecs
import os
import typing as T
+
from .mesonlib import MesonException
from . import mlog
@@ -240,13 +240,13 @@ class Lexer:
if not matched:
raise ParseException('lexer', self.getline(line_start), lineno, col)
-@dataclass(eq=False)
+@dataclass
class BaseNode:
- lineno: int
- colno: int
- filename: str
- end_lineno: T.Optional[int] = None
- end_colno: T.Optional[int] = None
+ lineno: int = field(hash=False)
+ colno: int = field(hash=False)
+ filename: str = field(hash=False)
+ end_lineno: T.Optional[int] = field(default=None, hash=False)
+ end_colno: T.Optional[int] = field(default=None, hash=False)
def __post_init__(self) -> None:
if self.end_lineno is None:
@@ -266,57 +266,63 @@ class BaseNode:
if callable(func):
func(self)
+@dataclass(init=False, unsafe_hash=True)
class ElementaryNode(T.Generic[TV_TokenTypes], BaseNode):
+
+ value: TV_TokenTypes # type: ignore[misc]
+ bytespan: T.Tuple[int, int] = field(hash=False) # type: ignore[misc]
+
def __init__(self, token: Token[TV_TokenTypes]):
super().__init__(token.lineno, token.colno, token.filename)
- self.value = token.value # type: TV_TokenTypes
- self.bytespan = token.bytespan # type: T.Tuple[int, int]
+ self.value = token.value
+ self.bytespan = token.bytespan
+@dataclass(init=False, unsafe_hash=True)
class BooleanNode(ElementaryNode[bool]):
- def __init__(self, token: Token[bool]):
- super().__init__(token)
- assert isinstance(self.value, bool)
+ pass
+@dataclass(init=False, unsafe_hash=True)
class IdNode(ElementaryNode[str]):
- def __init__(self, token: Token[str]):
- super().__init__(token)
- assert isinstance(self.value, str)
def __str__(self) -> str:
return "Id node: '%s' (%d, %d)." % (self.value, self.lineno, self.colno)
+@dataclass(init=False, unsafe_hash=True)
class NumberNode(ElementaryNode[int]):
- def __init__(self, token: Token[int]):
- super().__init__(token)
- assert isinstance(self.value, int)
+ pass
+@dataclass(init=False, unsafe_hash=True)
class StringNode(ElementaryNode[str]):
- def __init__(self, token: Token[str]):
- super().__init__(token)
- assert isinstance(self.value, str)
def __str__(self) -> str:
return "String node: '%s' (%d, %d)." % (self.value, self.lineno, self.colno)
+@dataclass(init=False, unsafe_hash=True)
class FormatStringNode(ElementaryNode[str]):
- def __init__(self, token: Token[str]):
- super().__init__(token)
- assert isinstance(self.value, str)
def __str__(self) -> str:
return f"Format string node: '{self.value}' ({self.lineno}, {self.colno})."
+@dataclass(init=False, unsafe_hash=True)
class MultilineFormatStringNode(FormatStringNode):
def __str__(self) -> str:
return f"Multiline Format string node: '{self.value}' ({self.lineno}, {self.colno})."
+@dataclass(init=False, unsafe_hash=True)
class ContinueNode(ElementaryNode):
pass
+@dataclass(init=False, unsafe_hash=True)
class BreakNode(ElementaryNode):
pass
+@dataclass(init=False)
class ArgumentNode(BaseNode):
+
+ arguments: T.List[BaseNode] # type: ignore[misc]
+ commas: T.List[Token] # type: ignore[misc]
+ kwargs: T.Dict[BaseNode, BaseNode] # type: ignore[misc]
+
def __init__(self, token: Token[TV_TokenTypes]):
super().__init__(token.lineno, token.colno, token.filename)
self.arguments = [] # type: T.List[BaseNode]
@@ -357,12 +363,20 @@ class ArgumentNode(BaseNode):
def __len__(self) -> int:
return self.num_args() # Fixme
+@dataclass(init=False, unsafe_hash=True)
class ArrayNode(BaseNode):
+
+ args: ArgumentNode # type: ignore[misc]
+
def __init__(self, args: ArgumentNode, lineno: int, colno: int, end_lineno: int, end_colno: int):
super().__init__(lineno, colno, args.filename, end_lineno=end_lineno, end_colno=end_colno)
- self.args = args # type: ArgumentNode
+ self.args = args
+@dataclass(init=False, unsafe_hash=True)
class DictNode(BaseNode):
+
+ args: ArgumentNode # type: ignore[misc]
+
def __init__(self, args: ArgumentNode, lineno: int, colno: int, end_lineno: int, end_colno: int):
super().__init__(lineno, colno, args.filename, end_lineno=end_lineno, end_colno=end_colno)
self.args = args
@@ -372,95 +386,177 @@ class EmptyNode(BaseNode):
super().__init__(lineno, colno, filename)
self.value = None
+ def __eq__(self, other: object) -> bool:
+ return isinstance(other, EmptyNode)
+
+@dataclass(init=False, unsafe_hash=True)
class OrNode(BaseNode):
+
+ left: BaseNode # type: ignore[misc]
+ right: BaseNode # type: ignore[misc]
+
def __init__(self, left: BaseNode, right: BaseNode):
super().__init__(left.lineno, left.colno, left.filename)
- self.left = left # type: BaseNode
- self.right = right # type: BaseNode
+ self.left = left
+ self.right = right
+@dataclass(init=False, unsafe_hash=True)
class AndNode(BaseNode):
+
+ left: BaseNode # type: ignore[misc]
+ right: BaseNode # type: ignore[misc]
+
def __init__(self, left: BaseNode, right: BaseNode):
super().__init__(left.lineno, left.colno, left.filename)
- self.left = left # type: BaseNode
- self.right = right # type: BaseNode
+ self.left = left
+ self.right = right
+@dataclass(init=False, unsafe_hash=True)
class ComparisonNode(BaseNode):
+
+ left: BaseNode # type: ignore[misc]
+ right: BaseNode # type: ignore[misc]
+ ctype: COMPARISONS # type: ignore[misc]
+
def __init__(self, ctype: COMPARISONS, left: BaseNode, right: BaseNode):
super().__init__(left.lineno, left.colno, left.filename)
- self.left = left # type: BaseNode
- self.right = right # type: BaseNode
+ self.left = left
+ self.right = right
self.ctype = ctype
+@dataclass(init=False, unsafe_hash=True)
class ArithmeticNode(BaseNode):
+
+ left: BaseNode # type: ignore[misc]
+ right: BaseNode # type: ignore[misc]
+ # TODO: use a Literal for operation
+ operation: str # type: ignore[misc]
+
def __init__(self, operation: str, left: BaseNode, right: BaseNode):
super().__init__(left.lineno, left.colno, left.filename)
- self.left = left # type: BaseNode
- self.right = right # type: BaseNode
- self.operation = operation # type: str
+ self.left = left
+ self.right = right
+ self.operation = operation
+
+@dataclass(init=False, unsafe_hash=True)
class NotNode(BaseNode):
+
+ value: BaseNode # type: ignore[misc]
+
def __init__(self, token: Token[TV_TokenTypes], value: BaseNode):
super().__init__(token.lineno, token.colno, token.filename)
- self.value = value # type: BaseNode
+ self.value = value
+@dataclass(init=False, unsafe_hash=True)
class CodeBlockNode(BaseNode):
+
+ lines: T.List[BaseNode] # type: ignore[misc]
+
def __init__(self, token: Token[TV_TokenTypes]):
super().__init__(token.lineno, token.colno, token.filename)
- self.lines = [] # type: T.List[BaseNode]
+ self.lines = []
+@dataclass(init=False, unsafe_hash=True)
class IndexNode(BaseNode):
+
+ iobject: BaseNode # type: ignore[misc]
+ index: BaseNode # type: ignore[misc]
+
def __init__(self, iobject: BaseNode, index: BaseNode):
super().__init__(iobject.lineno, iobject.colno, iobject.filename)
- self.iobject = iobject # type: BaseNode
- self.index = index # type: BaseNode
+ self.iobject = iobject
+ self.index = index
+@dataclass(init=False, unsafe_hash=True)
class MethodNode(BaseNode):
+
+ source_object: BaseNode # type: ignore[misc]
+ name: str # type: ignore[misc]
+ args: ArgumentNode = field(hash=False) # type: ignore[misc]
+
def __init__(self, filename: str, lineno: int, colno: int, source_object: BaseNode, name: str, args: ArgumentNode):
super().__init__(lineno, colno, filename)
- self.source_object = source_object # type: BaseNode
- self.name = name # type: str
+ self.source_object = source_object
+ self.name = name
assert isinstance(self.name, str)
- self.args = args # type: ArgumentNode
+ self.args = args
+@dataclass(init=False, unsafe_hash=True)
class FunctionNode(BaseNode):
+
+ func_name: str # type: ignore[misc]
+ args: ArgumentNode = field(hash=False) # type: ignore[misc]
+
def __init__(self, filename: str, lineno: int, colno: int, end_lineno: int, end_colno: int, func_name: str, args: ArgumentNode):
super().__init__(lineno, colno, filename, end_lineno=end_lineno, end_colno=end_colno)
- self.func_name = func_name # type: str
+ self.func_name = func_name
assert isinstance(func_name, str)
- self.args = args # type: ArgumentNode
+ self.args = args
+
+@dataclass(init=False, unsafe_hash=True)
class AssignmentNode(BaseNode):
+
+ var_name: str # type: ignore[misc]
+ value: BaseNode # type: ignore[misc]
+
def __init__(self, filename: str, lineno: int, colno: int, var_name: str, value: BaseNode):
super().__init__(lineno, colno, filename)
- self.var_name = var_name # type: str
+ self.var_name = var_name
assert isinstance(var_name, str)
- self.value = value # type: BaseNode
+ self.value = value
+
+@dataclass(init=False, unsafe_hash=True)
class PlusAssignmentNode(BaseNode):
+
+ var_name: str # type: ignore[misc]
+ value: BaseNode # type: ignore[misc]
+
def __init__(self, filename: str, lineno: int, colno: int, var_name: str, value: BaseNode):
super().__init__(lineno, colno, filename)
- self.var_name = var_name # type: str
+ self.var_name = var_name
assert isinstance(var_name, str)
- self.value = value # type: BaseNode
+ self.value = value
+
+@dataclass(init=False)
class ForeachClauseNode(BaseNode):
+
+ varnames: T.List[str] # type: ignore[misc]
+ items: BaseNode # type: ignore[misc]
+ block: CodeBlockNode # type: ignore[misc]
+
def __init__(self, token: Token, varnames: T.List[str], items: BaseNode, block: CodeBlockNode):
super().__init__(token.lineno, token.colno, token.filename)
- self.varnames = varnames # type: T.List[str]
- self.items = items # type: BaseNode
- self.block = block # type: CodeBlockNode
+ self.varnames = varnames
+ self.items = items
+ self.block = block
+
+@dataclass(init=False)
class IfNode(BaseNode):
+
+ condition: BaseNode # type: ignore[misc]
+ block: CodeBlockNode # type: ignore[misc]
+
def __init__(self, linenode: BaseNode, condition: BaseNode, block: CodeBlockNode):
super().__init__(linenode.lineno, linenode.colno, linenode.filename)
self.condition = condition # type: BaseNode
self.block = block # type: CodeBlockNode
+
+@dataclass(init=False)
class IfClauseNode(BaseNode):
+
+ ifs: T.List[IfNode] # type: ignore[misc]
+ elseblock: T.Union[EmptyNode, CodeBlockNode] # type: ignore[misc]
+
def __init__(self, linenode: BaseNode):
super().__init__(linenode.lineno, linenode.colno, linenode.filename)
- self.ifs = [] # type: T.List[IfNode]
- self.elseblock = None # type: T.Union[EmptyNode, CodeBlockNode]
+ self.ifs = []
+ self.elseblock = None
class TestCaseClauseNode(BaseNode):
def __init__(self, condition: BaseNode, block: CodeBlockNode):
@@ -468,12 +564,23 @@ class TestCaseClauseNode(BaseNode):
self.condition = condition
self.block = block
+@dataclass(init=False)
class UMinusNode(BaseNode):
+
+ value: BaseNode # type: ignore[misc]
+
def __init__(self, current_location: Token, value: BaseNode):
super().__init__(current_location.lineno, current_location.colno, current_location.filename)
- self.value = value # type: BaseNode
+ self.value = value
+
+@dataclass(init=False)
class TernaryNode(BaseNode):
+
+ condition: BaseNode # type: ignore[misc]
+ trueblock: BaseNode # type: ignore[misc]
+ falseblock: BaseNode # type: ignore[misc]
+
def __init__(self, condition: BaseNode, trueblock: BaseNode, falseblock: BaseNode):
super().__init__(condition.lineno, condition.colno, condition.filename)
self.condition = condition # type: BaseNode