packages feed

hydra-0.5.0: src/main/haskell/Hydra/Sources/Tier4/Langs/Python/Python3.hs

{-# LANGUAGE OverloadedStrings #-}

module Hydra.Sources.Tier4.Langs.Python.Python3 (python3Module) where

import Hydra.Kernel
import Hydra.Dsl.Grammars
import Hydra.Tools.GrammarToModule
import qualified Hydra.Dsl.Annotations as Ann
import qualified Hydra.Grammar as G

import qualified Data.List as L


python3Module :: Module
python3Module = grammarToModule ns python3Grammar $
    Just "A Python 3 syntax model, based on the BNF/PEG grammar at https://docs.python.org/3/reference/grammar.html as of 2023-04-03."
  where
    ns = Namespace "hydra/langs/python/python3"

python3Grammar :: G.Grammar
python3Grammar = G.Grammar $ tokens ++ productions

amp_ = terminal "&"
assign_ = terminal ":="
star_ = terminal "*"
at_ = terminal "@"
close_curly_ = terminal "}"
close_paren_ = terminal ")"
close_square_ = terminal "]"
colon_ = terminal ":"
comma_ = terminal ","
dot_ = terminal "."
double_equal_ = terminal "=="
double_gt_ = terminal ">>"
double_lt_ = terminal "<<"
double_slash_ = terminal "//"
double_star_ = terminal "**"
ellipsis_ = terminal "..."
endmarker_ = terminal ""
equal_ = terminal "="
gt_ = terminal ">"
gte_ = terminal ">="
hat_ = terminal "^"
lt_ = terminal "<"
lte_ = terminal "<="
minus_ = terminal "-"
newline_ = terminal "\n"
noteq_ = terminal "!="
open_curly_ = terminal "{"
open_paren_ = terminal "("
open_square_ = terminal "["
percent_ = terminal "%"
pipe_ = terminal "|"
plus_ = terminal "+"
right_arrow_ = terminal "->"
semi_ = terminal ";"
slash_ = terminal "/"
tilde_ = terminal "~"
underscore_ = terminal "_"

tokens :: [G.Production]
tokens = [
  define "Async" [terminal "async"],                -- TODO: not specified in the BNF
  define "Await" [terminal "await"],                -- TODO: not specified in the BNF
  define "Dedent" [terminal "\n"],                  -- TODO: not specified in the BNF
  define "Indent" [terminal "\t"],                  -- TODO: not specified in the BNF
  define "Name" [regex "[A-Za-z0-9][A-Za-z0-9_]*"], -- TODO: not specified in the BNF
  define "Number" [regex "[0-9]+"],                 -- TODO: not specified in the BNF
  define "String" [regex "\"[^\"]*\""],              -- TODO: not specified in the BNF
  define "TypeComment" [terminal ""]]               -- TODO: not specified in the BNF

productions :: [G.Production]
productions = [
-- # STARTING RULES
-- # ==============

-- file: [statements] ENDMARKER
  define "File" [
    star "Statement"],

-- interactive: statement_newline
  define "Interactive" [
    "StatementNewline"],

-- eval: expressions NEWLINE* ENDMARKER
  define "Eval" [
    list[sepp comma_ "Expression", star newline_]],

-- func_type: '(' [type_expressions] ')' '->' expression NEWLINE* ENDMARKER
  define "FuncType" [open_paren_, opt "TypeExpressions", close_paren_, right_arrow_, star newline_, endmarker_],

-- fstring: star_expressions
  define "Fstring" [
    sepp comma_ "StarExpression"],

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

-- statements: statement+

-- statement: compound_stmt  | simple_stmts
  define "Statement" [
    "CompoundStmt",
    "SimpleStmts"],

-- statement_newline:
--     | compound_stmt NEWLINE
--     | simple_stmts
--     | NEWLINE
--     | ENDMARKER
  define "StatementNewline" [
    list["CompoundStmt", newline_],
    "SimpleStmts",
    newline_,
    endmarker_],

-- simple_stmts:
--     | simple_stmt !';' NEWLINE  # Not needed, there for speedup
--     | ';'.simple_stmt+ [';'] NEWLINE
  define "SimpleStmts" [
    list ["SimpleStmt", star(list[semi_, dot_, "SimpleStmt"]), newline_]],

-- # NOTE: assignment MUST precede expression, else parsing a simple assignment
-- # will throw a SyntaxError.
-- simple_stmt:
--     | assignment
--     | star_expressions
--     | return_stmt
--     | import_stmt
--     | raise_stmt
--     | 'pass'
--     | del_stmt
--     | yield_stmt
--     | assert_stmt
--     | 'break'
--     | 'continue'
--     | global_stmt
--     | nonlocal_stmt
  define "SimpleStmt" [
    "Assignment",
    sepp comma_ "StarExpression",
    "ReturnStmt",
    "ImportStmt",
    "RaiseStmt",
    terminal "pass",
    "DelStmt",
    "YieldStmt",
    "AssertStmt",
    terminal "break",
    terminal "continue",
    "GlobalStmt",
    "NonlocalStmt"],

-- compound_stmt:
--     | function_def
--     | if_stmt
--     | class_def
--     | with_stmt
--     | for_stmt
--     | try_stmt
--     | while_stmt
--     | match_stmt
  define "CompoundStmt" [
    "FunctionDef",
    "IfStmt",
    "ClassDef",
    "WithStmt",
    "ForStmt",
    "TryStmt",
    "WhileStmt",
    "MatchStmt"],

-- # 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 '=' )+ (yield_expr | star_expressions) !'=' [TYPE_COMMENT]
--     | single_target augassign ~ (yield_expr | star_expressions)
  define "Assignment" [
    list["Name", colon_, "Expression", opt(list[equal_, "AnnotatedRhs"])],
    list[alts[list[open_paren_, "SingleTarget", close_paren_], "SingleSubscriptAttributeTarget"], colon_, "Expression", opt(list[equal_, "AnnotatedRhs"])],
    list[plus(list[sepp comma_ "StarTarget", equal_]), alts["YieldExpr", sepp comma_ "StarExpression"], opt("TypeComment")],
    list["SingleTarget", "Augassign", tilde_, alts["YieldExpr", sepp comma_ "StarExpression"]]],

-- annotated_rhs: yield_expr | star_expressions
  define "AnnotatedRhs" [
    "YieldExpr",
    sepp comma_ "StarExpression"],

-- augassign:
--     | '+='
--     | '-='
--     | '*='
--     | '@='
--     | '/='
--     | '%='
--     | '&='
--     | '|='
--     | '^='
--     | '<<='
--     | '>>='
--     | '**='
--     | '//='
  define "Augassign" [
    terminal "+=",
    terminal "-=",
    terminal "*=",
    terminal "@=",
    terminal "/=",
    terminal "%=",
    terminal "&=",
    terminal "|=",
    terminal "^=",
    terminal "<<=",
    terminal ">>=",
    terminal "**=",
    terminal "//="],

-- return_stmt:
--     | 'return' [star_expressions]
  define "ReturnStmt" [
    list [terminal "return", opt(sepp comma_ "StarExpression")]],

-- raise_stmt:
--     | 'raise' expression ['from' expression ]
--     | 'raise'
  define "RaiseStmt" [
    list[terminal "raise", "Expression", opt(list[terminal "from", "Expression"])],
    terminal "raise"],

-- global_stmt: 'global' ','.NAME+
  define "GlobalStmt" [
    list[terminal "global", comma_, dot_, plus(list[dot_, "Name"])]], -- Note: interpreting the above as (.NAME)+, not .(Name+)

-- nonlocal_stmt: 'nonlocal' ','.NAME+
  define "NonlocalStmt" [
    list[terminal "nonlocal", comma_, plus(list[dot_, "Name"])]], -- Note: interpreting the above as (.NAME)+, not .(Name+)

-- del_stmt:
--     | 'del' del_targets &(';' | NEWLINE)
  define "DelStmt" [
    list[terminal "del", "DelTargets"]],

-- yield_stmt: yield_expr
  define "YieldStmt" [
    "YieldExpr"],

-- assert_stmt: 'assert' expression [',' expression ]
  define "AssertStmt" [
    list[terminal "assert", "Expression", opt(list[comma_, "Expression"])]],

-- import_stmt: import_name | import_from
  define "ImportStmt" [
    "ImportName",
    "ImportFrom"],

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

-- import_name: 'import' dotted_as_names
  define "ImportName" [
    list[terminal "import", sep comma_ "DottedAsName"]],

-- # note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
-- import_from:
--     | 'from' ('.' | '...')* dotted_name 'import' import_from_targets
--     | 'from' ('.' | '...')+ 'import' import_from_targets
  define "ImportFrom" [
    list[terminal "from", star(alts[dot_, ellipsis_]), "DottedName", terminal "import", "ImportFromTargets"],
    list[terminal "from", plus(alts[dot_, ellipsis_]), terminal "import", "ImportFromTargets"]],

-- import_from_targets:
--     | '(' import_from_as_names [','] ')'
--     | import_from_as_names !','
--     | '*'
  define "ImportFromTargets" [
    list[open_paren_, "ImportFromAsNames", opt(comma_), close_paren_],
    "ImportFromAsNames"],

-- import_from_as_names:
--     | ','.import_from_as_name+
  define "ImportFromAsNames" [
    list[comma_, plus(list[dot_, "ImportFromAsName"])]], -- Note: interpreting the above as (.import_from_as_name)+, not .(import_from_as_name+)

-- import_from_as_name:
--     | NAME ['as' NAME ]
  define "ImportFromAsName" [
    list["Name", opt(list[terminal "as", "Name"])]],

-- dotted_as_names:
--     | ','.dotted_as_name+
-- dotted_as_name:
--     | dotted_name ['as' NAME ]
  define "DottedAsName" [
    list["DottedName", opt(list[terminal "as", "Name"])]],

-- dotted_name:
--     | dotted_name '.' NAME
--     | NAME
  define "DottedName" [
    sep dot_ "Name"],

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

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

-- block:
--     | NEWLINE INDENT statements DEDENT
--     | simple_stmts
  define "Block" [
    list[newline_, "Indent", star "Statement", "Dedent"],
    "SimpleStmts"],

-- decorators: ('@' named_expression NEWLINE )+
  define "Decorators" [
    plus(list[at_, "NamedExpression", newline_])],

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

-- class_def:
--     | decorators class_def_raw
--     | class_def_raw
  define "ClassDef" [
    list["Decorators", "ClassDefRaw"],
    "ClassDefRaw"],

-- class_def_raw:
--     | 'class' NAME ['(' [arguments] ')' ] ':' block
  define "ClassDefRaw" [
    list[terminal "class", "Name", opt(list[open_paren_, opt("Arguments"), close_paren_]), colon_, "Block"]],

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

-- function_def:
--     | decorators function_def_raw
--     | function_def_raw
  define "FunctionDef" [
    list["Decorators", "FunctionDefRaw"],
    "FunctionDefRaw"],

-- function_def_raw:
--     | 'def' NAME '(' [params] ')' ['->' expression ] ':' [func_type_comment] block
--     | ASYNC 'def' NAME '(' [params] ')' ['->' expression ] ':' [func_type_comment] block
  define "FunctionDefRaw" [
    list[
      opt(list["Async", terminal "def"]), "Name",
      open_paren_, opt("Parameters"), close_paren_,
      opt(list[right_arrow_, "Expression"]),
      colon_, opt("FuncTypeComment"), "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
  define "Parameters" [
    list["SlashNoDefault", star("ParamNoDefault"), star("ParamWithDefault"), opt("StarEtc")],
    list["SlashWithDefault", star("ParamWithDefault"), opt("StarEtc")],
    list[plus("ParamNoDefault"), star("ParamWithDefault"), opt("StarEtc")],
    list[plus("ParamWithDefault"), opt("StarEtc")],
    "StarEtc"],

-- # 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+ '/' &')'
  define "SlashNoDefault" [
    list[plus("ParamNoDefault"), slash_, opt(comma_)]],

-- slash_with_default:
--     | param_no_default* param_with_default+ '/' ','
--     | param_no_default* param_with_default+ '/' &')'
  define "SlashWithDefault" [
    list[star("ParamNoDefault"), plus("ParamWithDefault"), slash_, opt(comma_)]],

-- star_etc:
--     | '*' param_no_default param_maybe_default* [kwds]
--     | '*' param_no_default_star_annotation param_maybe_default* [kwds]
--     | '*' ',' param_maybe_default+ [kwds]
--     | kwds
  define "StarEtc" [
    list[star_, "ParamNoDefault", star("ParamMaybeDefault"), opt("Kwds")],
    list[star_, "ParamNoDefaultStarAnnotation", star("ParamMaybeDefault"), opt("Kwds")],
    list[star_, comma_, plus("ParamMaybeDefault"), opt("Kwds")],
    "Kwds"],

-- kwds:
--     | '**' param_no_default
  define "Kwds" [
    list[double_star_, "ParamNoDefault"]],

-- # 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? &')'
  define "ParamNoDefault" [
    list["Param", opt(comma_), opt("TypeComment")]],

-- param_no_default_star_annotation::r
--     | param_star_annotation ',' TYPE_COMMENT?
--     | param_star_annotation TYPE_COMMENT? &')'
  define "ParamNoDefaultStarAnnotation" [
    list["ParamStarAnnotation", opt(comma_), opt("TypeComment")]],

-- param_with_default:
--     | param default ',' TYPE_COMMENT?
--     | param default TYPE_COMMENT? &')'
  define "ParamWithDefault" [
    list["Param", "Default", opt(comma_), opt("TypeComment")]],

-- param_maybe_default:
--     | param default? ',' TYPE_COMMENT?
--     | param default? TYPE_COMMENT? &')'
  define "ParamMaybeDefault" [
    list["Param", opt("Default"), opt(comma_), opt("TypeComment")]],

-- param: NAME annotation?
  define "Param" [
    list["Name", opt("Annotation")]],

-- param_star_annotation: NAME star_annotation
  define "ParamStarAnnotation" [
    list["Name", "StarAnnotation"]],

-- annotation: ':' expression
  define "Annotation" [
    list[colon_, "Expression"]],

-- star_annotation: ':' star_expression
  define "StarAnnotation" [
    list[colon_, "StarExpression"]],

-- default: '=' expression  | invalid_default
  define "Default" [
    list[equal_, "Expression"]],
    -- "InvalidDefault"], -- TODO: invalid_default is referenced but not defined in the source grammar

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

-- if_stmt:
--     | 'if' named_expression ':' block elif_stmt
--     | 'if' named_expression ':' block [else_block]
  define "IfStmt" [
    list[terminal "if", "NamedExpression", colon_, "Block", alts["ElifStmt", "ElseBlock"]]],

-- elif_stmt:
--     | 'elif' named_expression ':' block elif_stmt
--     | 'elif' named_expression ':' block [else_block]
  define "ElifStmt" [
    list[terminal "elif", "NamedExpression", colon_, "Block", alts["ElifStmt", "ElseBlock"]]],

-- else_block:
--     | 'else' ':' block
  define "ElseBlock" [
    list[terminal "else", colon_, "Block"]],

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

-- while_stmt:
--     | 'while' named_expression ':' block [else_block]
  define "WhileStmt" [
    list[terminal "while", "NamedExpression", colon_, "Block", opt("ElseBlock")]],

-- # 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]
  define "ForStmt" [
    list[opt("Async"), terminal "for", sepp comma_ "StarTarget", terminal "in", tilde_, sepp comma_ "StarExpression", colon_, opt("TypeComment"), "Block", opt("ElseBlock")]],

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

-- with_stmt:
--     | 'with' '(' ','.with_item+ ','? ')' ':' block
--     | 'with' ','.with_item+ ':' [TYPE_COMMENT] block
--     | ASYNC 'with' '(' ','.with_item+ ','? ')' ':' block
--     | ASYNC 'with' ','.with_item+ ':' [TYPE_COMMENT] block
  define "WithStmt" [
    list[opt("Async"), terminal "with", alts[
      list[open_paren_, sepp comma_ "WithItem", close_paren_, colon_],
      list[sepp comma_ "WithItem", colon_, opt("TypeComment")]], "Block"]],

-- with_item:
--     | expression 'as' star_target &(',' | ')' | ':')
--     | expression
  define "WithItem" [
    list["Expression", opt(list[terminal "as", "StarTarget"])]],

-- # 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]
  define "TryStmt" [
    list[terminal "try", colon_, "Block", alts[
      "FinallyBlock",
      list[plus("ExceptBlock"), opt("ElseBlock"), opt("FinallyBlock")],
      list[plus("ExceptStarBlock"), opt("ElseBlock"), opt("FinallyBlock")]]]],

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

-- except_block:
--     | 'except' expression ['as' NAME ] ':' block
--     | 'except' ':' block
  define "ExceptBlock" [
    list[terminal "except", opt(list["Expression", opt(list[terminal "as", "Name"])]), colon_, "Block"]],

-- except_star_block:
--     | 'except' '*' expression ['as' NAME ] ':' block
  define "ExceptStarBlock" [
    list[terminal "except", star_, "Expression", opt(list[terminal "as", "Name"]), colon_, "Block"]],

-- finally_block:
--     | 'finally' ':' block
  define "FinallyBlock" [
    list[terminal "finally", colon_, "Block"]],

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

-- match_stmt:
--     | "match" subject_expr ':' NEWLINE INDENT case_block+ DEDENT
  define "MatchStmt" [
    list[terminal "match", "SubjectExpr", colon_, newline_, "Indent", plus("CaseBlock"), "Dedent"]],

-- subject_expr:
--     | star_named_expression ',' star_named_expressions?
--     | named_expression
  define "SubjectExpr" [
    list["StarNamedExpression", comma_, opt(sepp comma_ "StarNamedExpression")]],

-- case_block:
--     | "case" patterns guard? ':' block
  define "CaseBlock" [
    list[terminal "case", "Patterns", opt("Guard"), colon_, "Block"]],

-- guard: 'if' named_expression
  define "Guard" [
    list[terminal "if", "NamedExpression"]],

-- patterns:
--     | open_sequence_pattern
--     | pattern
  define "Patterns" [
    "OpenSequencePattern",
    "Pattern"],

-- pattern:
--     | as_pattern
--     | or_pattern
  define "Pattern" [
    "AsPattern",
    "OrPattern"],

-- as_pattern:
--     | or_pattern 'as' pattern_capture_target
  define "AsPattern" [
    list["OrPattern", terminal "as", "PatternCaptureTarget"]],

-- or_pattern:
--     | '|'.closed_pattern+
  define "OrPattern" [
    list[pipe_, sep dot_ "ClosedPattern"]],

-- closed_pattern:
--     | literal_pattern
--     | capture_pattern
--     | wildcard_pattern
--     | value_pattern
--     | group_pattern
--     | sequence_pattern
--     | mapping_pattern
--     | class_pattern
  define "ClosedPattern" [
    "LiteralPattern",
    "CapturePattern",
    "WildcardPattern",
    "ValuePattern",
    "GroupPattern",
    "SequencePattern",
    "MappingPattern",
    "ClassPattern"],

-- # Literal patterns are used for equality and identity constraints
-- literal_pattern:
--     | signed_number !('+' | '-')
--     | complex_number
--     | strings
--     | 'None'
--     | 'True'
--     | 'False'
  define "LiteralPattern" [
    "SignedNumber",
    "ComplexNumber",
    plus("String"),
    terminal "None",
    terminal "True",
    terminal "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
  define "ComplexNumber" [
    list["SignedRealNumber", alts[plus_, minus_], "ImaginaryNumber"]],

-- signed_number:
--     | NUMBER
--     | '-' NUMBER
  define "SignedNumber" [
    list[opt(minus_), "Number"]],

-- signed_real_number:
--     | real_number
--     | '-' real_number
  define "SignedRealNumber" [
    list[opt(minus_), "RealNumber"]],

-- real_number:
--     | NUMBER
  define "RealNumber" [
    "Number"],

-- imaginary_number:
--     | NUMBER
  define "ImaginaryNumber" [
    "Number"],

-- capture_pattern:
--     | pattern_capture_target
  define "CapturePattern" [
    "PatternCaptureTarget"],

-- pattern_capture_target:
--     | !"_" NAME !('.' | '(' | '=')
  define "PatternCaptureTarget" [
    "Name"],

-- wildcard_pattern:
--     | "_"
  define "WildcardPattern" [
    underscore_],

-- value_pattern:
--     | attr !('.' | '(' | '=')
  define "ValuePattern" [
    "Attr"],

-- attr:
--     | name_or_attr '.' NAME
  define "Attr" [
    list["NameOrAttr", dot_, "Name"]],

-- name_or_attr:
--     | attr
--     | NAME
  define "NameOrAttr" [
    "Attr",
    "Name"],

-- group_pattern:
--     | '(' pattern ')'
  define "GroupPattern" [
    list[open_paren_, "Pattern", close_paren_]],

-- sequence_pattern:
--     | '[' maybe_sequence_pattern? ']'
--     | '(' open_sequence_pattern? ')'
  define "SequencePattern" [
    list[open_square_, opt("MaybeSequencePattern"), close_square_],
    list[open_paren_, opt("MaybeSequencePattern"), close_paren_]],

-- open_sequence_pattern:
--     | maybe_star_pattern ',' maybe_sequence_pattern?
  define "OpenSequencePattern" [
    list["MaybeStarPattern", comma_, opt("MaybeSequencePattern")]],

-- maybe_sequence_pattern:
--     | ','.maybe_star_pattern+ ','?
  define "MaybeSequencePattern" [
    seq comma_ "MaybeStarPattern"],

-- maybe_star_pattern:
--     | star_pattern
--     | pattern
  define "MaybeStarPattern" [
    "StarPattern",
    "Pattern"],

-- star_pattern:
--     | '*' pattern_capture_target
--     | '*' wildcard_pattern
  define "StarPattern" [
    list[star_, alts["PatternCaptureTarget", "WildcardPattern"]]],

-- mapping_pattern:
--     | '{' '}'
--     | '{' double_star_pattern ','? '}'
--     | '{' items_pattern ',' double_star_pattern ','? '}'
--     | '{' items_pattern ','? '}'
  define "MappingPattern" [
    "empty">: list[open_curly_, close_curly_],
    list[open_curly_, "DoubleStarPattern", opt(comma_), close_curly_],
    list[open_curly_, "ItemsPattern", comma_, "DoubleStarPattern", opt(comma_), close_curly_],
    list[open_curly_, "ItemsPattern", opt(comma_), close_curly_]],

-- items_pattern:
--     | ','.key_value_pattern+
  define "ItemsPattern" [
    sep comma_ "KeyValuePattern"],

-- key_value_pattern:
--     | (literal_expr | attr) ':' pattern
  define "KeyValuePattern" [
    list[alts["LiteralPattern", "Attr"], colon_, "Pattern"]],

-- double_star_pattern:
--     | '**' pattern_capture_target
  define "DoubleStarPattern" [
    list[double_star_, "PatternCaptureTarget"]],

-- class_pattern:
--     | name_or_attr '(' ')'
--     | name_or_attr '(' positional_patterns ','? ')'
--     | name_or_attr '(' keyword_patterns ','? ')'
--     | name_or_attr '(' positional_patterns ',' keyword_patterns ','? ')'
  define "ClassPattern" [
    list["NameOrAttr", open_paren_, alts[
      nil,
      list[seq comma_ "Pattern", opt(comma_)],
      list[seq comma_ "KeywordPattern", opt(comma_)],
      list[seq comma_ "Pattern", comma_, seq comma_ "KeywordPattern", opt(comma_)]
    ], close_paren_]],

-- positional_patterns:
--     | ','.pattern+
-- keyword_patterns:
--     | ','.keyword_pattern+
-- keyword_pattern:
--     | NAME '=' pattern
  define "KeywordPattern" [
    list["Name", equal_, "Pattern"]],
    
-- # EXPRESSIONS
-- # -----------

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

-- expression:
--     | disjunction 'if' disjunction 'else' expression
--     | disjunction
--     | lambdef
  define "Expression" [
    list["Disjunction", terminal "if", "Disjunction", terminal "else", "Expression"],
    "Disjunction",
    "Lambdef"],
    
-- yield_expr:
--     | 'yield' 'from' expression
--     | 'yield' [star_expressions]
  define "YieldExpr" [
    list[terminal "yield", alts[list[terminal "from", "Expression"], opt(sepp comma_ "StarExpression")]]],
    
-- star_expressions:
--     | star_expression (',' star_expression )+ [',']
--     | star_expression ','
--     | star_expression

-- star_expression:
--     | '*' bitwise_or
--     | expression
  define "StarExpression" [
    list[star_, "BitwiseOr"],
    "Expression"],
    
-- star_named_expressions: ','.star_named_expression+ [',']
--
-- star_named_expression:
--     | '*' bitwise_or
--     | named_expression
  define "StarNamedExpression" [
    list[star_, "BitwiseOr"],
    "NamedExpression"],
    
-- assignment_expression:
--     | NAME ':=' ~ expression
  define "AssignmentExpression" [
    list["Name", assign_, tilde_, "Expression"]],
    
-- named_expression:
--     | assignment_expression
--     | expression !':='
  define "NamedExpression" [
    "AssignmentExpression",
    "Expression"],
    
-- disjunction:
--     | conjunction ('or' conjunction )+
--     | conjunction
  define "Disjunction" [
    sep (terminal "or") "Conjunction"],
 
-- conjunction:
--     | inversion ('and' inversion )+
--     | inversion
  define "Conjunction" [
    sep (terminal "and") "Inversion"],
    
-- inversion:
--     | 'not' inversion
--     | comparison
  define "Inversion" [
    list[terminal "not", "Inversion"],
    "Comparison"],
    
-- # Comparison operators
-- # --------------------

-- comparison:
--     | bitwise_or compare_op_bitwise_or_pair+
--     | bitwise_or
  define "Comparison" [
    list["BitwiseOr", star("CompareOpBitwiseOrPair")]],
    
-- 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
  define "CompareOpBitwiseOrPair" [
    "EqBitwiseOr",
    "NoteqBitwiseOr",
    "LteBitwiseOr",
    "LtBitwiseOr",
    "GteBitwiseOr",
    "GtBitwiseOr",
    "NotinBitwiseOr",
    "InBitwiseOr",
    "IsnotBitwiseOr",
    "IsBitwiseOr"],
    
-- eq_bitwise_or: '==' bitwise_or
  define "EqBitwiseOr" [
    list[double_equal_, "BitwiseOr"]],
    
-- noteq_bitwise_or:
--     | ('!=' ) bitwise_or
  define "NoteqBitwiseOr" [
    list[noteq_, "BitwiseOr"]],

-- lte_bitwise_or: '<=' bitwise_or
  define "LteBitwiseOr" [
    list[lte_, "BitwiseOr"]],

-- lt_bitwise_or: '<' bitwise_or
  define "LtBitwiseOr" [
    list[lt_, "BitwiseOr"]],

-- gte_bitwise_or: '>=' bitwise_or
  define "GteBitwiseOr" [
    list[gte_, "BitwiseOr"]],

-- gt_bitwise_or: '>' bitwise_or
  define "GtBitwiseOr" [
    list[gt_, "BitwiseOr"]],

-- notin_bitwise_or: 'not' 'in' bitwise_or
  define "NotinBitwiseOr" [
    list[terminal "not", terminal "in", "BitwiseOr"]],

-- in_bitwise_or: 'in' bitwise_or
  define "InBitwiseOr" [
    list[terminal "in", "BitwiseOr"]],

-- isnot_bitwise_or: 'is' 'not' bitwise_or
  define "IsnotBitwiseOr" [
    list[terminal "is", terminal "not", "BitwiseOr"]],

-- is_bitwise_or: 'is' bitwise_or
  define "IsBitwiseOr" [
    list[terminal "is", "BitwiseOr"]],

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

-- bitwise_or:
--     | bitwise_or '|' bitwise_xor
--     | bitwise_xor
  define "BitwiseOr" [
    list[opt(list["BitwiseOr", pipe_]), "BitwiseXor"]],

-- bitwise_xor:
--     | bitwise_xor '^' bitwise_and
--     | bitwise_and
  define "BitwiseXor" [
    list[opt(list["BitwiseXor", hat_]), "BitwiseAnd"]],
    
-- bitwise_and:
--     | bitwise_and '&' shift_expr
--     | shift_expr
  define "BitwiseAnd" [
    list[opt(list["BitwiseAnd", amp_]), "ShiftExpr"]],
    
-- shift_expr:
--     | shift_expr '<<' sum
--     | shift_expr '>>' sum
--     | sum
  define "ShiftExpr" [
    list[
      opt(list["ShiftExpr", alts[double_lt_, double_gt_]]),
      "Sum"]],
    
-- # Arithmetic operators
-- # --------------------

-- sum:
--     | sum '+' term
--     | sum '-' term
--     | term
  define "Sum" [
    list[opt(list["Sum", alts[plus_, minus_]]), "Term"]],
    
-- term:
--     | term '*' factor
--     | term '/' factor
--     | term '//' factor
--     | term '%' factor
--     | term '@' factor
--     | factor
  define "Term" [
    list[opt(list["Term", alts[star_, slash_, double_slash_, percent_, at_]]), "Factor"]],
    
-- factor:
--     | '+' factor
--     | '-' factor
--     | '~' factor
--     | power
  define "Factor" [
    list[alts[plus_, minus_, tilde_], "Factor"],
    "Power"],

-- power:
--     | await_primary '**' factor
--     | await_primary
  define "Power" [
    list["AwaitPrimary", opt(list[double_star_, "Factor"])]],

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

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

-- await_primary:
--     | AWAIT primary
--     | primary
  define "AwaitPrimary" [
    list[opt("Await"), "Primary"]],

-- primary:
--     | primary '.' NAME
--     | primary genexp
--     | primary '(' [arguments] ')'
--     | primary '[' slices ']'
--     | atom
  define "Primary" [
    list["Primary", alts[
      list[dot_, "Name"],
      "Genexp",
      list[open_paren_, opt("Arguments"), close_paren_],
      list[open_square_, "Slices", close_square_]]],
    "Atom"],

-- slices:
--     | slice !','
--     | ','.(slice | starred_expression)+ [',']
  define "Slices" [
    "Slice",
    sepp comma_ (alts["Slice", "StarredExpression"])],

-- slice:
--     | [expression] ':' [expression] [':' [expression] ]
--     | named_expression
  define "Slice" [
    list[opt("Expression"), colon_, opt("Expression"), opt(list[colon_, opt("Expression")])],
    "NamedExpression"],

-- atom:
--     | NAME
--     | 'True'
--     | 'False'
--     | 'None'
--     | strings
--     | NUMBER
--     | (tuple | group | genexp)
--     | (list | listcomp)
--     | (dict | set | dictcomp | setcomp)
--     | '...'
  define "Atom" [
    "Name",
    terminal "True",
    terminal "False",
    terminal "None",
    plus("String"),
    "Number",
    "Tuple",
    "Group",
    "Genexp",
    "List",
    "Listcomp",
    "Dict",
    "Set",
    "Dictcomp",
    "Setcomp",
    ellipsis_],

-- group:
--     | '(' (yield_expr | named_expression) ')'
  define "Group" [
    list[open_paren_, alts["YieldExpr", "NamedExpression"], close_paren_]],

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

-- lambdef:
--     | 'lambda' [lambda_params] ':' expression
  define "Lambdef" [
    list[terminal "lambda", opt("LambdaParameters"), colon_, "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
  define "LambdaParameters" [
    list["LambdaSlashNoDefault", star("LambdaParamNoDefault"), star("LambdaParamWithDefault"), opt("LambdaStarEtc")],
    list["LambdaSlashWithDefault", star("LambdaParamWithDefault"), opt("LambdaStarEtc")],
    list[plus("LambdaParamNoDefault"), star("LambdaParamWithDefault"), opt("LambdaStarEtc")],
    list[plus("LambdaParamWithDefault"), opt("LambdaStarEtc")],
    "LambdaStarEtc"],

-- lambda_slash_no_default:
--     | lambda_param_no_default+ '/' ','
--     | lambda_param_no_default+ '/' &':'
  define "LambdaSlashNoDefault" [
    list[plus("LambdaParamNoDefault"), slash_, opt(comma_)]],

-- lambda_slash_with_default:
--     | lambda_param_no_default* lambda_param_with_default+ '/' ','
--     | lambda_param_no_default* lambda_param_with_default+ '/' &':'
  define "LambdaSlashWithDefault" [
    list[star("LambdaParamNoDefault"), plus("LambdaParamWithDefault"), slash_, opt(comma_)]],

-- lambda_star_etc:
--     | '*' lambda_param_no_default lambda_param_maybe_default* [lambda_kwds]
--     | '*' ',' lambda_param_maybe_default+ [lambda_kwds]
--     | lambda_kwds
  define "LambdaStarEtc" [
    list[star_, "LambdaParamNoDefault", star("LambdaParamMaybeDefault"), opt("LambdaKwds")],
    list[star_, comma_, plus("LambdaParamMaybeDefault"), opt("LambdaKwds")],
    "LambdaKwds"],

-- lambda_kwds:
--     | '**' lambda_param_no_default
  define "LambdaKwds" [
    list[double_star_, "LambdaParamNoDefault"]],

-- lambda_param_no_default:
--     | lambda_param ','
--     | lambda_param &':'
  define "LambdaParamNoDefault" [
    list["LambdaParam", opt(comma_)]],

-- lambda_param_with_default:
--     | lambda_param default ','
--     | lambda_param default &':'
  define "LambdaParamWithDefault" [
    list["LambdaParam", "Default", opt(comma_)]],

-- lambda_param_maybe_default:
--     | lambda_param default? ','
--     | lambda_param default? &':'
  define "LambdaParamMaybeDefault" [
    list["LambdaParam", opt("Default"), opt(comma_)]],

-- lambda_param: NAME
  define "LambdaParam" [
    "Name"],

-- # LITERALS
-- # ========

-- strings: STRING+

-- list:
--     | '[' [star_named_expressions] ']'
  define "List" [
    list[open_square_, sepp comma_ "StarNamedExpression", close_square_]],

-- tuple:
--     | '(' [star_named_expression ',' [star_named_expressions]  ] ')'
  define "Tuple" [
    list[open_paren_, opt(sepp comma_ "StarNamedExpression"), close_paren_]],

-- set: '{' star_named_expressions '}'
  define "Set" [
    list[open_curly_, sepp comma_ "StarNamedExpression", close_curly_]],

-- # Dicts
-- # -----

-- dict:
--     | '{' [double_starred_kvpairs] '}'
  define "Dict" [
    list[open_curly_, opt(sepp comma_ "DoubleStarredKvpair"), close_curly_]],

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

-- double_starred_kvpair:
--     | '**' bitwise_or
--     | kvpair
  define "DoubleStarredKvpair" [
    list[double_star_, "BitwiseOr"],
    "Kvpair"],

-- kvpair: expression ':' expression
  define "Kvpair" [
    list["Expression", colon_, "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 )*
  define "ForIfClause" [
    list[opt("Async"), terminal "for", sepp comma_ "StarTarget", terminal "in", tilde_, "Disjunction", star(list[terminal "if", "Disjunction"])]],

-- listcomp:
--     | '[' named_expression for_if_clauses ']'
  define "Listcomp" [
    list[open_square_, "NamedExpression", plus("ForIfClause"), close_square_]],
    
-- setcomp:
--     | '{' named_expression for_if_clauses '}'
  define "Setcomp" [
    list[open_curly_, "NamedExpression", plus("ForIfClause"), close_curly_]],
    
-- genexp:
--     | '(' ( assignment_expression | expression !':=') for_if_clauses ')'
  define "Genexp" [
    list[open_paren_, alts["AssignmentExpression", "Expression"], plus("ForIfClause"), close_paren_]],

-- dictcomp:
--     | '{' kvpair for_if_clauses '}'
  define "Dictcomp" [
    list[open_curly_, "Kvpair", plus("ForIfClause"), close_curly_]],
    
-- # FUNCTION CALL ARGUMENTS
-- # =======================
--
-- arguments:
--     | args [','] &')'
  define "Arguments" [
    list["Args", opt(comma_)]],

-- args:
--     | ','.(starred_expression | ( assignment_expression | expression !':=') !'=')+ [',' kwargs ]
--     | kwargs
  define "Args" [
    list[sep comma_ (alts["StarredExpression", "AssignmentExpression", "Expression"]), opt(list[comma_, "Kwargs"])],
    "Kwargs"],

-- kwargs:
--     | ','.kwarg_or_starred+ ',' ','.kwarg_or_double_starred+
--     | ','.kwarg_or_starred+
--     | ','.kwarg_or_double_starred+
  define "Kwargs" [
    list[comma_, alts[
      list[plus(list[dot_, "KwargOrStarred"]), opt(list[comma_, comma_, plus(list[dot_, "KwargOrDoubleStarred"])])],
      plus(list[dot_, "KwargOrDoubleStarred"])]]],

-- starred_expression:
--     | '*' expression
  define "StarredExpression" [
    list[star_, "Expression"]],

-- kwarg_or_starred:
--     | NAME '=' expression
--     | starred_expression
  define "KwargOrStarred" [
    list["Name", equal_, "Expression"],
    "StarredExpression"],

-- kwarg_or_double_starred:
--     | NAME '=' expression
--     | '**' expression
  define "KwargOrDoubleStarred" [
    list["Name", equal_, "Expression"],
    list[double_star_, "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+ [',']
  define "StarTargetsListSeq" [
    sepp comma_ (plus(list[dot_, "StarTarget"]))],

-- star_targets_tuple_seq:
--     | star_target (',' star_target )+ [',']
--     | star_target ','
  define "StarTargetsTupleSeq" [
    sepp comma_ (plus("StarTarget"))],

-- star_target:
--     | '*' (!'*' star_target)
--     | target_with_star_atom
  define "StarTarget" [
    list[star_, "StarTarget"],
    "TargetWithStarAtom"],

-- target_with_star_atom:
--     | t_primary '.' NAME !t_lookahead
--     | t_primary '[' slices ']' !t_lookahead
--     | star_atom
  define "TargetWithStarAtom" [
    list["TPrimary", dot_, "Name"],
    list["TPrimary", open_square_, "Slices", close_square_],
    "StarAtom"],

-- star_atom:
--     | NAME
--     | '(' target_with_star_atom ')'
--     | '(' [star_targets_tuple_seq] ')'
--     | '[' [star_targets_list_seq] ']'
  define "StarAtom" [
    "Name",
    list[open_paren_, "TargetWithStarAtom", close_paren_],
    list[open_paren_, "StarTargetsTupleSeq", close_paren_],
    list[open_square_, "StarTargetsListSeq", close_square_]],

-- single_target:
--     | single_subscript_attribute_target
--     | NAME
--     | '(' single_target ')'
  define "SingleTarget" [
    "SingleSubscriptAttributeTarget",
    "Name",
    list[open_paren_, "SingleTarget", close_paren_]],

-- single_subscript_attribute_target:
--     | t_primary '.' NAME !t_lookahead
--     | t_primary '[' slices ']' !t_lookahead
  define "SingleSubscriptAttributeTarget" [
    list["TPrimary", dot_, "Name"],
    list["TPrimary", open_square_, "Slices", close_square_]],

-- 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
  define "TPrimary" [
    list["TPrimary", dot_, "Name"],
    list["TPrimary", open_square_, "Slices", close_square_],
    list["TPrimary", "Genexp"],
    list["TPrimary", open_paren_, opt("Arguments"), close_paren_],
    "Atom"],

-- t_lookahead: '(' | '[' | '.'
  define "TLookahead" [
    open_paren_, open_square_, dot_],

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

-- del_targets: ','.del_target+ [',']
  define "DelTargets" [
    list["TPrimary", dot_, "Name"],
    list["TPrimary", open_square_, "Slices", close_square_],
    "DelTAtom"],

-- del_target:
--     | t_primary '.' NAME !t_lookahead
--     | t_primary '[' slices ']' !t_lookahead
--     | del_t_atom
  define "DelTarget" [
    list["TPrimary", dot_, "Name"],
    list["TPrimary", open_square_, "Slices", close_square_],
    "DelTAtom"],

-- del_t_atom:
--     | NAME
--     | '(' del_target ')'
--     | '(' [del_targets] ')'
--     | '[' [del_targets] ']'
  define "DelTAtom" [
    "Name",
    list[open_paren_, "DelTarget", close_paren_],
    list[open_paren_, opt("DelTargets"), close_paren_],
    list[open_square_, opt("DelTargets"), close_square_]],

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

-- # type_expressions allow */** but ignore them
-- type_expressions:
--     | ','.expression+ ',' '*' expression ',' '**' expression
--     | ','.expression+ ',' '*' expression
--     | ','.expression+ ',' '**' expression
--     | '*' expression ',' '**' expression
--     | '*' expression
--     | '**' expression
--     | ','.expression+
  define "TypeExpressions" [
    list[sepp comma_ "Expression", star_, "Expression", opt(list[comma_, double_star_, "Expression"])],
    list[sepp comma_ "Expression", star_, double_star_, "Expression"],
    list[star_, "Expression", opt(list[comma_, double_star_, "Expression"])],
    list[double_star_, "Expression"],
    list[sepp comma_ "Expression"]],

-- func_type_comment:
--     | NEWLINE TYPE_COMMENT &(NEWLINE INDENT)   # Must be followed by indented block
--     | TYPE_COMMENT
  define "FuncTypeComment" [
    list[opt(newline_), "TypeComment"]]]