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,
source to share
just to give you a head, I put together a hack to embed scala actions directly into g4 grammar.
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
source to share
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.
source to share