aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
authorDaniel Mensinger <daniel@mensinger-ka.de>2019-12-07 14:42:23 +0100
committerDaniel Mensinger <daniel@mensinger-ka.de>2020-03-02 10:34:55 +0100
commitc14aea2812fd2be94998bdb174e9a4681aeea394 (patch)
tree7f8532528ea3d84db5491f5d766415649592bcd1 /mesonbuild
parent200738a3e6b48671aac2865c304dded96179e4ac (diff)
downloadmeson-c14aea2812fd2be94998bdb174e9a4681aeea394.zip
meson-c14aea2812fd2be94998bdb174e9a4681aeea394.tar.gz
meson-c14aea2812fd2be94998bdb174e9a4681aeea394.tar.bz2
types: Annotate mparser.py
This also fixes that the keys in ArgumentNode.kwargs are all of the type BaseNode now. Before this commit, it was possible that both strings and Nodes where used as keys.
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/ast/interpreter.py10
-rw-r--r--mesonbuild/ast/printer.py7
-rw-r--r--mesonbuild/cmake/interpreter.py4
-rw-r--r--mesonbuild/interpreterbase.py17
-rw-r--r--mesonbuild/mintro.py5
-rw-r--r--mesonbuild/mparser.py452
-rw-r--r--mesonbuild/optinterpreter.py4
-rw-r--r--mesonbuild/rewriter.py8
8 files changed, 254 insertions, 253 deletions
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index 5f47ad3..09d8d32 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -29,6 +29,7 @@ from ..mparser import (
ElementaryNode,
EmptyNode,
IdNode,
+ StringNode,
MethodNode,
PlusAssignmentNode,
TernaryNode,
@@ -201,9 +202,16 @@ class AstInterpreter(interpreterbase.InterpreterBase):
def reduce_arguments(self, args):
if isinstance(args, ArgumentNode):
+ kwargs = {} # type: T.Dict[T.Union[str, BaseNode], BaseNode]
+ for key, val in args.kwargs.items():
+ if isinstance(key, (StringNode, IdNode)):
+ assert isinstance(key.value, str)
+ kwargs[key.value] = val
+ else:
+ kwargs[key] = val
if args.incorrect_order():
raise InvalidArguments('All keyword arguments must be after positional arguments.')
- return self.flatten_args(args.arguments), args.kwargs
+ return self.flatten_args(args.arguments), kwargs
else:
return self.flatten_args(args), {}
diff --git a/mesonbuild/ast/printer.py b/mesonbuild/ast/printer.py
index f245a36..033b1d5 100644
--- a/mesonbuild/ast/printer.py
+++ b/mesonbuild/ast/printer.py
@@ -143,7 +143,7 @@ class AstPrinter(AstVisitor):
node.value.accept(self)
def visit_ForeachClauseNode(self, node: mparser.ForeachClauseNode):
- varnames = [x.value for x in node.varnames]
+ varnames = [x for x in node.varnames]
self.append_padded('foreach', node)
self.append_padded(', '.join(varnames), node)
self.append_padded(':', node)
@@ -192,10 +192,7 @@ class AstPrinter(AstVisitor):
if break_args:
self.newline()
for key, val in node.kwargs.items():
- if isinstance(key, str):
- self.append(key, node)
- else:
- key.accept(self)
+ key.accept(self)
self.append_padded(':', node)
val.accept(self)
self.append(', ', node)
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index 941baed..fdcd287 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -1002,7 +1002,7 @@ class CMakeInterpreter:
if not isinstance(args, list):
args = [args]
args_n.arguments = [nodeify(x) for x in args if x is not None]
- args_n.kwargs = {k: nodeify(v) for k, v in kwargs.items() if v is not None}
+ args_n.kwargs = {id_node(k): nodeify(v) for k, v in kwargs.items() if v is not None}
func_n = FunctionNode(self.subdir, 0, 0, 0, 0, name, args_n)
return func_n
@@ -1013,7 +1013,7 @@ class CMakeInterpreter:
if not isinstance(args, list):
args = [args]
args_n.arguments = [nodeify(x) for x in args if x is not None]
- args_n.kwargs = {k: nodeify(v) for k, v in kwargs.items() if v is not None}
+ args_n.kwargs = {id_node(k): nodeify(v) for k, v in kwargs.items() if v is not None}
return MethodNode(self.subdir, 0, 0, obj, name, args_n)
def assign(var_name: str, value: BaseNode) -> AssignmentNode:
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
index d723da5..152c65b 100644
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -20,6 +20,7 @@ from . import environment, dependencies
import os, copy, re
from functools import wraps
+from typing import Union, Optional
class ObjectHolder:
def __init__(self, obj, subproject=None):
@@ -494,7 +495,7 @@ class InterpreterBase:
@FeatureNew('dict', '0.47.0')
def evaluate_dictstatement(self, cur):
- (arguments, kwargs) = self.reduce_arguments(cur.args)
+ (arguments, kwargs) = self.reduce_arguments(cur.args, resolve_key_nodes=False)
assert (not arguments)
result = {}
self.argument_depth += 1
@@ -693,7 +694,7 @@ The result of this is undefined and will become a hard error in a future Meson r
if isinstance(items, list):
if len(node.varnames) != 1:
raise InvalidArguments('Foreach on array does not unpack')
- varname = node.varnames[0].value
+ varname = node.varnames[0]
for item in items:
self.set_variable(varname, item)
try:
@@ -706,8 +707,8 @@ The result of this is undefined and will become a hard error in a future Meson r
if len(node.varnames) != 2:
raise InvalidArguments('Foreach on dict unpacks key and value')
for key, value in items.items():
- self.set_variable(node.varnames[0].value, key)
- self.set_variable(node.varnames[1].value, value)
+ self.set_variable(node.varnames[0], key)
+ self.set_variable(node.varnames[1], value)
try:
self.evaluate_codeblock(node.block)
except ContinueRequest:
@@ -1025,7 +1026,7 @@ The result of this is undefined and will become a hard error in a future Meson r
raise InterpreterException('Dictionaries do not have a method called "%s".' % method_name)
- def reduce_arguments(self, args):
+ def reduce_arguments(self, args: mparser.ArgumentNode, resolve_key_nodes: Optional[bool] = True):
assert(isinstance(args, mparser.ArgumentNode))
if args.incorrect_order():
raise InvalidArguments('All keyword arguments must be after positional arguments.')
@@ -1033,8 +1034,12 @@ The result of this is undefined and will become a hard error in a future Meson r
reduced_pos = [self.evaluate_statement(arg) for arg in args.arguments]
reduced_kw = {}
for key in args.kwargs.keys():
+ reduced_key = key # type: Union[str, mparser.BaseNode]
+ if resolve_key_nodes and isinstance(key, mparser.IdNode):
+ assert isinstance(key.value, str)
+ reduced_key = key.value
a = args.kwargs[key]
- reduced_kw[key] = self.evaluate_statement(a)
+ reduced_kw[reduced_key] = self.evaluate_statement(a)
self.argument_depth -= 1
final_kw = self.expand_default_kwargs(reduced_kw)
return reduced_pos, final_kw
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index cfa4574..cca4372 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -25,7 +25,7 @@ from . import mesonlib
from .ast import IntrospectionInterpreter, build_target_functions, AstConditionLevel, AstIDGenerator, AstIndentationGenerator
from . import mlog
from .backend import backends
-from .mparser import FunctionNode, ArrayNode, ArgumentNode, StringNode
+from .mparser import BaseNode, FunctionNode, ArrayNode, ArgumentNode, StringNode
from .interpreter import Interpreter
from pathlib import PurePath
import typing as T
@@ -110,7 +110,7 @@ def list_targets_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[st
for i in intr.targets:
sources = [] # type: T.List[str]
for n in i['sources']:
- args = [] # type: T.List[T.Union[str, StringNode]]
+ args = [] # type: T.List[BaseNode]
if isinstance(n, FunctionNode):
args = list(n.args.arguments)
if n.func_name in build_target_functions:
@@ -121,6 +121,7 @@ def list_targets_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[st
args = n.arguments
for j in args:
if isinstance(j, StringNode):
+ assert isinstance(j.value, str)
sources += [j.value]
elif isinstance(j, str):
sources += [j]
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
index 430c89e..84b686e 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -14,10 +14,16 @@
import re
import codecs
+import textwrap
import types
+import typing as T
+from typing import Dict, List, Tuple, Optional, Union, TYPE_CHECKING
from .mesonlib import MesonException
from . import mlog
+if TYPE_CHECKING:
+ from .ast import AstVisitor
+
# This is the regex for the supported escape sequences of a regular string
# literal, like 'abc\x00'
ESCAPE_SEQUENCE_SINGLE_RE = re.compile(r'''
@@ -72,22 +78,22 @@ class BlockParseException(MesonException):
self.colno = colno
class Token:
- def __init__(self, tid, filename, line_start, lineno, colno, bytespan, value):
- self.tid = tid
- self.filename = filename
- self.line_start = line_start
- self.lineno = lineno
- self.colno = colno
- self.bytespan = bytespan
- self.value = value
-
- def __eq__(self, other):
+ def __init__(self, tid: str, filename: str, line_start: int, lineno: int, colno: int, bytespan: Tuple[int, int], value: Union[str, bool, int]) -> None:
+ self.tid = tid # type: str
+ self.filename = filename # type: str
+ self.line_start = line_start # type: int
+ self.lineno = lineno # type: int
+ self.colno = colno # type: int
+ self.bytespan = bytespan # type: Tuple[int, int]
+ self.value = value # type: Union[str, bool, int]
+
+ def __eq__(self, other) -> bool:
if isinstance(other, str):
return self.tid == other
return self.tid == other.tid
class Lexer:
- def __init__(self, code):
+ def __init__(self, code: str) -> None:
self.code = code
self.keywords = {'true', 'false', 'if', 'else', 'elif',
'endif', 'and', 'or', 'not', 'foreach', 'endforeach',
@@ -129,10 +135,10 @@ class Lexer:
('questionmark', re.compile(r'\?')),
]
- def getline(self, line_start):
+ def getline(self, line_start: int) -> str:
return self.code[line_start:self.code.find('\n', line_start)]
- def lex(self, filename):
+ def lex(self, filename: str) -> T.Generator[Token, None, None]:
line_start = 0
lineno = 1
loc = 0
@@ -142,7 +148,7 @@ class Lexer:
col = 0
while loc < len(self.code):
matched = False
- value = None
+ value = None # type: Union[str, bool, int]
for (tid, reg) in self.token_specification:
mo = reg.match(self.code, loc)
if mo:
@@ -174,8 +180,14 @@ class Lexer:
elif tid == 'string':
# Handle here and not on the regexp to give a better error message.
if match_text.find("\n") != -1:
- mlog.warning("""Newline character in a string detected, use ''' (three single quotes) for multiline strings instead.
-This will become a hard error in a future Meson release.""", self.getline(line_start), lineno, col)
+ mlog.warning(textwrap.dedent("""\
+ Newline character in a string detected, use ''' (three single quotes) for multiline strings instead.
+ This will become a hard error in a future Meson release.\
+ """),
+ self.getline(line_start),
+ str(lineno),
+ str(col)
+ )
value = match_text[1:-1]
try:
value = ESCAPE_SEQUENCE_SINGLE_RE.sub(decode_match, value)
@@ -213,7 +225,14 @@ This will become a hard error in a future Meson release.""", self.getline(line_s
raise ParseException('lexer', self.getline(line_start), lineno, col)
class BaseNode:
- def accept(self, visitor):
+ def __init__(self, lineno: int, colno: int, filename: str, end_lineno: Optional[int] = None, end_colno: Optional[int] = None) -> None:
+ self.lineno = lineno # type: int
+ self.colno = colno # type: int
+ self.filename = filename # type: str
+ self.end_lineno = end_lineno if end_lineno is not None else self.lineno
+ self.end_colno = end_colno if end_colno is not None else self.colno
+
+ def accept(self, visitor: 'AstVisitor') -> None:
fname = 'visit_{}'.format(type(self).__name__)
if hasattr(visitor, fname):
func = getattr(visitor, fname)
@@ -221,21 +240,19 @@ class BaseNode:
func(self)
class ElementaryNode(BaseNode):
- def __init__(self, token):
- self.lineno = token.lineno
- self.filename = token.filename
- self.colno = token.colno
+ def __init__(self, token: Token) -> None:
+ super().__init__(token.lineno, token.colno, token.filename)
self.value = token.value
self.bytespan = token.bytespan
class BooleanNode(ElementaryNode):
- def __init__(self, token, value):
+ def __init__(self, token: Token, value: bool) -> None:
super().__init__(token)
self.value = value
assert(isinstance(self.value, bool))
class IdNode(ElementaryNode):
- def __init__(self, token):
+ def __init__(self, token: Token) -> None:
super().__init__(token)
assert(isinstance(self.value, str))
@@ -243,12 +260,12 @@ class IdNode(ElementaryNode):
return "Id node: '%s' (%d, %d)." % (self.value, self.lineno, self.colno)
class NumberNode(ElementaryNode):
- def __init__(self, token):
+ def __init__(self, token: Token) -> None:
super().__init__(token)
assert(isinstance(self.value, int))
class StringNode(ElementaryNode):
- def __init__(self, token):
+ def __init__(self, token: Token) -> None:
super().__init__(token)
assert(isinstance(self.value, str))
@@ -261,203 +278,163 @@ class ContinueNode(ElementaryNode):
class BreakNode(ElementaryNode):
pass
+class ArgumentNode(BaseNode):
+ def __init__(self, token: Token) -> None:
+ super().__init__(token.lineno, token.colno, token.filename)
+ self.arguments = [] # type: List[BaseNode]
+ self.commas = [] # type: List[Token]
+ self.kwargs = {} # type: Dict[BaseNode, BaseNode]
+ self.order_error = False
+
+ def prepend(self, statement: BaseNode) -> None:
+ if self.num_kwargs() > 0:
+ self.order_error = True
+ if not isinstance(statement, EmptyNode):
+ self.arguments = [statement] + self.arguments
+
+ def append(self, statement: BaseNode) -> None:
+ if self.num_kwargs() > 0:
+ self.order_error = True
+ if not isinstance(statement, EmptyNode):
+ self.arguments += [statement]
+
+ def set_kwarg(self, name: IdNode, value: BaseNode) -> None:
+ if name.value in [x.value for x in self.kwargs.keys() if isinstance(x, IdNode)]:
+ mlog.warning('Keyword argument "{}" defined multiple times.'.format(name.value), location=self)
+ mlog.warning('This will be an error in future Meson releases.')
+ self.kwargs[name] = value
+
+ def set_kwarg_no_check(self, name: BaseNode, value: BaseNode) -> None:
+ self.kwargs[name] = value
+
+ def num_args(self) -> int:
+ return len(self.arguments)
+
+ def num_kwargs(self) -> int:
+ return len(self.kwargs)
+
+ def incorrect_order(self) -> bool:
+ return self.order_error
+
+ def __len__(self) -> int:
+ return self.num_args() # Fixme
+
class ArrayNode(BaseNode):
- def __init__(self, args, lineno, colno, end_lineno, end_colno):
- self.filename = args.filename
- self.lineno = lineno
- self.colno = colno
- self.end_lineno = end_lineno
- self.end_colno = end_colno
- self.args = args
+ def __init__(self, args: ArgumentNode, lineno: int, colno: int, end_lineno: int, end_colno: int) -> None:
+ super().__init__(lineno, colno, args.filename, end_lineno=end_lineno, end_colno=end_colno)
+ self.args = args # type: ArgumentNode
class DictNode(BaseNode):
- def __init__(self, args, lineno, colno, end_lineno, end_colno):
- self.filename = args.filename
- self.lineno = lineno
- self.colno = colno
- self.end_lineno = end_lineno
- self.end_colno = end_colno
+ def __init__(self, args: ArgumentNode, lineno: int, colno: int, end_lineno: int, end_colno: int) -> None:
+ super().__init__(lineno, colno, args.filename, end_lineno=end_lineno, end_colno=end_colno)
self.args = args
class EmptyNode(BaseNode):
- def __init__(self, lineno, colno):
- self.filename = ''
- self.lineno = lineno
- self.colno = colno
+ def __init__(self, lineno: int, colno: int, filename: str) -> None:
+ super().__init__(lineno, colno, filename)
self.value = None
class OrNode(BaseNode):
- def __init__(self, left, right):
- self.filename = left.filename
- self.lineno = left.lineno
- self.colno = left.colno
- self.left = left
- self.right = right
+ def __init__(self, left: BaseNode, right: BaseNode) -> None:
+ super().__init__(left.lineno, left.colno, left.filename)
+ self.left = left # type: BaseNode
+ self.right = right # type: BaseNode
class AndNode(BaseNode):
- def __init__(self, left, right):
- self.filename = left.filename
- self.lineno = left.lineno
- self.colno = left.colno
- self.left = left
- self.right = right
+ def __init__(self, left: BaseNode, right: BaseNode) -> None:
+ super().__init__(left.lineno, left.colno, left.filename)
+ self.left = left # type: BaseNode
+ self.right = right # type: BaseNode
class ComparisonNode(BaseNode):
- def __init__(self, ctype, left, right):
- self.lineno = left.lineno
- self.colno = left.colno
- self.filename = left.filename
- self.left = left
- self.right = right
- self.ctype = ctype
+ def __init__(self, ctype: str, left: BaseNode, right: BaseNode) -> None:
+ super().__init__(left.lineno, left.colno, left.filename)
+ self.left = left # type: BaseNode
+ self.right = right # type: BaseNode
+ self.ctype = ctype # type: str
class ArithmeticNode(BaseNode):
- def __init__(self, operation, left, right):
- self.filename = left.filename
- self.lineno = left.lineno
- self.colno = left.colno
- self.left = left
- self.right = right
- self.operation = operation
+ def __init__(self, operation: str, left: BaseNode, right: BaseNode) -> None:
+ super().__init__(left.lineno, left.colno, left.filename)
+ self.left = left # type: BaseNode
+ self.right = right # type: BaseNode
+ self.operation = operation # type: str
class NotNode(BaseNode):
- def __init__(self, location_node, value):
- self.filename = location_node.filename
- self.lineno = location_node.lineno
- self.colno = location_node.colno
- self.value = value
+ def __init__(self, token: Token, value: BaseNode) -> None:
+ super().__init__(token.lineno, token.colno, token.filename)
+ self.value = value # type: BaseNode
class CodeBlockNode(BaseNode):
- def __init__(self, location_node):
- self.filename = location_node.filename
- self.lineno = location_node.lineno
- self.colno = location_node.colno
- self.lines = []
+ def __init__(self, token: Token) -> None:
+ super().__init__(token.lineno, token.colno, token.filename)
+ self.lines = [] # type: List[BaseNode]
class IndexNode(BaseNode):
- def __init__(self, iobject, index):
- self.iobject = iobject
- self.index = index
- self.filename = iobject.filename
- self.lineno = iobject.lineno
- self.colno = iobject.colno
+ def __init__(self, iobject: BaseNode, index: BaseNode) -> None:
+ super().__init__(iobject.lineno, iobject.colno, iobject.filename)
+ self.iobject = iobject # type: BaseNode
+ self.index = index # type: BaseNode
class MethodNode(BaseNode):
- def __init__(self, filename, lineno, colno, source_object, name, args):
- self.filename = filename
- self.lineno = lineno
- self.colno = colno
- self.source_object = source_object
- self.name = name
+ def __init__(self, filename: str, lineno: int, colno: int, source_object: BaseNode, name: str, args: ArgumentNode) -> None:
+ super().__init__(lineno, colno, filename)
+ self.source_object = source_object # type: BaseNode
+ self.name = name # type: str
assert(isinstance(self.name, str))
- self.args = args
+ self.args = args # type: ArgumentNode
class FunctionNode(BaseNode):
- def __init__(self, filename, lineno, colno, end_lineno, end_colno, func_name, args):
- self.filename = filename
- self.lineno = lineno
- self.colno = colno
- self.end_lineno = end_lineno
- self.end_colno = end_colno
- self.func_name = func_name
+ def __init__(self, filename: str, lineno: int, colno: int, end_lineno: int, end_colno: int, func_name: str, args: ArgumentNode) -> None:
+ super().__init__(lineno, colno, filename, end_lineno=end_lineno, end_colno=end_colno)
+ self.func_name = func_name # type: str
assert(isinstance(func_name, str))
- self.args = args
+ self.args = args # type: ArgumentNode
class AssignmentNode(BaseNode):
- def __init__(self, filename, lineno, colno, var_name, value):
- self.filename = filename
- self.lineno = lineno
- self.colno = colno
- self.var_name = var_name
+ def __init__(self, filename: str, lineno: int, colno: int, var_name: str, value: BaseNode) -> None:
+ super().__init__(lineno, colno, filename)
+ self.var_name = var_name # type: str
assert(isinstance(var_name, str))
- self.value = value
+ self.value = value # type: BaseNode
class PlusAssignmentNode(BaseNode):
- def __init__(self, filename, lineno, colno, var_name, value):
- self.filename = filename
- self.lineno = lineno
- self.colno = colno
- self.var_name = var_name
+ def __init__(self, filename: str, lineno: int, colno: int, var_name: str, value: BaseNode) -> None:
+ super().__init__(lineno, colno, filename)
+ self.var_name = var_name # type: str
assert(isinstance(var_name, str))
- self.value = value
+ self.value = value # type: BaseNode
class ForeachClauseNode(BaseNode):
- def __init__(self, lineno, colno, varnames, items, block):
- self.lineno = lineno
- self.colno = colno
- self.varnames = varnames
- self.items = items
- self.block = block
+ def __init__(self, token: Token, varnames: List[str], items: BaseNode, block: CodeBlockNode) -> None:
+ super().__init__(token.lineno, token.colno, token.filename)
+ self.varnames = varnames # type: List[str]
+ self.items = items # type: BaseNode
+ self.block = block # type: CodeBlockNode
+
+class IfNode(BaseNode):
+ 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
class IfClauseNode(BaseNode):
- def __init__(self, lineno, colno):
- self.lineno = lineno
- self.colno = colno
- self.ifs = []
- self.elseblock = EmptyNode(lineno, colno)
+ def __init__(self, linenode: BaseNode) -> None:
+ super().__init__(linenode.lineno, linenode.colno, linenode.filename)
+ self.ifs = [] # type: List[IfNode]
+ self.elseblock = EmptyNode(linenode.lineno, linenode.colno, linenode.filename) # type: Union[EmptyNode, CodeBlockNode]
class UMinusNode(BaseNode):
- def __init__(self, current_location, value):
- self.filename = current_location.filename
- self.lineno = current_location.lineno
- self.colno = current_location.colno
- self.value = value
-
-class IfNode(BaseNode):
- def __init__(self, lineno, colno, condition, block):
- self.lineno = lineno
- self.colno = colno
- self.condition = condition
- self.block = block
+ def __init__(self, current_location: Token, value: BaseNode):
+ super().__init__(current_location.lineno, current_location.colno, current_location.filename)
+ self.value = value # type: BaseNode
class TernaryNode(BaseNode):
- def __init__(self, filename, lineno, colno, condition, trueblock, falseblock):
- self.filename = filename
- self.lineno = lineno
- self.colno = colno
- self.condition = condition
- self.trueblock = trueblock
- self.falseblock = falseblock
-
-class ArgumentNode(BaseNode):
- def __init__(self, token):
- self.lineno = token.lineno
- self.colno = token.colno
- self.filename = token.filename
- self.arguments = []
- self.commas = []
- self.kwargs = {}
- self.order_error = False
-
- def prepend(self, statement):
- if self.num_kwargs() > 0:
- self.order_error = True
- if not isinstance(statement, EmptyNode):
- self.arguments = [statement] + self.arguments
-
- def append(self, statement):
- if self.num_kwargs() > 0:
- self.order_error = True
- if not isinstance(statement, EmptyNode):
- self.arguments += [statement]
-
- def set_kwarg(self, name, value):
- if name in self.kwargs:
- mlog.warning('Keyword argument "{}" defined multiple times.'.format(name), location=self)
- mlog.warning('This will be an error in future Meson releases.')
- self.kwargs[name] = value
-
- def num_args(self):
- return len(self.arguments)
-
- def num_kwargs(self):
- return len(self.kwargs)
-
- def incorrect_order(self):
- return self.order_error
-
- def __len__(self):
- return self.num_args() # Fixme
+ def __init__(self, condition: BaseNode, trueblock: BaseNode, falseblock: BaseNode):
+ super().__init__(condition.lineno, condition.colno, condition.filename)
+ self.condition = condition # type: BaseNode
+ self.trueblock = trueblock # type: BaseNode
+ self.falseblock = falseblock # type: BaseNode
comparison_map = {'equal': '==',
'nequal': '!=',
@@ -485,58 +462,60 @@ comparison_map = {'equal': '==',
# 9 plain token
class Parser:
- def __init__(self, code, filename):
+ def __init__(self, code: str, filename: str) -> None:
self.lexer = Lexer(code)
self.stream = self.lexer.lex(filename)
self.current = Token('eof', '', 0, 0, 0, (0, 0), None)
self.getsym()
self.in_ternary = False
- def getsym(self):
+ def getsym(self) -> None:
try:
self.current = next(self.stream)
except StopIteration:
self.current = Token('eof', '', self.current.line_start, self.current.lineno, self.current.colno + self.current.bytespan[1] - self.current.bytespan[0], (0, 0), None)
- def getline(self):
+ def getline(self) -> str:
return self.lexer.getline(self.current.line_start)
- def accept(self, s):
+ def accept(self, s: str) -> bool:
if self.current.tid == s:
self.getsym()
return True
return False
- def expect(self, s):
+ def expect(self, s: str) -> bool:
if self.accept(s):
return True
raise ParseException('Expecting %s got %s.' % (s, self.current.tid), self.getline(), self.current.lineno, self.current.colno)
- def block_expect(self, s, block_start):
+ def block_expect(self, s: str, block_start: Token) -> bool:
if self.accept(s):
return True
raise BlockParseException('Expecting %s got %s.' % (s, self.current.tid), self.getline(), self.current.lineno, self.current.colno, self.lexer.getline(block_start.line_start), block_start.lineno, block_start.colno)
- def parse(self):
+ def parse(self) -> CodeBlockNode:
block = self.codeblock()
self.expect('eof')
return block
- def statement(self):
+ def statement(self) -> BaseNode:
return self.e1()
- def e1(self):
+ def e1(self) -> BaseNode:
left = self.e2()
if self.accept('plusassign'):
value = self.e1()
if not isinstance(left, IdNode):
raise ParseException('Plusassignment target must be an id.', self.getline(), left.lineno, left.colno)
+ assert isinstance(left.value, str)
return PlusAssignmentNode(left.filename, left.lineno, left.colno, left.value, value)
elif self.accept('assign'):
value = self.e1()
if not isinstance(left, IdNode):
raise ParseException('Assignment target must be an id.',
self.getline(), left.lineno, left.colno)
+ assert isinstance(left.value, str)
return AssignmentNode(left.filename, left.lineno, left.colno, left.value, value)
elif self.accept('questionmark'):
if self.in_ternary:
@@ -547,10 +526,10 @@ class Parser:
self.expect('colon')
falseblock = self.e1()
self.in_ternary = False
- return TernaryNode(left.filename, left.lineno, left.colno, left, trueblock, falseblock)
+ return TernaryNode(left, trueblock, falseblock)
return left
- def e2(self):
+ def e2(self) -> BaseNode:
left = self.e3()
while self.accept('or'):
if isinstance(left, EmptyNode):
@@ -559,7 +538,7 @@ class Parser:
left = OrNode(left, self.e3())
return left
- def e3(self):
+ def e3(self) -> BaseNode:
left = self.e4()
while self.accept('and'):
if isinstance(left, EmptyNode):
@@ -568,7 +547,7 @@ class Parser:
left = AndNode(left, self.e4())
return left
- def e4(self):
+ def e4(self) -> BaseNode:
left = self.e5()
for nodename, operator_type in comparison_map.items():
if self.accept(nodename):
@@ -577,47 +556,47 @@ class Parser:
return ComparisonNode('notin', left, self.e5())
return left
- def e5(self):
+ def e5(self) -> BaseNode:
return self.e5add()
- def e5add(self):
+ def e5add(self) -> BaseNode:
left = self.e5sub()
if self.accept('plus'):
return ArithmeticNode('add', left, self.e5add())
return left
- def e5sub(self):
+ def e5sub(self) -> BaseNode:
left = self.e5mod()
if self.accept('dash'):
return ArithmeticNode('sub', left, self.e5sub())
return left
- def e5mod(self):
+ def e5mod(self) -> BaseNode:
left = self.e5mul()
if self.accept('percent'):
return ArithmeticNode('mod', left, self.e5mod())
return left
- def e5mul(self):
+ def e5mul(self) -> BaseNode:
left = self.e5div()
if self.accept('star'):
return ArithmeticNode('mul', left, self.e5mul())
return left
- def e5div(self):
+ def e5div(self) -> BaseNode:
left = self.e6()
if self.accept('fslash'):
return ArithmeticNode('div', left, self.e5div())
return left
- def e6(self):
+ def e6(self) -> BaseNode:
if self.accept('not'):
return NotNode(self.current, self.e7())
if self.accept('dash'):
return UMinusNode(self.current, self.e7())
return self.e7()
- def e7(self):
+ def e7(self) -> BaseNode:
left = self.e8()
block_start = self.current
if self.accept('lparen'):
@@ -626,6 +605,7 @@ class Parser:
if not isinstance(left, IdNode):
raise ParseException('Function call must be applied to plain id',
self.getline(), left.lineno, left.colno)
+ assert isinstance(left.value, str)
left = FunctionNode(left.filename, left.lineno, left.colno, self.current.lineno, self.current.colno, left.value, args)
go_again = True
while go_again:
@@ -638,7 +618,7 @@ class Parser:
left = self.index_call(left)
return left
- def e8(self):
+ def e8(self) -> BaseNode:
block_start = self.current
if self.accept('lparen'):
e = self.statement()
@@ -655,7 +635,7 @@ class Parser:
else:
return self.e9()
- def e9(self):
+ def e9(self) -> BaseNode:
t = self.current
if self.accept('true'):
return BooleanNode(t, True)
@@ -667,15 +647,15 @@ class Parser:
return NumberNode(t)
if self.accept('string'):
return StringNode(t)
- return EmptyNode(self.current.lineno, self.current.colno)
+ return EmptyNode(self.current.lineno, self.current.colno, self.current.filename)
- def key_values(self):
- s = self.statement()
- a = ArgumentNode(s)
+ def key_values(self) -> ArgumentNode:
+ s = self.statement() # type: BaseNode
+ a = ArgumentNode(self.current)
while not isinstance(s, EmptyNode):
if self.accept('colon'):
- a.set_kwarg(s, self.statement())
+ a.set_kwarg_no_check(s, self.statement())
potential = self.current
if not self.accept('comma'):
return a
@@ -686,9 +666,9 @@ class Parser:
s = self.statement()
return a
- def args(self):
- s = self.statement()
- a = ArgumentNode(s)
+ def args(self) -> ArgumentNode:
+ s = self.statement() # type: BaseNode
+ a = ArgumentNode(self.current)
while not isinstance(s, EmptyNode):
potential = self.current
@@ -699,7 +679,7 @@ class Parser:
if not isinstance(s, IdNode):
raise ParseException('Dictionary key must be a plain identifier.',
self.getline(), s.lineno, s.colno)
- a.set_kwarg(s.value, self.statement())
+ a.set_kwarg(s, self.statement())
potential = self.current
if not self.accept('comma'):
return a
@@ -710,11 +690,12 @@ class Parser:
s = self.statement()
return a
- def method_call(self, source_object):
+ def method_call(self, source_object) -> MethodNode:
methodname = self.e9()
if not(isinstance(methodname, IdNode)):
raise ParseException('Method name must be plain id',
self.getline(), self.current.lineno, self.current.colno)
+ assert isinstance(methodname.value, str)
self.expect('lparen')
args = self.args()
self.expect('rparen')
@@ -723,68 +704,73 @@ class Parser:
return self.method_call(method)
return method
- def index_call(self, source_object):
+ def index_call(self, source_object) -> IndexNode:
index_statement = self.statement()
self.expect('rbracket')
return IndexNode(source_object, index_statement)
- def foreachblock(self):
+ def foreachblock(self) -> ForeachClauseNode:
t = self.current
self.expect('id')
+ assert isinstance(t.value, str)
varname = t
- varnames = [t]
+ varnames = [t.value] # type: List[str]
if self.accept('comma'):
t = self.current
self.expect('id')
- varnames.append(t)
+ assert isinstance(t.value, str)
+ varnames.append(t.value)
self.expect('colon')
items = self.statement()
block = self.codeblock()
- return ForeachClauseNode(varname.lineno, varname.colno, varnames, items, block)
+ return ForeachClauseNode(varname, varnames, items, block)
- def ifblock(self):
+ def ifblock(self) -> IfClauseNode:
condition = self.statement()
- clause = IfClauseNode(condition.lineno, condition.colno)
+ clause = IfClauseNode(condition)
self.expect('eol')
block = self.codeblock()
- clause.ifs.append(IfNode(clause.lineno, clause.colno, condition, block))
+ clause.ifs.append(IfNode(clause, condition, block))
self.elseifblock(clause)
- clause.elseblock = self.elseblock()
+ elseblock = self.elseblock()
+ if elseblock:
+ clause.elseblock = elseblock
return clause
- def elseifblock(self, clause):
+ def elseifblock(self, clause) -> None:
while self.accept('elif'):
s = self.statement()
self.expect('eol')
b = self.codeblock()
- clause.ifs.append(IfNode(s.lineno, s.colno, s, b))
+ clause.ifs.append(IfNode(s, s, b))
- def elseblock(self):
+ def elseblock(self) -> Optional[CodeBlockNode]:
if self.accept('else'):
self.expect('eol')
return self.codeblock()
+ return None
- def line(self):
+ def line(self) -> BaseNode:
block_start = self.current
if self.current == 'eol':
- return EmptyNode(self.current.lineno, self.current.colno)
+ return EmptyNode(self.current.lineno, self.current.colno, self.current.filename)
if self.accept('if'):
- block = self.ifblock()
+ ifblock = self.ifblock()
self.block_expect('endif', block_start)
- return block
+ return ifblock
if self.accept('foreach'):
- block = self.foreachblock()
+ forblock = self.foreachblock()
self.block_expect('endforeach', block_start)
- return block
+ return forblock
if self.accept('continue'):
return ContinueNode(self.current)
if self.accept('break'):
return BreakNode(self.current)
return self.statement()
- def codeblock(self):
+ def codeblock(self) -> CodeBlockNode:
block = CodeBlockNode(self.current)
cond = True
while cond:
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index fd0a8c9..2695a26 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -176,10 +176,10 @@ class OptionInterpreter:
reduced_pos = [self.reduce_single(arg) for arg in args.arguments]
reduced_kw = {}
for key in args.kwargs.keys():
- if not isinstance(key, str):
+ if not isinstance(key, mparser.IdNode):
raise OptionException('Keyword argument name is not a string.')
a = args.kwargs[key]
- reduced_kw[key] = self.reduce_single(a)
+ reduced_kw[key.value] = self.reduce_single(a)
return reduced_pos, reduced_kw
def evaluate_statement(self, node):
diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py
index 39d8337..6aaa269 100644
--- a/mesonbuild/rewriter.py
+++ b/mesonbuild/rewriter.py
@@ -113,7 +113,7 @@ class MTypeBase:
def _new_node(self):
# Overwrite in derived class
- return BaseNode()
+ raise RewriterException('Internal error: _new_node of MTypeBase was called')
def can_modify(self):
return self.node_type is not None
@@ -189,7 +189,7 @@ class MTypeList(MTypeBase):
def _new_element_node(self, value):
# Overwrite in derived class
- return BaseNode()
+ raise RewriterException('Internal error: _new_element_node of MTypeList was called')
def _ensure_array_node(self):
if not isinstance(self.node, ArrayNode):
@@ -522,6 +522,8 @@ class Rewriter:
mlog.error('Unable to find the function node')
assert(isinstance(node, FunctionNode))
assert(isinstance(arg_node, ArgumentNode))
+ # Transform the key nodes to plain strings
+ arg_node.kwargs = {k.value: v for k, v in arg_node.kwargs.items()}
# Print kwargs info
if cmd['operation'] == 'info':
@@ -585,6 +587,8 @@ class Rewriter:
arg_node.kwargs[key] = modifyer.get_node()
num_changed += 1
+ # Convert the keys back to IdNode's
+ arg_node.kwargs = {IdNode(Token('', '', 0, 0, 0, None, k)): v for k, v in arg_node.kwargs.items()}
if num_changed > 0 and node not in self.modefied_nodes:
self.modefied_nodes += [node]