Skip to content

Commit 511f441

Browse files
committed
Merge pull request #928 from jhance/refactor-arglist
Refactor handling of arguments in parser.
2 parents 5d0f6d2 + 49000d8 commit 511f441

File tree

1 file changed

+114
-111
lines changed

1 file changed

+114
-111
lines changed

mypy/parse.py

Lines changed: 114 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,20 @@ def parse(source: Union[str, bytes], fnam: str = None, errors: Errors = None,
8585
return tree
8686

8787

88+
class Argument:
89+
"""Ideally this would be in nodes and be an actual node. However,
90+
since its not yet used by FuncItem, etc, it makes sense to keep
91+
it here so that it avoids confusion. It should be easily relocatable
92+
in the future.
93+
"""
94+
def __init__(self, variable: Var, type: Optional[Type],
95+
initializer: Optional[Node], kind: int) -> None:
96+
self.variable = variable
97+
self.type = type
98+
self.initializer = initializer
99+
self.kind = kind
100+
101+
88102
class Parser:
89103
"""Mypy parser that parses a string into an AST.
90104
@@ -385,8 +399,12 @@ def parse_function(self, no_type_checks: bool=False) -> FuncDef:
385399
is_method = self.is_class_body
386400
self.is_class_body = False
387401
try:
388-
(name, args, init, kinds,
389-
typ, is_error) = self.parse_function_header(no_type_checks)
402+
(name, args, typ, is_error) = self.parse_function_header(no_type_checks)
403+
404+
init = [arg.initializer for arg in args]
405+
kinds = [arg.kind for arg in args]
406+
arg_names = [arg.variable.name() for arg in args]
407+
arg_vars = [arg.variable for arg in args]
390408

391409
body, comment_type = self.parse_block(allow_type=True)
392410
if comment_type:
@@ -404,7 +422,7 @@ def parse_function(self, no_type_checks: bool=False) -> FuncDef:
404422
typ = CallableType(
405423
first_arg + sig.arg_types,
406424
kinds,
407-
[arg.name() for arg in args],
425+
arg_names,
408426
sig.ret_type,
409427
None)
410428
else:
@@ -413,7 +431,7 @@ def parse_function(self, no_type_checks: bool=False) -> FuncDef:
413431
typ = CallableType(
414432
sig.arg_types,
415433
kinds,
416-
[arg.name() for arg in args],
434+
arg_names,
417435
sig.ret_type,
418436
None)
419437

@@ -422,7 +440,7 @@ def parse_function(self, no_type_checks: bool=False) -> FuncDef:
422440
if is_error:
423441
return None
424442

425-
node = FuncDef(name, args, kinds, init, body, typ)
443+
node = FuncDef(name, arg_vars, kinds, init, body, typ)
426444
node.set_line(def_tok)
427445
return node
428446
finally:
@@ -445,19 +463,15 @@ def check_argument_kinds(self, funckinds: List[int], sigkinds: List[int],
445463
"Inconsistent use of '{}' in function "
446464
"signature".format(token), line)
447465

448-
def parse_function_header(self, no_type_checks: bool=False) -> Tuple[str, List[Var],
449-
List[Node], List[int],
466+
def parse_function_header(self, no_type_checks: bool=False) -> Tuple[str, List[Argument],
450467
CallableType, bool]:
451468
"""Parse function header (a name followed by arguments)
452469
453-
Returns a 7-tuple with the following items:
470+
Returns a 4-tuple with the following items:
454471
name
455472
arguments
456-
initializers
457-
kinds
458473
signature (annotation)
459474
error flag (True if error)
460-
(name token, representation of arguments)
461475
"""
462476
name = ''
463477

@@ -467,27 +481,24 @@ def parse_function_header(self, no_type_checks: bool=False) -> Tuple[str, List[V
467481

468482
self.errors.push_function(name)
469483

470-
(args, init, kinds, typ) = self.parse_args(no_type_checks)
484+
args, typ = self.parse_args(no_type_checks)
471485
except ParseError:
472486
if not isinstance(self.current(), Break):
473487
self.ind -= 1 # Kludge: go back to the Break token
474488
# Resynchronise parsing by going back over :, if present.
475489
if isinstance(self.tok[self.ind - 1], Colon):
476490
self.ind -= 1
477-
return (name, [], [], [], None, True)
491+
return (name, [], None, True)
478492

479-
return (name, args, init, kinds, typ, False)
493+
return (name, args, typ, False)
480494

481-
def parse_args(self, no_type_checks: bool=False) -> Tuple[List[Var], List[Node], List[int],
495+
def parse_args(self, no_type_checks: bool=False) -> Tuple[List[Argument],
482496
CallableType]:
483497
"""Parse a function signature (...) [-> t]."""
484498
lparen = self.expect('(')
485499

486500
# Parse the argument list (everything within '(' and ')').
487-
(args, init, kinds,
488-
has_inits, arg_names,
489-
commas, asterisk,
490-
assigns, arg_types) = self.parse_arg_list(no_type_checks=no_type_checks)
501+
args = self.parse_arg_list(no_type_checks=no_type_checks)
491502

492503
self.expect(')')
493504

@@ -501,53 +512,32 @@ def parse_args(self, no_type_checks: bool=False) -> Tuple[List[Var], List[Node],
501512
else:
502513
ret_type = None
503514

504-
self.verify_argument_kinds(kinds, lparen.line)
505-
506-
names = [arg.name() for arg in args]
515+
arg_kinds = [arg.kind for arg in args]
516+
self.verify_argument_kinds(arg_kinds, lparen.line)
507517

508518
annotation = self.build_func_annotation(
509-
ret_type, arg_types, kinds, names, lparen.line)
519+
ret_type, args, lparen.line)
510520

511-
return args, init, kinds, annotation
521+
return args, annotation
512522

513-
def build_func_annotation(self, ret_type: Type, arg_types: List[Type],
514-
kinds: List[int], names: List[str],
515-
line: int, is_default_ret: bool = False) -> CallableType:
523+
def build_func_annotation(self, ret_type: Type, args: List[Argument],
524+
line: int, is_default_ret: bool = False) -> CallableType:
525+
arg_types = [arg.type for arg in args]
516526
# Are there any type annotations?
517527
if ((ret_type and not is_default_ret)
518528
or arg_types != [None] * len(arg_types)):
519529
# Yes. Construct a type for the function signature.
520-
return self.construct_function_type(arg_types, kinds, names,
521-
ret_type, line)
530+
return self.construct_function_type(args, ret_type, line)
522531
else:
523532
return None
524533

525-
def parse_arg_list(
526-
self, allow_signature: bool = True,
527-
no_type_checks: bool=False) -> Tuple[List[Var], List[Node],
528-
List[int], bool,
529-
List[Token], List[Token],
530-
List[Token], List[Token],
531-
List[Type]]:
534+
def parse_arg_list(self, allow_signature: bool = True,
535+
no_type_checks: bool=False) -> List[Argument]:
532536
"""Parse function definition argument list.
533537
534538
This includes everything between '(' and ')').
535-
536-
Return a 9-tuple with these items:
537-
arguments, initializers, kinds, has inits, arg name tokens,
538-
comma tokens, asterisk tokens, assignment tokens, argument types
539539
"""
540-
args = [] # type: List[Var]
541-
kinds = [] # type: List[int]
542-
names = [] # type: List[str]
543-
init = [] # type: List[Node]
544-
has_inits = False
545-
arg_types = [] # type: List[Type]
546-
547-
arg_names = [] # type: List[Token]
548-
commas = [] # type: List[Token]
549-
asterisk = [] # type: List[Token]
550-
assigns = [] # type: List[Token]
540+
args = [] # type: List[Argument]
551541

552542
require_named = False
553543
bare_asterisk_before = -1
@@ -562,60 +552,74 @@ def parse_arg_list(
562552
if bare_asterisk_before == len(args):
563553
# named arguments must follow bare *
564554
self.parse_error()
565-
asterisk.append(self.skip())
566-
isdict = asterisk[-1].string == '**'
567-
name = self.expect_type(Name)
568-
arg_names.append(name)
569-
names.append(name.string)
570-
var_arg = Var(name.string)
571-
args.append(var_arg)
572-
init.append(None)
573-
assigns.append(none)
574-
if isdict:
575-
kinds.append(nodes.ARG_STAR2)
576-
else:
577-
kinds.append(nodes.ARG_STAR)
578555

579-
if no_type_checks:
580-
self.parse_parameter_annotation()
581-
arg_types.append(None)
582-
else:
583-
arg_types.append(self.parse_arg_type(allow_signature))
556+
arg = self.parse_asterisk_arg(
557+
allow_signature,
558+
no_type_checks,
559+
)
560+
args.append(arg)
584561
require_named = True
585562
else:
586-
name = self.expect_type(Name)
587-
arg_names.append(name)
588-
args.append(Var(name.string))
589-
590-
if no_type_checks:
591-
self.parse_parameter_annotation()
592-
arg_types.append(None)
593-
else:
594-
arg_types.append(self.parse_arg_type(allow_signature))
595-
596-
if self.current_str() == '=':
597-
assigns.append(self.expect('='))
598-
init.append(self.parse_expression(precedence[',']))
599-
has_inits = True
600-
if require_named:
601-
kinds.append(nodes.ARG_NAMED)
602-
else:
603-
kinds.append(nodes.ARG_OPT)
604-
else:
605-
init.append(None)
606-
assigns.append(none)
607-
if require_named:
608-
# required keyword-only argument
609-
kinds.append(nodes.ARG_NAMED)
610-
else:
611-
kinds.append(nodes.ARG_POS)
563+
arg, require_named = self.parse_normal_arg(
564+
require_named,
565+
allow_signature,
566+
no_type_checks,
567+
)
568+
args.append(arg)
612569

613570
if self.current().string != ',':
614571
break
615-
commas.append(self.expect(','))
616572

617-
return (args, init, kinds, has_inits, arg_names, commas, asterisk,
618-
assigns, arg_types)
573+
self.expect(',')
574+
575+
return args
576+
577+
def parse_asterisk_arg(self,
578+
allow_signature: bool,
579+
no_type_checks: bool) -> Argument:
580+
asterisk = self.skip()
581+
name = self.expect_type(Name)
582+
variable = Var(name.string)
583+
if asterisk.string == '*':
584+
kind = nodes.ARG_STAR
585+
else:
586+
kind = nodes.ARG_STAR2
587+
588+
type = None # type: Type
589+
if no_type_checks:
590+
self.parse_parameter_annotation()
591+
else:
592+
type = self.parse_arg_type(allow_signature)
593+
594+
return Argument(variable, type, None, kind)
595+
596+
def parse_normal_arg(self, require_named: bool,
597+
allow_signature: bool,
598+
no_type_checks: bool) -> Tuple[Argument, bool]:
599+
name = self.expect_type(Name)
600+
variable = Var(name.string)
601+
602+
type = None # type: Type
603+
if no_type_checks:
604+
self.parse_parameter_annotation()
605+
else:
606+
type = self.parse_arg_type(allow_signature)
607+
608+
initializer = None # type: Node
609+
if self.current_str() == '=':
610+
self.expect('=')
611+
initializer = self.parse_expression(precedence[','])
612+
if require_named:
613+
kind = nodes.ARG_NAMED
614+
else:
615+
kind = nodes.ARG_OPT
616+
else:
617+
if require_named:
618+
kind = nodes.ARG_NAMED
619+
else:
620+
kind = nodes.ARG_POS
621+
622+
return Argument(variable, type, initializer, kind), require_named
619623

620624
def parse_parameter_annotation(self) -> Node:
621625
if self.current_str() == ':':
@@ -642,17 +646,18 @@ def verify_argument_kinds(self, kinds: List[int], line: int) -> None:
642646
self.fail('Invalid argument list', line)
643647
found.add(kind)
644648

645-
def construct_function_type(self, arg_types: List[Type], kinds: List[int],
646-
names: List[str], ret_type: Type,
649+
def construct_function_type(self, args: List[Argument], ret_type: Type,
647650
line: int) -> CallableType:
648651
# Complete the type annotation by replacing omitted types with 'Any'.
649-
arg_types = arg_types[:]
652+
arg_types = [arg.type for arg in args]
650653
for i in range(len(arg_types)):
651654
if arg_types[i] is None:
652655
arg_types[i] = AnyType()
653656
if ret_type is None:
654657
ret_type = AnyType()
655-
return CallableType(arg_types, kinds, names, ret_type, None, None,
658+
arg_kinds = [arg.kind for arg in args]
659+
arg_names = [arg.variable.name() for arg in args]
660+
return CallableType(arg_types, arg_kinds, arg_names, ret_type, None, None,
656661
None, [], line)
657662

658663
# Parsing statements
@@ -1626,19 +1631,13 @@ def parse_unary_expr(self) -> UnaryExpr:
16261631
def parse_lambda_expr(self) -> FuncExpr:
16271632
lambda_tok = self.expect('lambda')
16281633

1629-
(args, init, kinds, has_inits,
1630-
arg_names, commas, asterisk,
1631-
assigns, arg_types) = self.parse_arg_list(allow_signature=False)
1632-
1633-
names = [] # type: List[str]
1634-
for arg in args:
1635-
names.append(arg.name())
1634+
args = self.parse_arg_list(allow_signature=False)
16361635

16371636
# Use 'object' as the placeholder return type; it will be inferred
16381637
# later. We can't use 'Any' since it could make type inference results
16391638
# less precise.
16401639
ret_type = UnboundType('__builtins__.object')
1641-
typ = self.build_func_annotation(ret_type, arg_types, kinds, names,
1640+
typ = self.build_func_annotation(ret_type, args,
16421641
lambda_tok.line, is_default_ret=True)
16431642

16441643
colon = self.expect(':')
@@ -1648,7 +1647,11 @@ def parse_lambda_expr(self) -> FuncExpr:
16481647
body = Block([ReturnStmt(expr).set_line(lambda_tok)])
16491648
body.set_line(colon)
16501649

1651-
node = FuncExpr(args, kinds, init, body, typ)
1650+
arg_vars = [arg.variable for arg in args]
1651+
arg_kinds = [arg.kind for arg in args]
1652+
arg_initializers = [arg.initializer for arg in args]
1653+
1654+
node = FuncExpr(arg_vars, arg_kinds, arg_initializers, body, typ)
16521655
return node
16531656

16541657
# Helper methods

0 commit comments

Comments
 (0)