Peg 解析器 - 支持转义字符

问题描述

我正在研究 Peg 解析器。在其他结构中,它需要解析一个标签指令。标签可以包含任何字符。如果您希望标记包含花括号 },您可以使用反斜杠对其进行转义。如果您需要文字反斜杠,也应该对其进行转义。我试图在 JSON 的 Peg grammer 的启发下实现这一点: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

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

解决方法

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

第一个问题是标签字符的正则表达式中的字符类。在字符类中,\ 仍然是转义字符,因此 [^\}\r\n] 匹配除 }(用不必要的反斜杠转义写入)、回车或换行符以外的任何字符。 \ 就是这样一个字符,所以通过字符类匹配,Escape 从来没有尝试过。

由于您的标签字符模式无法成功将 \ 识别为 Escape,因此标签 { \\ } 被解析为四个字符(空格、反斜杠、反斜杠、空格)和标记 { \} } 被解析为在第一个 } 处终止,从而产生语法错误。

所以你应该将字符类固定为 [^}\\\r\n](我把右括号放在最前面,以便更容易阅读落下的木材。顺序无关紧要。)

一旦你这样做了,你会发现解析器仍然返回带有完整反斜杠的字符串。这是因为 $ 模式中的 Tag"{" _ tagContent:$(TagChar+) _ "}"。根据{{​​3}},$ 运算符的含义是:(强调

$ expression

尝试匹配表达式。如果匹配成功,返回匹配的文本而不是匹配结果。

,

作为参考,正确的语法如下:

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 \\ }

它会返回:

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