处理 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 将通用名称分配给隐式创建的文字,这使得很难找出哪个标记属于哪个文字。
我有一个语法可以解析以下片段(作为示例):
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 将通用名称分配给隐式创建的文字,这使得很难找出哪个标记属于哪个文字。