使用 Flex-Lexer 捕获字符串文字错误

Catching String Literal Errors using Flex-Lexer

我已经设法在我的 Flex 程序中有效地匹配有效的字符串文字,但我还想匹配未终止的字符串文字和带有错误转义序列的字符串文字。

例如,我的字符串文字是使用简单的正则表达式匹配的:

\"(\.|[^\"])*\"

然后我试图找到一个字符串文字以 " 开头的位置,然后是一些文本,然后是 \n。这对我的词法分析器来说是不正确的语法,我想捕获并产生一个错误。

我目前想出的正则表达式是这样的:

\"(\.|[^\"])*\n

它确实正确地捕获了错误,但随后似乎吃掉了其余的标记,因为之后没有输出。

此外,我还希望在未终止的字符串文字具有无效转义序列时出现特殊情况错误。例如:

"some text \
int abc

所以我的问题归结为,我当前匹配字符串文字的方式是否有问题影响了我捕获这些错误的能力,或者我的模式匹配是否不必要地消耗了令牌?也有可能我不知道自己在做什么!

一些字符串示例:

"a correct string literal"
"an unterminated string literal
"an unterminated string literal with escape \

所有字符串文字都是单行的,并遵循以下形式:

"(.*)"\n

字符串文字的正确弹性模式是(转义序列见下文):

\"(\(.|\n)|[^\"\n])*\"

这与您的模式不同,因为它允许在转义字符后换行(从技术上讲这是一种拼接,而不是字符串文字语法的一部分 [注 1]),否则禁止换行。这必须明确地完成,因为 [^...] 包含换行符,除非 \n 是要拒绝的字符列表的一部分。只有 . 隐式禁止换行。

要匹配不正确的字符串文字,您只需要相同的模式而无需终止 ":

\"(\(.|\n)|[^\"\n])*

您无需担心模式匹配正确的字符串文字,因为 flex 总是选择最长的匹配,并且带有终止引号的匹配保证更长。

如果你想更准确地了解转义字符,你需要这样的东西:

\"(\([abfnrtv'"?\\n]|[0-7]{1,3}|x[[:xdigit:]]+|u[[:xdigit:]]{4}|U[[:xdigit:]]{8})|[^\"\n])*\"

您可以使用相同的技术来匹配错误,但您可能想要区分未终止的引号错误和无效的转义错误,您可以使用两种错误模式来做到这一点:

\"(\([abfnrtv'"?\\n]|[0-7]{1,3}|x[[:xdigit:]]+|u[[:xdigit:]]{4}|U[[:xdigit:]]{8})|[^\"\n])*\"   { /* Valid string */ }
\"(\([abfnrtv'"?\\n]|[0-7]{1,3}|x[[:xdigit:]]+|u[[:xdigit:]]{4}|U[[:xdigit:]]{8})|[^\"\n])*/\  { /* Invalid escape sequence */ }
\"(\([abfnrtv'"?\\n]|[0-7]{1,3}|x[[:xdigit:]]+|u[[:xdigit:]]{4}|U[[:xdigit:]]{8})|[^\"\n])*     { /* Missing terminating quote */ }

备注

  1. “拼接”是行尾的反斜杠。您通常只在长宏的定义中看到这些,但 C 允许在任何地方进行拼接:反斜杠和随后的换行符只是从程序文本中删除,因此拼接甚至可以放在标识符或多字符运算符的中间。 (但不要那样做!)

    使用拼接在多行上继续字符串不是好的风格;最好使用字符串连接。但是 C 标准确实允许它。

    但是,拼接在标记化开始之前被删除,这意味着您不能反斜杠转义行尾的反斜杠:

    "This is a string literal which includes a \
    t tab, with a splice in the middle of the escape."
    

    请不要在生产代码中使用它:-)