首页 文章

Antlr - 如何使用NEWLINE终止的自由格式unicode字符串?

提问于
浏览
1

我正在尝试使用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换行符终止字符串(名称也可以包含重音字符)?

谢谢!附:我知道使用java,perl,awk等很容易 .

1 回答

  • 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 令牌,只需要一个lexer动作就可以轻松地包含 RESTOFLINE 's text. That':

    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 : -------------------]
    

    我不确定这对语法设计有多大帮助,但我希望它有助于显示令牌解析器和字符解析器/词法分析器之间的区别,以及每种语法分析器的一些局限性 .

相关问题