我试图使用Antlr来处理一个简单的文本文件,主要是为了重新学习语法设计。
文本文件中的每一行都由关键字' BY:'组成。和EOL终止字符串;该文件以一系列' - ';像这样:
BY: abc123@gmail.com
BY: myCrazy@#$%ID
BY: first_name second_name
-------------------
我将语法定义如下:
grammar authors;
prog : author+ DASHES;
author : BY STRING NEWLINE;
BY : 'BY: ';
STRING : ('!'..'~')*;
NEWLINE : '\r'? '\n' ;
DASHES : '-'+ NEWLINE;
这门语法识别出第一作者和第二作者,但因为空间而无法识别第三作者。所以我将STRING更改为包含空格STRING:('!'..'~'|' ')*
,但随后它停止了所有操作(它抛出MisstingTokenException
)。
我认为这是因为在匹配BY之前STRING规则匹配整行。但是,为什么当空间被排除在STRING之外时它会起作用?有没有办法可以强制词法分析器首先匹配BY规则?
一般情况下,我如何使用自由形式的unicode换行符终止字符串(名称也可以包含重音字符)?
谢谢! 附:我知道使用java,perl,awk等很容易。
答案 0 :(得分:3)
在ANTLR中,词法分析器处理字符,解析器处理抽象标记。因此,每当你发现自己说“从字符ABC开始并且不加区分地阅读每个字符直到字符XYZ”时,你最好写一个词法分析器规则而不是解析器规则,因为“每个字符”对词法分析器有意义但不对解析器。
沿着这些思路,考虑author
解析器规则的英文定义与用于C ++风格的单行注释的样板词法律规则之间的相似性:
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
的文本1}}片段。
因此,在调整解析器规则以适应新的词法分析器规则后,您最终会得到如下语法:
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 : -------------------]
我不确定这对语法设计有多大帮助,但是我希望它有助于显示令牌解析器和字符解析器/词法分析器之间的区别,以及每种语法分析器的一些限制。