先写一点学习文档之后的简单理解吧,后续补上实践过程中的采坑经历。
规则基本组成
peg文法基本的组成结构是
规则名 "规则别名"
= 解析表达式
初始化器
在第一个规则前可以定义一个初始化器,语法为花括号包一段js代码
初始化器会在规则被执行之前运行,其中定义的变量和函数可以在解析表达式中访问
{
function makeInteger(o) {
return parseInt(o.join(""), 10);
}
}
解析表达式
一个描述2*(3+4)
的文法示例
{
function makeInteger(o) {
return parseInt(o.join(""), 10);
}
}
start
= additive
additive
= left:multiplicative "+" right:additive { return left + right; }
/ multiplicative
multiplicative
= left:primary "*" right:multiplicative { return left * right; }
/ primary
primary
= integer
/ "(" additive:additive ")" { return additive; }
integer "integer"
= digits:[0-9]+ { return makeInteger(digits); }
一个解析表达式可能由下面这些部分组成
1.严格字面量
直接匹配一段字符文本,例如上面的 "+"
可以在后面加i表示忽略大小写,例如 "abC"i
2.单个字符匹配
就是一个点号 .
,匹配一个任意字符并返回,注意和"."
区分开,后者严格匹配一个点号字符
3.[characters] 类似正则的可选单字符匹配
如:[abc]+
[abc]*
[^abc]*
[^abc]*i
4. 其它解析表达式
并不是上面的 left:multiplicative
,left:multiplicative
是用一种带label的子解析表达式来解释的
这里应该是指直接引用解析表达式,而不给label,例如直接写multiplicative
5. 子解析表达式
子表达式咋理解呢? 我理解为,一系列匹配规则作为一个整体来解析,这个整体叫一个子表达式
( expression )
匹配一个子表达式expression *
匹配0次或多次的子表达式expression +
一次或多次expression ?
0次或1次& expression
类似于peek,看看能否匹配成功,但是不消耗字符串! expression
与上面的相反,看看是否匹配不成功$ expression
尝试匹配该表达式. 如果匹配成功, 不会返回匹配结果, 而是返回匹配成功的字符串.
TODO: 这个咋理解?label : expression
匹配一个表达式,将匹配结果放到一个label里边,这个大量用到,一般和parse action 或者 predicate 配合使用,其label值可以传递给js代码块使用expression1 expression2 ... expressionn
匹配一堆表达式,将结果放到数组中返回expression { action }
如果匹配成功,则运行actionexpression1 / expression2 / ... / expressionn
依次匹配,返回第一个匹配上的结果。
additive
= left:multiplicative "+" right:additive { return left + right; }
/ multiplicative
最后一行的斜杠就这么理解
6. parse action
花括号里边包一段js就是parse action,其作用是把其它rule的结果计算后作为当前rule的结果。如上面的 { return left + right; }
action是一段JavaScript代码, 可以把它当做一个方法来运行. labeled表达式的匹配结果会被当做action的参数, 传递给action. action应该通过return返回一个JavaScript结果, 该结果会被当做前面表达式的匹配结果.
在action代码块中, 遇到非预期情况, 想要中断parse可以调用 expected方法
, 该方法会抛出一个异常. expected方法接受俩个参数, 第一个参数是description, 表明当前位置期望输入以及可选的location信息(默认值是what location would return). description会被当做exception中的message的一部分.
在action代码中也可以调用 error方法
, 该方法也会抛出一个异常. error方法接受俩个参数, 第一个参数是error message, 第二个参数是可选的location信息(默认值是 what location would return). message会在抛出异常中使用. action中的代码块可以访问初始器中定义的方法和变量. action代码块的左右大括号必须都在. action 代码块中可以通过text方法访问匹配成功的字符. action代码块中还可以通过访问location方法得到location信息, 该方法会返回下面这种对象.
{
start: { offset: 23, line: 5, column: 6 },
end: { offset: 25, line: 5, column: 8 }
}
start属性指向了表达式开始位置, end属性指向表达式的结束位置. offset是一个基于0 的offset索引位置, line和 column 属性是基于1的索引位置.
action 代码块中可以通过options变量访问传递给parser的options.
7. 谓词
& { predicate }
判断true,不是true则报错! { predicate }
判断false,不是false则报错
这里没看到过实例,看起来是和parse action一样使用的,只是前面加了判断
本文链接:https://www.zoucz.com/blog/2020/07/28/23f78990-d0b3-11ea-90b5-eb40e9720ed0/