ANTLR + Scala: ideas for improvements?

I am trying to find a way to integrate the antlr parser in the Scala scope.

I've tried scala coded visitors to build scala objects from a parse tree, but the constraint on the uniform return type is not.

So I took the plunge and decided to create scala objects directly from the parser actions using the java interface of the scala classes.

This blog post was of great help:

http://blog.akquinet.de/2011/07/20/integrating-scala-components-in-a-java-application/

This is what I got:

scala AST DSL

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

      

Antlr grammar with java actions calling scala constructors

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<DefStmtContext, Stmt> () {
    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<ElsifContext, Elsif> () {
    public Elsif apply(ElsifContext ctx) {
        return ctx.result;
    }
};

}

program returns [scala.collection.immutable.List<Stmt> 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<Elsif> 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;  
            }
        }

    |<assoc=right> l=expr op=CARRET r=expr  
        { $result = Pow$.MODULE$.apply($l.ctx.result, $r.ctx.result); }

    |<assoc=left> 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;  
            }
        }
    |<assoc=left> 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); }

    |<assoc=left> l=expr op=AND r=expr
        { $result = And$.MODULE$.apply($l.ctx.result, $r.ctx.result); }

    |<assoc=left> l=expr op=OR r=expr
        { $result = Or$.MODULE$.apply($l.ctx.result, $r.ctx.result); }

    |<assoc=left> 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: '<' ;
LE: '<=' ;
GT: '>' ;
GE: '>=' ;
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]+ -> skip;
//NL: '\r'? \n;

      

And finally, the two Java utility classes, Conv and Fun, Conv contain the code for converting a java list to a well-typed scala list, Fun is an interface for actions that are rendered over the java list before converting to scala.

package utils;

import java.util.List;
import java.util.ArrayList;

public final class Conv<A, B> {

    public static <T> scala.collection.immutable.List<T> scalaList(List<T> javaList) {
        return scala.collection.JavaConversions.iterableAsScalaIterable(javaList).toList();
    }

    public static <A,B> List<B> map(List<A> from, Fun<A,B> convert) {

        ArrayList<B> res = new ArrayList<B>();

        for (A fromElem : from) {
            res.add(convert.apply(fromElem));
        }
        return res;
    }   
}

package utils;

public interface Fun<A, B> {
    B apply(A input);
}

      

The scala code that calls the antlr parser:

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)
}

      

Any idea of โ€‹โ€‹improving the conversion of the extract-results-from-contex-java-to-scala list is 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 a field named "foo" from each antlr rule (or token) context object of that list, and convert the result to a scala list.

I've looked all over the net and haven't found the slightest way to do this.

Hello,

+3


source to share


3 answers


just to give you a head, I put together a hack to embed scala actions directly into g4 grammar.

antlr2scala_v0.1.tar.gz

I modified ANTLRv4Lexer.g4 and ANTLRv4Parser.g4 found here:

https://github.com/antlr/grammars-v4/tree/master/antlr4

to allow special comments like

//! <scala code>

      

which must be added first to the file to define the scala listener header,

just before the list of rules for defining scala listener elements,



and on the right before or after the rule alternatives, specify the input and output actions for each alternative (which should be flagged).

Based on this modified format, I developed a tool that extracts and packages the code for a scala listener from custom comments.

In practice, the extended scala actions file looks like this:

//! // scala listener header
//! package toylang.parser.antlr
//! import toylang.ast._
//! import scala.collection.JavaConversions._

grammar ToyLang;

@lexer::header{
package toylang.parser.antlr;
}

@parser::header{
package toylang.parser.antlr;
import toylang.ast.*; // import scala ast symbols
}

//! // scala listener members
//! // stores the result of a successfull parse
//! var result: Option[List[Stmt]] = None 

program 
locals [scala.collection.immutable.List<Stmt> result]
    : sl+=defStmt sl+=defStmt* EOF # ProgramRule
        //! // exitAction for alternative
        //! ctx.result = (ctx.sl.view  map { _.result }).toList
        //! result = Some(ctx.result)

    ;

type 
locals [TypeExpr result]
    : 'int'  # IntType
        //! ctx.result = IntType
    | 'bool' # BoolType
        //! ctx.result = BoolType
    ;

defStmt 
locals[Stmt result]
    : 
        //! // entry action for alternative
        //! println("About to parse a statement!")
        //!
        id=ident ':' t=type  d=def? ';' # DefStmtRule
        //! // exit action for alternative
        //! val d = ctx.d match {
        //!   case null => None
        //!   case e@_ => Some(e.result)
        //! }
        //! ctx.result = DefStmt(ctx.id.result, ctx.t.result, d)
    ; 

