2011-09-09 40 views
2

我試圖讓我的解析器規則根據我的DIR令牌選擇一個枚舉值。有沒有辦法在不爲每個方向創建單獨的,完整的令牌的情況下做到這一點?或者一般來說更清潔的方法將令牌映射爲枚舉值的更好方法?

DIR : (NORTH|SOUTH) (EAST|WEST)? 
| EAST 
| WEST; 

fragment NORTH: N '.'? | N O R T H; 
fragment SOUTH: S '.'? | S O U T H; 
fragment EAST : E '.'? | E A S T; 
fragment WEST : W '.'? | W E S T; 

(有標記片段的每個字母,以方便不區分大小寫)

枚舉是public enum Direction { NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST }

現在我看到的唯一的解決辦法是DIR轉換爲解析器規則,使方向分隔標記:

NORTH: N '.'? | N O R T H; 
SOUTH: S '.'? | S O U T H; 

dir returns [Direction dir] 
: NORTH { dir = Direction.NORTH; } 
| SOUTH { dir = Direction.SOUTH; } 

這不是可怕這種情況下,但我有一些其他枚舉,將有更多的選擇,所以我正在尋找任何方法來簡化這一點。

回答

1

我對ANTLR並不是很熟悉,但是從快速掃描的文檔來看,它似乎和yacc/racc非常相似,它似乎允許在@member塊中定義任意方法,所以我期望在那裏你必須在@member塊定義一個單獨的

public Direction directionStringToEnum(String dir) { 
    Direction.valueOf(dir.toUpperCase()); 
} 

dir returns [Direction dir] 
: DIR { $result = directionStringToEnum($DIR.text); } 

:你可以使用類似。您可以將其推廣爲處理任意枚舉(但可能以任何難看的方式,需要Class.forName())。

+0

問題是,對於西南標記,它的內部文本('$ DIR.text')可能是「SWEST」,「SW」,「southw」 ... –

+0

這是我一直沒有的角度,但本身我不確定它有幫助。 'Direction.valueOf('North')'會起作用,但是'Direction.valueOf('N')'會導致一個'IllegalArgumentException'事情的發生。 –

+0

也許有一個幫助函數,就像.text,它給你'fragment'的名字?從[這裏]的東西(http://www.antlr.org/wiki/display/ANTLR3/Special+symbols+in+actions)看來,你可能能夠獲得當前令牌的各種屬性 – Confusion

1

另一種選擇是重寫令牌的內部文本,以便它們與您的枚舉值相匹配。在你的解析器中,你可以做Direction.valueOf(String)來解析它成爲一個真正的枚舉。

事情是這樣的:

... 

parse 
    : (
     DIR {System.out.println("enum=" + Direction.valueOf($DIR.text));} 
    )* 
    EOF 
    ; 

DIR 
    : (NORTH {setText("NORTH");}  | SOUTH {setText("SOUTH");}  ) 
    (EAST {setText($text+"EAST");} | WEST {setText($text+"WEST");})? 
    | EAST {setText("EAST");} 
    | WEST {setText("WEST");}  
    ; 

... 

以下測試:

import org.antlr.runtime.*; 

public class Main { 
    public static void main(String[] args) throws Exception { 
    String src = "N EaSt S. w NE N.w. Southe SWeSt"; 
    CompassLexer lexer = new CompassLexer(new ANTLRStringStream(src)); 
    CompassParser parser = new CompassParser(new CommonTokenStream(lexer)); 
    parser.parse(); 
    } 
} 
生產

java -cp antlr-3.3.jar org.antlr.Tool Compass.g 
javac -cp antlr-3.3.jar *.java 
java -cp .:antlr-3.3.jar Main 

enum=NORTH 
enum=EAST 
enum=SOUTH 
enum=WEST 
enum=NORTHEAST 
enum=NORTHWEST 
enum=SOUTHEAST 
enum=SOUTHWEST 

這是一個有點笨重,也許。但是,如果你要構建從(許多)不同的令牌令牌(像西南或東北),可能會縮短你的語法不是一樣的東西:

dir returns [Direction dir] 
: NORTH { dir = Direction.NORTH; } 
| SOUTH { dir = Direction.SOUTH; } 
... 
; 
+0

' setText'是我忘記的一招。你是否有理由更喜歡擁有單個「DIR」令牌而不是所有單獨的方向令牌? –

+0

@bemace,不,我不知道。 –

1

Confusion的想法擴大的評論,我確實找到了獲取令牌名稱的方法。所以,如果我爲每個方向的令牌,我應該能夠做這樣的事情:

dir returns [Direction dir] 
: (d=NORTH | d=SOUTH | d=EAST | d=WEST | d=NORTHEAST | d=NORTHWEST | d=SOUTHEAST | d=SOUTHWEST) 
    { dir = Direction.valueOf(getTokenNames()[$d.getType()]); } 

NORTH: N '.'? | N O R T H; 
SOUTH: S '.'? | S O U T H; 
EAST: E '.'? | E A S T; 
WEST: W '.'? | W E S T; 
NORTHEAST : N E | N '.' E '.' | N O R T H E A S T; 
NORTHWEST : N W | N '.' W '.' | N O R T H W E S T; 
SOUTHEAST : S E | S '.' E '.' | S O U T H E A S T; 
SOUTHWEST : S W | S '.' W '.' | S O U T H W E S T; 

這將意味着有更多的令牌,但確實減少了打字。

我也嘗試將它與Bart的建議結合起來,但似乎state.type在lexing階段沒有設置(它導致NullPointerException)。詞法分析器確實將類型ID分配給了片段,但似乎沒有任何方法可以從詞法分析器規則訪問它們。

main_rule[CustomObject object]: d=DIR ... 
      { object.setDirection(Direction.valueof($d.text)); }; 

DIR 
: (NORTH | SOUTH | EAST| WEST | NORTHEAST | NORTHWEST | SOUTHEAST | SOUTHWEST) 
    { setText(getTokenNames()[state.type]); 

fragment NORTH: N '.'? | N O R T H; 
...