我正在使用ANTLR4解析Magic卡(org.antlr:antlr4-runtime:4.0)。此最小化版本能够解析Enchant creature
,Enchant black creature
,......以及Protection from creatures
,Protection from legendary creatures
等内容,...
现在我正在尝试启用Protection from black
,这会给我带来麻烦。
我有以下lexer&解析器语法,以及测试类:
词法:
lexer grammar OracleLexer;
Black: 'black';
Creature: 'creature';
Creatures: 'creatures';
Enchant: 'enchant';
From: 'from';
Legendary: 'legendary';
Protection: 'protection';
WS: [ \t\f\r\n]+ -> skip;
分析器:
parser grammar OracleParser;
options {
tokenVocab = OracleLexer;
}
line:
keywordAbility EOF;
keywordAbility:
Enchant singularObject #Enchant |
Protection From pluralObject #Protection; // (A)
// Protection From (pluralObject | objectQuality) #Protection; // (B)
singularObject:
objectQuality? Creature;
pluralObject:
objectQuality? Creatures;
objectQuality:
cardtypeQuality+? |
supertypeQuality+ cardtypeQuality*? |
colorQuality+ supertypeQuality* cardtypeQuality*?;
colorQuality:
Black;
supertypeQuality:
Legendary;
cardtypeQuality:
Creature;
测试类:
import static java.util.Arrays.*;
import static java.util.Collections.*;
import java.util.List;
import org.antlr.v4.runtime.*;
public class OracleParserTest {
private static final List<String> ruleNames = unmodifiableList(asList(OracleParser.ruleNames));
public static void main(String[] args) throws Exception {
parse("Enchant creature");
parse("Enchant black creature");
parse("Protection from black");
parse("Protection from black creatures");
}
private static void parse(String ability) throws RecognitionException {
OracleLexer lexer = new OracleLexer(new ANTLRInputStream(ability.toLowerCase()));
lexer.removeErrorListeners();
lexer.addErrorListener(new BailErrorListener());
OracleParser parser = new OracleParser(new CommonTokenStream(lexer));
parser.removeErrorListeners();
parser.setErrorHandler(new BailErrorStrategy());
RuleContext ctx = parser.line();
System.out.println(ctx.toStringTree(ruleNames));
ctx.inspect(ruleNames);
}
}
在解析器中,有keywordAbility
规则,我将替代(A)
更改为(B)
,突然之间,我再也无法匹配Enchant black creature
。以下是文本形式的所有解析树,如果它有帮助:
original strings
Enchant creature
Enchant black creature
Protection from black
Protection from black creatures
parse trees variant (A)
(line (keywordAbility enchant (singularObject creature)) <EOF>)
(line (keywordAbility enchant (singularObject (objectQuality (colorQuality black)) creature)) <EOF>)
does not parse - expected
(line (keywordAbility protection from (pluralObject (objectQuality (colorQuality black)) creatures)) <EOF>)
parse trees variant (B)
(line (keywordAbility enchant (singularObject creature)) <EOF>)
does not parse - problem!
(line (keywordAbility protection from (objectQuality (colorQuality black))) <EOF>)
(line (keywordAbility protection from (pluralObject (objectQuality (colorQuality black)) creatures)) <EOF>)
这是Enchant black creature
的堆栈跟踪:
Exception in thread "main" org.antlr.v4.runtime.misc.ParseCancellationException
at org.antlr.v4.runtime.BailErrorStrategy.recover(BailErrorStrategy.java:51)
at net.slightlymagic.laterna.oracle.grammar.OracleParser.objectQuality(OracleParser.java:462)
at net.slightlymagic.laterna.oracle.grammar.OracleParser.singularObject(OracleParser.java:235)
at net.slightlymagic.laterna.oracle.grammar.OracleParser.keywordAbility(OracleParser.java:161)
at net.slightlymagic.laterna.oracle.grammar.OracleParser.line(OracleParser.java:79)
at net.slightlymagic.laterna.oracle.grammar.OracleParserTest.parse(OracleParserTest.java:49)
at net.slightlymagic.laterna.oracle.grammar.OracleParserTest.main(OracleParserTest.java:35)
Caused by: org.antlr.v4.runtime.NoViableAltException
at org.antlr.v4.runtime.atn.ParserATNSimulator.noViableAlt(ParserATNSimulator.java:1532)
at org.antlr.v4.runtime.atn.ParserATNSimulator.execATNWithFullContext(ParserATNSimulator.java:816)
at org.antlr.v4.runtime.atn.ParserATNSimulator.execATN(ParserATNSimulator.java:701)
at org.antlr.v4.runtime.atn.ParserATNSimulator.predictATN(ParserATNSimulator.java:389)
at org.antlr.v4.runtime.atn.ParserATNSimulator.adaptivePredict(ParserATNSimulator.java:346)
at net.slightlymagic.laterna.oracle.grammar.OracleParser.objectQuality(OracleParser.java:440)
... 5 more
如何让语法解析这种能力(再次),为什么它不能正常工作?
(我已经尝试删除BailErrorStrategy,这使得它可以工作,但也导致90%的魔法能力解析为误报,并且不能解释为什么该能力之前有效.BailErrorStrategy会干扰回溯吗? )
答案 0 :(得分:1)
ANTLR 4不使用回溯(运行时库实际上不包含任何形式的此类功能的代码)。
我无法使用此消息末尾的测试重现此内容。我的猜测是导致问题的原因之一:
BailErrorStrategy
添加到词法分析器中。它从未打算以这种方式使用。相反,请按照my other answer中的建议将语法错误专门推迟到解析器。测试案例
@Test
public void testCardParsing() throws Exception {
String grammar =
"grammar Oracle;\n" +
"\n" +
"line @init{setErrorHandler(new BailErrorStrategy());} @after {System.out.println($ctx.toStringTree(this));} :\n" +
" keywordAbility EOF;\n" +
"\n" +
"keywordAbility:\n" +
" Enchant singularObject #Enchant |\n" +
"// Protection From pluralObject #Protection; // (A)\n" +
" Protection From (pluralObject | objectQuality) #Protection; // (B)\n" +
"\n" +
"singularObject:\n" +
" objectQuality? Creature;\n" +
"\n" +
"pluralObject:\n" +
" objectQuality? Creatures;\n" +
"\n" +
"objectQuality:\n" +
" cardtypeQuality+? |\n" +
" supertypeQuality+ cardtypeQuality*? |\n" +
" colorQuality+ supertypeQuality* cardtypeQuality*?;\n" +
"\n" +
"colorQuality:\n" +
" Black;\n" +
"\n" +
"supertypeQuality:\n" +
" Legendary;\n" +
"\n" +
"cardtypeQuality:\n" +
" Creature;\n" +
"\n" +
"Black: 'black';\n" +
"Creature: 'creature';\n" +
"Creatures: 'creatures';\n" +
"Enchant: 'enchant';\n" +
"From: 'from';\n" +
"Legendary: 'legendary';\n" +
"Protection: 'protection';\n" +
"\n" +
"WS: [ \\t\\f\\r\\n]+ -> skip;";
String input = "enchant creature";
String found = execParser("Oracle.g4", grammar, "OracleParser", "OracleLexer", "line", input, true);
assertEquals("(line (keywordAbility enchant (singularObject creature)) <EOF>)\n", found);
assertNull(stderrDuringParse);
input = "enchant black creature";
found = execParser("Oracle.g4", grammar, "OracleParser", "OracleLexer", "line", input, false);
assertEquals("(line (keywordAbility enchant (singularObject (objectQuality (colorQuality black)) creature)) <EOF>)\n", found);
assertNull(stderrDuringParse);
input = "protection from black";
found = execParser("Oracle.g4", grammar, "OracleParser", "OracleLexer", "line", input, false);
assertEquals("(line (keywordAbility protection from (objectQuality (colorQuality black))) <EOF>)\n", found);
assertNull(stderrDuringParse);
input = "protection from black creatures";
found = execParser("Oracle.g4", grammar, "OracleParser", "OracleLexer", "line", input, false);
assertEquals("(line (keywordAbility protection from (pluralObject (objectQuality (colorQuality black)) creatures)) <EOF>)\n", found);
assertNull(stderrDuringParse);
}