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 parsePython 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
#
# 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 ')'
| attribute_or_subscript_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] '='