简体   繁体   English

ANTLR4语法:是由听众/访客进行评估还是对递归进行更好的评估?

[英]ANTLR4 grammar: evaluation by listener/visitor or better with recursions?

I would like to evaluate this grammar with ANTLR4: 我想用ANTLR4评估这个语法:

    grammar GrammarStack;

    prog: sentence+;

    sentence:
            ID 'owns' carDef  
    ;   

    carDef:
            'a' car ( 'and' 'a' car)* '.'
    ;

    car:
        type = ('Toyota'  | 'Ford' | 'Hyundai' | 'Chevrolet' | 'Opel' | 'BMW')
    ;

    COLON: ':' ;
    HASH: '#';
    SEMI: ';';
    ID: [a-zA-Z][a-zA-z0-9]+;
    WS  :   [ \t\n\r]+ -> channel(HIDDEN);
    ANY_CHAR : . ; 

And the implementation of the listener: 以及监听器的实现:

    import org.antlr.v4.runtime.ParserRuleContext;
    import org.antlr.v4.runtime.tree.ErrorNode;
    import org.antlr.v4.runtime.tree.TerminalNode;
    import java.util.Stack;


    public class MyGrammarStackListener extends GrammarStackBaseListener {
        Stack lifo = new Stack();

        @Override public void enterCarDef(GrammarStackParser.CarDefContext ctx) { 

        }
        @Override public void exitCarDef(GrammarStackParser.CarDefContext ctx) {
            GrammarStackParser.SentenceContext  sctx = (GrammarStackParser.SentenceContext )ctx.parent;
            System.out.println("this is the carDef for : " + sctx.ID().getText());
            for (int i=0;i<ctx.car().size();i++) {
                if (ctx.car(i)!=null) System.out.println("car no. " + (ctx.car().size()-i)       +  ": " + lifo.pop());
            }
            // here I should definitely also find out, if there are AND options

        }

        @Override public void enterCar(GrammarStackParser.CarContext ctx) {
             lifo.push(ctx.type.getText());
        }
    }

In this example the implementation of the listener is straightforward, although I need a stack to collect the variables. 在此示例中,侦听器的实现很简单,尽管我需要一个堆栈来收集变量。

But if car would be even more complicated (say some cars would have definitions of depending inforamtions), I would prefer to use recursion instead of a listener. 但是,如果汽车变得更加复杂(例如,某些汽车将定义依赖信息的定义),我宁愿使用递归而不是侦听器。

like 喜欢

Object exec(int ruletype, Context ctx) {
    switch (ruleType) ..
      case CARDEF_ :  {
        CarStruct cs = exec(ctx.car);
      }

To say this maybe clearer: I would like to use a recursive function for evualating the rules instead of writing separate functions for each rule. 要说得更清楚一点:我想使用递归函数来求证规则,而不是为每个规则编写单独的函数。 Instead of storing the relevant informations in each specific function I would like to call some eval-function, which goes down the tree (as long as necessary) and gives back the information to the point, where it is needed. 与其将相关信息存储在每个特定的函数中,我不希望调用一些eval函数,该函数沿着树(只要有必要)停下来,然后将信息反馈给需要的地方。

Could this be implemented in ANTLR4? 可以在ANTLR4中实现吗?

I found some code for this type of recursive execution-logic in the book "Language implementation patterns", but there an AST (abstract syntax tree) is used and to me it is not obvious how to apply this to the above example (eg from where (or: if) the exec function can be inherited or where the AST could be accessed). 我在“语言实现模式”一书中找到了这种递归执行逻辑的一些代码,但是使用了AST(抽象语法树),对我来说,如何将其应用于上述示例并不明显(例如,可以继承exec函数的位置(或:如果)或可以访问AST的位置)。

To simplify these operations, Antlr implements a base class called YourGrammarNameBaseVisitor , using the visitor pattern to descend into nodes of the syntax tree. 为了简化这些操作, YourGrammarNameBaseVisitor实现了一个名为YourGrammarNameBaseVisitor的基类,该类使用访问者模式来下降到语法树的节点中。 The BaseVisitor have a method called Visit , which implements more-or-less the switch you have for choosing which rule should be "visited" next. BaseVisitor有一个称为Visit的方法,该方法或多或少地实现了您选择下一步应“访问”哪个规则的switch There are also a VisitRuleName method for each rule in the grammar. 语法中的每个规则也有一个VisitRuleName方法。 The base implementation of these methods will simply descend into the inner rules, but these should be overriden to take some actions during the descend or change the order the rules are visited. 这些方法的基本实现将简单地分解为内部规则,但是应该重写这些方法以在下降过程中采取一些措施,或者更改访问规则的顺序。

Note the Visitor class includes a generic parameter that is the return of each Visit method. 请注意,Visitor类包含一个通用参数,该参数是每个Visit方法的返回。 Sometimes it is useful to put a type like Integer if you are making a very specific visitor, like a calculator grammar, but you can always set the generic parameter to be Object or Void . 有时,如果要创建非常特定的访问者(例如计算器语法),则可以使用Integer类的类型,但是始终可以将通用参数设置为ObjectVoid

In your example grammar, we could have a code similar to this: 在您的示例语法中,我们可以有一个类似于以下的代码:

class MyVisitor extends GrammarStackBaseVisitor<Object> {
    @Override
    public Object visitCarDef(GrammarStackParser.CarDefContext ctx) {
        List<Car> cars = new ArrayList<Car>();
        // now for each car inside carDef
        for (GrammarStackParser.CarContext carCtx : ctx.car()) {
            Car car = (Car)visitCar(carCtx); // here is the recursion!
            cars.add(car);
        }
        return cars;
    }
    @Override
    public Object visitCar(GrammarStackParser.CarContext ctx) {
        String type = car.type().getText();
        return new Car(type);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM