首页 新闻 会员 周边 捐助

BNF/语法手册

0
[已解决问题] 解决于 2025-09-07 17:07
BNF = Backus–Naur Form(巴科斯-瑙尔范式)
1960 年为 ALGOL 60 设计,现在几乎所有语言规范都用它(或扩展版 EBNF)来描述“语法长什么样”。
核心记号就三个:
  1. ::= —— “定义为”
    1. <digit>  ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
      意思是:
      <digit> 可以被替换成 01 或 … 或 9 中的任意一个。

        

  2. <word> —— 非终结符(还要再展开)举例:
  3. <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,解析结束。

     

  4. | —— 或者
  5. <bool> ::= true | false
    只要看到 | 就想到:“左边符号可以替换成竖线分隔的任意一项。”

     

外加三个常用元符号(EBNF 扩展):
  • [] —— 可选(0 或 1 次)
  • {} —— 重复(0 或多次)
  • () —— 分组
[] —— 可选(出现 0 次 或 1 次)
例
<整数> ::= ["+" | "-"] <数字> {<数字>}
表示正负号可以要,也可以不要。
{} —— 重复(0 次或多次,即“零个或多个”)
例
<数字串> ::= <数字> {<数字>}
表示至少一个数字,后面还能跟任意多个数字。
() —— 分组(把一堆符号当成一个整体,再在外面加量词)
例
<表达式> ::= <项> { ("+" | "-") <项> }
括号把 ("+" | "-") <项> 视为一个整体,再整体重复 0+ 次。
记忆口诀(对照正则)
[] ➜ 相当于 ?
{} ➜ 相当于 *
() ➜ 就是普通分组
看到它们就立即想到:可选、重复、整体打包

 


把 Python 的 super 说明书翻译成 BNF 就是:

super_call ::= "super" "(" [cls_argument [, instance_argument]] ")"

 
读法:
super() 整个叫 super_call
圆括号里 cls_argumentinstance_argument 两段可以同时省、省一个、或都不省——因为被 [] 包住了。

再举几个一眼能看懂的例子:
表格
复制
语法规则意思
`digit ::= "0" "1" "9"` 数字是哪几个字符
`integer ::= ["+" "-"] {digit}` 整数 = 可选正负号 + 一串数字
if_stmt ::= "if" condition ":" suite ["elif" condition ":" suite]* ["else" ":" suite]? if/elif/else 结构

一句话:
BNF/EBNF 就是**“语言的铁路图”**——用符号告诉你哪个词能省、哪个能重复,本身不是代码,千万别照抄进程序
_java_python的主页 _java_python | 小虾三级 | 园豆:984
提问于:2025-09-07 16:59
< >
分享
最佳答案
0
函数调用(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、表达式等,这里被简化掉。)


  1. 列表 / 字典推导式(listcomp | dictcomp)
ebnf
 
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)。
  •  

 

  1. 子句定义(for_clause | comp_clause)
ebnf
 
for_clause ::= "for" <name> "in" <iterable>
comp_clause ::= ("for" <name> "in" <iterable>) | ("if" <condition>)
  • for_clause
    对应源码里的 for 变量 in 可迭代对象: 这一整段。
  • comp_clause
    () 把两个候选包起来,再用 | 给出多选一:
    要么再来一个 for … in …,要么来一个 if 条件
    因此推导式可以无限级联 forif,顺序任意。
  •  

 

 

<name> 也是非终结符,同样需要进一步展开!
我刚才只举了 <word> 当例子,但 所有被 < > 包起来的符号 都归到“非终结符”这一桶里,不管它叫 <word><name> 还是 <iterable>

所以完整流程应该是:
ebnf
复制
for_clause ::= "for" <name> "in" <iterable> # 当前这一层 ↓ <name> ::= <identifier> # 还要继续往下拆 <iterable> ::= <expr> # 可能再展开成一串规则
 
直到某一步右边再也不出现尖括号,才算到达终结符(真正的关键字、运算符、字面量等)。

一句话总结
  • 只要被 < > 包住,就是非终结符,都需要再展开;
  • 名字无所谓 (<word>, <name>, <foo>, <bar>) 规则一样;
  • 最终目标是让所有 < > 全部消失,只剩下源码里能直接出现的字符/关键字。
 
日常写代码时,鼠标悬浮看到的“帮助”已经是人类友好的终极签名,所有 < > 非终结符早被编译器/文档生成器彻底展开完毕;
我们要“拆”只是为了读懂语法手册,而不是去拆 IDE 提示。

一、官方手册里到底怎么“拆”
Python 的权威语法写在
https://docs.python.org/3/reference/grammar.html
这是完全展开的 BNF 文件(CPython 源码 Grammar/python.gram 的同源文档)。
例子:把“for_clause”一路点到头:
复制
for_clause: | 'for' star_targets 'in' ~ disjunction ':' [TYPE_COMMENT] block [else_block]
 
再往下点 star_targetsdisjunctionblock … 直到出现纯关键字或符号('for'、'in'、':' …)就算终结。

二、实战:悬浮提示 ← 怎么来的
PyCharm/VSCode 用的 language server 背后就是同一份 grammar + 静态分析库;
它把 BNF 展开、填好类型、默认值、文档字符串,最后生成你看到的那一行:
Python
复制
for_stmt: str | Iterable[str] -> None
 
所有尖括号已被吃掉,所以:
  • 鼠标悬浮 = 终结版签名,不需要你再拆;
  • 想“拆”只有两种场景
    1. 读官方语言规范(链接上面)做语法研究;
    2. 自己写解析器/AST 工具,需要知道每一层非终结符。

三、快速判断“我还能不能再拆”的小技巧
看到提示里还有英文单词不是关键字/字面量,就继续点:
  • 关键字/符号:'for'':'',''**' → 终结
  • 大写首字母:<Name><Number><String> → 终结(词法记号)
  • 小写驼峰:for_clausearglistpower → 非终结,还能点进去

一句话
鼠标提示已经是“拆到底”的成品;
只有去翻官方 grammar 网页才需要你自己一层层展开 < >——日常编码根本不用管。
_java_python | 小虾三级 |园豆:984 | 2025-09-07 17:07
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册