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.
pull/6316/head
Daniel Mensinger 5 years ago
parent 200738a3e6
commit c14aea2812
No known key found for this signature in database
GPG Key ID: 54DD94C131E277D4
  1. 2
      .github/workflows/lint_mypy.yml
  2. 10
      mesonbuild/ast/interpreter.py
  3. 7
      mesonbuild/ast/printer.py
  4. 4
      mesonbuild/cmake/interpreter.py
  5. 17
      mesonbuild/interpreterbase.py
  6. 5
      mesonbuild/mintro.py
  7. 452
      mesonbuild/mparser.py
  8. 4
      mesonbuild/optinterpreter.py
  9. 8
      mesonbuild/rewriter.py

@ -30,4 +30,4 @@ jobs:
with:
python-version: '3.x'
- run: python -m pip install mypy
- run: mypy --follow-imports=skip mesonbuild/mtest.py mesonbuild/minit.py mesonbuild/mintro.py mesonbuild/msetup.py mesonbuild/wrap tools/ mesonbuild/modules/fs.py mesonbuild/dependencies/boost.py mesonbuild/dependencies/mpi.py mesonbuild/dependencies/hdf5.py mesonbuild/compilers/mixins/intel.py mesonbuild/mlog.py
- run: mypy --follow-imports=skip mesonbuild/mtest.py mesonbuild/minit.py mesonbuild/mintro.py mesonbuild/mparser.py mesonbuild/msetup.py mesonbuild/wrap tools/ mesonbuild/modules/fs.py mesonbuild/dependencies/boost.py mesonbuild/dependencies/mpi.py mesonbuild/dependencies/hdf5.py mesonbuild/compilers/mixins/intel.py mesonbuild/mlog.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), {}

@ -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)

@ -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:

@ -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

@ -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]

@ -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:

@ -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):

@ -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]

Loading…
Cancel
Save