10. Full Grammar specification
******************************

This is the full Python grammar, derived directly from the grammar
used to generate the CPython parser (see Grammar/python.gram). The
version here omits details related to code generation and error
recovery.

The notation used here is the same as in the preceding docs, and is
described in the notation section, except for an extra complication:

* "~" ("cut"): commit to the current alternative; fail the rule if the
  alternative fails to parse

  Python mainly uses cuts for optimizations or improved error
  messages. They often appear to be useless in the listing below.

  Cuts currently don't appear inside parentheses, brackets, lookaheads
  and similar. Their behavior in these contexts is deliberately left
  unspecified.

   # PEG grammar for Python



   # ========================= START OF THE GRAMMAR =========================

   # General grammatical elements and rules:
   #
   # * Strings with double quotes (") denote SOFT KEYWORDS
   # * Strings with single quotes (') denote KEYWORDS
   # * Upper case names (NAME) denote tokens in the Grammar/Tokens file
   # * Rule names starting with "invalid_" are used for specialized syntax errors
   #     - These rules are NOT used in the first pass of the parser.
   #     - Only if the first pass fails to parse, a second pass including the invalid
   #       rules will be executed.
   #     - If the parser fails in the second phase with a generic syntax error, the
   #       location of the generic failure of the first pass will be used (this avoids
   #       reporting incorrect locations due to the invalid rules).
   #     - The order of the alternatives involving invalid rules matter
   #       (like any rule in PEG).
   #
   # Grammar Syntax (see PEP 617 for more information):
   #
   # rule_name: expression
   #   Optionally, a type can be included right after the rule name, which
   #   specifies the return type of the C or Python function corresponding to the
   #   rule:
   # rule_name[return_type]: expression
   #   If the return type is omitted, then a void * is returned in C and an Any in
   #   Python.
   # e1 e2
   #   Match e1, then match e2.
   # e1 | e2
   #   Match e1 or e2.
   #   The first alternative can also appear on the line after the rule name for
   #   formatting purposes. In that case, a | must be used before the first
   #   alternative, like so:
   #       rule_name[return_type]:
   #            | first_alt
   #            | second_alt
   # ( e )
   #   Match e (allows also to use other operators in the group like '(e)*')
   # [ e ] or e?
   #   Optionally match e.
   # e*
   #   Match zero or more occurrences of e.
   # e+
   #   Match one or more occurrences of e.
   # s.e+
   #   Match one or more occurrences of e, separated by s. The generated parse tree
   #   does not include the separator. This is otherwise identical to (e (s e)*).
   # &e
   #   Succeed if e can be parsed, without consuming any input.
   # !e
   #   Fail if e can be parsed, without consuming any input.
   # ~
   #   Commit to the current alternative, even if it fails to parse.
   # &&e
   #   Eager parse e. The parser will not backtrack and will immediately
   #   fail with SyntaxError if e cannot be parsed.
   #

   # STARTING RULES
   # ==============

   file: [statements] ENDMARKER 
   interactive: statement_newline 
   eval: expressions NEWLINE* ENDMARKER 
   func_type: '(' [type_expressions] ')' '->' expression NEWLINE* ENDMARKER 

   # GENERAL STATEMENTS
   # ==================

   statements: statement+ 

   statement:
       | compound_stmt 
       | simple_stmts 

   single_compound_stmt:
       | compound_stmt 

   statement_newline:
       | single_compound_stmt NEWLINE 
       | simple_stmts
       | NEWLINE 
       | ENDMARKER 

   simple_stmts:
       | simple_stmt !';' NEWLINE  # Not needed, there for speedup
       | ';'.simple_stmt+ [';'] NEWLINE 

   # NOTE: assignment MUST precede expression, else parsing a simple assignment
   # will throw a SyntaxError.
   simple_stmt:
       | assignment
       | type_alias
       | import_stmt
       | star_expressions 
       | return_stmt
       | raise_stmt
       | pass_stmt
       | del_stmt
       | yield_stmt
       | assert_stmt
       | break_stmt
       | continue_stmt
       | global_stmt
       | nonlocal_stmt

   compound_stmt:
       | function_def
       | if_stmt
       | class_def
       | with_stmt
       | for_stmt
       | try_stmt
       | while_stmt
       | match_stmt

   # SIMPLE STATEMENTS
   # =================

   # NOTE: annotated_rhs may start with 'yield'; yield_expr must start with 'yield'
   assignment:
       | NAME ':' expression ['=' annotated_rhs ] 
       | ('(' single_target ')' 
            | single_subscript_attribute_target) ':' expression ['=' annotated_rhs ] 
       | (star_targets '=' )+ annotated_rhs !'=' [TYPE_COMMENT] 
       | single_target augassign ~ annotated_rhs 

   annotated_rhs: yield_expr | star_expressions

   augassign:
       | '+=' 
       | '-=' 
       | '*=' 
       | '@=' 
       | '/=' 
       | '%=' 
       | '&=' 
       | '|=' 
       | '^=' 
       | '<<=' 
       | '>>=' 
       | '**=' 
       | '//=' 

   return_stmt:
       | 'return' [star_expressions] 

   raise_stmt:
       | 'raise' expression 'from' expression 
       | 'raise' expression 
       | 'raise' 

   pass_stmt:
       | 'pass' 

   break_stmt:
       | 'break' 

   continue_stmt:
       | 'continue' 

   global_stmt: 'global' ','.NAME+ 

   nonlocal_stmt: 'nonlocal' ','.NAME+ 

   del_stmt:
       | 'del' del_targets &(';' | NEWLINE) 

   yield_stmt: yield_expr 

   assert_stmt:
       | 'assert' expression [',' expression ] 

   import_stmt:
       | import_name
       | import_from

   # Import statements
   # -----------------

   import_name:
       | "lazy" 'import' dotted_as_names 
       | 'import' dotted_as_names 

   # note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
   import_from:
       | "lazy" 'from' ('.' | '...')* dotted_name 'import' import_from_targets 
       | "lazy" 'from' ('.' | '...')+ 'import' import_from_targets 
       | 'from' ('.' | '...')* dotted_name 'import' import_from_targets 
       | 'from' ('.' | '...')+ 'import' import_from_targets 

   import_from_targets:
       | '(' import_from_as_names [','] ')' 
       | import_from_as_names !','
       | '*' 
   import_from_as_names:
       | ','.import_from_as_name+ 
   import_from_as_name:
       | NAME ['as' NAME ] 

   dotted_as_names:
       | ','.dotted_as_name+ 
   dotted_as_name:
       | dotted_name ['as' NAME ] 

   dotted_name:
       | dotted_name '.' NAME 
       | NAME

   # COMPOUND STATEMENTS
   # ===================

   # Common elements
   # ---------------

   block:
       | NEWLINE INDENT statements DEDENT 
       | simple_stmts

   decorators: ('@' named_expression NEWLINE )+ 

   # Class definitions
   # -----------------

   class_def:
       | decorators class_def_raw 
       | class_def_raw

   class_def_raw:
       | 'class' NAME [type_params] ['(' [arguments] ')' ] ':' block 

   # Function definitions
   # --------------------

   function_def:
       | decorators function_def_raw 
       | function_def_raw

   function_def_raw:
       | 'def' NAME [type_params] '(' [params] ')' ['->' expression ] ':' [func_type_comment] block 
       | 'async' 'def' NAME [type_params] '(' [params] ')' ['->' expression ] ':' [func_type_comment] block 

   # Function parameters
   # -------------------

   params:
       | parameters

   parameters:
       | slash_no_default param_no_default* param_with_default* [star_etc] 
       | slash_with_default param_with_default* [star_etc] 
       | param_no_default+ param_with_default* [star_etc] 
       | param_with_default+ [star_etc] 
       | star_etc 

   # Some duplication here because we can't write (',' | &')'),
   # which is because we don't support empty alternatives (yet).

   slash_no_default:
       | param_no_default+ '/' ',' 
       | param_no_default+ '/' &')' 
   slash_with_default:
       | param_no_default* param_with_default+ '/' ',' 
       | param_no_default* param_with_default+ '/' &')' 

   star_etc:
       | '*' param_no_default param_maybe_default* [kwds] 
       | '*' param_no_default_star_annotation param_maybe_default* [kwds] 
       | '*' ',' param_maybe_default+ [kwds] 
       | kwds 

   kwds:
       | '**' param_no_default 

   # One parameter.  This *includes* a following comma and type comment.
   #
   # There are three styles:
   # - No default
   # - With default
   # - Maybe with default
   #
   # There are two alternative forms of each, to deal with type comments:
   # - Ends in a comma followed by an optional type comment
   # - No comma, optional type comment, must be followed by close paren
   # The latter form is for a final parameter without trailing comma.
   #

   param_no_default:
       | param ',' TYPE_COMMENT? 
       | param TYPE_COMMENT? &')' 
   param_no_default_star_annotation:
       | param_star_annotation ',' TYPE_COMMENT? 
       | param_star_annotation TYPE_COMMENT? &')' 
   param_with_default:
       | param default ',' TYPE_COMMENT? 
       | param default TYPE_COMMENT? &')' 
   param_maybe_default:
       | param default? ',' TYPE_COMMENT? 
       | param default? TYPE_COMMENT? &')' 
   param: NAME annotation? 
   param_star_annotation: NAME star_annotation 
   annotation: ':' expression 
   star_annotation: ':' star_expression 
   default: '=' expression  | invalid_default

   # If statement
   # ------------

   if_stmt:
       | 'if' named_expression ':' block elif_stmt 
       | 'if' named_expression ':' block [else_block] 
   elif_stmt:
       | 'elif' named_expression ':' block elif_stmt 
       | 'elif' named_expression ':' block [else_block] 
   else_block:
       | 'else' ':' block 

   # While statement
   # ---------------

   while_stmt:
       | 'while' named_expression ':' block [else_block] 

   # For statement
   # -------------

   for_stmt:
       | 'for' star_targets 'in' ~ star_expressions ':' [TYPE_COMMENT] block [else_block] 
       | 'async' 'for' star_targets 'in' ~ star_expressions ':' [TYPE_COMMENT] block [else_block] 

   # With statement
   # --------------

   with_stmt:
       | 'with' '(' ','.with_item+ ','? ')' ':' [TYPE_COMMENT] block 
       | 'with' ','.with_item+ ':' [TYPE_COMMENT] block 
       | 'async' 'with' '(' ','.with_item+ ','? ')' ':' block 
       | 'async' 'with' ','.with_item+ ':' [TYPE_COMMENT] block 

   with_item:
       | expression 'as' star_target &(',' | ')' | ':') 
       | expression 

   # Try statement
   # -------------

   try_stmt:
       | 'try' ':' block finally_block 
       | 'try' ':' block except_block+ [else_block] [finally_block] 
       | 'try' ':' block except_star_block+ [else_block] [finally_block] 


   # Except statement
   # ----------------

   except_block:
       | 'except' expression ':' block 
       | 'except' expression 'as' NAME ':' block 
       | 'except' expressions ':' block 
       | 'except' ':' block 
   except_star_block:
       | 'except' '*' expression ':' block 
       | 'except' '*' expression 'as' NAME ':' block 
       | 'except' '*' expressions ':' block 
   finally_block:
       | 'finally' ':' block 

   # Match statement
   # ---------------

   match_stmt:
       | "match" subject_expr ':' NEWLINE INDENT case_block+ DEDENT 

   subject_expr:
       | star_named_expression ',' star_named_expressions? 
       | named_expression

   case_block:
       | "case" patterns guard? ':' block 

   guard: 'if' named_expression 

   patterns:
       | open_sequence_pattern 
       | pattern

   pattern:
       | as_pattern
       | or_pattern

   as_pattern:
       | or_pattern 'as' pattern_capture_target 

   or_pattern:
       | '|'.closed_pattern+ 

   closed_pattern:
       | literal_pattern
       | capture_pattern
       | wildcard_pattern
       | value_pattern
       | group_pattern
       | sequence_pattern
       | mapping_pattern
       | class_pattern

   # Literal patterns are used for equality and identity constraints
   literal_pattern:
       | signed_number !('+' | '-') 
       | complex_number 
       | strings 
       | 'None' 
       | 'True' 
       | 'False' 

   # Literal expressions are used to restrict permitted mapping pattern keys
   literal_expr:
       | signed_number !('+' | '-')
       | complex_number
       | strings
       | 'None' 
       | 'True' 
       | 'False' 

   complex_number:
       | signed_real_number '+' imaginary_number 
       | signed_real_number '-' imaginary_number  

   signed_number:
       | NUMBER
       | '+' NUMBER 
       | '-' NUMBER 

   signed_real_number:
       | real_number
       | '+' real_number 
       | '-' real_number 

   real_number:
       | NUMBER 

   imaginary_number:
       | NUMBER 

   capture_pattern:
       | pattern_capture_target 

   pattern_capture_target:
       | !"_" NAME !('.' | '(' | '=') 

   wildcard_pattern:
       | "_" 

   value_pattern:
       | attr !('.' | '(' | '=') 

   attr:
       | name_or_attr '.' NAME 

   name_or_attr:
       | attr
       | NAME

   group_pattern:
       | '(' pattern ')' 

   sequence_pattern:
       | '[' maybe_sequence_pattern? ']' 
       | '(' open_sequence_pattern? ')' 

   open_sequence_pattern:
       | maybe_star_pattern ',' maybe_sequence_pattern? 

   maybe_sequence_pattern:
       | ','.maybe_star_pattern+ ','? 

   maybe_star_pattern:
       | star_pattern
       | pattern

   star_pattern:
       | '*' pattern_capture_target 
       | '*' wildcard_pattern 

   mapping_pattern:
       | '{' '}' 
       | '{' double_star_pattern ','? '}' 
       | '{' items_pattern ',' double_star_pattern ','? '}' 
       | '{' items_pattern ','? '}' 

   items_pattern:
       | ','.key_value_pattern+

   key_value_pattern:
       | (literal_expr | attr) ':' pattern 

   double_star_pattern:
       | '**' pattern_capture_target 

   class_pattern:
       | name_or_attr '(' ')' 
       | name_or_attr '(' positional_patterns ','? ')' 
       | name_or_attr '(' keyword_patterns ','? ')' 
       | name_or_attr '(' positional_patterns ',' keyword_patterns ','? ')' 

   positional_patterns:
       | ','.pattern+ 

   keyword_patterns:
       | ','.keyword_pattern+

   keyword_pattern:
       | NAME '=' pattern 

   # Type statement
   # ---------------

   type_alias:
       | "type" NAME [type_params] '=' expression 

   # Type parameter declaration
   # --------------------------

   type_params:
       | '[' type_param_seq ']' 

   type_param_seq: ','.type_param+ [','] 

   type_param:
       | NAME [type_param_bound] [type_param_default] 
       | '*' NAME [type_param_starred_default] 
       | '**' NAME [type_param_default] 

   type_param_bound: ':' expression 
   type_param_default: '=' expression 
   type_param_starred_default: '=' star_expression 

   # EXPRESSIONS
   # -----------

   expressions:
       | expression (',' expression )+ [','] 
       | expression ',' 
       | expression

   expression:
       | if_expression
       | disjunction
       | lambdef

   if_expression:
       | disjunction 'if' disjunction 'else' expression 

   yield_expr:
       | 'yield' 'from' expression 
       | 'yield' [star_expressions] 

   star_expressions:
       | star_expression (',' star_expression )+ [','] 
       | star_expression ',' 
       | star_expression

   star_expression:
       | '*' bitwise_or 
       | expression

   star_named_expressions: ','.star_named_expression+ [','] 

   star_named_expressions_sequence: ','.star_named_expression_sequence+ [','] 

   star_named_expression:
       | '*' bitwise_or 
       | named_expression

   star_named_expression_sequence:
       | star_named_expression

   assignment_expression:
       | NAME ':=' ~ expression 

   named_expression:
       | assignment_expression
       | expression !':='

   disjunction:
       | conjunction ('or' conjunction )+ 
       | conjunction

   conjunction:
       | inversion ('and' inversion )+ 
       | inversion

   inversion:
       | 'not' inversion 
       | comparison

   # Comparison operators
   # --------------------

   comparison:
       | bitwise_or compare_op_bitwise_or_pair+ 
       | bitwise_or

   compare_op_bitwise_or_pair:
       | eq_bitwise_or
       | noteq_bitwise_or
       | lte_bitwise_or
       | lt_bitwise_or
       | gte_bitwise_or
       | gt_bitwise_or
       | notin_bitwise_or
       | in_bitwise_or
       | isnot_bitwise_or
       | is_bitwise_or

   eq_bitwise_or: '==' bitwise_or 
   noteq_bitwise_or:
       | ('!=' ) bitwise_or 
   lte_bitwise_or: '<=' bitwise_or 
   lt_bitwise_or: '<' bitwise_or 
   gte_bitwise_or: '>=' bitwise_or 
   gt_bitwise_or: '>' bitwise_or 
   notin_bitwise_or: 'not' 'in' bitwise_or 
   in_bitwise_or: 'in' bitwise_or 
   isnot_bitwise_or: 'is' 'not' bitwise_or 
   is_bitwise_or: 'is' bitwise_or 

   # Bitwise operators
   # -----------------

   bitwise_or:
       | bitwise_or '|' bitwise_xor 
       | bitwise_xor

   bitwise_xor:
       | bitwise_xor '^' bitwise_and 
       | bitwise_and

   bitwise_and:
       | bitwise_and '&' shift_expr 
       | shift_expr

   shift_expr:
       | shift_expr '<<' sum 
       | shift_expr '>>' sum 
       | sum

   # Arithmetic operators
   # --------------------

   sum:
       | sum '+' term 
       | sum '-' term 
       | term

   term:
       | term '*' factor 
       | term '/' factor 
       | term '//' factor 
       | term '%' factor 
       | term '@' factor 
       | factor

   factor:
       | '+' factor 
       | '-' factor 
       | '~' factor 
       | power

   power:
       | await_primary '**' factor 
       | await_primary

   # Primary elements
   # ----------------

   # Primary elements are things like "obj.something.something", "obj[something]", "obj(something)", "obj" ...

   await_primary:
       | 'await' primary 
       | primary

   primary:
       | primary '.' NAME 
       | primary genexp 
       | primary '(' [arguments] ')' 
       | primary '[' slices ']' 
       | atom

   slices:
       | slice !',' 
       | ','.(slice | starred_expression)+ [','] 

   slice:
       | [expression] ':' [expression] [':' [expression] ] 
       | named_expression 

   atom:
       | NAME
       | 'True' 
       | 'False' 
       | 'None' 
       | strings
       | NUMBER
       | (genexp | tuple | group)
       | (listcomp | list)
       | (dictcomp | setcomp | dict | set)
       | '...' 

   group:
       | '(' (yield_expr | named_expression) ')' 

   # Lambda functions
   # ----------------

   lambdef:
       | 'lambda' [lambda_params] ':' expression 

   lambda_params:
       | lambda_parameters

   # lambda_parameters etc. duplicates parameters but without annotations
   # or type comments, and if there's no comma after a parameter, we expect
   # a colon, not a close parenthesis.  (For more, see parameters above.)
   #
   lambda_parameters:
       | lambda_slash_no_default lambda_param_no_default* lambda_param_with_default* [lambda_star_etc] 
       | lambda_slash_with_default lambda_param_with_default* [lambda_star_etc] 
       | lambda_param_no_default+ lambda_param_with_default* [lambda_star_etc] 
       | lambda_param_with_default+ [lambda_star_etc] 
       | lambda_star_etc 

   lambda_slash_no_default:
       | lambda_param_no_default+ '/' ',' 
       | lambda_param_no_default+ '/' &':' 

   lambda_slash_with_default:
       | lambda_param_no_default* lambda_param_with_default+ '/' ',' 
       | lambda_param_no_default* lambda_param_with_default+ '/' &':' 

   lambda_star_etc:
       | '*' lambda_param_no_default lambda_param_maybe_default* [lambda_kwds] 
       | '*' ',' lambda_param_maybe_default+ [lambda_kwds] 
       | lambda_kwds 

   lambda_kwds:
       | '**' lambda_param_no_default 

   lambda_param_no_default:
       | lambda_param ',' 
       | lambda_param &':' 
   lambda_param_with_default:
       | lambda_param default ',' 
       | lambda_param default &':' 
   lambda_param_maybe_default:
       | lambda_param default? ',' 
       | lambda_param default? &':' 
   lambda_param: NAME 

   # LITERALS
   # ========

   fstring_middle:
       | fstring_replacement_field
       | FSTRING_MIDDLE 
   fstring_replacement_field:
       | '{' annotated_rhs '='? [fstring_conversion] [fstring_full_format_spec] '}' 
   fstring_conversion:
       | "!" NAME 
   fstring_full_format_spec:
       | ':' fstring_format_spec* 
   fstring_format_spec:
       | FSTRING_MIDDLE 
       | fstring_replacement_field
   fstring:
       | FSTRING_START fstring_middle* FSTRING_END 

   tstring_format_spec_replacement_field:
       | '{' annotated_rhs '='? [fstring_conversion] [tstring_full_format_spec] '}' 
   tstring_format_spec:
       | TSTRING_MIDDLE 
       | tstring_format_spec_replacement_field
   tstring_full_format_spec:
       | ':' tstring_format_spec* 
   tstring_replacement_field:
       | '{' annotated_rhs '='? [fstring_conversion] [tstring_full_format_spec] '}' 
   tstring_middle:
       | tstring_replacement_field
       | TSTRING_MIDDLE 
   tstring:
       | TSTRING_START tstring_middle* TSTRING_END 

   string: STRING 
   strings:
       | (fstring|string)+ 
       | tstring+ 

   list:
       | '[' [star_named_expressions_sequence] ']' 

   tuple:
       | '(' [star_named_expression_sequence ',' [star_named_expressions_sequence]  ] ')' 

   set: '{' star_named_expressions_sequence '}' 

   # Dicts
   # -----

   dict:
       | '{' [double_starred_kvpairs] '}' 

   double_starred_kvpairs: ','.double_starred_kvpair+ [','] 

   double_starred_kvpair:
       | '**' bitwise_or 
       | kvpair

   kvpair: expression ':' expression 

   # Comprehensions & Generators
   # ---------------------------

   for_if_clauses:
       | for_if_clause+ 

   for_if_clause:
       | 'async' 'for' star_targets 'in' ~ disjunction ('if' disjunction )* 
       | 'for' star_targets 'in' ~ disjunction ('if' disjunction )* 

   listcomp:
       | '[' star_named_expression for_if_clauses ']' 

   setcomp:
       | '{' star_named_expression for_if_clauses '}' 

   genexp:
       | '(' ( assignment_expression | expression !':=' | starred_expression ) for_if_clauses ')' 

   dictcomp:
       | '{' kvpair for_if_clauses '}' 
       | '{' '**' expression for_if_clauses '}' 

   # FUNCTION CALL ARGUMENTS
   # =======================

   arguments:
       | args [','] &')' 

   args:
       | ','.(starred_expression | ( assignment_expression | expression !':=') !'=')+ [',' kwargs ] 
       | kwargs 

   kwargs:
       | ','.kwarg_or_starred+ ',' ','.kwarg_or_double_starred+ 
       | ','.kwarg_or_starred+
       | ','.kwarg_or_double_starred+

   starred_expression:
       | '*' expression 

   kwarg_or_starred:
       | NAME '=' expression 
       | starred_expression 

   kwarg_or_double_starred:
       | NAME '=' expression 
       | '**' expression 

   # ASSIGNMENT TARGETS
   # ==================

   # Generic targets
   # ---------------

   # NOTE: star_targets may contain *bitwise_or, targets may not.
   star_targets:
       | star_target !',' 
       | star_target (',' star_target )* [','] 

   star_targets_list_seq: ','.star_target+ [','] 

   star_targets_tuple_seq:
       | star_target (',' star_target )+ [','] 
       | star_target ',' 

   star_target:
       | '*' (!'*' star_target) 
       | target_with_star_atom

   target_with_star_atom:
       | t_primary '.' NAME !t_lookahead 
       | t_primary '[' slices ']' !t_lookahead 
       | star_atom

   star_atom:
       | NAME 
       | '(' target_with_star_atom ')' 
       | '(' [star_targets_tuple_seq] ')' 
       | '[' [star_targets_list_seq] ']' 

   single_target:
       | single_subscript_attribute_target
       | NAME 
       | '(' single_target ')' 

   single_subscript_attribute_target:
       | t_primary '.' NAME !t_lookahead 
       | t_primary '[' slices ']' !t_lookahead 

   t_primary:
       | t_primary '.' NAME &t_lookahead 
       | t_primary '[' slices ']' &t_lookahead 
       | t_primary genexp &t_lookahead 
       | t_primary '(' [arguments] ')' &t_lookahead 
       | atom &t_lookahead 

   t_lookahead: '(' | '[' | '.'

   # Targets for del statements
   # --------------------------

   del_targets: ','.del_target+ [','] 

   del_target:
       | t_primary '.' NAME !t_lookahead 
       | t_primary '[' slices ']' !t_lookahead 
       | del_t_atom

   del_t_atom:
       | NAME 
       | '(' del_target ')' 
       | '(' [del_targets] ')' 
       | '[' [del_targets] ']' 

   # TYPING ELEMENTS
   # ---------------

   # type_expressions allow */** but ignore them
   type_expressions:
       | ','.expression+ ',' '*' expression ',' '**' expression 
       | ','.expression+ ',' '*' expression 
       | ','.expression+ ',' '**' expression 
       | '*' expression ',' '**' expression 
       | '*' expression 
       | '**' expression 
       | ','.expression+ 

   func_type_comment:
       | NEWLINE TYPE_COMMENT &(NEWLINE INDENT)   # Must be followed by indented block
       | TYPE_COMMENT

   # ========================= END OF THE GRAMMAR ===========================



   # ========================= START OF INVALID RULES =======================

