标识符通常由下划线、数字组成;以及第一个字符不是数字的大小写字符。在编写lexer时,通常具有诸如is_digit
或is_alnum
之类的辅助函数。如果要实现这样一个函数来扫描标识符中使用的字符,它会被称为什么?显然,is_identifier
是错误的,因为它将是lexer扫描的整个令牌,而不是单个字符。我认为is_alnum_or_underscore
是准确的,尽管相当冗长。对于像这样常见的东西,我觉得应该用一个词来形容它
Unicode附件31(Unicode标识符和模式语法,UAX31)定义了一个定义标识符词汇语法的框架,这可能与我们将要使用的标准术语一样接近。UAX31被Python和Rust使用(作为参考),并已被批准用于C++23。所以我想这是很好的主流。
UAX31定义了三组标识符字符,称为Start、Continue和Medial。所有开始的字符也是继续字符;noMedial字符是Continue角色。
这导致了简单的正则表达式(UAX31-D1默认标识符语法):
<Identifier> := <Start> <Continue>* (<Medial> <Continue>+)*
一种声称符合UAX31的编程语言不需要接受这些集合中每一个的确切成员资格,但它必须在所谓的";配置文件";。(还有其他七项要求,与这个问题无关。如果你想掉进一个很深的兔子洞,请参阅文档。)
这可以简化得更多,因为无论是UAX31还是(据我所知)任何主要语言的配置文件都没有在Medial中放置任何字符。因此,您可以按照流程定义两个类别:标识符开始和标识继续,其中第一个是第二个的子集。
你会在许多语法文档中看到这一点:
- Python
identifier ::= xid_start xid_continue*
- >Rust
IDENTIFIER_OR_KEYWORD : XID_Start XID_Continue* | _ XID_Continue+
C++
identifier:
identifier-start
identifier identifier-continue
- Swift
- 调用集合标识符头和标识字符
- Java 调用它们JavaLetter,Java LetterOrDigit
- C
- 定义非数字标识符以及识别数字;继续就是两个集合的并集