【问题标题】:Flex match string literal, escaping line feedFlex 匹配字符串文字,转义换行符
【发布时间】:2021-02-20 08:25:31
【问题描述】:

我正在使用 flex 来尝试匹配类似 C 的简化字符串文字。 这样的正则表达式:

\"([^"\\]|\\["?\\btnr]|\\x{HEXDIG}{HEXDIG})*\"

将匹配我感兴趣的所有单行字符串文字。

字符串文字不能包含非转义的反斜杠。字符串文字也不能包含文字换行符 (0x0a),除非它被反斜杠转义,在这种情况下,换行符和任何后续空格和制表符都将被忽略。

例如,假设 {LF} 是一个实际的换行符,而 {TAB} 是一个实际的制表符(我没有比这更好的格式了)。

"This is an example \{LF}{TAB}{TAB}{TAB}of a confusing valid string"

令牌"This is an example of a confusing valid string"

我的第一个想法是使用起始状态、尾随上下文和 yymore() 来匹配我想要的内容并检查是否存在错误,如下所示:

...
%%
\" { BEGIN STRING; yymore(); }
<STRING>{
    \n { /* ERROR HERE! */ }
    <<EOF>> { /* ERROR HERE AS WELL */ }
    
    ([^"\\]|\\["?\\btnr]|\\x{HEXDIG}{HEXDIG})* { 
          /* String ok up to here*/ 
          yymore(); 
    }
    \\\n[ \t]* { 
         /*Vadid inside a tring but needs to be ignored! */ 
         yymore(); 
    }
    \" { /* Full string matched */ BEGIN INITIAL;}
    .|\n { \* Anything else is considered an error *\  }
}
%%
...

有没有办法按照我想做的方式做我想做的事?是否有任何其他可能由 flex 提供的“标准”方法,而我只是愚蠢地没有想到?在我看来,这并不像一个不常见的用例。我是否应该单独匹配字符串(从 before 开始,在空格结束之后)并将它们连接起来。这有点复杂,因为可以使用反斜杠将字符串分解为任意数量的行。

【问题讨论】:

  • 如果你真的需要它是正确的,最好从 C 语言flex 词法分析器中获取你需要的所有功能,假设许可证允许的话。有几个边缘情况很棘手。

标签: c regex bison flex-lexer string-literals


【解决方案1】:

如果您只想识别字符串文字,则不需要开始条件。您可以使用在许多答案中都可以找到的简单模式的一些变体:

    ["]({normal}|{escape})*["]

(我使用宏来使结构清晰,尽管在实践中我几乎不会使用它们。)

这里的“普通”是指字符串中没有特殊意义的任何字符。换句话说,除了"(结束文字)、\(开始转义序列或换行符(尽管某些语言允许字符串中的换行符,这通常是错误)之外的任何字符。换句话说,@ 987654326@(或类似的)。

"escape" 可以是任何有效的转义序列。如果您不想验证转义序列,您可以匹配一个反斜杠,后跟任何单个字符(包括换行符):\\(.|\n)。但是由于您似乎确实想要验证,因此您需要明确说明您准备的转义序列:

    \\([\n\\btnr"]|x[[:xdigit:]]{2})

但所有这些都只能识别有效的字符串文字。无效的字符串文字与模式不匹配,因此将回退到您用作回退规则的任何内容(仅匹配初始 ")。由于这实际上从来都不是您想要的,因此您需要添加第二条检测错误的规则。编写第二条规则的最简单方法是["]({normal}|{escape})*,即没有最终双引号的有效规则。由于 (f)lex 的最大咀嚼规则,这只会​​匹配错误的字符串文字:有效的字符串文字与有效规则的匹配比与错误规则的匹配更长(因为有效规则的匹配包括最后的双引号)。

在现实生活中的词法扫描器(与学校练习相反)中,更常见的是期望词法扫描器通过将转义序列替换为相应的字符,将字符串文字实际解析为它所代表的实际字节。这通常是通过一个开始条件来完成的,但是单个模式更加集中(并且有更多)。对于这种解析器的示例,您可以查看以下两个答案(以及许多其他答案):

【讨论】:

  • 我找到了解决我的问题的方法,它实际上与您提出的建议很好地结合在一起。然而,我尝试的不仅是匹配转义序列:“ ... \{LF}[{SPACE}|{TAB}]* ...” 这可以用你的一行来完成,而且忽略LF,结果标记中的空格和制表符,即从 \ 删除任何空格直到下一个字符。正如其他两个答案所示,我猜想这在开始条件下是可能的。您还让我认为使用纯 C 是一个坏主意,我应该改用 C++。无论如何,谢谢!
  • @Desperados:酷。是的,如果要删除拼接,最好使用开始条件(就像要将 \n 更改为真正的换行符一样)。 (注意:[[:blank:]] 是一个预定义的字符类,只包含空格和制表符。[[:space:]] 包含所有 C 空格:空格、制表符、换行符、回车、换页和垂直制表符。使用标准字符类比滚动更好的样式您自己的恕我直言。(我假设 C/Posix 语言环境。允许将语言环境添加到列表中,但现在单字节非 unicode 语言环境非常罕见。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多