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

Why am I getting "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 {


};

此命令行:

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 冲突。

你的规则 ExprElem 是词法分析器规则,而不是解析器规则(它以大写字母开头)并且屏蔽了 Addr 规则,所以,没有 Addr :(

此外,因为 ExprElem 是词法分析器规则,它依赖于 IdNum 规则。因此,当找到 Id 时,ANTLR 词法分析器将其赋予 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   : '#';

据我推测,这绝对不是你想要的,但我只是把它放在这里来说明词法分析器规则如何重用其他规则。

当您对令牌的用途有疑问时,请毫不犹豫地显示识别令牌。这是我经常使用的 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_];