Я пытаюсь найти способ интегрировать парсер 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.
Я просмотрел всю сеть и не нашел ни малейших мелочей о том, как это сделать.
С уважением,
http://codereview.stackexchange.com/ – Reimeus
Вы также можете рассмотреть вместо использования ANTLR сгенерированной грамматики, используя некоторые из других библиотек парсеров, то есть Parboiled (http://parboiled.org), Parboiled2 (https://github.com/sirthias/parboiled2) или комбинаторы Parser из стандартной библиотеки. – Martijn
Hi Martijn, Я никогда не пробовал паровоть, я посмотрю на это, если скорость и потребление памяти лучше, чем комбинаторы Scala, это может быть решение. На самом деле я пытаюсь отойти от комбайнов Scala по соображениям производительности и удобочитаемости/ремонтопригодности. Я также пробовал scala-bison, что тоже довольно приятно, но я никогда не умел разрешать конфликты смены/уменьшения или уменьшения/уменьшения. – remi