为什么我会"mismatched input 'addr' expecting {<EOF>, 'addr'}"



给定g4语法:

grammar smaller;
root
  : ( componentDefinition )* EOF;
componentDefinition
  : Addr
    Id?
    Lbrace
    Rbrace
    Semi
  ;

ExprElem
  : Num
  | Id
  ;
Addr : 'addr' {System.out.println("addr");};
Lbrace  : '{' ;
Rbrace  : '}' ;
Semi    : ';' ;
Id      : [a-zA-z0-9_]+ {System.out.println("id");};
Num     : [0-9]+;

//------------------------------------------------
// Whitespace and Comments
//------------------------------------------------
Wspace  : [ t]+ -> skip;
Newline : ('r' 'n'?
        | 'n'
        ) -> skip;

和这个文件来解析

addr basic {

};

此cmdline:

rm *.class *.java ; java -Xmx500M org.antlr.v4.Tool smaller.g4 ; javac *.java ; cat basic | java org.antlr.v4.runtime.misc.TestRig smaller root -tree

我得到这个错误:

line 2:0 mismatched input 'addr' expecting {<EOF>, 'addr'}
(root addr basic { } ;)

如果我删除ExprLem(它在语法中的其他地方没有使用),解析器就会工作:

addr
id
(root (componentDefinition addr basic { } ;) <EOF>)

为什么?请注意,这是一个大大简化的语法版本。通常情况下,表达式确实有目的。

Addr是一个文字,所以它不应该像其他问题一样与Id冲突。

您的规则ExprElem是lexer规则,而不是解析器规则(它以supercase开头),并且正在屏蔽Addr规则,因此,没有Addr:(

此外,由于ExprElem是lexer规则,它依赖于IdNum规则。因此,当找到Id时,ANTLR lexer给它ExprElem令牌类型,而不是Id令牌类型。

因此,有两件事,您可以将ExprElem规则重写为exprElem(假设您想要一个解析器规则):

exprElem : Num | Id;

或者,您可以在ExprElem中使用Id令牌作为规则的一部分,但您需要能够区分ExprElemId的东西(下面的示例,但我确实认为您需要一个解析器规则):

Addr : 'addr' {System.out.println("addr");};
ExprElem
  : Sharp Num  // This token use others but defines its own 'pattern'
  | Sharp Id
  ;
Lbrace  : '{' ;
Rbrace  : '}' ;
Semi    : ';' ;
Id      : [a-zA-z0-9_]+ {System.out.println("id");};
Num     : [0-9]+;
Sharp   : '#';

据我所知,这绝对不是您想要的,但我把它放在这里只是为了说明lexer规则如何重用其他规则。

当您对代币的作用有疑问时,请毫不犹豫地显示识别代币。以下是我经常使用的Java代码片段(在本例中,我将语法命名为test):

public class Main {
    public static void main(String[] args) throws InterruptedException {
        String txt = 
            "addr Basic {n"
            + "n"
            + "};";
        TestLexer lexer = new TestLexer(new ANTLRInputStream(txt));
        CommonTokenStream tokens = new CommonTokenStream(lexer);        
        TestParser parser = new TestParser(tokens);
        parser.root();
        for (Token t : tokens.getTokens()) {
            System.out.println(t);
        }
    }
}

注意:顺便说一句,Num永远不会被识别为Id规则可以匹配相同的东西。试试这个:

Id      : Letter (Letter | [0-9])*;
Num     : [0-9]+;
fragment Letter : [a-zA-z_];

相关内容