2016-06-07 79 views
0

我试图在词法分析器中使用语义谓词来展望一个标记,但不知何故我无法正确理解它。下面是我有:ANRLR4词法分析器语义谓词问题

词法语法

lexer grammar TLLexer; 

DirStart 
    : { getCharPositionInLine() == 0 }? '#dir' 
    ; 

DirEnd 
    : { getCharPositionInLine() == 0 }? '#end' 
    ; 

Cont 
    : 'contents' [ \t]* -> mode(CNT) 
    ; 

WS 
    : [ \t]+ -> channel(HIDDEN) 
    ; 

NL 
    : '\r'? '\n' 
    ; 

mode CNT; 

CNT_DirEnd 
    : '#end' [ \t]* '\n'? 
     { System.out.println("--matched end--"); } 
    ; 

CNT_LastLine 
    : ~ '\n'* '\n' 
     { _input.LA(1) == CNT_DirEnd }? -> mode(DEFAULT_MODE) 
    ; 

CNT_Line 
    : ~ '\n'* '\n' 
    ; 

解析器语法

parser grammar TLParser; 

options { tokenVocab = TLLexer; } 

dirs 
    : (dir 
     | NL 
    )* 
    ; 

dir 
    : DirStart Cont 
     contents 
     DirEnd 
    ; 

contents 
    : CNT_Line* CNT_LastLine 
    ; 

基本上在CNT模式的东西,每一行是免费的形式,但它从来没有开始#end后跟可选空白。基本上我希望在默认词法分析器模式下保持匹配#结束标记。

我的测试输入如下:

#dir contents 
..line.. 
#end 

如果我在GRUN运行此我得到以下

$ grun TL dirs test.txt 
--matched end-- 
line 3:0 extraneous input '#end\n' expecting {CNT_LastLine, CNT_Line} 

所以很明显CNT_DirEnd得到匹配,但不知何故谓词没有检测到。

我知道这个特定任务不需要语义谓词,但这只是不起作用的部分。实际的解析器,虽然它可能没有谓词,但如果我简单地将标签的匹配移动到模式CNT中,将会不那么干净。

谢谢,
Kesha。

+0

看来CNT_Line定义是优雅不匹配'..线..' –

+0

@ThomasG它匹配它,你可以用-gui选项看到它,或者如果你添加打印动作到CNT_Line(然后grun打印它3次,因为它永远不会逃脱CNT模式)和CNT_LastLine(从不打印)。 –

回答

0

我想我想通了。 _input表示原始输入的字符,因此_input.LA返回字符,而不是词法分析标识ID(是否是正确的术语?)。无论哪种方式,由词法分析器返回给解析器的数字与_input.LA返回的值无关,因此谓词失败,除非出现一些奇怪的运气,否则返回的字符值为_input.LA(1)等于词法分析器ID为CNT_DirEnd

我作如下修改词法分析器和现在的作品,尽管它并不像我希望这将是(也许有人知道更好的办法?)

lexer grammar TLLexer; 

@lexer::members { 
    private static final String END_DIR = "#end"; 

    private boolean isAtEndDir() { 
     StringBuilder sb = new StringBuilder(); 

     int n = 1; 
     int ic; 

     // read characters until EOF 
     while ((ic = _input.LA(n++)) != -1) { 
      char c = (char) ic; 
      // we're interested in the next line only 
      if (c == '\n') break; 
      if (c == '\r') continue; 
      sb.append(c); 
     } 

     // Does the line begin with #end ? 
     if (sb.indexOf(END_DIR) != 0) return false; 
     // Is the #end followed by whitespace only? 
     for (int i = END_DIR.length(); i < sb.length(); i++) { 
      switch (sb.charAt(i)) { 
      case ' ': 
      case '\t': 
       continue; 
      default: return false; 
      } 
     } 
     return true; 
    } 
} 

[skipped .. nothing changed in the default mode] 

mode CNT; 

/* removed CNT_DirEnd */ 

CNT_LastLine 
    : ~ '\n'* '\n' 
     { isAtEndDir() }? -> mode(DEFAULT_MODE) 
    ; 

CNT_Line 
    : ~ '\n'* '\n' 
    ;