十六进制和操作码之间的ANTLR歧义

时间:2014-06-06 23:35:14

标签: java parsing antlr antlr4 lexical-analysis

我正在为基本的汇编语言编写一个简单的组合词法分析器/解析器。我的问题是,在解析操作码时,我需要解析一些十六进制,这是指令计数器,可能是一个立即值,等等,以及实际的操作码。

基本上,在解析类似add之类的内容时,有一些可能性,我可以使用基本的add或带条件代码addeq的附加内容。这里的问题是add也是一个有效的十六进制序列,所以我得到一个词法分析器错误line 1:4 token recognition error at: 'q'。当然,如果我注释掉HEX规则,并忽略了我需要解析偏移的事实,那么错误就会消失;这就是我找到错误来源的方式。

示例行看起来像这样:0000b3ec addeq。如您所见,它贪婪地将其解析为十六进制,而不是匹配add令牌,然后匹配eq令牌。

我想知道如何切换上下文,以便在这种情况下我可以忽略在此序列中出现十六进制的可能性。我对分离我的词法分析器/语法并不感到兴奋,但如果绝对需要我可以。 pushModepopMode似乎是在正确的方向,或者某种程度上是非贪婪的规则,但我有点不知所措。我非常感谢对这个或这类问题的任何见解,因为匹配的上下文似乎非常适合lexing和正确解析。

我正在使用Antlr4。

    grammar failadd;
    opcode      : add cond_code?
                ;
    cond_code   : COND_CODE;
    add         : ADDS | ADD;

    ADD         : 'add';
    ADDS        : 'adds';

    COND_CODE   : CC_EQ;
    CC_EQ       : 'eq'; 
    HEX32       : SINGLEHEX SINGLEHEX SINGLEHEX SINGLEHEX SINGLEHEX SINGLEHEX SINGLEHEX SINGLEHEX;
    HEX16       : SINGLEHEX SINGLEHEX SINGLEHEX SINGLEHEX;
    SINGLEHEX   : [a-fA-F0-9];
    WS          : [ \n\t\r]+ -> skip;

3 个答案:

答案 0 :(得分:1)

处理此问题的一种方法是在定义十六进制标记的方式中使用一些前瞻。

注意:以下不是解决方案......而是指向解决方案的指针。


如果ANTLR使用Perl / Java / Javascript / etc样式的正则表达式来指定词法标记,那么你可以定义这样的'hex'标记:

[a-fA-F0-9]+($=[^a-zA-Z0-9])

即。十六进制数字后跟零非宽度前瞻,非alpha,非数字。如果您的语法中确实需要不同的“大小”十六进制数,请将+更改为更有限的重复。

请注意,前瞻必须排除十六进制字符以及其他字母,否则最终可能会将“addeq”分析为HEX16("adde") Identifier("q")


经过一些进一步的研究,我意识到ANTLR正在使用一种更简单,更纯粹的正则表达式,它不支持前瞻性语法。但是,我认为您仍然可以使用Java中实现的语法操作来实现前瞻。请参阅参考资料,特别是第二篇。

参考文献:

答案 1 :(得分:1)

好的,我解决了我的问题,我会发布完整的例子,以便人们可以从我所做的事情中学习。为了切换模式,我必须制作一个单独的词法分析器和语法。此外,我将所有相同类型的类似令牌提升为可在解析器语法中使用的通用令牌名称。通过推送和弹出模式,我可以设置适当的令牌类型。感谢您的所有建议,它让我得到了这个相对干净的答案!

词霸:

    lexer grammar testLexer;

    tokens {WHITE_SPACE, TOKEN_OPCODE, TOKEN_ADDRESS, TOKEN_ENCODING, TOKEN_CONDITION_CODE}

    ADDRESS         : [a-fA-F0-9]+ -> type(TOKEN_ADDRESS);
    WS              : [ \t]+ -> type(WHITE_SPACE), skip, pushMode(ENCODE);
    NEWLINE         : [\r\n]+ -> type(WHITE_SPACE), skip;

    mode ENCODE; 
    ENCODING        : [a-fA-F0-9]+ -> type(TOKEN_ENCODING);
    ENC_WS          : [ \t]+ -> type(WHITE_SPACE), skip, popMode, pushMode(OPCODES);

    mode OPCODES;
    OP_WS       : [ \t]+ -> type(WHITE_SPACE), popMode;
    ALL_ADD     : (ADD | ADDS) -> popMode, pushMode(CONDITION_CODES), type(TOKEN_OPCODE) ;
    ADD         : 'add';
    ADDS        : 'adds';

    mode CONDITION_CODES;
    CONDITION_CODE   : 'eq' -> type(TOKEN_CONDITION_CODE);
    WS_COND          : [ \t]+ -> type(WHITE_SPACE), skip, popMode;

解析器:

    parser grammar testParser;
    options { tokenVocab=testLexer; }

    line    : address encoding opcode condition_code?;
    address : ADDRESS;
    encoding: TOKEN_ENCODING;
    opcode  : TOKEN_OPCODE;
    condition_code : TOKEN_CONDITION_CODE;

编辑:添加了图形输出。 Parser tree

答案 2 :(得分:0)

您的分析几乎正确。您的语法不是将add解析为十六进制,而是adde。由于这是四位数,因此符合您的HEX16规则。

除非您能保证有效的四位或八位十六进制序列永远不会是有效的操作码,否则我不确定您是否可以在词法分析器级别执行此操作。在解析十六进制时,你正在谈论切换上下文,但是在没有十六进制的情况下,这不会起作用。

我宁愿在解析器级别上完成这项工作。您需要定义HEX_OR_OPCODE令牌,然后在解析器中,您可以使用结构信息来确定是将令牌解释为前者还是后者:

line: hex? opcode;
hex: HEX_OR_OPCODE;
opcode: HEX_OR_OPCODE;

0000b3ec addeq案例中,0000b3ec将使用hexaddeq解析opcode。在您的访问者中,您可以适当地处理它们。

作为一个注释,我认为这类似于常见的标识符与关键字问题,并且该问题的解决方案也可能适用。

相关问题