在 ANTLR 中,词法分析器处理字符,解析器处理抽象标记。因此,每当您发现自己说“从字符 ABC 开始,不加选择地读取每个字符,直到字符 XYZ”时,您可能最好编写词法分析器规则而不是解析器规则,因为“每个字符”对词法分析器有意义,但对解析器。
沿着这些思路,考虑author
解析器规则的英文定义与 C++ 风格的单行注释的样板词法分析器规则之间的相似性:
- An
author
是一些以 'BY: ' 开头的文本,后跟每个字符,直到行尾。
- 单行注释是一些以“//”开头的文本,后跟每个字符,直到行尾。
这种单行注释的词法分析器规则通常遵循以下形式:
SINGLE_LINE_COMMENT : '//' ~('\r'|'\n')*;
作者行的词法分析器规则看起来相似:
AUTHOR : 'BY: ' ~('\r'|'\n')*;
但这不会完全正确,因为AUTHOR
生成的令牌将以“BY:”开头,而您只需要后面的内容。您可以修剪掉第一个字符,或者最好先将文本分开,如下所示:
AUTHOR: BY RESTOFLINE; //TODO ignore BY
这种分离可以用词法分析器片段来完成:
AUTHOR : BY RESTOFLINE; //TODO ignore BY
fragment BY : 'BY: ';
fragment RESTOFLINE
: ~('\r'|'\n')*;
词法分析器片段的行为类似于私有词法分析器级别的宏:只有在词法分析器规则中引用它时它才是“活动的”,并且只有词法分析器规则才能激活它。(解析器可以按名称引用片段,但通常不应该......但这是一个不同的主题。)
现在我们只需要包含' 文本的AUTHOR
标记。RESTOFLINE
使用词法分析器操作很容易:
AUTHOR : BY RESTOFLINE {setText($RESTOFLINE.text);};
现在,在AUTHOR
规则完成读取RESTOFLINE
片段后,setText
调用将传出AUTHOR
令牌的文本更改为仅来自RESTOFLINE
片段的文本。
因此,在调整解析器规则以适应新的词法分析器规则之后,您最终会得到这样的语法:
grammar authors;
prog : author+ DASHES;
author : AUTHOR NEWLINE;
NEWLINE : '\r'? '\n' ;
DASHES : '-'+ NEWLINE;
AUTHOR : BY RESTOFLINE {setText($RESTOFLINE.text);};
fragment BY
: 'BY: ';
fragment RESTOFLINE
: ~('\r'|'\n')*;
这是一个快速测试用例:
输入
BY: abc123@gmail.com
BY: myCrazy@#$%ID
BY: first_name second_name
-------------------
产生的代币
[AUTHOR : abc123@gmail.com] [NEWLINE : ] [AUTHOR : myCrazy@#$%ID] [NEWLINE : ] [AUTHOR : first_name second_name] [NEWLINE : ] [DASHES : -------------------]
我不确定这对您的语法设计有多大帮助,但我希望它有助于显示标记解析器和字符解析器/词法分析器之间的区别,以及它们各自的一些限制。