当你想根据一组语法规则解析文本并执行命令时,我们可以先以BNF或者EBNF形式指定一个标准语法,再根据正则规则将文本分解为一组令牌流,然后根据(E)BNF语法依次处理令牌流。
1. EBNF简介
EBNF(Extended Backus–Naur Form,扩展的巴克斯范式)是一种用于描述计算机编程语言等正式语言的与上下文无关语法的元语法(metasyntax)符号表示法。简而言之,它是一种描述语言的语言。
EBNF一条规则基本书写规范如下:
symbol ::= alternative1 | alternative2 ...
出现在规则左边的符号称为非终端,终端用双引号或单引号包裹起来。
BNF中有一个特殊符号“@”,表示符号可以去掉。如果用@替换符号,只需要将符号去掉。
记号 | 意义 |
---|---|
[…] | 可选 |
{…}* | 重复,*表示重复0次或多次,和正则中一致。(?出现0次或1次,+出现至少1次) |
(…) | 分组 |
| | 并列选项,只能选一个 |
“…” | 终端字符串 |
‘…’ | 终端字符串 |
例如,用EBNF定义实数:
S := '-' FN | FN
FN := DL FP
FP := @ | '.' DL
DL := D DR
DR := D DR | @
D := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
2. 解析文本为令牌流
2.1 先将文本分割成最小处理单元,同一类单元称为同一个Token,为所有Token定义匹配模式,并使用?P<TOKENNAME>
指定模式名称。
例如,对于'foo = 23 + 43 * 10'
定义匹配模式如下:
1 | import re |
2.2 根据上面生成的模式规则,将文本扫描成令牌流
1 | from collections import namedtuple |
说明:
re.Pattern
对象的scanner()
方法返回<class '_sre.SRE_Scanner'
>类型对象<class '_sre.SRE_Scanner'
>类型对象拥有match()方法,该方法类似于可迭代对象中的__next__()
方法,每次调用根据匹配顺序依次返回一个匹配到的re.Match
对象。直到文本内容结束或者碰到文本中存在不可匹配的内容时,扫描结束,返回None
re.Match
对象拥有属性lastgroup
保存分组组名(即?P<TOKENNAME>...
中的TOKENNAME);拥有方法group()
返回匹配到的文本内容iter(object[, sentinel])
如果没有传入第二个参数,则第一个参数需传入一个序列对象,例如list,str等等; 如果传入第二个参数,则第一个参数需传入可调用对象或方法,且其支持多次调用,直到调用返回值等于sentinel时,调用结束
3. 源码如下:
1 | import re |