pythonbnf

Python Assignment statement


I was going through the python assignment statement docs .

Here python uses below Backus–Naur form for assignment statements.

assignment_stmt ::=  (target_list "=")+ (starred_expression | yield_expression)
target_list     ::=  target ("," target)* [","]
target          ::=  identifier
                     | "(" [target_list] ")"
                     | "[" [target_list] "]"
                     | attributeref
                     | subscription
                     | slicing
                     | "*" target

Where as starred_expression is in Backus-Naur Form is

starred_expression ::=  expression | (starred_item ",")* [starred_item]
starred_item       ::=  assignment_expression | "*" or_expr

and yield_expression in Backus-Naur Form is

yield_atom       ::=  "(" yield_expression ")"
yield_expression ::=  "yield" [expression_list | "from" expression]

After recursively going through all those related backnaur form of each sub expression given above. I am still scratching my head how does simple assignment like a=9 can fit into above back naur form. Specially how does the 9, on the RHS of the given statement can fall into yield_expression or starred_exression


Solution

  • Isn't it right here?

    starred_expression ::=  expression | …
    

    A starred_expression can be just an expression. It must be the case that expression encompasses numeric literals like 9.

    (Edited for clarity following comments.)

    UPDATE

    Here is the full line from starred_expression to 9.

    starred_expression     ::=  expression | (starred_item ",")* [starred_item]
    expression             ::=  conditional_expression | lambda_expr
    conditional_expression ::=  or_test ["if" or_test "else" expression]
    or_test                ::=  and_test | or_test "or" and_test
    and_test               ::=  not_test | and_test "and" not_test
    not_test               ::=  comparison | "not" not_test
    comparison             ::=  or_expr (comp_operator or_expr)*
    or_expr                ::=  xor_expr | or_expr "|" xor_expr
    xor_expr               ::=  and_expr | xor_expr "^" and_expr
    and_expr               ::=  shift_expr | and_expr "&" shift_expr
    shift_expr             ::=  a_expr | shift_expr ("<<" | ">>") a_expr
    a_expr                 ::=  m_expr | a_expr "+" m_expr | a_expr "-" m_expr
    m_expr                 ::=  u_expr | m_expr "*" u_expr | m_expr "@" m_expr |
                                  m_expr "//" u_expr | m_expr "/" u_expr |
                                  m_expr "%" u_expr
    u_expr                 ::=  power | "-" u_expr | "+" u_expr | "~" u_expr
    power                  ::=  (await_expr | primary) ["**" u_expr]
    primary                ::=  atom | attributeref | subscription | slicing | call
    atom                   ::=  identifier | literal | enclosure
    literal                ::=  stringliteral | bytesliteral
                                  | integer | floatnumber | imagnumber
    integer                ::=  decinteger | bininteger | octinteger | hexinteger
    decinteger             ::=  nonzerodigit (["_"] digit)* | "0"+ (["_"] "0")*
    nonzerodigit           ::=  "1"..."9"
    

    What makes it confusing is that for every element from conditional_expression down to power, the thing that makes it look like the "thing it is" is optional!

    For instance, in power, the ** operator is actually not even required. So we think of 2**16 as a power, but 2 also qualifies as a power. Similarly for or_test, an or keyword is not actually required.

    It works like that all the way up. For every line, 9 satisfies the simplest version of the syntactic element with none of the optional parts included.