::= —— “定义为”<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_clausefor 变量 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 → 非终结,还能点进去< >——日常编码根本不用管。