出于好奇心的缘故,我正在学习ANTLR,特别是4,我正在尝试创建一个简单的语法。我选择NES(Nintentdo娱乐系统)Game Genie文件是第一次尝试。让我们说,这里有一个示例游戏精灵文件,用于在互联网上找到的侏罗纪公园:
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
的末尾,则整个文档被视为无效。我想PRINTABLE
可能会以某种方式安全地内联到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"
还有一点,我正在使用:
答案 0 :(得分:1)
实际上很简单,只需使用词法模式。一旦你击中了某些令牌,就改变模式。
这是词法分析器语法,解析器很容易基于它(文件名是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 :(得分:0)
花费整个不眠之夜,没有多少时间睡觉,我似乎设法写了词法分析器和解析器语法。无需多解释,请参阅源代码中的注释,简而言之:
词霸:
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提供模式的想法。