扩充巴科斯范式(ABNF)初识

巴科斯范式是以美国人巴科斯(Backus)和丹麦人诺尔(Naur)的名字命名的一种形式化的语法表示方法,用来描述语法的一种形式体系,是一种典型的元语言

起步

一个ABNF规范是一些推导规则的集合:

规则 = 定义;注释CR LF

可细分为以下几个部分:

  • 规则
  • 定义
  • 注释 (可选)
  • CR LF(回车、换行)结束

规则名

大小写不敏感

规则名字不区分大小写:
<rulename>, <Rulename>, <RULENAME><rUlENamE>指的都是同一个规则。
规则名由一个字母或多个字母数字连字符(减号)组成。

尖括号非必须

用尖括号(<>)包围规则名不是必需的(与BNF一样),它们可以用来在文中方便识别出规则名。

最终值

最终值为一个或多个数值字符。

数值字符可按下面的方式指定:先是一个百分号%,紧跟着基数(b=二进制, d = 十进制, x = 十六进制),再其后是这个数值或数值串(用.来指示串联)。例如:回车可以用十进制的%d13或十六进制的%x0D来指定,而回车换行则可以用%d13.10来指定。

字面文本通过包含在在双引号"中字符串指定。这些字符串不区分大小写,使用 (US-)ASCII字符集。所以字符串abc将匹配abcAbcaBcabCABcAbCaBCABC
区分大小写的匹配必须定义明确的字符,例如:若要匹配aBc,定义必须是%d97 %d66 %d99

操作符

空白字符

空白字符被用来分隔定义中的各个元素:要使空格被识别为分割符则必须明确的包含它。

串联

规则1规则2

规则可以通过列出一系列的规则名来定义。

要匹配字符串aba可以使用下列规则:

foo = %x61 ; a
bar = %x62 ; b
mumble = foo bar foo

选择 /

规则1 / 规则2

一个规则可以通过用斜杠(/)分隔的可供选择的子规则列表来定义。

要接受规则或规则可构造如下规则:

foobar = foo / bar

增量选择 =/

规则1 =/ 规则2

可以通过在规则名和定义之间使用=/来向一个规则增加补充选择。

规则

ruleset = alt1 / alt2 / alt3 / alt4 / alt5

等价于

ruleset = alt1 / alt2
ruleset =/ alt3
ruleset =/ alt4 / alt5

值范围%c##-##

%c##-##

数值范围可以通过使用连字符(-)来指定。

规则

OCTAL = "0" / "1" / "2" / "3" / "4" / "5" / "6" / "7"

等价于

OCTAL = %x30-37

序列组合 ()

(规则1规则2)

在定义中,元素可以放置在圆括号中来将规则组合起来,该组合视为单个元素。

要匹配elem foobar snafooelem tarfoo snafoo可以构造下列规则:

group = elem (foobar / tarfoo) snafoo

要匹配elem foobartarfoo snafoo可以构造下列规则:

group = elem foobar / tarfoo snafoo
group = (elem foobar) / (tarfoo snafoo)

不定量重复m*n

元素前面的星号*表示重复,其完整形式如下:

m*n规则

要表示一个元素的重复,就要使用<m>*<n>元素形式。可选的<m>给出要包含的元素的最小数目,默认为0;可选的给出要包含的元素的最大数目,默认为无穷大。

例子:

*元素 ; 表示零个或更多元素
1*元素 ; 表示一个或更多元素
2*4元素 ; 表示两个至四个元素

定量重复n

n规则

要表示特定数目的元素可使用形式<n>元素,相当于用不定量重复形式表示的<n>*<n>元素。

使用2DIGIT得到两个数字,使用3DIGIT得到三个数字。(DIGIT在下面的核心规则中定义,也见例子中的zip-code)。

可选序列[]

[规则]

要表示可选元素,下列构造等价:

[foobar snafoo]
*1(foobar snafoo)
0*1(foobar snafoo)

注释;

;注释

注释从一个分号(;)开始,并持续到此行的结束。

操作符优先级

下面的操作符给出了从高(结合最紧密)到低(结合最松散)的优先级:

  1. 规则名、最终值
  2. 注释;
  3. 值范围%c##-##
  4. 重复*
  5. 组合 ()、可选[]
  6. 串联
  7. 选择 /

选择操作符与串联一起使用会造成混淆,因此建议使用组合来确保串联组的明确。

例如:

我们 = 你 我/他 她

会产生下面两种歧义:

(你 我)/(他 她)
(你) (我/他) (她)

所以,使用组合来确保不会产生歧义:

(你 我)/(他 她)

核心规则

核心规则定义于 ABNF 标准中。

规则 形式定义 意义
ALPHA %x41-5A / %x61-7A 大写和小写 ASCII 字母(A-Z, a-z)
DIGIT %x30-39 数字(0-9)
HEXDIG DIGIT / "A" / "B" / "C" / "D" / "E" / "F" 十六进制数字(0-9, A-F, a-f)
DQUOTE %x22 双引号
SP %x20 空格
HTAB %x09 横向制表符
WSP SP / HTAB 空格或横向制表符
LWSP *(WSP / CRLF WSP) 直线空白(晚于换行)
VCHAR %x21-7E 可见(打印)字符
CHAR %x01-7F 任何 7 - 位 US-ASCII 字符,不包括 NUL(%x00)
OCTET %x00-FF 8 位数据
CTL %x00-1F / %x7F 控制字符
CR %x0D 回车
LF %x0A 换行
CRLF CR LF 互联网标准换行
BIT "0" / "1" 二进制数字

实践

电子邮箱地址

email = 1*( atext / "." ) "@" label *( "." label ) label = let-dig [ [ ldh-str ] let-dig ]

JSON

// 语法树的跟【Root是系统内置,必须定义Root作为语法根节点,没有定义会报错】
Root = Value?;
// 字符串
Text : "\""@ = "\"([^\"\\]|\\.)*\"";
// 整型数值
Number : "[0-9]" = "0x[0-9a-fA-F]+" | "[0-9]+(%.[0-9]+)?";
// 空
Null [53,155,185] = <null>;
// bool
Bool = <true> | <false>;
// Json值
Value = Text | Bool | Number | Null | Array | Object;
// 数组
Array = '['@ (Value ArrayValuePair*)? ']';
// Array元素
ArrayValuePair = ','@ Value;
// 映射表
Object = '{'@ (ObjectValue ObjectValuePair*)? '}';
// 键
ObjectKey = Text;
// 键值对
ObjectValue = ObjectKey ':'@ Value;
// Object元素
ObjectValuePair = ','@ ObjectValue;

参考资料

  1. 扩充巴科斯范式 - 维基百科,自由的百科全书
  2. 从零开始的 JSON 库教程(一):启程
  3. RFC 5234: Augmented BNF for Syntax Specifications: ABNF
  4. 从零开始设计一门属于自己的开发语言