def
locals [Expr result]
    : op=DEFINE e=expr  # DefRule
        //! ctx.result = ctx.e.result
    ;

expr 
locals[Expr result]
    : e=boolNum  # BoolNumExpr
        //! ctx.result = ctx.e.result

    | e=intNum   # IntNumxpr
        //! ctx.result = ctx.e.result

    | e=ident    # IdentExpr
        //! ctx.result = ctx.e.result

    | op='(' e=expr ')' # ParenExpr
        //! ctx.result = ctx.e.result

    | op=(ADD|SUB) e=expr # UnopArithExpr
        //! import ToyLangParser.{ADD, SUB}
        //!  val op = ctx.op.getType match {
        //!  case ADD => Plus
        //!  case SUB => Minus
        //! }
        //! ctx.result = op(ctx.e.result)

    |<assoc=right> l=expr op=CARRET r=expr  # PowerExpr
        //! ctx.result = Pow(ctx.l.result, ctx.r.result)

    |<assoc=left> l=expr op=(STAR|SLASH) r=expr # MulDivExpr
        //! import ToyLangParser.{STAR, SLASH}
        //!  val op = ctx.op.getType match {
        //!  case STAR => Mul
        //!  case SLASH => Div
        //! }
        //! ctx.result = op(ctx.l.result, ctx.r.result) 

    |<assoc=left> l=expr op=(ADD|SUB) r=expr # AddSubExpr
        //! import ToyLangParser.{ADD, SUB}
        //!  val op = ctx.op.getType match {
        //!  case ADD => Add
        //!  case SUB => Sub
        //! }
        //! ctx.result = op(ctx.l.result, ctx.r.result) 

    | l=expr op=(NEQ|EQ|LT|LE|GT|GE) r=expr # RelExpr
        //! import ToyLangParser.{NEQ,EQ,LT,LE,GT,GE}
        //!  val op = ctx.op.getType match {
        //!  case NEQ => Neq
        //!  case EQ => Eq
        //!  case LT => Lt
        //!  case GT => Gt
        //!  case LE => Le
        //!  case GE => Ge
        //! }
        //! ctx.result = op(ctx.l.result, ctx.r.result) 

    | op=NOT e=expr   # NotExpr
        //! ctx.result = Not(ctx.e.result)

    |<assoc=left> l=expr op=AND r=expr # AndExpr
        //! ctx.result = And(ctx.l.result, ctx.r.result)

    |<assoc=left> l=expr op=OR r=expr  # OrExpr
        //! ctx.result = Or(ctx.l.result, ctx.r.result)

    |<assoc=left> l=expr op=IMPLIES r=expr # ImpliesExpr
        //! ctx.result = Implies(ctx.l.result, ctx.r.result)

    | IF c=expr THEN t=expr ei+=elsif* ELSE e=expr  # IteExpr
        //! val elsifList = ctx.ei.view  map { _.result } 
        //! ctx.result = Ite(ctx.c.result, 
        //!                  ctx.t.result, 
        //!                  elsifList.toList, 
        //!                  ctx.e.result)
   ;

elsif 
locals [Elsif result]
    : op=ELSIF c=expr THEN t=expr # ElsifRule
        //! ctx.result = Elsif(ctx.c.result, ctx.t.result)
    ;

ident
locals [Ident result]
    : id=IDENT # IdentRule
        //! ctx.result = Ident(ctx.id.getText)
    ;

intNum
locals [IntNum result]
    : num=INT_NUM # IntNumRule
        //! ctx.result = IntNum(ctx.num.getText)
    ;

boolNum
locals [BoolNum result]
    : num=(TRUE | FALSE) # BoolNumRule
        //! ctx.result = BoolNum(ctx.num.getText)

    ;

COMMENT: (EOL | MLC) -> skip ;
fragment MLC: '/*' ( COMMENT | . )*? '*/' ;
fragment EOL: '//' .*? '\n' ;

LT: '<' ;
LE: '<=' ;
GT: '>' ;
GE: '>=' ;
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_NUM :'0' |[1-9][0-9]* ; 
IDENT:[_a-zA-Z][_a-zA-Z0-9]* ;
WS: [ \t\f\r\n]+ -> skip;

      

The selected listener looks like this:

// !!! DO NOT EDIT!!! 
// Code generated from grammar ToyLang by antlr4scala
// 

 // scala listener header
 package toylang.parser.antlr
 import toylang.ast._
 import scala.collection.JavaConversions._


class  Listener extends ToyLangBaseListener {

     // scala listener members
     // stores the result of a successfull parse
     var result: Option[List[Stmt]] = None 


