Considering an example of if 1 == 1,2:
which should cause SyntaxError
, following the full grammar:
if 1 == 1,2:
Using the if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
, we get to shift the if
keyword and start parsing 1 == 1,2:
For the test
rule, only first production matches:
test: or_test ['if' or_test 'else' test] | lambdef
Then we get:
or_test: and_test ('or' and_test)*
And step down into and_test
:
and_test: not_test ('and' not_test)*
Here we just step into not_test
at the moment:
not_test: 'not' not_test | comparison
Notice, our input is 1 == 1,2:
, thus the first production doesn't match and we check the other one: (1)
comparison: expr (comp_op expr)*
Continuing on stepping down (we take the only the first non-terminal as the zero-or-more star requires a terminal we don't have at all in our input):
expr: xor_expr ('|' xor_expr)*
xor_expr: and_expr ('^' and_expr)*
and_expr: shift_expr ('&' shift_expr)*
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power
Now we use the power
production:
power: atom trailer* ['**' factor]
atom: ('(' [yield_expr|testlist_comp] ')' |
'[' [testlist_comp] ']' |
'{' [dictorsetmaker] '}' |
NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')
And shift NUMBER
(1
in our input) and reduce. Now we are back at (1) with input ==1,2:
to parse. ==
matches comp_op
:
comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
So we shift it and reduce, leaving us with input 1,2:
(current parsing output is NUMBER comp_op
, we need to match expr
now). We repeat the process for the left-hand side, going straight to the atom
nonterminal and selecting the NUMBER
production. Shift and reduce.
Since ,
does not match any comp_op
we reduce the test
non-terminal and receive 'if' NUMBER comp_op NUMBER
. We need to match else
, elif
or :
now, but we have ,
so we fail with SyntaxError
.