Peg 解析器 - 支持转义字符

Peg parser - support for escape characters

我正在研究 Peg 解析器。在其他结构中,它需要解析标记指令。标签可以包含任何字符。如果您希望标签包含大括号 },您可以使用反斜杠将其转义。如果你需要一个文字反斜杠,那也应该被转义。我试图通过 JSON 的 Peg 语法来实现这一点:https://github.com/pegjs/pegjs/blob/master/examples/json.pegjs

有两个问题:

{ some characters but escape with a \ }
{ some characters but escape \} with a \ }

相关语法为:

Tag
  = "{" _ tagContent:$(TagChar+) _ "}" {
  return { type: "tag", content: tagContent }
}

TagChar
  = [^\}\r\n]
  / Escape
    sequence:(
        "\" { return {type: "char", char: "\"}; }
      / "}" { return {type: "char", char: "\x7d"}; }
    )
    { return sequence; }
    
_ "whitespace"
  = [ \t\n\r]*
  
Escape
  = "\"

您可以使用在线 PegJS 沙箱轻松测试语法和测试输入:https://pegjs.org/online

我希望有人有办法解决这个问题。

这些错误基本上都是拼写错误。

第一个问题是标记字符的正则表达式中的 class 字符。在字符 class 中,\ 继续作为转义字符,因此 [^\}\r\n] 匹配除 } 以外的任何字符(使用不必要的反斜杠转义),回车 return 或换行符。 \就是这样一个字符,所以用字符class来匹配,而Escape从来没有尝试过。

由于您的标记字符模式未能成功将 \ 识别为 Escape,因此标记 { \ } 被解析为四个字符(space、反斜杠, 反斜杠, space) 并且标记 { \} } 被解析为在第一个 } 处终止,造成语法错误。

所以你应该把字符class固定为[^}\\r\n](我把右大括号放在前面是为了更容易阅读落木。顺序无关紧要。)

完成后,您会发现解析器 return 的字符串中的反斜杠仍然完好无损。这是因为 Tag 模式中的 $"{" _ tagContent:$(TagChar+) _ "}"。根据the documentation$运算符的含义是:(强调

$ expression

Try to match the expression. If the match succeeds, return the matched text instead of the match result.

供参考,正确的语法如下:

Tag
  = "{" _ tagContent:TagChar+ _ "}" {
  return { type: "tag", content: tagContent.map(c => c.char || c).join('') }
}

TagChar
  = [^}\\r\n]
  / Escape
    sequence:(
        "\" { return {type: "char", char: "\"}; }
      / "}" { return {type: "char", char: "\x7d"}; }
    )
    { return sequence; }
    
_ "whitespace"
  = [ \t\n\r]*
  
Escape
  = "\"

使用以下输入时:

{ some characters but escape \} with a \ }

会 return:

{
   "type": "tag",
   "content": "some characters but escape } with a \ "
}