【问题标题】:Using Visitors in AntLR4 in a Simple Integer List Grammar在简单整数列表语法中使用 AntLR4 中的访问者
【发布时间】:2014-12-10 16:19:47
【问题描述】:

我是 AntLR 的新手。我用的是 AntLR4 版本。

我编写了以下属性语法,它识别整数列表并在末尾打印列表的总和。

list.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 Tool。

现在,我想用Visitors来清楚地把代码和语法分开。

我知道我需要使用带有 -visitor 选项的 antlr 来为我的应用程序生成访问者类。

我想知道如何在访问者方法类中访问给定产品的属性,以及如何将词法分析器、解析器和访问者代码片段“粘合”在一起。

【问题讨论】:

    标签: java antlr grammar antlr4


    【解决方案1】:

    您的语法没有动作,并且在WS 规则中包含\r

    grammar list;
    
    list
     : BEGL elems? ENDL
     ;
    
    elems
     : elem ( SEP elem )*
     ;
    
    elem
     : NUM
     ;
    
    BEGL : '[';
    ENDL : ']';
    SEP  : ',';
    NUM  : [0-9]+;
    WS   : [ \t\r\n]+ -> 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);
    

    将打印:

    sum=6

    【讨论】:

      【解决方案2】:

      创建访客是创建一个扩展 YourGrammarBaseVisitor&lt;T&gt; 的类,在您的情况下是 ListBaseVisitor&lt;T&gt;。此类不需要任何方法,但可以覆盖称为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 规则中使用了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程序员

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多