我是AntLR的新手。我使用的是 AntLR4 版本。
我编写了以下属性语法,用于识别整数列表并在末尾打印列表的总和。
列表.g4
grammar list;
@header
{
import java.util.List;
import java.util.ArrayList;
}
list
: BEGL (elems[new ArrayList<Integer>()])? ENDL
{
int sum = 0;
if($elems.text != null)
for(Integer i : $elems.listOut)
sum += i;
System.out.println("List Sum: " + sum);
}
;
elems [List<Integer> listIn] returns [List<Integer> listOut]
: a=elem (SEP b=elem
{ listIn.add($b.value); }
)*
{
listIn.add($a.value);
$listOut = $listIn;
}
;
elem returns [int value]
: NUM { $value = $NUM.int; }
;
BEGL : '[';
ENDL : ']';
SEP : ',';
NUM : [0-9]+;
WS : (' '|'t'|'n')+ -> skip;
有效的输入为:
[1, 2, 3]
为了测试我的语法,我正在使用TestRig工具。
现在,我想使用 Visitors 将代码与语法清楚地分开。
我知道我需要将 antlr 与 -visitor 选项一起使用,为我的应用程序生成 Visitor 类。
我想知道如何在 Visitor 方法类中访问给定生产的目录,以及如何将词法分析器、解析器和访问者代码片段"粘合"在一起。
没有操作的语法,并在WS
规则中包含r
:
grammar list;
list
: BEGL elems? ENDL
;
elems
: elem ( SEP elem )*
;
elem
: NUM
;
BEGL : '[';
ENDL : ']';
SEP : ',';
NUM : [0-9]+;
WS : [ trn]+ -> skip;
然后,访问者可能如下所示:
public class SumVisitor extends listBaseVisitor<Integer> {
@Override
public Integer visitList(@NotNull listParser.ListContext ctx) {
return ctx.elems() == null ? 0 : this.visitElems(ctx.elems());
}
@Override
public Integer visitElems(@NotNull listParser.ElemsContext ctx) {
int sum = 0;
for (listParser.ElemContext elemContext : ctx.elem()) {
sum += this.visitElem(elemContext);
}
return sum;
}
@Override
public Integer visitElem(@NotNull listParser.ElemContext ctx) {
return Integer.valueOf(ctx.NUM().getText());
}
}
并且可以按如下方式进行测试:
listLexer lexer = new listLexer(new ANTLRInputStream("[1, 2, 3]"));
listParser parser = new listParser(new CommonTokenStream(lexer));
Integer sum = new SumVisitor().visit(parser.list());
System.out.println("sum=" + sum);
这将打印:
总和=6
制作访客是创建一个扩展YourGrammarBaseVisitor<T>
的类,在您的情况下,ListBaseVisitor<T>
.此类不需要任何方法,但可以覆盖名为 visitX
的方法,其中 X 是规则名称,如 visitElem
。泛型类型T
是visit
调用的返回,但您可以使用Object
返回类型返回任何内容。它会像
class MyVisitor extends ListBaseVisitor<Object> {
@Override
public Object visitElem(ListParser.ElemRuleContext ctx) {
return new Integer(Integer.parseInt(ctx.NUM().getText()));
}
@Override
public Object visitElems(ListParser.ElemsRuleContext ctx) {
ArrayList<Integer> l = new ArrayList<Integer>();
for (ListParser.ElemRuleContext innerCtx : ctx.elem()) {
l.Add((Integer)visitElem(innerCtx));
}
return l;
}
// TODO: visitList method, and suppose it returns an Integer object containing the sum
}
请注意,每个方法都将接收其自己的上下文类型(visitList
方法将接收ListParser.ListRuleContext
等(,并且这些对象包含有关所分析规则的信息。例如,在elem
规则中,a 使用了NUM()
方法,在elems
规则中,我使用了elem()
方法。并不是说多个规则(rule*
或rule+
(的EBNF表示法使该方法成为可以迭代的集合。
为了使用你的新访问者,你只需要像以前一样实例化 Antlr 对象,并使用解析器生成的树访问语法的第一条规则,如下所示:
AntlrInputStream input = new AntlrInputStream(languageInputComingFromSomewhere);
ListLexer lex = new ListLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lex);
ListParser parser = new ListParser(tokens);
// here we are parsing the tree
ListParser.ListRuleContext parseTree = parser.list();
MyVisitor visitor = new MyVisitor();
// here the visitor will do its work, visiting the tree parsed before
Integer sum = (Integer)visitor.visitList(parseTree);
请原谅任何Java错误,我不是Java程序