2014-11-19 5 views
3

Я пытаюсь найти способ интегрировать парсер antlr в области Scala.ANTLR + Scala: идеи для улучшения?

Я пробовал посетителей, закодированных в scala, для создания объектов scala из дерева синтаксического анализа, но ограничение на равномерный тип возврата - это не выход.

Итак, я сделал решительный шаг и решил создать объекты scala непосредственно из действий парсера, используя интерфейс Java классов scala.

Этот блог был весьма полезным:

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

Вот что я получил:

Ла Скала АСТ 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 с ява действия вызова scala

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; 

И, наконец, два класса утилиты java, Conv и Fun, Conv содержат код для преобразования списка java в хорошо типизированный список scala, Fun - это интерфейс для действий, которые отображаются по списку java до перехода в список 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); 
} 

код, который Scala называет Antlr анализатор:

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

Любой идеей для улучшения на приточно-конечных результатах из-Contex-Java-к-преобразования списка лестницы будет приветствоваться. Сделать его полностью универсальным было бы здорово, возможно, используя API отражения Java. В идеале я хотел бы использовать один оператор, чтобы сказать: извлечь поле с именем «foo» из каждого объекта контекста правила antlr (или токена) этого списка и преобразовать результат в список scala.

Я просмотрел всю сеть и не нашел ни малейших мелочей о том, как это сделать.

С уважением,

+0

http://codereview.stackexchange.com/ – Reimeus

+0

Вы также можете рассмотреть вместо использования ANTLR сгенерированной грамматики, используя некоторые из других библиотек парсеров, то есть Parboiled (http://parboiled.org), Parboiled2 (https://github.com/sirthias/parboiled2) или комбинаторы Parser из стандартной библиотеки. – Martijn

+0

Hi Martijn, Я никогда не пробовал паровоть, я посмотрю на это, если скорость и потребление памяти лучше, чем комбинаторы Scala, это может быть решение. На самом деле я пытаюсь отойти от комбайнов Scala по соображениям производительности и удобочитаемости/ремонтопригодности. Я также пробовал scala-bison, что тоже довольно приятно, но я никогда не умел разрешать конфликты смены/уменьшения или уменьшения/уменьшения. – remi

ответ

0

как раз для того, чтобы дать вам голову, я собрал взломы, чтобы встраивать действия scala непосредственно в грамматику g4.

antlr2scala_v0.1.tar.gz

Я изменил ANTLRv4Lexer.g4 и ANTLRv4Parser.g4 здесь:

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

, чтобы специальные комментарии, как этот

//! <scala code> 

добавляемого первым в файле, чтобы определить заголовок SCALA слушателя,

непосредственно перед списком правил для определения членов слушателя scala,

и справа перед или после использования правил, указать действия ввода и выхода для каждой альтернативы (которая должна быть маркирована).

На основе этого измененного формата я разработал инструмент, который извлекает и упаковывает код для слушателя scala из специальных комментариев.

На практике расширенный файл с SCALA действий выглядит следующим образом:

//! // 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 [email protected]_ => 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; 

Извлеченный слушатель выглядит следующим образом:

// !!! 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 [email protected]_ => 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) 
    } 


} 

оригинальный ANTLR 4.4 все еще может быть использован для создания Java лексических и синтаксические анализаторы из расширенного файла g4, сгенерированный слушатель может быть присоединен к парсера java из scala.

/Rémi

1

Ответ был под моими глазами все время, и я был слишком слеп, чтобы видеть это: Слушателей.

Обновлен грамматик с определениями местных жителей, так что Scala объекты могут быть сохранены:

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; 

И один Scala слушатель:

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 [email protected]_ => 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) 
    } 
    } 
} 

Последнего, создание экземпляра ANTLR парсера из Скале и зарегистрировать Scala Слушатель:

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

Это не может быть проще.

0

Вы можете преобразовать результаты синтаксического анализа для любой структуры данных, используя возвращаемое значение вашего parser.program вызова. Я использовал этот подход и нахожу его намного проще, чем посетители или слушатели, потому что это то место, где я действительно ожидал результатов анализатора.

+0

Да дерево разбора, генерируемое antlr, может быть пройдено, чтобы генерировать что-либо, однако, если язык большой, существует много кода шаблона для записи, вложение действий в грамматику позволяет автоматически генерировать этот шаблон, следовательно, решение, которое я предложил. Вы даже можете использовать мой переводчик для создания единого scala-слушателя, который может делегировать действия нескольким различным процессорам, если вам нужно сделать несколько проходов в дереве разбора. – remi

Смежные вопросы