为什么这个语法无法解析这个输入?

Why does this grammar fail to parse this input?

我正在为一种小型语言和 Antlr4 定义语法。这个想法是在那种语言中,有一个关键字 "function" 可用于定义函数或在定义参数时用作类型说明符。我希望能够做这样的事情:

function aFunctionHere(int a, function callback) ....

但是,Antlr 似乎不喜欢我在两个不同的地方使用 "function"。据我所知,语法甚至没有歧义。

在下面的语法中,如果我删除第 1 行,生成的解析器将毫无问题地解析样本输入。此外,如果我更改第 2 行或第 3 行中的标记字符串,使它们不相等,解析器就会工作。

我按原样使用语法时遇到的错误:

line 1:0 mismatched input 'function' expecting <INVALID>

"expecting <INVALID>" 是什么意思?

(精简)语法:

grammar test;

begin : function ;

function: FUNCTION IDENTIFIER '(' parameterlist? ')' ;

parameterlist: parameter (',' parameter)+ ;

parameter: BaseParamType IDENTIFIER ;

// Lexer stuff

BaseParamType:
      INT_TYPE
    | FUNCTION_TYPE // <---- LINE 1
    ;

FUNCTION : 'function'; // <---- LINE 2

INT_TYPE : 'int';
FUNCTION_TYPE : 'function'; // <---- LINE 3

IDENTIFIER : [a-zA-Z_$]+[a-zA-Z_[=11=]-9]*;

WS : [ \t\r\n]+ -> skip ;

我正在使用的输入:

function abc(int c, int d, int a)

测试生成解析器的程序:

from antlr4 import *
from testLexer import testLexer as Lexer
from testParser import testParser as Parser
from antlr4.tree.Trees import Trees

def main(argv):
    input = FileStream(argv[1] if len(argv)>1 else "test.in")
    lexer =  Lexer(input)
    tokens = CommonTokenStream(lexer)
    parser = Parser(tokens)

    tree = parser.begin()

    print Trees.toStringTree(tree, None, parser)


if __name__ == '__main__':
    import sys
    main(sys.argv)

只需为令牌使用一个名称 function

令牌只是令牌。孤立地看function,无法判断它是FUNCTION还是FUNCTION_TYPE。由于 FUNCTION 在文件中排在第一位,因此词法分析器使用了它。这使得无法匹配 FUNCTION_TYPE,从而成为无效的标记类型。

解析器将找出令牌的句法角色function。因此,即使可能,也没有必要对同一个标记使用两个不同的词法描述符。

OP中的文法中,BaseParamType也是词法类型,会吸收token的所有用法function,防止FUNCTION在产生式中被识别为function。将其名称更改为 baseParamType,这实际上将其更改为解析器 non-terminal,将允许解析器工作,尽管我认为它可能会以不希望的方式改变解析树。

鉴于 Antlr 的预测解析策略的性质,我理解解析器 "should know" 哪些词法标记在上下文中是可能的反对意见。我远不是 Antlr 专家,所以我不会假装解释为什么它似乎不起作用,但是对于大多数解析器生成器——以及我常用的所有解析器生成器——词法分析作为之前传递给解析,因此文本输入到标记流的转换是在解析器建立上下文之前完成的。 (大多数词法生成器,包括 Antlr,都有用户可以用来构建词法上下文的机制,但恕我直言,这些机制会降低语法可读性,只有在绝对必要时才应使用。)

这是我测试的语法文件:

grammar test;  
begin : function ;
function: FUNCTION IDENTIFIER '(' parameterlist? ')' ;
parameterlist: parameter (',' parameter)+ ;
parameter: baseParamType IDENTIFIER ;

// Lexer stuff
baseParamType:
      INT_TYPE
    | FUNCTION //
    ;

FUNCTION : 'function';
INT_TYPE : 'int';
IDENTIFIER : [a-zA-Z_$]+[a-zA-Z_[=10=]-9]*;
WS : [ \t\r\n]+ -> skip ;