Flex(词法分析器)对Unicode的支持

29
3个回答

28

目前,Flex只生成8位扫描器,这基本上限制了您使用UTF-8。因此,如果您有一个模式:

肖晗   { printf ("xiaohan\n"); }

如果模式和输入中的字节序列相同,它将像预期的那样工作。更困难的是字符类。如果要匹配字符"肖"或"晗",则不能编写以下内容:

[肖晗]   { printf ("xiaohan/2\n"); }

因为这将匹配六个字节0xe8、0x82、0x96、0xe6、0x99和0x97,实际上这意味着如果您提供肖晗作为输入,该模式将匹配六次。因此,在这种简单情况下,您必须将模式重写为(肖|晗)

对于范围,Hans Aberg编写了一个在Haskell中转换为8位模式的工具

Unicode> urToRegU8 0 0xFFFF
[\0-\x7F]|[\xC2-\xDF][\x80-\xBF]|(\xE0[\xA0-\xBF]|[\xE1-\xEF][\x80-\xBF])[\x80-\xBF]
Unicode> urToRegU32 0x00010000 0x001FFFFF
\0[\x01-\x1F][\0-\xFF][\0-\xFF]
Unicode> urToRegU32L 0x00010000 0x001FFFFF
[\x01-\x1F][\0-\xFF][\0-\xFF]\0

这不太漂亮,但应该可以工作。


我将我的回复从邮件列表复制到了答案中。 - Tim Landscheidt
谢谢。看起来给我很多启发! - xiaohan2012
你能帮我一下吗?我尝试编译你提到的程序源代码,但 Glasgow Haskell 编译器输出了解析错误。你自己成功编译过源代码吗?如果是这样,能否给我一些提示呢? - xiaohan2012
3
抱歉突然换工具了,我使用了错误的工具。我应该使用 hugs 而不是 ghc - xiaohan2012
1
所以这个可以工作,但我们应该添加以下内容:如果您正在使用 %option full 或参数 -Cf, 那么您还需要使用选项 %option 8bit 或参数 -8 或 --8bit(这让我头疼了很久...) - Algoman
以上示例中的范围如果我们谈论将代码点(而不是文字值)的范围转换为flex表达式,那么看起来不正确。在utf-8编码中,代码点0xffff应该是\xef\xbf\xbf。然后从代码点0到0xffff的范围将是“[\x00-\xee]..|\xef(\xbf[\x00-\xbf]|[\x00-\xbe].)”。如果0x0000和0xffff被理解为某些其他代码点的utf8编码,则表达式将是“...”。这有意义吗,还是我看起来完全混乱=) - Todd

25

Flex不支持Unicode。但是,Flex支持“8位清洁”的二进制输入。因此,您可以编写匹配UTF-8的词法模式。您可以在输入语言的特定词法区域(例如标识符、注释或字符串文字)中使用这些模式。

这对于典型的编程语言来说很有效,其中您可以向实现的用户断言源语言是用ASCII / UTF-8编写的(而且不支持其他任何编码,期间)。

如果您的扫描仪必须处理可能以任何编码方式进行的文本,则此方法将无效。如果您需要在扫描仪本身中明确表示Unicode元素的词法规则,则此方法也不会很好地工作。即您需要Unicode字符和Unicode正则表达式。

思路是,您可以使用词法规则识别包含UTF-8字节的模式(然后可能获取yytext并将其从UTF-8转换出来或至少验证它)。

有一个可行的示例,请参见TXR语言的源代码,特别是此文件:http://www.kylheku.com/cgit/txr/tree/parser.l

向下滚动到此部分:

ASC     [\x00-\x7f]
ASCN    [\x00-\t\v-\x7f]
U       [\x80-\xbf]
U2      [\xc2-\xdf]
U3      [\xe0-\xef]
U4      [\xf0-\xf4]

