代码之家  ›  专栏  ›  技术社区  ›  Alex Arvanitidis

Java ANTLR如何忽略部分规则?忽略子规则后的部分

  •  1
  • Alex Arvanitidis  · 技术社区  · 12 年前

    我正在尝试使用ANTLR和Java创建一个编译器。我有一个问题,我有一条规则,但我不能只使用其中的一部分。我有一个命令,例如0:HALT 0,0,0,我想忽略之后的所有其他内容。

    例如0:HALT 0,0,0等等,我想忽略这些等等

    我的规则是:

        rule returns [String value]
        :
        INTEGER':' ro=rocommand i1=INTEGER',' i2=INTEGER ',' i3=INTEGER rest {$value = $ro.text+" "+$i1.text+","+$i2.text+","+$i3.text;   }
        | INTEGER':' rm=rmcommand j1=INTEGER ',' j2=INTEGER '('j3=INTEGER')' rest {$value = $rm.text+" "+$j1.text+","+$j2.text+"("+$j3.text+")"; }
    ;
    

    我的代码是:

    CharStream charStream = new ANTLRStringStream(strLine);
    simulatorLexer lexer = new simulatorLexer(charStream);
    TokenStream tokenStream = new CommonTokenStream(lexer);
    simulatorParser parser = new simulatorParser(tokenStream);
    System.out.println(parser.rule());
    

    我得到的是:

    0: rule:IN 0,0,0
    1: rule:LDC 1,1,0
    line 1:15 no viable alternative at character 'r'
    line 1:18 no viable alternative at character '='
    line 1:15 no viable alternative at character 'i'
    

    对于文本:

    0: rule:IN 0,0,0
    1: rule:LDC 1,1,0 r1=0
    

    因此,它应该正确地解析第一行和第二行,直到0。那么它应该忽略r1=0。到目前为止,它工作正常,但它显示了许多错误,我想消除它们。请帮帮我!

    编辑

    我把整个语法发出来,这样你就能更好地帮助我。我只想认识一下规则部分。

    program:
        rule+
    ;
    
    
    rocommand:
        'HALT'|'IN'|'OUT'|'ADD'|'SUB'|'MUL'|'DIV'|'LDC'
    ;
    
    rmcommand:
        'LD'|'LDA'|'LDC'|'ST'|'JLT'|'JLE'|'JGE'|'JGT'|'JEQ'|'JNE' 
    ;
    
    rest:
      ~('\n'|'\r')* '\r'? ('\n'|EOF)
    ;
    
    rule returns [String value]
        :
        INTEGER':' ro=rocommand i1=INTEGER',' i2=INTEGER ',' i3=INTEGER rest {$value = $ro.text+" "+$i1.text+","+$i2.text+","+$i3.text;   }
        | INTEGER':' rm=rmcommand j1=INTEGER ',' j2=INTEGER '('j3=INTEGER')' rest {$value = $rm.text+" "+$j1.text+","+$j2.text+"("+$j3.text+")"; }
    ;
    
    WS  : (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;};
    INTEGER : '0'..'9'+;
    IGNORELINE : '*' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;};
    
    1 回复  |  直到 3 年前
        1
  •  4
  •   Bart Kiers    12 年前

    该规则有以下几点错误:

    rest:
      ~('\n'|'\r')* '\r'? ('\n'|EOF)
    ;
    

    在解析器规则中 ~ 否定lexer生成的整个令牌集。所以 ~('\n'|'\r') 没有 匹配除之外的单个字符 '\n' '\r' 。它匹配任何 代币 除了匹配的令牌 \r \n

    此外,由于你的lexer '\n' '\r' 在隐藏通道上,这些令牌在解析器中不可用。这意味着 '\n' rest 规则永远无法匹配。

    简而言之:你不能“告诉”你的解析器一行的末尾是什么,因为这些字符被你的 WS 规则这意味着你没有办法正确地写出这样的 休息 语法分析器规则。

    请输入:

    0: IN 0,0,0
    1: LDC 1,1,0 r1=0
    

    (注意,我删除了 'rule:' 的)

    以下令牌由lexer生成:

    token[type=INTEGER text='0']
    token[type=':'     text=':']
    token[type='IN'    text='IN']
    token[type=INTEGER text='0']
    token[type=','     text=',']
    token[type=INTEGER text='0']
    token[type=','     text=',']
    token[type=INTEGER text='0']
    token[type=INTEGER text='1']
    token[type=':'     text=':']
    token[type='LDC'   text='LDC']
    token[type=INTEGER text='1']
    token[type=','     text=',']
    token[type=INTEGER text='1']
    token[type=','     text=',']
    token[type=INTEGER text='0']
    token[type=INTEGER text='1']
    token[type=INTEGER text='0']
    

    因此,这些是解析器规则中可用的令牌。

    请注意以下两个字符: '=' 'r' 无法被lexer匹配,正如您通过查看错误所看到的:

    line 2:13 no viable alternative at character 'r'
    line 2:15 no viable alternative at character '='
    

    一个可能的解决方案是创建一个匹配整数和冒号的lexer规则:

    START : INTEGER ':';
    

    让你的 rule 从这个令牌开始:

    rule
     : START ro=rocommand i1=INTEGER ',' i2=INTEGER ',' i3=INTEGER rest ...
     | ...
     ;
    

    那样,你的 休息 可以匹配除此之外的零个或多个令牌 START 令牌:

    rest
     : ~START*
     ;
    

    为了捕捉 '=' “r” 字符,创建 ANY 规则,并将此规则放在lexer规则的末尾:

    ANY : . ; // match any char
    

    这样,解析器将创建以下解析树:

    enter image description here

    另一个解决方案是创建 LINE_BREAK 令牌:

    LINE_BREAK : '\r'? '\n' | '\r';
    

    (并移除 \r \n个 从…起 操作系统 ,当然!)

    然后这样做:

    rule
     : INTEGER ':' ro=rocommand i1=INTEGER ',' i2=INTEGER ',' i3=INTEGER rest LINE_BREAK ...
     | ...
     ;
    
    rest
     : ~LINE_BREAK*
     ;