Unicode 类别的十六进制范围是多少?

What are the hex ranges for Unicode categories?

我正在用 c 为 Cooklang 语言构建一个解析器。它在 EBNF 中描述如下: https://github.com/cooklang/spec/blob/main/EBNF.md

我正在使用 flex/bison 构建解析器。我需要以 UTF-8 的形式输入,而不仅仅是 ASCII。问题是,我只接受一些 UTF-8 字符、除 C 和 N 之外的所有一般类别,以及一些换行符。 我还需要能够分辨出字符来自哪个类别,所以这不仅仅是复制和粘贴 Unicode 字符串,就像使用 C 字符串一样。

我发现这个:https://lists.gnu.org/archive/html/help-flex/2005-01/msg00043.html 作为将十六进制范围转换为词法分析器表达式以捕获这些十六进制范围的好脚本。但我仍然遇到的问题是弄清楚哪些十六进制范围用于不同的类别。

那么,哪些十六进制范围代表 Unicode 的不同一般类别?如果没有包含整个类别且仅包含该类别中的代码点的单个十六进制范围,我在哪里可以找到其中所有代码点的列表?

如果您需要更多信息,请告诉我。

我不知道 Flex 和 Bison 是否是解决这个特定问题的最佳工具。首先,您的语法不容易适应可以将输入明确划分为标记,然后使用有限的前瞻上下文无关语法解析这些标记的模型。

也许您已经想到 flex 只会对每个传入的符号进行分类,然后让解析器处理将单个字符组装成语法结构的问题。这是可以做到的,尽管这不是一种常见的方法。例如,通常我们会将 word 设为单个标记。不过,在这种情况下,这会很困难,因为在某些上下文中,您希望将仅由数字组成的单词视为数字,而在其他上下文中,它应该被视为 word(留下暂且不考虑处理 text 的难度)。这可能会完成,但它需要对语法进行一些调整,而且我完全不确定在从左到右的解析过程中是否可以解决所有歧义。

当然可以仅使用 Flex 将 UTF-8 序列分类为单个字符,按一般主要类别分隔(可能有一些小的改进)。我想这就是您要问的问题,坏消息是 Flex 确实不是为 UTF-8 处理而设计的,而且 Unicode 并不是为了合并代码点范围而设计的。 Unicode 尝试放置代码点,以便来自同一脚本的字符都放置在几个对齐的代码点块中,留下一些未使用的代码点,以避免混合来自其他脚本的字符。此外,未使用的代码并不总是在块的末尾,因为有时代码点是根据某种算法分配的——比如大写字母与等效的小写字母有固定的偏移——这不会产生所有代码点(也许是因为有时大写到小写的映射不是一对一的。)您可以在 Greek characters, with commonly used characters mostly assigned to the range 0x0370-0x03FF, with accented and other less commonly-used characters in the code block 0x1F00-0x1FFF 的两个主要块中看到这一点。其他一些块用于历史符号,因此如果您有足够的兴趣,可以花很多时间探索那个兔子洞。)

一次又一次地重复该模式后,您会发现有大约一千个小的未分配字符(类别 Cn)打断了其他字符类别的运行。因此,您可以为每个字符类别收集所有可能的 UTF-8 序列,但最终会得到很多范围,可能会超出 Flex 对模式大小的限制 [请参阅注释 1 和 2]。

无论如何,我认为这个想法是死胡同,因为如果符号是单个字符,您将在编写单符号先行语法时遇到巨大问题。如果要标记化为单词,如何编写单符号先行语法对我来说甚至都不明显。以 EBNF 产品为例:

step     = { text item | ingredient | cookware | timer }-, new line character ;

ingredient           = one word ingredient | multiword ingredient ;
one word ingredient  = "@", ( word,          [ "{", { white space }, [ amount ], { white space }, "}" ] ) ;
multiword ingredient = "@", ( word, multiword, "{", { white space }, [ amount ], { white space }, "}" ) ;

据我了解,这里的想法是您可以编写

Mix the @Demerara sugar { 50 % grams} with the @butter and if desired @cinnamon

其中成分为Demarara sugarbuttercinnamon;后两种情况没有必要使用括号,因为它们是单词成分;因此,and if desiredtext item,就像 Mix the.

这里的问题是,虽然语法是明确的(至少,在这个小摘录中),但解析器无法判断 and if desired 不是多词成分的一部分,直到它看到 @ 开始下一个成分。不幸的是,单符号先行语法必须在看到以下 @ 之前很久就决定是否减少 one word ingredient 。而且不能过早的减少,因为以后可能有{,那one word ingredient根本不应该减少。

现在,您可以尝试通过从语法中删除 one word ingredient 来解决这个问题,这样所有的可能性都简化为要素,从而使解析器不必提前做出决定。但是,这最终会导致 text item(如果是这样的话)被包含在 ingredient 产生式中,因此您需要一个语义操作来修复这些情况下的语法树。这一切都是可能的,但它既不清晰也不易于维护,而你能否做到这一点基本上取决于你有多固执。

当我对它感到困惑时,我注意到了关于该语法的其他一些事情,尽管它们与您的问题根本不相关,但我将在这里提及。 (而且我可能错过了很多其他细节;这些细节突然引起了我的注意。)

首先,如果你写:

decimal = integer, ".", integer ;

在禁止以 0 开头的 integer 之后,您就无法使用像 1.075 这样的小数。 075 不是整数,因为它以 0 开头。小数的小数部分是 digit 的任意序列,可能形成也可能不形成整数。

其次,我没有得到

的理由
units    = multiword | punctuation character; 

这好像是说heaping tablespoon.都是可能的(但是.有什么意义呢?),但是我不能写fld. oz.(因为缩写中的点不是单词字符)。可能我误会了什么。

我打算添加一些关于在自由文本中禁止特定字符的利弊的内容(部分原因是该字符在以后的 Unicode 版本中可能完全有效,这会给您的各种用户带来互操作性问题)但我想我已经写够了。我很乐意查看带有相关详细信息的更具体的问题。

注释

  1. Flex 处理宏——出于某种原因在大学里非常流行类——通过直接将宏的主体插入引用模式,结果你可以' 通过使用宏来避免大小限制。

  2. Flex 几乎是 8 位的,所以您实际上不必使用四字节转义码来表示单个字节值。不过,您的文本编辑器可能用处不大。