2

我正在尝试使用 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)。

我认为这是因为 STRING 规则在匹配 BY 之前匹配整行。但是,当空间从 STRING 中排除时,为什么它会起作用?有没有办法可以强制词法分析器首先匹配 BY 规则?

一般来说,我怎样才能使用自由格式的 unicode 换行符终止的字符串(名称也可以有重音字符)?

谢谢!PS 我知道使用 java、perl、awk 等很容易做到这一点。

4

1 回答 1

4

在 ANTLR 中,词法分析器处理字符,解析器处理抽象标记。因此,每当您发现自己说“从字符 ABC 开始,不加选择地读取每个字符,直到字符 XYZ”时,您可能最好编写词法分析器规则而不是解析器规则,因为“每个字符”对词法分析器有意义,但对解析器。

沿着这些思路,考虑author解析器规则的英文定义与 C++ 风格的单行注释的样板词法分析器规则之间的相似性:

  • Anauthor是一些以 '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 : -------------------] 

我不确定这对您的语法设计有多大帮助,但我希望它有助于显示标记解析器和字符解析器/词法分析器之间的区别,以及它们各自的一些限制。

于 2012-12-26T18:59:29.757 回答