aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/mparser.py
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/mparser.py
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/mparser.py')
-rw-r--r--mesonbuild/mparser.py452
1 files changed, 219 insertions, 233 deletions
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: