首页 文章

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

提问于
浏览
1

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

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

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

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

我正在使用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 回答

  • 1

    处理这种情况的一种方法是使用一些预先定义十六进制令牌的方式 .

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


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

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

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

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


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

    参考文献:

  • 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

  • 0

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

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

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

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

    0000b3ec addeq 情况下, 0000b3ec 将使用 hexaddeq 解析 opcode . 在您的访问者中,您可以适当地处理它们 .

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

相关问题