我正在尝试构建自己的编译器,它输出用户给出的输入类型,例如,abcd
是标识符,1242
是整数。我已经实现如下:
text程序l
%{
#define IDENTIFIER 10
#define INTEGER 11
%}
IDENTIFIER [a-zA-Z_][a-zA-Z0-9_]*
INTEGER [1-9][0-9]*|"0"
%%
{IDENTIFIER} { return IDENTIFIER; }
{INTEGER} { return INTEGER; }
%%
int main() {
int token;
while(token = yylex()) {
if(token == IDENTIFIER) { printf("IDENTIFIER"); }
else if(token == INTEGER) { printf("INTEGER"); }
else { printf("INVALID"); }
}
}
当我运行以下命令时,这非常有效:
flex testProg.l
cc lex.yy.c -lfl
./a.out
样本工作输入
sample
IDENTIFIER
1993
INTEGER
当我尝试输入一个无效的令牌(例如12abc
(时,就会出现问题。这既不是整数也不是标识符,并且应该输出"strong";INVALID"但它输出:
12abc
INTEGER
IDENTIFIER
所发生的情况是12
和abc
被视为单独的令牌,而不是一个。我该如何避免这种情况?
许多语言都使用词法分析器,它们非常乐意让12abc
是一个后跟标识符的整数。为什么不呢?如果这在语言中意味着什么,那么这可能就是用户的意思。如果它没有任何意义,它将触发语法错误,因此会通知用户。
但是,好吧,你想认识到这是一个错误。在这种情况下,您需要将错误输入识别为错误,第一步是将其识别为令牌。如果你还记得flex的比赛规则,那就很容易了:
[[:alpha:]_][[:alnum:]_]* { return IDENTIFIER; }
[1-9][[:digit:]]*|0 { return NUMBER; }
[[:alnum:]_]+ { return BADTOKEN; }
请注意,为了可读性,我使用命名字符类将宏替换为实际模式,并删除了"0"
上多余的引号。
Flex将12abc
解析为两个独立的令牌,因为您没有告诉它不应该这样做。
Lex衍生物,如Flex,通过一个非常简单但有效的算法工作:它们从最后一个标记结束的位置(或文本的开头(开始,并试图从这一点找到匹配最多字符的规则。(如果有多个规则匹配相同数量的字符,则首先选择在"*.l"文件中定义的规则。(就是这样。注意,它不需要匹配一个完整的单词。
这其实是一件好事。这就是为什么在大多数编程语言中不需要显式地分离标记的原因。您可以编写像(2+30L)/2
这样的东西,该语言的lexer将计算出每个令牌的结束位置,而不需要像空白这样的额外提示。(令牌将是(
、2
、+
、30
、L
、)
、/
和2
。(
如果你想在将数字和标识符放在一起的特定情况下禁用这种奇特的机制,你需要创建一个明确禁止它的规则,例如:
{IDENTIFIER} { return IDENTIFIER; }
{INTEGER} { return INTEGER; }
[0-9A-Za-z_]+ { return ERROR; }
请注意,这个新规则还匹配有效的标识符和整数。但是,它不会用于它们,因为它在规则列表中位于它们之下。