::=
—— “定义为”<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 意思是: <digit> 可以被替换成 0 或 1 或 … 或 9 中的任意一个。
<word>
—— 非终结符(还要再展开)举例:<expr> ::= <term> { ("+" | "-") <term> } <term> ::= <factor> { ("*" | "/") <factor> } <factor> ::= <digit> | "(" <expr> ")" <digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 解析 2 + 3 * 4 时: 从非终结符 <expr> 开始; 一路展开 <term>、<factor>、<digit> … 最终全部替换成终结符 2、+、3、*、4,解析结束。
|
—— 或者<bool> ::= true | false 只要看到 | 就想到:“左边符号可以替换成竖线分隔的任意一项。”
[]
—— 可选(0 或 1 次){}
—— 重复(0 或多次)()
—— 分组[] —— 可选(出现 0 次 或 1 次) 例 <整数> ::= ["+" | "-"] <数字> {<数字>} 表示正负号可以要,也可以不要。 {} —— 重复(0 次或多次,即“零个或多个”) 例 <数字串> ::= <数字> {<数字>} 表示至少一个数字,后面还能跟任意多个数字。 () —— 分组(把一堆符号当成一个整体,再在外面加量词) 例 <表达式> ::= <项> { ("+" | "-") <项> } 括号把 ("+" | "-") <项> 视为一个整体,再整体重复 0+ 次。 记忆口诀(对照正则) [] ➜ 相当于 ? {} ➜ 相当于 * () ➜ 就是普通分组 看到它们就立即想到:可选、重复、整体打包
super
说明书翻译成 BNF 就是:super_call ::= "super" "(" [cls_argument [, instance_argument]] ")"
super()
整个叫 super_call;[]
包住了。语法规则 | 意思 | |||
---|---|---|---|---|
`digit ::= "0" | "1" | … | "9"` | 数字是哪几个字符 |
`integer ::= ["+" | "-"] {digit}` | 整数 = 可选正负号 + 一串数字 | ||
if_stmt ::= "if" condition ":" suite ["elif" condition ":" suite]* ["else" ":" suite]? |
if/elif/else 结构 |
函数调用(call) ebnf call ::= <name> "(" [ <arglist> ] ")" arglist ::= <argument> { "," <argument> } argument ::= <name> | <number> | <string> call 对应源码里的一次函数调用:名字 + 圆括号,括号里可以空着([] 表示可选),也可以给参数列表。 例:foo()、bar(1, a, "ok") arglist 用 {} 表示“0 个或多个”由逗号分隔的 <argument>,所以能写任意个数实参。 例:1, a, "x" 三段就是 {} 重复两次。 argument 目前只列出 3 种原子实参:变量名、数字字面量、字符串字面量。 (完整 Python 还能放 *args、**kwargs、表达式等,这里被简化掉。)
listcomp ::= "[" <expr> <for_clause> { <comp_clause> } "]"
dictcomp ::= "{" <expr> ":" <expr> <for_clause> { <comp_clause> } "}"
<for_clause>
是必须出现的(没有 []
包),因为推导式至少要有一个 for
。{ <comp_clause> }
表示后面还能再跟任意多个额外的 for …
或 if …
子句。[x for x in L if x > 0]
里 {}
只重复 1 次(一个 if
);[y for x in data if x for y in x]
里 {}
重复 2 次(第二个 for
)。
for_clause ::= "for" <name> "in" <iterable>
comp_clause ::= ("for" <name> "in" <iterable>) | ("if" <condition>)
for_clause
for 变量 in 可迭代对象:
这一整段。comp_clause
()
把两个候选包起来,再用 |
给出多选一:for … in …
,要么来一个 if 条件
。for
和 if
,顺序任意。
<name>
也是非终结符,同样需要进一步展开!<word>
当例子,但 所有被 < >
包起来的符号 都归到“非终结符”这一桶里,不管它叫 <word>
、<name>
还是 <iterable>
。for_clause ::= "for" <name> "in" <iterable> # 当前这一层 ↓ <name> ::= <identifier> # 还要继续往下拆 <iterable> ::= <expr> # 可能再展开成一串规则
< >
包住,就是非终结符,都需要再展开;<word>
, <name>
, <foo>
, <bar>
) 规则一样;< >
全部消失,只剩下源码里能直接出现的字符/关键字。< >
非终结符早被编译器/文档生成器彻底展开完毕;Grammar/python.gram
的同源文档)。for_clause: | 'for' star_targets 'in' ~ disjunction ':' [TYPE_COMMENT] block [else_block]
star_targets
、disjunction
、block
… 直到出现纯关键字或符号('for'、'in'、':' …)就算终结。for_stmt: str | Iterable[str] -> None
'for'
、':'
、','
、'**'
→ 终结<Name>
、<Number>
、<String>
→ 终结(词法记号)for_clause
、arglist
、power
→ 非终结,还能点进去< >
——日常编码根本不用管。