我正在尝试让我的解析器规则根据我的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; }
对于这种情况,这不是糟糕,但我还有其他一些枚举,它们有更多选项,所以我正在寻找任何方法来简化这一点。
答案 0 :(得分:1)
我对ANTLR并不是很熟悉,但是从文档的快速扫描来看,它似乎与yacc / racc非常相似,它似乎允许在@member
块中定义任意方法,所以我希望你可以使用类似的东西:
dir returns [Direction dir]
: DIR { $result = directionStringToEnum($DIR.text); }
你必须定义一个单独的
public Direction directionStringToEnum(String dir) {
Direction.valueOf(dir.toUpperCase());
}
@member
块中的。您可能会将其概括为处理任意枚举(但可能以任何丑陋的方式,需要Class.forName())。
答案 1 :(得分: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; }
...
;
答案 2 :(得分: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的建议结合起来,但似乎在lexing阶段没有设置state.type
(导致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;
...