为什么我的ANTLR4语法的这个加法打破了一个不相关的规则替代?

时间:2014-10-01 13:42:13

标签: java parsing antlr antlr4

我正在使用ANTLR4解析Magic卡(org.antlr:antlr4-runtime:4.0)。此最小化版本能够解析Enchant creatureEnchant black creature,......以及Protection from creaturesProtection 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会干扰回溯吗? )

1 个答案:

答案 0 :(得分:1)

ANTLR 4不使用回溯(运行时库实际上不包含任何形式的此类功能的代码)。

我无法使用此消息末尾的测试重现此内容。我的猜测是导致问题的原因之一:

  1. 您已将BailErrorStrategy添加到词法分析器中。它从未打算以这种方式使用。相反,请按照my other answer中的建议将语法错误专门推迟到解析器。
  2. 您可能正在使用旧版本的ANTLR,该版本遭受了已修复的错误。
  3. 只有在使用单独的词法分析器和解析器语法时才会出现问题(测试使用组合语法)。
  4. 测试案例

    @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);
    }