Antlr4 ANTLR v4:如何在某个标记之后捕获行/文件末尾的任意修剪字符串?

Antlr4 ANTLR v4:如何在某个标记之后捕获行/文件末尾的任意修剪字符串?,antlr4,Antlr4,出于好奇,我正在学习ANTLR,尤其是,4,我正在尝试创建一个简单的语法。我选择了NES(Nintendo娱乐系统)游戏精灵文件的第一次尝试。比方说,这是一个在互联网上找到的侏罗纪公园的游戏精灵文件示例: GZUXXKVS Infinite ammo on pick-up PAVPAGZE More bullets picked up from small dinosaurs PAVPAGZA Fewer bullets picked up from s

出于好奇,我正在学习ANTLR,尤其是,4,我正在尝试创建一个简单的语法。我选择了NES(Nintendo娱乐系统)游戏精灵文件的第一次尝试。比方说,这是一个在互联网上找到的侏罗纪公园的游戏精灵文件示例:

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]*;
好吧,它是可笑的短和容易,但它仍然有两个问题,我可以看到:

  • 作弊描述会导致识别错误,如:'In'处的
    行1:16令牌识别错误,因为没有为语法提供描述规则
  • #
    符号添加到描述中可能会导致忽略行尾的其余部分。至少,
    AAAAAA玩家#1弹药
    只报告
    玩家
    #1弹药
    不幸被解析为注释,但我认为一旦引入描述规则,它就可以修复
我以前添加描述规则的尝试导致了许多错误,我发现了一个无错误但仍然不是一个好的解决方案:

...
codeLine: code description?;
...
description: PRINTABLE+;
...
PRINTABLE: [\u0020-\uFFFE];
...
不幸的是,每个字符都被解析为一个可打印的
描述
规则,用于匹配任意文本,直到行(或文件)的末尾,包括空格,但在左右两侧进行修剪。如果我将
+
添加到可打印的
末尾,则整个文档将被视为无效。我猜
PRINTABLE
可能以某种方式安全地内联到
description
规则,但是
description:('\u0020'..'\uFFFE')+捕获更多信息

应如何声明
描述
规则,使其捕获代码后面行尾的所有字符,但只在左侧和右侧修剪空白(
[\t]
)?简单地说,我将有一个语法,它将解析为如下内容(包括
#
字符,而不是将其解析为注释):

还有一个注意事项,我正在使用:

  • IntelliJ IDEA 2016.1.1 CE
  • IJ插件:antlrv4语法插件1.8.1
  • IJ插件:ANTLRWorks 1.3.1

实际上相当简单,只需使用lexer模式即可。一旦你击中某些标记,改变模式

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

我假设
+
不能出现在注释中。如果使用ANTLRWorks lexer调试器,您可以看到所有标记类型和标记模式都很好地高亮显示

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


在这里,我假设
code
只是
PLUS
之前的一组字符,但显然这很容易改变:)

花了整个不眠之夜,睡眠时间也少了很多,我似乎已经成功地编写了lexer和parser语法。不需要太多解释,请参阅源代码中的注释,因此简而言之:

lexer:

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给出了模式的想法。

这个问题很好,但因为NES,我会给出+2!:)@坎特内斯真是太棒了PI会尽快检查的!谢谢大家!对我来说,那是一个疯狂的夜晚挑战,但似乎奏效了。请看--基于您的想法的解决方案。我不知道它是否写得很好,但它也修复了
+
字符问题,修复了主要的空白问题,允许使用多个代码和可选的描述(可能还有一些我现在想不起来的东西)。文本修剪黑客看起来很糟糕,但我不知道如何解决它或简化更多。谢谢你提出这个想法!
parser grammar NesGameGenieParser;

options {
  tokenVocab=NesGameGenieLexer;
}

file: line+;

line : code comment
     | code PLUS code comment;

code: CODE;

comment: COMMENT;
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
    ;