    override def enterDefStmtRule( ctx: ToyLangParser.DefStmtRuleContext ): Unit = {
         // entry action for alternative
         println("About to parse a statement!")

    }


    override def exitProgramRule( ctx: ToyLangParser.ProgramRuleContext ): Unit = {
         // exitAction for alternative
         ctx.result = (ctx.sl.view  map { _.result }).toList
         result = Some(ctx.result)
    }


    override def exitIntType( ctx: ToyLangParser.IntTypeContext ): Unit = {
         ctx.result = IntType
    }


    override def exitBoolType( ctx: ToyLangParser.BoolTypeContext ): Unit = {
         ctx.result = BoolType
    }


    override def exitDefStmtRule( ctx: ToyLangParser.DefStmtRuleContext ): Unit = {
         // exit action for alternative
         val d = ctx.d match {
           case null => None
           case e@_ => Some(e.result)
         }
         ctx.result = DefStmt(ctx.id.result, ctx.t.result, d)
    }


    override def exitDefRule( ctx: ToyLangParser.DefRuleContext ): Unit = {
         ctx.result = ctx.e.result
    }


    override def exitBoolNumExpr( ctx: ToyLangParser.BoolNumExprContext ): Unit = {
            ctx.result = ctx.e.result
    }


    override def exitIntNumxpr( ctx: ToyLangParser.IntNumxprContext ): Unit = {
            ctx.result = ctx.e.result
    }


    override def exitIdentExpr( ctx: ToyLangParser.IdentExprContext ): Unit = {
            ctx.result = ctx.e.result
    }


    override def exitParenExpr( ctx: ToyLangParser.ParenExprContext ): Unit = {
            ctx.result = ctx.e.result
    }


    override def exitUnopArithExpr( ctx: ToyLangParser.UnopArithExprContext ): Unit = {
         import ToyLangParser.{ADD, SUB}
          val op = ctx.op.getType match {
          case ADD => Plus
          case SUB => Minus
         }
         ctx.result = op(ctx.e.result)
    }


    override def exitPowerExpr( ctx: ToyLangParser.PowerExprContext ): Unit = {
         ctx.result = Pow(ctx.l.result, ctx.r.result)
    }


    override def exitMulDivExpr( ctx: ToyLangParser.MulDivExprContext ): Unit = {
         import ToyLangParser.{STAR, SLASH}
          val op = ctx.op.getType match {
          case STAR => Mul
          case SLASH => Div
         }
         ctx.result = op(ctx.l.result, ctx.r.result) 
    }


    override def exitAddSubExpr( ctx: ToyLangParser.AddSubExprContext ): Unit = {
         import ToyLangParser.{ADD, SUB}
          val op = ctx.op.getType match {
          case ADD => Add
          case SUB => Sub
         }
         ctx.result = op(ctx.l.result, ctx.r.result) 
    }


    override def exitRelExpr( ctx: ToyLangParser.RelExprContext ): Unit = {
         import ToyLangParser.{NEQ,EQ,LT,LE,GT,GE}
          val op = ctx.op.getType match {
          case NEQ => Neq
          case EQ => Eq
          case LT => Lt
          case GT => Gt
          case LE => Le
          case GE => Ge
         }
         ctx.result = op(ctx.l.result, ctx.r.result) 
    }


    override def exitNotExpr( ctx: ToyLangParser.NotExprContext ): Unit = {
         ctx.result = Not(ctx.e.result)
    }


    override def exitAndExpr( ctx: ToyLangParser.AndExprContext ): Unit = {
         ctx.result = And(ctx.l.result, ctx.r.result)
    }


    override def exitOrExpr( ctx: ToyLangParser.OrExprContext ): Unit = {
         ctx.result = Or(ctx.l.result, ctx.r.result)
    }


    override def exitImpliesExpr( ctx: ToyLangParser.ImpliesExprContext ): Unit = {
         ctx.result = Implies(ctx.l.result, ctx.r.result)
    }


    override def exitIteExpr( ctx: ToyLangParser.IteExprContext ): Unit = {
         val elsifList = ctx.ei.view  map { _.result } 
         ctx.result = Ite(ctx.c.result, 
                          ctx.t.result, 
                          elsifList.toList, 
                          ctx.e.result)
    }


    override def exitElsifRule( ctx: ToyLangParser.ElsifRuleContext ): Unit = {
         ctx.result = Elsif(ctx.c.result, ctx.t.result)
    }


    override def exitIdentRule( ctx: ToyLangParser.IdentRuleContext ): Unit = {
         ctx.result = Ident(ctx.id.getText)
    }


