问题描述
我正在研究 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 \ "
}