为什么我得到“不匹配的输入'addr'期待{<eof>,'addr'}”</eof>

时间:2015-03-19 23:52:50

标签: antlr antlr4

考虑到这个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 { } ;)

如果我删除ExprElem(语法中没有其他地方使用),解析器可以工作:

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

为什么呢?请注意,这是语法的大大减少版本。通常,ExprElem确实有目的。

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

1 个答案:

答案 0 :(得分:1)

您的规则ExprElem是词法分析器规则,而不是解析器规则(以upercase开头)并屏蔽Addr规则,因此,没有Addr :(

此外,由于ExprElem是词法分析器规则,它依赖于IdNum规则。因此,当找到Id时,ANTLR词法分析器会为其提供ExprElem令牌类型而不是Id令牌类型。

所以,有两件事,您可以将ExprElem规则重写为exprElem(假设您需要解析器规则):

exprElem : Num | Id;

或者您可以在Id中使用ExprElem令牌作为规则的一部分,但您需要能够区分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_];