处理 ANTLR4 中的语法歧义

Handling Grammar Ambiguity in ANTLR4

我有一个语法可以解析以下片段(作为示例):

vmthread programm_start
{
    CALL main
}

subcall main
{
    // Declarations
    DATAF i

    CALL i

    // Statements
    MOVEF_F 3 i
}

问题是CALL语句之间的歧义。此操作代码在 vmthread 部分(并且仅在 CALL!)中有效,但在那些子调用部分中也有效。如果我定义一个包含所有操作码的 OP_CODES 标记和一个额外的 OC_CALL 标记,词法分析器无法处理这种情况(显然)。

以下列表是我的语法片段(第一个词法分析器,第二个解析器):

VMTHREAD
    : 'vmthread'
    ;

SUBCALL
    : 'subcall'
    ;

CURLY_OPEN
    : '{'
    ;

CURLY_CLOSE
    : '}'
    ;

OP_CODES
    : 'DATA8'
    | 'DATAF'
    | 'MOVE8_8'
    | 'MOVEF_F'
    | 'CALL'
    ;

OC_CALL
    : 'CALL'
    ;

lms
    : vmthread subcalls+
    ;

vmthread
    : VMTHREAD name = ID CURLY_OPEN vmthreadCall CURLY_CLOSE
    ;

vmthreadCall
    : oc = OC_CALL name = ID
    ;

subcalls
    : SUBCALL name = ID CURLY_OPEN ins = instruction* CURLY_CLOSE
    ;

//instruction+
instruction
    : oc = OP_CODES args = argumentList
    ;

argumentList
    : arguments+
    ;

arguments
    : INTEGER
    | NUMBER
    | TEXT
    | ID
    ;

为了继续我的工作,我将 vmthreadCall 解析器规则中的 OC_CALL 标记切换为 OP_CODES 标记。这暂时解决了问题,因为代码是自动生成的。但是用户可能会键入此代码,因此这可能会出错。

是否有解决方案,或者我应该将验证移到解析器中。在那里我可以很容易地确定 vmthread 部分中的语句是否仅包含调用语句。

澄清一下:在 vmthread 中只允许调用。在子调用中(可能不止一个)允许每个操作码(CALL +定义的每个其他操作码)。而且我不想区分那些不同的 CALL 语句。我知道这在上下文无关语法中是不可能的。我将在解析器中处理它。我只想将 vmthread 限制为一个 CALL 语句并允许子调用中的所有语句(所有操作代码)。希望这更清楚。

像这样更改词法分析器规则:

OP_CODES
    : 'DATA8'
    | 'DATAF'
    | 'MOVE8_8'
    | 'MOVEF_F'
    | OP_CALL
    ;

OC_CALL
    : 'CALL'
    ;

或者这样:

OP_CODES
    : 'DATA8'
    | 'DATAF'
    | 'MOVE8_8'
    | 'MOVEF_F'
    | CALL
    ;

OC_CALL
    : CALL
    ;

fragment CALL: 'CALL';

顺便说一句,我建议您为文字创建明确的词法分析器规则(例如那个 CALL 片段),这将使以后的处理更容易。 ANTLR 将通用名称分配给隐式创建的文字,这使得很难找出哪个标记属于哪个文字。