Fanthomas90
Fanthomas90

Reputation: 45

Delegate some evaluation to the visitor of the imported grammar in ANTLR 4

I am currently trying to implement multiple different grammars which share some concepts.

Let's say they look like this - this has nothing to do with my actual grammar:

grammar pathBase;

extension
  : 'txt'                     #txtExt
  | 'tar'                     #tarExt
  | 'gz'                      #gzExt
  | extension '.' extension   #compositeExt
  ;

And say there are some os specific path grammars. One for windows:

grammar win;
import base;

path: DRIVE ('\' NAME)+ ('.' extension)?;

And one for linux:

grammar linux;
import base;

path: ('/' NAME)+ ('.' extension)?;

Now I have two different grammars which have two different parsers (WinParser and LinuxParser) with completely seperate contexts for extension. Since I want to avoid code repetition I now create a PathBaseBaseVisitor which reacts to all the rules defined in this base grammar:

visitTxtExt, visitTarExt, visitGzExt, visitCompositeExt

They all only accept PathBaseParser.*Context objects.

This means when I implement my Visitors for win and linux which only provide WinParser.*Context and LinuxParser.*Context (which are not subclasses of the contexts in PathBaseParser), I can't seem to be able to use the PathBaseBaseVisitor I just implemented by simply calling visit() on the given ExtensionContext.

Does anybody have an idea how to solve this problem?

The only thing I seem to come up with is calling getText() on the context and reparsing it with the PathBaseParser but that seems dirty to me.

Upvotes: 2

Views: 360

Answers (1)

GRosenberg
GRosenberg

Reputation: 6001

To handle multiple syntax variants, as you appear to be trying to do, your target should be to construct a single grammar that imports the subset of rules that defines a variant. That is, in your example, 'base' should be your primary grammar and the imported grammar should contain the 'path' rule. No other practical way to avoid the inheritance issues you encountered.

To be certain, if the difference in variants is large or likely to become large (eg Python 2 -> Python 3), the effort to maintain interdependent grammars will quickly outweigh the perceived benefits. Conversely, if small, you can use predicates to handle the differences

path : { os().isWindows() }? DRIVE ('\' NAME)+ ('.' extension)?
     | { os().isLinux() }?   DRIVE ('/' NAME)+ ('.' extension)?
     | ...
     ;

Upvotes: 1

Related Questions