    override def exitIntNumRule( ctx: ToyLangParser.IntNumRuleContext ): Unit = {
         ctx.result = IntNum(ctx.num.getText)
    }


    override def exitBoolNumRule( ctx: ToyLangParser.BoolNumRuleContext ): Unit = {
         ctx.result = BoolNum(ctx.num.getText)
    }


}

      

The original ANTLR 4.4 can still be used to generate java lexer and parsers from the extended g4 file, the generated listener can be attached to the java parser from scala.

/Raya

0


source


The answer was in front of my eyes all along, and I was too blind to see it: Listeners.

Updated grammar with LAN definitions so that scala objects can be saved:

grammar ToyLang2;

@lexer::header{
package toylang.parser.antlr;
}

@parser::header{
package toylang.parser.antlr;
import toylang.ast.*; // import scala ast symbols
}

program 
locals [scala.collection.immutable.List<Stmt> result]
    : sl+=defStmt sl+=defStmt* EOF 
    ;

type 
locals [TypeExpr result]
    : 'int'  # IntType
    | 'bool' # BoolType
    ;

defStmt 
locals[Stmt result]
    : id=ident ':' t=type  d=def? ';'
    ; 

def
locals [Expr result]
    : op=DEFINE e=expr
    ;

expr 
locals[Expr result]
    : e=boolNum                                     #BoolNumExpr
    | e=intNum                                      #IntNumxpr
    | e=ident                                       #IdentExpr
    | op='(' e=expr ')'                             #ParenExpr
    | IF c=expr THEN t=expr ei+=elsif* ELSE e=expr  #IteExpr
    | op=(ADD|SUB) e=expr                           #UnopArithExpr
    |<assoc=right> l=expr op=CARRET r=expr          #PowerExpr
    |<assoc=left> l=expr op=(STAR|SLASH) r=expr     #MulDivExpr
    |<assoc=left> l=expr op=(ADD|SUB) r=expr        #AddSubExpr
    | l=expr op=(NEQ|EQ|LT|LE|GT|GE) r=expr         #RelExpr
    | op=NOT e=expr                                 #NotExpr
    |<assoc=left> l=expr op=AND r=expr              #AndExpr
    |<assoc=left> l=expr op=OR r=expr               #OrExpr
    |<assoc=left> l=expr op=IMPLIES r=expr          #ImpliesExpr
    ;

elsif 
locals [Elsif result]
    : op=ELSIF c=expr THEN t=expr 
    ;

ident
locals [Ident result]
    : id=IDENT
    ;

intNum
locals [IntNum result]
    : num=INT_NUM 
    ;

boolNum
locals [BoolNum result]
    : num=(TRUE | FALSE)
    ;

LT: '<' ;
LE: '<=' ;
GT: '>' ;
GE: '>=' ;
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_NUM :'0' |[1-9][0-9]* ; 
IDENT:[_a-zA-Z][_a-zA-Z0-9]* ;
WS: [ \t\f\r\n]+ -> skip;
//NL: '\r'? \n;

      

And one scala listener:



package toylang.parser.antlr
import toylang.ast._
import scala.collection.JavaConversions._

class Listener extends ToyLang2BaseListener {

  // stores the result of a successfull parse
  var result: Option[List[Stmt]] = None 

  override def exitBoolNum(ctx: ToyLang2Parser.BoolNumContext ): Unit = {
    ctx.result = BoolNum(ctx.num.getText)
  }

  override def exitImpliesExpr(ctx: ToyLang2Parser.ImpliesExprContext ): Unit = {
    ctx.result = Implies (ctx.l.result, ctx.r.result)
  }

  override def exitAddSubExpr(ctx: ToyLang2Parser.AddSubExprContext ): Unit = { 
    import ToyLang2Parser.{ADD, SUB}
    ctx.result = ctx.op.getType match {
      case ADD => Add(ctx.l.result, ctx.r.result)
      case SUB => Sub(ctx.l.result, ctx.r.result)
    }
  }

  override def exitIteExpr(ctx: ToyLang2Parser.IteExprContext ): Unit = { 
    // using views to avoid creation of intermediate data
    val elsifList = ctx.ei.view  map { _.result } 
    ctx.result = Ite(ctx.c.result, ctx.t.result, elsifList.toList, ctx.e.result)
  }

  override def exitBoolNumExpr(ctx: ToyLang2Parser.BoolNumExprContext ): Unit = {
    ctx.result = ctx.e.result
  }

  override def exitParenExpr(ctx: ToyLang2Parser.ParenExprContext ): Unit = {
    ctx.result = ctx.e.result
  }

