我尝试使用用于从解析树构建Scala对象的Scala编写的访问者,但是要求具有统一返回类型的限制不可行。
因此,我决定冒险并直接从解析器操作中构建Scala对象,使用Scala类的Java接口。
这篇博客文章非常有帮助:
http://blog.akquinet.de/2011/07/20/integrating-scala-components-in-a-java-application/
这是我得到的:
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语法,使用Java操作调用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列表之前映射到Java列表上的操作的接口。
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);
}
调用antlr解析器的Scala代码:
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)
}
任何关于从Java转换为Scala的extract-results-from-contex-java-to-scala列表转换的改进想法都将受到欢迎。使其完全通用将是很好的,也许可以使用Java反射API。理想情况下,我希望使用一个语句从这个列表中的每个antlr规则上下文对象(或标记)中提取名为“foo”的字段,并将结果转换为Scala列表。
我已经在网络上搜索了很久,但没有找到如何做到这一点的教程。
问候,