2016-06-07 71 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' 
    ;