Reputation: 524
I would like to evaluate this grammar with 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.
Could this be implemented in 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 (e.g. from where (or: if) the exec function can be inherited or where the AST could be accessed).
Upvotes: 1
Views: 1948
Reputation: 2986
To simplify these operations, Antlr implements a base class called YourGrammarNameBaseVisitor
, using the visitor pattern to descend into nodes of the syntax tree. The BaseVisitor
have a method called Visit
, which implements more-or-less the switch
you have for choosing which rule should be "visited" next. There are also a VisitRuleName
method for each rule in the grammar. 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. 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
.
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);
}
}
Upvotes: 1