语言与文法
BNF / EBNF、什么是语法的形式化定义
商用
◎学完你会
- 说清"语言 = 合法句子的集合","文法 = 描述这个集合的规则";
- 读懂 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,产生歧义。