语言与文法

BNF / EBNF、什么是语法的形式化定义

商用

◎学完你会

1文法:一本"句子合格证"的规则书

编程语言 = 一堆"合法句子"的集合。文法就是把这些句子一条条规则地写出来的方式:

概念含义例子
终结符真正出现的字面字符1 + (
非终结符可被替换的"占位"<expr> <term>
产生式一条"替换规则"<term> ::= <factor>
# 一条简单的算术文法(3 层:expr → term → factor) <expr> ::= <expr> '+' <term> | <term> <term> ::= <term> '*' <factor> | <factor> <factor> ::= '(' <expr> ')' | <number>
一句话:::= 读作"可定义为",| 读作"或者"。文法不关心"几加几等于几",只负责说清哪些句子长得合法。

2动手:从 <expr> 推出 1 + 2 * 3

按顺序看"派生":从 <expr> 出发,一次次选一条产生式替换非终结符,最终得到一串终结符。注意:乘法 * 被卡在更深的 term/factor 层。

<expr> → <expr> '+' <term> // ① 选加号那条 左 <expr> → <term> → <factor> → 1 // ② 左边推成 1 右 <term> → <term> '*' <factor> // ③ 右边选乘号那条 <term> → <factor> → 2 // ④ 推成 2 <factor> → 3 → 得到 1+2*3 // ⑤ 推成 3,完整句子
点左边按钮开始。

3三层不是白分:它决定了优先级

为什么 1 + 2 * 3 按数学习惯先算乘法?因为文法把 * 放在更深(term/factor)层:

写法派生结构结果
三层文法1 + (2 * 3)7
若只有一层(num (+|*) num)(1 + 2) * 3(歧义)9
EBNF 的缩写:* 零或多次、+ 一或多次、? 零或一次。例如 <digit>+ = 一长串数字。
最大的坑:以为文法只是"装样子"。实际上语法结构 = 运算顺序:不加层,`1+2*3` 就可能被读成 `(1+2)*3=9`。编译器和人算的区别,从这条文法就定了。

?跨学科:一句规则能"生"出多少句子

文法像遗传:一条规则能无限派生。量一下:光这条 3 层算术文法,<number> 用 1..9,长度为 1 的表达式就有 9 个;用 <digit>+ 让数字可多位数,合法表达式数立刻指数增长、无穷无尽——这正是"语法"和"死名单"的区别:语法用几条规则描述无限集合,死名单要一条条列。

类比:母语语法就几十条规则,却能生成你一辈子说不完的句子。文法 = 语言的"压缩算法"。

✎练一练

BNF 里 ::= 表示什么?
::= 是一条产生式:左边的非终结符可被右边替换。
EBNF 里 *(如 <digit>*)表示?
EBNF 的 * 是"任意次(含 0 次)重复",+ 是一次及以上,? 是零或一次。
为什么算术文法要分 expr/term/factor 三层?
更深的一层优先结合;不分层则 1+2*3 会被读成 (1+2)*3=9,产生歧义。