马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
在 rust 参考手册中,有大量类似:
- 句法
- MacroInvocation :
- SimplePath ! DelimTokenTree
- DelimTokenTree :
- ( TokenTree* )
- | [ TokenTree* ]
- | { TokenTree* }
- TokenTree :
- Token排除 定界符(delimiters) | DelimTokenTree
- MacroInvocationSemi :
- SimplePath ! ( TokenTree* ) ;
- | SimplePath ! [ TokenTree* ] ;
- | SimplePath ! { TokenTree* }
复制代码 如许抽象的玩意儿(宏 - Rust 参考手册)。这种阅读体验差的要死的东西,一看就是学术界搞出来的东西,这篇文章就是讲这种东西应该怎么读。
1. 什么是 EBNF?
在盘算机科学中,当我们描述一种语言(好比编程语言、数据格式或设置文件)的结构时,须要一种精确、无歧义的方式。扩展巴科斯范式 (Extended Backus-Naur Form, EBNF) 就是如许一种元语言(描述其他语言的语言)标志法。它通过一系列严格界说的规则,清晰地表达一种语言的语法。
EBNF 源自并扩展了巴科斯范式 (Backus-Naur Form, BNF)。BNF 最初由约翰·巴科斯 (John Backus) 和彼得·诺尔 (Peter Naur) 为描述 ALGOL 60 编程语言的语法而计划。EBNF 在 BNF 的底子上增长了一些方便的符号,使得语法描述更为简便和易读。
2. 核心概念
在学习 EBNF 之前,我们须要相识几个根本概念:
- **规则 (Rule / Production):**EBNF 的核心是规则。每条规则界说了一个特定的语法结构是怎样由更小的部门构成的。
- **非闭幕符 (Non-terminal Symbol):**代表一个语法概念或一个可以被进一步分解的结构。它通常是其他规则的名称,表现“这里可以放置一个符合某某规则界说的结构”。在 EBNF 中,非闭幕符通常用描述性的名称表现,比方表达式, 语句, 数字。在一些传统 BNF 中,非闭幕符会被尖括号 < > 困绕,如 <expression>,但在现代 EBNF 中,直接利用名称更为常见。
- **闭幕符 (Terminal Symbol):**代表语言中最小的、不可再分的词法单元或字面值。它们是语法结构终极落实到的具体字符或字符串。比方,编程语言中的关键字 (if, while)、利用符 (+, -, *, /)、数字字面量 (123, 3.14)、字符串字面量 ("hello") 等都是闭幕符。在 EBNF 中,闭幕符通常用引号 " 或 ' 困绕,大概直接写出(如果不会引起歧义)。
3. EBNF 语法符号详解
EBNF 通过一些特别符号来构造规则、非闭幕符和闭幕符:
- ::= 或 =(界说为):
- 这是规则界说的核心利用符,读作“被界说为”或“由…构成”。它将左侧的非闭幕符(要界说的结构)与右侧的该结构的具体构成接洽起来。
- 示例:数字 ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
- |(或 / 选择):
- 竖线表现“大概”,用于在规则界说的右侧提供多个大概的选择。被 | 分隔的各项是互斥的选项。
- 示例:布尔值 ::= "true" | "false"
- “布尔值”可以是 “true” 大概 “false”。
- 并列(次序毗连):
- 当规则界说的右侧有多个符号(闭幕符或非闭幕符)依次分列时,它们表现这些部门必须按照给定的次序出现。
- 示例:赋值语句 ::= 标识符 "=" 表达式
- 一条“赋值语句”由一个“标识符”,后跟一个等号闭幕符,再后跟一个“表达式”构成。
- ()(分组):
- 圆括号用于将一组符号括起来,形成一个逻辑单元。这使得可以将重复、可选等利用符应用于整个组。
- 示例:函数调用 ::= 函数名 "(" (参数列表)? ")"
- 这里 (参数列表)? 被括号括起来,表现可选的参数列表部门。
- ?(可选 / 零次或一次):
- 问号表现其紧邻的前一个符号或分组是可选的,即可以出现零次或一次。
- 示例:整数 ::= ("+" | "-")? 数字序列
- 一个“整数”可以有一个可选的正负号,背面跟着一个“数字序列”。
- *(重复零次或多次):
- 星号表现其紧邻的前一个符号或分组可以出现零次、一次或多次。
- 示例:标识符 ::= 字母 (字母 | 数字)*
- 一个“标识符”由一个“字母”开头,背面可以跟零个或多个“字母”或“数字”。
- +(重复一次或多次):
- 加号表现其紧邻的前一个符号或分组必须至少出现一次,也可以出现多次。
- 示例:数字序列 ::= 数字+
- [...](可选分组 - 另一种常见表现):
- 一些 EBNF 变体利用方括号 [] 来表现一个可选的部门,等同于 (...)?。
- 示例:参数列表 ::= "[" 参数 ("," 参数)* "]" (这里假设 [ 和 ] 是可选参数列表的定界符)
- 大概:整数 ::= ["+" | "-"] 数字序列 (等同于上面的 ("+" | "-")?)
- {...}(重复分组 - 另一种常见表现):
- 一些 EBNF 变体利用花括号 {} 来表现一个可以重复零次或多次的部门,等同于 (...)*。
- 示例:解释 ::= "/*" {恣意字符} "*/"
- 一个“解释”由 /* 开始,中央可以有零个或多个“恣意字符”,并以 */ 竣事。
- 闭幕符的表现:
- 如前所述,闭幕符通常用引号困绕,比方 "if", '+'。
- 如果闭幕符本身就是一个不会引起歧义的字符序列(比方不包含 EBNF 特别符号),偶然也可以直接写出。
- 解释:
- 不同的 EBNF 工具或规范大概有不同的解释方式,常见的有 (* 这是一个解释 *) 或类似 C 语言的 //。本教程重要关注语法规则本身。
4. 怎样阅读 EBNF 规则
- 找到规则界说: 每条规则通常以一个非闭幕符开始,后跟 ::= 或 =。
- 从左到右分析: 阅读界说右侧的符号序列。
- 明白选择: 碰到 | 时,知道这代表多个选项中的一个。
- 留意次序: 并列的符号表现它们必须按次序出现。
- 辨认重复和可选: 留意 *, +, ? (或 [], {}) 的寄义。
- 处理分组: () (以及偶然 [] 或 {}) 会将一部门符号视为一个团体。
- 递归: 规则的右侧可以包含规则本身或其他非闭幕符,这种递归是界说复杂结构(如嵌套表达式)的关键。
- 区分闭幕符与非闭幕符: 闭幕符是语言的“原子”,非闭幕符是须要进一步展开的“概念”。
5. 示例
让我们看一些利用 EBNF 界说的例子:
示例 1:简朴的电子邮件所在
EBNF
- 邮箱地址 ::= 用户名 "@" 域名
- 用户名 ::= 字符+
- 域名 ::= (子域名 ".")*顶级域名
- 子域名 ::= 字符+
- 顶级域名 ::= 字符+
- 字符 ::= 字母 | 数字 | "_" | "-"
- 字母 ::= "a" | ... | "z" | "A" | ... | "Z" // 省略所有字母
- 数字 ::= "0" | ... | "9" // 省略所有数字
复制代码
- 这个例子界说了“邮箱所在”的结构。
- 用户名 和 字符 用了 + 表现至少一个。
- 域名 中的 (子域名 ".")* 表现可以有零个或多个由点分隔的子域名。
示例 2:算术表达式(简化版)
EBNF
- 表达式 ::= 项 (("+" | "-") 项)*
- 项 ::= 因子 (("*" | "/") 因子)*
- 因子 ::= 数字 | "(" 表达式 ")"
- 数字 ::= ("0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9")+
复制代码
- 这个例子展示了怎样界说包含运算符优先级和括号的算术表达式。
- 表达式 和 项 的界说是递归的(因子中包含 表达式),这答应嵌套。
- (("+" | "-") 项)* 表现可以有零个或多个由加号或减号毗连的“项”。
示例 3:一个简朴列表结构
EBNF
- 列表 ::= "[" [元素 ("," 元素)*] "]"
- 元素 ::= 标识符 | 数字
- 标识符 ::= 字母 (字母 | 数字)*
- // 字母和数字的定义同上
复制代码
- 列表由方括号困绕。
- [元素 ("," 元素)*] 表现方括号内的内容是可选的(由于整个被 [] 包裹,这里 [] 是 EBNF 的可选符号,而非列表的定界符——为了清晰,我们也可以写成 (元素 ("," 元素)*)?)。
- 如果列表非空,则至少有一个“元素”,后续可以有零个或多个由逗号分隔的“元素”。
6. 优点与局限
- 优点:
- 精确性: 可以大概无歧义地描述复杂的语法结构。
- 可读性: 相较于其他情势化方法,EBNF 相对容易阅读和明白(尤其是带有扩展符号时)。
- 标准化: 固然存在一些变体,但核心概念是共通的。
- 工具支持: 有很多工具(如剖析器天生器 ANTLR, YACC/Bison)可以直接利用 EBNF 或类似的语法界说来主动天生剖析代码。
- 局限:
- 上下文无关: EBNF 重要描述上下文无关文法。它通常不直接处理那些依靠于上下文的语义规则(比方,变量必须先声明后利用,范例匹配等)。这些通常须要额外的语义分析来处理。
- 歧义性: 只管目的是无歧义,但仍大概写出有歧义的 EBNF 规则(即一个输入串可以有多种剖析方式)。消除歧义偶然须要重写规则或依靠剖析器的特定战略(如运算符优先级和团结性声明)。
- 冗余和复杂性: 一旦里面界说的内容多了,难读得要死。
|