【问题标题】:Antlr4 parser fails - need backtracking?Antlr4 解析器失败 - 需要回溯?
【发布时间】:2016-01-20 16:42:13
【问题描述】:

我正在为给定语言开发语法。 我相信我提出的语法应该有效 - 但 Antlr4 有不同的看法。鉴于这些错误,它看起来像是缺少回溯。但是 Antlr4 应该在没有那个的情况下解析......

每个示例都应该只有一个解决方案。解析过程中存在歧义,但是除了一个选项之外的所有选项都应该是死胡同。所以我希望解析器返回并尝试下一个可能的方法。但它只是报告语法错误。

语法小结: 有以“#”分隔的元素。在一个元素之后,可以有一个可选的跳转,由一个 '=' 表示。如果元素本身包含 '#' 或 '=' ,则会通过复制它们来进行转义。 为避免歧义,不允许元素以“#”结尾。所以'###'总是首先是分隔符,然后是下一个元素的转义第一个字符。 '####' 没有分隔符,只有两个转义的 '#' 在名称中。

语法:

grammar ConfigPath;
configpath: toplevelement subprojectelement* EOF;
subprojectelement:  '#' path jump?;
toplevelement:      '#' path jump?;
jump:   jumpcommand '=' jumpdestination;
jumpcommand: '#d' | '#devpath';
jumpdestination: NONHASHCHAR+;              
path: pathelement ( '/' pathelement)*;             
pathelement: escapedCharacterHash* escapedCharacter ;
escapedCharacterHash: escapedCharacter | '##';
escapedCharacter: NONHASHCHAR | '==';
NONHASHCHAR: ~('#' | '/' | '=' );
HASH: '#';
EQ: '=';

测试,解析器错误为 cmets

@Test
public void testTripleHash() throws Exception {
    ConfigpathContext c = parse("#BU/ConfigPath###sub"); 
    // line 1:16 extraneous input '#' expecting {'##', '==', NONHASHCHAR}

    Assert.assertEquals( "#BU/ConfigPath", c.toplevelement().getText() );
    Assert.assertEquals( "###sub", c.subprojectelement().get(0).path().getText() );
}

由于路径元素不能以散列结尾,三重散列的第一个应该关闭顶层元素并开始子项目元素,它以##开头

@Test
public void testDoubleHash() throws Exception {
    ConfigpathContext c = parse("#BU/proj##bla#d==u##bla");
    // line 1:15 mismatched input '==' expecting '='

    Assert.assertEquals( "#BU/proj##bla", c.toplevelement().getText() );
    Assert.assertEquals( "#d==u##bla", c.subprojectelement().get(0).getText() );
}

@Test
public void testJumps() throws Exception {
    ConfigpathContext c = parse("#BU/pro##dla#du##d==la#d=dest");
    // line 1:14 missing '=' at 'u'

    Assert.assertEquals( "#BU/pro##dla", c.toplevelement().getText() );
    Assert.assertEquals( 1, c.subprojectelement().size());
    Assert.assertEquals( "#du##d==la", c.subprojectelement().get(0).path().getText() );
    Assert.assertEquals( "dest", c.subprojectelement().get(0).jump().jumpdestination().getText() );
}


private ConfigpathContext parse(String src) {
    ConfigPathParser parser = new ConfigPathParser(new CommonTokenStream(new ConfigPathLexer(new ANTLRInputStream(src))));
    parser.addErrorListener(new BaseErrorListener() {
        @Override
        public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
            throw new RuntimeException("line " + line + ":" + charPositionInLine + " " + msg );
        }
    });
    return parser.configpath();
}

有没有办法改变语法来接受测试? 还是 Antlr4 只是无法解析这样的语法?回溯的 Antlr3 会找到解决方案吗?

【问题讨论】:

  • 您的语法错误 - 假设解析器应该接受您的示例。只是一些提示 - 解析器和词法分析器检测到第一条规则,因此您需要确保没有歧义。如果您要使用 antlrworks 之类的东西,您会看到识别出哪些规则等

标签: java antlr4 ebnf


【解决方案1】:

语法错误 - 感谢 cantSleepNow 指出。

虽然我没有理解问题的每一个细节,但它似乎与 Lexer 中的歧义有关。解析器能够通过替代回溯来解决歧义,但词法分析器不能。

所以这里是工作语法:

grammar ConfigPath;

configpath: toplevelement subprojectelement* EOF;

subprojectelement:  '#' path jump?;

toplevelement:      '#' path jump?;

jump:   jumpcommand '=' jumpdestination;

jumpdestination : string;

jumpcommand: HASH D 'devpath'?;

path: pathelement ( '/' pathelement)*;             
pathelement: escapedCharacterHash* escapedCharacter ;

escapedCharacterHash: escapedCharacter | HASH HASH;
escapedCharacter: string | EQ EQ;
string  : (NONHASHCHAR | D)+;
NONHASHCHAR: ~('#' | '/' | '=' | 'd' );
D: 'd';
HASH: '#';
EQ: '=';

【讨论】:

  • 字符串的定义看起来令人困惑:NONHASHCHAR 禁止使用 'd',但 D=='d' 是字符串的有效替代方案。
猜你喜欢
  • 2017-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-25
  • 1970-01-01
  • 2016-03-18
  • 2020-10-19
相关资源
最近更新 更多