Antlr-如何使用以换行符结尾的自由格式unicode字符串?

Antlr-如何使用以换行符结尾的自由格式unicode字符串?,antlr,antlr3,Antlr,Antlr3,我正在尝试使用Antlr处理一个简单的文本文件,主要是为了重新学习语法设计 文本文件中的每一行由关键字“BY:”和以EOL结尾的字符串组成;该文件以一系列“-”结尾;像这样: BY: abc123@gmail.com BY: myCrazy@#$%ID BY: first_name second_name ------------------- 我对语法的定义如下: grammar authors; prog : author+ DASHES; author : BY STR

我正在尝试使用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;
该语法可以识别第一作者和第二作者,但由于空格的原因,无法识别第三作者。因此,我将字符串更改为包含空格
字符串:(“!”..“~”|“)*
,但随后它停止一起工作(它抛出
MisstingTokenException

我认为这是因为字符串规则在匹配BY之前匹配了整行。但是,当空格被排除在字符串之外时,它为什么会起作用呢?有没有办法我可以强制lexer首先匹配BY规则

一般来说,如何使用以unicode换行符结尾的自由格式字符串(名称也可以有重音字符)

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

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

沿着这些线,考虑C++代码的英文定义>作者< /COD>语法分析器规则和C++语言风格的样板词法规则,单行注释:

  • author
    是一些以“BY:”开头的文本,后跟直到行尾的每个字符
  • 单行注释是以“//”开头的文本,后跟直到行尾的每个字符
此类单行注释的词法规则通常遵循以下形式:

SINGLE_LINE_COMMENT : '//' ~('\r'|'\n')*;
作者行的lexer规则看起来类似:

AUTHOR : 'BY: ' ~('\r'|'\n')*;
但这并不完全正确,因为生成的
AUTHOR
标记将以“BY:”开头,而您只需要后面的内容。您可以将第一个字符修剪掉,或者最好将文本分开开始,如下所示:

AUTHOR: BY RESTOFLINE; //TODO ignore BY
这种分离可以通过lexer片段完成:

AUTHOR  : BY RESTOFLINE; //TODO ignore BY

fragment BY :   'BY: ';
fragment RESTOFLINE  
        :   ~('\r'|'\n')*;
lexer片段的行为类似于私有lexer级宏:它只有在lexer规则中引用时才是“活动”的,并且只有lexer规则才能激活它。(解析器可以按名称引用片段,但通常不应该……但这是另一个主题。)

现在我们只需要
AUTHOR
标记只包含
RESTOFLINE
的文本。使用lexer操作很容易:

    AUTHOR  : BY RESTOFLINE {setText($RESTOFLINE.text);};
现在,在
AUTHOR
规则读取完
RESTOFLINE
片段后,调用
setText
将传出
AUTHOR
标记的文本更改为仅来自
RESTOFLINE
片段的文本

因此,在调整解析器规则以适应新的lexer规则之后,您将得到如下语法:

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')*;
下面是一个快速测试用例:

输入 生产的代币
我不确定这对语法设计的总体帮助有多大,但我希望这有助于说明令牌解析器和字符解析器/词法分析器之间的区别,以及它们各自的一些局限性。

wow-谢谢。让我把它看完,然后回到论坛。不客气。我对
RESTOFLINE
片段做了一个小的修改,因为我在帖子中有两个定义:一个使用
*
,另一个使用
+
。太棒了!谢谢你的清楚解释。我还没有找到学习lexer或parser的好来源,如果您有任何建议,我将非常感谢。再次感谢@我很高兴能帮上忙。我不知道在网上有没有一个完整的、前后衔接的解释,但是Terence Parr擅长从语言实现的角度解释这些主题。我对他不熟悉,但我怀疑这也是一个不错的选择。
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 : -------------------]