首页 文章

ANTLR v4:如何在某个令牌之后将任意修剪过的字符串捕获到行/文件的末尾?

提问于
浏览
1

出于好奇心的缘故,我正在学习ANTLR,尤其是4,我正在尝试创建一个简单的语法 . 我选择NES(Nintentdo娱乐系统)Game Genie文件是第一次尝试 . 让我们说,这里有一个侏罗纪公园的Game Genie文件示例,该文件位于Internet的某个地方:

GZUXXKVS        Infinite ammo on pick-up
PAVPAGZE        More bullets picked up from small dinosaurs
PAVPAGZA        Fewer bullets picked up from small dinosaurs
GZEULOVK        Infinite lives--1st 2 Levels only
ATVGZOSA        Immune to most attacks
VEXASASA + VEUAXASA     3-ball bolas picked up
NEXASASA + NEUAXASA     Explosive multi-shots

这是我正在研究的语法 .

grammar NesGameGenie;

all: lines EOF;

lines: (anyLine? EOL+)* anyLine?;

anyLine: codeLine;

codeLine: code;
code: CODE (PLUS? CODE)*;

CODE: SHORT_CODE | LONG_CODE;
fragment SHORT_CODE: FF FF FF FF FF FF;
fragment LONG_CODE: FF FF FF FF FF FF FF FF;
fragment FF: [APZLGITYEOXUKSVN];

COMMENT: COMMENT_START NOEOL -> skip;
COMMENT_START: [#;];

EOL: '\r'? '\n';
PLUS: '+';
WS: [ \t]+ -> skip;
fragment NOEOL: ~[\r\n]*;

好吧,这简直荒谬,但它仍然有两个我能看到的问题:

  • 作弊描述导致识别错误,如 line 1:16 token recognition error at: 'In' ,因为没有为语法提供描述规则 .

  • # 符号添加到描述中可能会导致忽略其余部分到行尾 . 至少, AAAAAA Player #1 ammo 仅报告 Player#1 ammo 遗憾地被解析为评论,但我认为一旦引入描述规则就可以修复它 .

我之前尝试添加描述规则导致了很多错误,我发现了一个非错误但仍不是一个好的解决方案:

...
codeLine: code description?;
...
description: PRINTABLE+;
...
PRINTABLE: [\u0020-\uFFFE];
...

不幸的是,每个字符都被解析为单个 PRINTABLE ,而我正在寻找的是一个 description 规则来匹配arbirtrary文本,直到包含空格的行(或文件)结尾,但是左右修剪 . 如果我将 + 添加到 PRINTABLE 的末尾,则整个文档被视为无效 . 我猜_1174212_可能会以某种方式安全地内联到 description 规则,但 description: ('\u0020' .. '\uFFFE')+; 捕获的方式更多 .

如何声明 description 规则,让它在代码之后将所有字符捕获到行尾,但仅在左右两侧修剪空格( [ \t] )?简单来说,我会有一个语法解析成类似的东西(包括 # 字符不解析它作为注释):

code=..., description="Infinite ammo on pick-up"
code=..., description="More bullets picked up from small dinosaurs"
code=..., description="Fewer bullets picked up from small dinosaurs"
code=..., description="Infinite lives--1st 2 Levels only"
code=..., description="Immune to most attacks"
code=..., description="3-ball bolas picked up"
code=..., description="Explosive multi-shots"

还有一点,我正在使用:

  • IntelliJ IDEA 2016.1.1 CE

  • IJ插件:ANTLR v4语法插件1.8.1

  • IJ插件:ANTLRWorks 1.3.1

2 回答

  • 0

    实际上很简单,只需使用词法模式 . 一旦击中某些令牌,请更改模式 .

    这是词法分析器语法,解析器很容易基于它(文件名为 NesGameGenieLexer.g4 ):

    lexer grammar NesGameGenieLexer;
    
    CODE: [A-Z]+;
    
    WS : [ ]+ -> skip, mode(COMMENT_MODE);
    
    mode COMMENT_MODE;
    PLUS: '+' (' ')* -> mode(DEFAULT_MODE);
    fragment ANY_CHAR: [a-zA-Z_/0-9=.\-\\ ];
    COMMENT: ANY_CHAR+;
    NEWLINE: [\r\n] -> skip, mode(DEFAULT_MODE);
    

    我假设 + 不能在评论中 . 如果您使用ANTLRWorks lexer调试器,您可以很好地突出显示所有令牌类型和令牌模式 .

    这是解析器语法(文件名为 NesGameGenieParser.g4 ):

    parser grammar NesGameGenieParser;
    
    options {
      tokenVocab=NesGameGenieLexer;
    }
    
    file: line+;
    
    line : code comment
         | code PLUS code comment;
    
    code: CODE;
    
    comment: COMMENT;
    

    在这里我假设 CODE 只是 PLUS 之前的字符集,但很明显这很容易改变:)

  • 1

    我花了整个不眠之夜,没有多少时间睡觉,我似乎设法写了词法分析器和解析器语法 . 无需多解释,请参阅源代码中的注释,简而言之:

    词法分析员:

    lexer grammar NesGameGenieLexer;
    
    COMMENT: [#;] ~[\r\n]+ [\r\n]+ -> skip;
    
    CODE: (SHORT_CODE | LONG_CODE) -> mode(CODE_FOUND_MODE);
    fragment SHORT_CODE: FF FF FF FF FF FF;
    fragment LONG_CODE: FF FF FF FF FF FF FF FF;
    fragment FF: [APZLGITYEOXUKSVN];
    WS: [\t ]+ -> skip;
    
    mode CODE_FOUND_MODE;
    PLUS: [\t ]* '+' [\t ]* -> mode(DEFAULT_MODE);
    // Skip inline whitespaces and switch to the description detection mode.
    DESCRIPTION_LEFT_DELIMITER: [\t ]+ -> skip, mode(DESCRIPTION_FOUND_MODE);
    NEW_LINE_IN_CODE_FOUND_MODE: [\r\n]+ -> skip, mode(DEFAULT_MODE);
    
    mode DESCRIPTION_FOUND_MODE;
    // Greedily grab all non-CRLF characters and ignore trailing spaces - this is a trimming operation equivalent, I guess.
    DESCRIPTION: ~[\r\n]*~[\r\n\t ]+;
    // But then terminate the line and switch to the code detection mode.
    // This operation is probably required because the `DESCRIPTION: ... -> mode(CODE_FOUND_MODE)` seems not working
    NEW_LINE_IN_DESCRIPTION_FOUND_MODE: [\r\n]+ -> skip, mode(DEFAULT_MODE);
    

    解析器:

    parser grammar NesGameGenieParser;
    
    options {
        tokenVocab = NesGameGenieLexer;
    }
    
    file
        : line+
        ;
    
    line
        : code description?
        | code (PLUS code)* description?
        ;
    
    code
        : CODE
        ;
    
    description
        : DESCRIPTION
        ;
    

    它看起来比我想象的要复杂得多,但它似乎完全按照我想要的方式工作 . 另外,我不确定上面的语法是否真的写得好而且惯用 . 感谢@cantSleepNow给出了模式的想法 .

相关问题