UANY    {ASC}|{U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
UANYN   {ASCN}|{U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U} 
UONLY   {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}

正如您所看到的,我们可以定义模式来匹配ASCII字符以及UTF-8的起始和续字节。UTF-8是一种词法符号表示法,而这是一个词法分析器生成器,所以...没问题!
一些解释:`UANY` 表示匹配任何字符,单字节ASCII或多字节UTF-8。`UANYN` 表示与 `UANY` 相同,但不匹配换行符。这对于不跨越多行的标记非常有用,例如从 `#` 到行尾的注释,包含国际文本。`UONLY` 表示仅匹配UTF-8扩展字符,而不是ASCII字符。这对于编写需要排除某些特定ASCII字符(而不仅仅是换行符)但所有扩展字符都可以的词法规则非常有用。
免责声明:请注意,扫描程序的规则使用名为 `utf8_dup_from` 的函数将 `yytext` 转换为包含Unicode代码点的宽字符字符串。该函数很强大,能检测出过长序列和无效字节等问题并进行正确处理。也就是说,此程序不依赖这些词法规则进行验证和转换,只是进行基本的词法识别。这些规则将把过长形式(例如使用多个字节编码的ASCII代码)识别为有效语法,但转换函数将正确地处理它们。无论如何,我不希望程序源代码中存在与UTF-8相关的安全问题,因为您必须信任源代码才能运行它(但程序处理的数据可能是不可信的!)如果您正在为不受信任的UTF-8数据编写扫描程序,请小心!

1
只是想知道,U4的定义难道不应该像这样吗:U4 [\xf0-\xf7]以实际容纳从11110000到11110111的所有可能性吗? - exa
1
在我看来,这个答案比被采纳的答案更好。 - Rahat Zaman
@Dr.AlexRE 没错,基本上规则不验证UTF-8。它们提取单元,然后yytext[]空终止字符字符串需要通过适当的UTF-8解码器进行处理,该解码器详细处理所有这些情况。 - Kaz
将Unicode视为与UTF-8相同是一种误导,这是一个常见的错误。所提出的方法似乎只适用于UTF-8,并且仅在文件中没有UTF BOM的情况下才有效。Unicode需要支持UTF BOM、UTF-16和UTF-32输入,也许还包括UCS-2和UCS-4,尽管这些已被UTF-16和UTF-32取代。 - Dr. Alex RE
1
@Dr.AlexRE BOM在UTF-8中是毫无意义的,因为它只有一个明确定义的字节顺序。> Unicode要求支持... 不,它并不要求。您和您的客户定义程序的要求。http://utf8everywhere.org/ - Kaz
显示剩余7条评论

5
我想知道最新版本的Flex是否支持Unicode?
如果支持,如何使用模式匹配中文字符?
要在兼容Flex的词法分析器中与中文字符和其他Unicode码点匹配模式,您可以使用C++的RE/flex词法分析器
RE/flex 安全地支持完整的Unicode标准,并且接受UTF-8、UTF-16和UTF-32输入文件,而不需要UTF-8 hack(甚至不能支持UTF-16/32输入并处理UTF BOM)。
此外,使用Flex的UTF-8 hack无法编写完全受RE/flex支持的Unicode正则表达式,例如[肖晗]
它可以与Bison无缝协作以构建词法分析器和解析器。
实际上,使用RE/flex,我们可以在词法分析器的.l规范中将任何Unicode模式编写为基于UTF-8的正则表达式,例如:
%option flex unicode
%%
[肖晗]   { printf ("xiaohan/2\n"); }
%%

这会生成一个扫描UTF-8、UTF-16和UTF-32文件的词法分析器。根据UTF标准,对于UTF-16/32输入,期望在输入中有UTF BOM,而UTF-8 BOM是可选的。
我们可以使用全局选项%option unicode来启用Unicode,并使用%option flex来指定Flex规范。可以使用本地修饰符(?u:)将Unicode限制为单个模式(因此其他所有内容仍然是ASCII/8位,如Flex)。
%option flex
%%
(?u:[肖晗])   { printf ("xiaohan/2\n"); }
(?u:\p{Han})  { printf ("Han character %s\n", yytext); }
.             { printf ("8-bit character %d\n", yytext[0]); }
%%

选项flex启用了Flex兼容性,因此您可以使用yytextyylengECHO等。如果没有flex选项,RE / flex会期望Lexer方法调用:text()(或者str()wstr()用于std::stringstd::wstring),size()(或wsize()用于宽字符长度)和echo()。个人认为,RE/flex方法调用更加简洁,包括宽字符操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接