官方文档

先写一点学习文档之后的简单理解吧,后续补上实践过程中的采坑经历。

规则基本组成

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:multiplicativeleft: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 } 如果匹配成功,则运行action
expression1 / 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一样使用的,只是前面加了判断

☞ 参与评论