在简单的整数列表语法中使用 AntLR4 中的访问者



我是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 。泛型类型Tvisit调用的返回,但您可以使用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程序

相关内容

  • 没有找到相关文章

最新更新