  override def exitPowerExpr(ctx: ToyLang2Parser.PowerExprContext ): Unit = { 
    ctx.result = Pow(ctx.l.result, ctx.r.result)
  }

  override def exitIntNum(ctx: ToyLang2Parser.IntNumContext ): Unit = {
    ctx.result = IntNum(ctx.num.getText)
  }

  override def exitIdentExpr(ctx: ToyLang2Parser.IdentExprContext ): Unit = { 
    ctx.result = ctx.e.result
  }

  override def exitNotExpr(ctx: ToyLang2Parser.NotExprContext ): Unit = { 
    ctx.result = Not(ctx.e.result)
  }

  override def exitElsif(ctx: ToyLang2Parser.ElsifContext ): Unit = { 
    ctx.result = Elsif(ctx.c.result, ctx.t.result)
  }

  override def exitBoolType(ctx: ToyLang2Parser.BoolTypeContext ): Unit = { 
    ctx.result = BoolType
  }

  override def exitIdent(ctx: ToyLang2Parser.IdentContext ): Unit = { 
    ctx.result = Ident(ctx.id.getText)
  }

  override def exitAndExpr(ctx: ToyLang2Parser.AndExprContext ): Unit = { 
    ctx.result = And(ctx.l.result, ctx.r.result)
  }


  override def exitOrExpr(ctx: ToyLang2Parser.OrExprContext ): Unit = { 
    ctx.result = Or(ctx.l.result, ctx.r.result)
}

  override def exitDef(ctx: ToyLang2Parser.DefContext ): Unit = { 
    ctx.result = ctx.e.result
  }

  override def exitProgram(ctx: ToyLang2Parser.ProgramContext ): Unit = {
    ctx.result = (ctx.sl.view  map { _.result }).toList
    result = Some(ctx.result)

  }

  override def exitIntType(ctx: ToyLang2Parser.IntTypeContext ): Unit = { 
    ctx.result = IntType
  }

  override def exitMulDivExpr(ctx: ToyLang2Parser.MulDivExprContext ): Unit = { 
    import ToyLang2Parser.{STAR,SLASH}
    ctx.result = ctx.op.getType match {
      case STAR => Mul(ctx.l.result, ctx.r.result)
      case SLASH => Div(ctx.l.result, ctx.r.result)
    }
  }

  override def exitUnopArithExpr(ctx: ToyLang2Parser.UnopArithExprContext ): Unit = { 
    import ToyLang2Parser.{ADD, SUB}
    ctx.result = ctx.op.getType match {
      case ADD => Plus(ctx.e.result)
      case SUB => Minus(ctx.e.result)
    }
  }

  override def exitIntNumxpr(ctx: ToyLang2Parser.IntNumxprContext ): Unit = { 
    ctx.result = ctx.e.result
  }

  override def exitDefStmt(ctx: ToyLang2Parser.DefStmtContext ): Unit = { 
    val d = ctx.d match {
      case null => None
      case e@_ => Some(e.result)
    }
    ctx.result = DefStmt(ctx.id.result, ctx.t.result, d)
  }

  override def exitRelExpr(ctx: ToyLang2Parser.RelExprContext ): Unit = { 
    import ToyLang2Parser.{NEQ,EQ,LT,LE,GT,GE}
    ctx.op.getType match {
      case NEQ => Neq(ctx.l.result, ctx.r.result)
      case EQ => Eq(ctx.l.result, ctx.r.result)
      case LT => Lt(ctx.l.result, ctx.r.result)
      case LE => Le(ctx.l.result, ctx.r.result)
      case GT => Gt(ctx.l.result, ctx.r.result)
      case GE => Ge(ctx.l.result, ctx.r.result)
    }
  }
}

      

Last, instanciate antlr parser from scala and register a scala listener:

package toylang.parser
import toylang.parser.antlr.{Listener, ToyLang2Lexer, ToyLang2Parser}
import org.antlr.v4.runtime.{ANTLRInputStream, CommonTokenStream}
import java.io.FileInputStream

class Parser2( arg: String ) {
  val input = new ANTLRInputStream(new FileInputStream(arg))
  val lexer = new ToyLang2Lexer(input)
  val tokens = new CommonTokenStream(lexer)
  val parser = new ToyLang2Parser(tokens)
  val listener = new Listener
  parser.addParseListener(listener)
  val prog = parser.program
  println (listener.result)
}

      

It couldn't be easier.

+1


source


You can convert the parser results to any data structure using the return value of your call parser.program

. I have used this approach and find it much easier than visitors or listeners because this is where I actually expected the analyzer results.

0


source







All Articles