title: ANTLR + Scala : ideas for improvements?
body: <p>I'm trying to find a way to integrate an antlr parser in the realm of Scala.</p>
  
  <p>I've tried visitors coded in scala to build scala objects from the parse tree, but the restriction to have a uniform return type is a no go.</p>
  
  <p>So I took the plunge and decided to build scala objects directly from parser actions  using the java interface of the scala classes.</p>
  
  <p>This blog post was of great help:</p>
  
  <p><a href="http://blog.akquinet.de/2011/07/20/integrating-scala-components-in-a-java-application/" rel="nofollow">http://blog.akquinet.de/2011/07/20/integrating-scala-components-in-a-java-application/</a></p>
  
  <p>Here is what I have obtained:</p>
  
  <p>The scala AST DSL </p>
  
  <pre><code>package toylang.ast
  
  trait TypeExpr
  case object IntType extends TypeExpr
  case object BoolType extends TypeExpr
  
  trait Expr
  case class Ident(name: String) extends Expr
  case class IntNum(repr: String) extends Expr
  case object True extends Expr
  case object False extends Expr
  case class Plus(e: Expr) extends Expr
  case class Minus(e: Expr) extends Expr
  case class Add(l: Expr, r: Expr) extends Expr
  case class Sub(l: Expr, r: Expr) extends Expr
  case class Mul(l: Expr, r: Expr) extends Expr
  case class Div(l: Expr, r: Expr) extends Expr
  case class Pow(e: Expr, exponent: Expr) extends Expr
  case class Not(e: Expr) extends Expr
  case class And(l: Expr, r: Expr) extends Expr
  case class Or(l: Expr, r: Expr) extends Expr
  case class Implies(l: Expr, r: Expr) extends Expr
  
  case class Ite(c: Expr, t: Expr, eif: List[Elsif], e:Expr) extends Expr
  case class Elsif(c: Expr, t: Expr) 
  
  case class Neq(l: Expr, r: Expr) extends Expr
  case class Eq(l: Expr, r: Expr) extends Expr
  case class Lt(l: Expr, r: Expr) extends Expr
  case class Le(l: Expr, r: Expr) extends Expr
  case class Gt(l: Expr, r: Expr) extends Expr
  case class Ge(l: Expr, r: Expr) extends Expr
  
  trait Stmt
  case class DefStmt(id: Ident, t: TypeExpr, e: Expr) extends Stmt
  </code></pre>
  
  <p>The antlr grammar with java actions calling scala constructors</p>
  
  <pre><code>grammar ToyLang;
  
  // lexer customized header
  @lexer::header{
  package toylang.parser.antlr;
  }
  
  // parser customized header
  @parser::header{
  package toylang.parser.antlr;
  import toylang.ast.*;
  import java.util.List;
  import java.util.ArrayList;
  import utils.Fun;
  import utils.Conv;
  }
  
  @parser::members {
  
  // a class which extracts an object from a context an can be mapped over a java list.
  Fun defStmtMap = new Fun&lt;DefStmtContext, Stmt&gt; () {
      public Stmt apply(DefStmtContext ctx) {
          return ctx.result;
      }
  };
  
  // a class which extracts an object from a context an can be mapped over a java list.
  Fun elsifMap = new Fun&lt;ElsifContext, Elsif&gt; () {
      public Elsif apply(ElsifContext ctx) {
          return ctx.result;
      }
  };
  
  }
  
  program returns [scala.collection.immutable.List&lt;Stmt&gt; result]
      : sl+=defStmt sl+=defStmt* EOF { 
              $result = Conv.scalaList(Conv.map($sl, defStmtMap)); 
          }
      ;
  
  type returns[TypeExpr result]
      : 'int'  { $result = IntType$.MODULE$; }
      | 'bool' { $result = IntType$.MODULE$; }
      ;
  
  defStmt returns[Stmt result]
      : id=ident ':' t=type  op=DEFINE e=expr ';' { 
              $result = DefStmt$.MODULE$.apply($id.ctx.result, $t.ctx.result, $e.ctx.result); 
          }
      ; 
  
  expr returns[Expr result]
      : lit=TRUE
          { $result = True$.MODULE$; }
  
      | lit=FALSE
          { $result = False$.MODULE$; }                        
  
      | lit=INT_LIT
          { $result = IntNum$.MODULE$.apply($lit.text); }        
  
      | id=ident
          { $result = $id.ctx.result; }
  
      | op='(' e=expr ')'
          { $result = $e.ctx.result; }
  
      | IF c=expr THEN t=expr ei+=elsif* ELSE e=expr 
          {
              scala.collection.immutable.List&lt;Elsif&gt; l = Conv.scalaList(Conv.map($ei, elsifMap)); 
              $result = Ite$.MODULE$.apply($c.ctx.result, $t.ctx.result, l, $e.ctx.result);
          }
  
      | op=(ADD|SUB) e=expr
          { 
              switch($op.type) {
                  case ADD : $result = Plus$.MODULE$.apply($e.ctx.result); break;  
                  case SUB : $result = Minus$.MODULE$.apply($e.ctx.result); break;  
              }
          }
  
      |&lt;assoc=right&gt; l=expr op=CARRET r=expr  
          { $result = Pow$.MODULE$.apply($l.ctx.result, $r.ctx.result); }
  
      |&lt;assoc=left&gt; l=expr op=(STAR|SLASH) r=expr  
          { 
              switch($op.type) {
                  case STAR : $result = Mul$.MODULE$.apply($l.ctx.result, $r.ctx.result); break;  
                  case SLASH : $result = Div$.MODULE$.apply($l.ctx.result, $r.ctx.result); break;  
              }
          }
      |&lt;assoc=left&gt; l=expr op=(ADD|SUB) r=expr 
          {
              switch($op.type) {
                  case ADD : $result = Add$.MODULE$.apply($l.ctx.result, $r.ctx.result); break;  
                  case SUB : $result = Sub$.MODULE$.apply($l.ctx.result, $r.ctx.result); break;  
              }
          }
      | l=expr op=(NEQ|EQ|LT|LE|GT|GE) r=expr
          {
              switch($op.type) {
                  case NEQ : $result = Neq$.MODULE$.apply($l.ctx.result, $r.ctx.result); break;  
                  case EQ : $result = Eq$.MODULE$.apply($l.ctx.result, $r.ctx.result); break;  
                  case LT : $result = Lt$.MODULE$.apply($l.ctx.result, $r.ctx.result); break;  
                  case LE : $result = Le$.MODULE$.apply($l.ctx.result, $r.ctx.result); break;  
                  case GT : $result = Gt$.MODULE$.apply($l.ctx.result, $r.ctx.result); break;  
                  case GE : $result = Ge$.MODULE$.apply($l.ctx.result, $r.ctx.result); break;  
              }
          }
  
      | op=NOT e=expr
          { $result = Not$.MODULE$.apply($e.ctx.result); }
  
      |&lt;assoc=left&gt; l=expr op=AND r=expr
          { $result = And$.MODULE$.apply($l.ctx.result, $r.ctx.result); }
  
      |&lt;assoc=left&gt; l=expr op=OR r=expr
          { $result = Or$.MODULE$.apply($l.ctx.result, $r.ctx.result); }
  
      |&lt;assoc=left&gt; l=expr op=IMPLIES r=expr
          { $result = Implies$.MODULE$.apply($l.ctx.result, $r.ctx.result); }
      ;
  
  elsif returns[Elsif result]
      : op=ELSIF c=expr THEN t=expr 
          {
              $result = Elsif$.MODULE$.apply($c.ctx.result, $t.ctx.result);
          }
      ;
  
  ident returns [Ident result]
          : IDENT { $result = Ident$.MODULE$.apply($IDENT.text); }
          ;
  LT: '&lt;' ;
  LE: '&lt;=' ;
  GT: '&gt;' ;
  GE: '&gt;=' ;
  EQ: '=' ;
  NEQ: '!=' ;
  ADD: '+';
  AND: 'and';
  DEFINE: ':=';
  CARRET: '^';
  ELSE: 'else';
  ELSIF: 'elsif';
  FALSE: 'false';
  IF: 'if';
  IMPLIES: 'implies';
  KW_BOOL: 'bool';
  KW_INT: 'int';
  NOT: 'not';
  OR: 'or';
  SLASH: '*';
  STAR: '*';
  SUB: '-';
  THEN: 'then';
  TRUE: 'true';
  
  INT_LIT
      :'0' 
      |[1-9][0-9]*
      ; 
  
  IDENT:[_a-zA-Z][_a-zA-Z0-9]*;
  WS: [ \t\f\r\n]+ -&gt; skip;
  //NL: '\r'? \n;
  </code></pre>
  
  <p>And last the two java utility classes, Conv and Fun, Conv contain the code to convert a java list to a well typed scala list, Fun is an interface for actions that get mapped over the java list before conversion to a scala list.</p>
  
  <pre><code>package utils;
  
  import java.util.List;
  import java.util.ArrayList;
  
  public final class Conv&lt;A, B&gt; {
  
      public static &lt;T&gt; scala.collection.immutable.List&lt;T&gt; scalaList(List&lt;T&gt; javaList) {
          return scala.collection.JavaConversions.iterableAsScalaIterable(javaList).toList();
      }
  
      public static &lt;A,B&gt; List&lt;B&gt; map(List&lt;A&gt; from, Fun&lt;A,B&gt; convert) {
  
          ArrayList&lt;B&gt; res = new ArrayList&lt;B&gt;();
  
          for (A fromElem : from) {
              res.add(convert.apply(fromElem));
          }
          return res;
      }   
  }
  
  package utils;
  
  public interface Fun&lt;A, B&gt; {
      B apply(A input);
  }
  </code></pre>
  
  <p>The scala code which calls the antlr parser:</p>
  
  <pre><code>package toylang.parser
  
  import org.antlr.v4.runtime._
  import org.antlr.v4.runtime.tree._
  import org.stringtemplate.v4._
  import toylang.parser.antlr._
  import java.io.FileInputStream
  import scala.collection.JavaConverters._
  
  
  class Parser( arg: String ) {
    val input = new ANTLRInputStream(new FileInputStream(arg))
    val lexer = new ToyLangLexer(input)
    val tokens = new CommonTokenStream(lexer)
    val parser = new ToyLangParser(tokens)
    val prog = parser.program
    println (prog.result)
  }
  </code></pre>
  
  <p>Any idea for improvement on the extract-results-from-contex-java-to-scala list conversion would be welcome. Making it fully generic would be great, perhaps using the java reflection API. Ideally I would like to use a single statement to say: extract field named 'foo' from each antlr rule context objects (or token) of this list and convert the result to a scala list.</p>
  
  <p>I've looked all around the net and found no tuto on how to do that.</p>
  
  <p>Regards,</p>