在 Scala 宏中探索表达式树
Exploring expression tree within scala macro
我正在学习 scala,为了完成一项作业,我必须编写一个宏。
宏应探索 表达式树 ,然后构建自定义 Expression
。我通过添加 println(showRaw(exprTree))
在表达式处管理 "to take a look"。但是我仍然无法迭代它并构建 Expression
我有以下两个文件:
ExpressionImplicits.scala:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
// Expression is defined elsewhere and mainly only overrides toString()
abstract class Expression
case class Var(name: String) extends Expression
case class Number(num: Double) extends Expression
case class BinOp(operator: String, left: Expression, right: Expression) extends Expression
class ExpressionImplicitsImpl(val c: Context) {
import c.universe._
// Task complete macro
// Add necessary definitions here
// This definition was added by me
def expr(exprTree: c.Expr[AnyRef]): c.Expr[Expression] = {
println(showRaw(exprTree))
//prints
//Expr(Function(List(ValDef(Modifiers(PARAM), TermName("x"), TypeTree().setOriginal(Select(Ident(scala), scala.Double)), EmptyTree)), Apply(Select(Apply(Select(Ident(TermName("x")), TermName("$times")), List(Literal(Constant(2)))), TermName("$plus")), List(Apply(Select(Literal(Constant(3.0)), TermName("$times")), List(Ident(TermName("x"))))))))
//Expr(Function(List(ValDef(Modifiers(PARAM), TermName("x"), TypeTree().setOriginal(Select(Ident(scala), scala.Double)), EmptyTree), ValDef(Modifiers(PARAM), TermName("y"), TypeTree().setOriginal(Select(Ident(scala), scala.Double)), EmptyTree)), Apply(Select(Apply(Select(Ident(TermName("x")), TermName("$times")), List(Ident(TermName("y")))), TermName("$times")), List(Ident(TermName("x"))))))
}
}
// This definition is given
object ExpressionImplicits {
def expr(exprTree: AnyRef): Expression = macro ExpressionImplicitsImpl.expr
}
ExpressionsTest.scala:
object ExpressionsTest {
def main(args: Array[String]) {
import ExpressionImplicits._
val e1 = expr { (x: Double) => (x * 2) + (3.0 * x) }
println(e1) // BinOp(+,BinOp(*,Var(x),Number(2.0)),BinOp(*,Number(3.0),Var(x)))
val e2 = expr { (x: Double, y: Double) => x * y * x }
println(e2) // BinOp(*,BinOp(*,Var(x),Var(y)),Var(x))
// val e3 = expr { (x: Double) => x.toInt } // Fails during compilation
}
}
你们很亲近。您现在只需要匹配 showRaw
转储的表达式。
这里是完整的 solution:
object ExpressionImplicits {
def expr(expr: AnyRef): Expression = macro expr_impl
def expr_impl(c: blackbox.Context)(expr: c.Expr[AnyRef]): c.Expr[Expression] = {
import c.universe._
def treeToExpression(functionBody: c.Tree): c.Expr[Expression] = {
functionBody match {
case Apply(Select(leftTree, operator), List(rightTree)) =>
val operatorName = Constant(operator.toString)
c.Expr[Expression](q"sk.ygor.Whosebug.q53326545.macros.BinOp($operatorName, ${treeToExpression(leftTree)}, ${treeToExpression(rightTree)})")
case Ident(TermName(varName)) =>
c.Expr[Expression](q"sk.ygor.Whosebug.q53326545.macros.Var($varName)")
case Literal(Constant(num)) if num.isInstanceOf[java.lang.Number] =>
c.Expr[Expression](q"sk.ygor.Whosebug.q53326545.macros.Number(${num.asInstanceOf[java.lang.Number].doubleValue()})")
case unsupported =>
sys.error("Unsupported function body: " + unsupported);
}
}
expr.tree match {
case Function(_, body) => treeToExpression(body)
case unsupported =>
sys.error("Only functions are accepted. Got: " + unsupported);
}
}
}
你应该试着去理解,这是怎么回事:
- 我们正在通过模式匹配和递归进行树遍历。 Link已在评论中提供:https://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html#traversing-trees
- 第一个匹配只检查顶层树是函数的定义
- 我们在函数体上递归匹配
List(rightTree)
意味着,我们期望方法只有一个参数,例如x.foo(y)
、x foo y
、x.+(y)
、x + y
,但不是 x.foo()
、x.foo(y, z)
、x.+(y, z)
- 我们使用 Scala 宏构建和组合部分输出树 quasiqotes
- 我们为
BinOp
、Var
和 Number
使用完全限定的名称,因此宏的使用者不必导入这些子类
我正在学习 scala,为了完成一项作业,我必须编写一个宏。
宏应探索 表达式树 ,然后构建自定义 Expression
。我通过添加 println(showRaw(exprTree))
在表达式处管理 "to take a look"。但是我仍然无法迭代它并构建 Expression
我有以下两个文件:
ExpressionImplicits.scala:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
// Expression is defined elsewhere and mainly only overrides toString()
abstract class Expression
case class Var(name: String) extends Expression
case class Number(num: Double) extends Expression
case class BinOp(operator: String, left: Expression, right: Expression) extends Expression
class ExpressionImplicitsImpl(val c: Context) {
import c.universe._
// Task complete macro
// Add necessary definitions here
// This definition was added by me
def expr(exprTree: c.Expr[AnyRef]): c.Expr[Expression] = {
println(showRaw(exprTree))
//prints
//Expr(Function(List(ValDef(Modifiers(PARAM), TermName("x"), TypeTree().setOriginal(Select(Ident(scala), scala.Double)), EmptyTree)), Apply(Select(Apply(Select(Ident(TermName("x")), TermName("$times")), List(Literal(Constant(2)))), TermName("$plus")), List(Apply(Select(Literal(Constant(3.0)), TermName("$times")), List(Ident(TermName("x"))))))))
//Expr(Function(List(ValDef(Modifiers(PARAM), TermName("x"), TypeTree().setOriginal(Select(Ident(scala), scala.Double)), EmptyTree), ValDef(Modifiers(PARAM), TermName("y"), TypeTree().setOriginal(Select(Ident(scala), scala.Double)), EmptyTree)), Apply(Select(Apply(Select(Ident(TermName("x")), TermName("$times")), List(Ident(TermName("y")))), TermName("$times")), List(Ident(TermName("x"))))))
}
}
// This definition is given
object ExpressionImplicits {
def expr(exprTree: AnyRef): Expression = macro ExpressionImplicitsImpl.expr
}
ExpressionsTest.scala:
object ExpressionsTest {
def main(args: Array[String]) {
import ExpressionImplicits._
val e1 = expr { (x: Double) => (x * 2) + (3.0 * x) }
println(e1) // BinOp(+,BinOp(*,Var(x),Number(2.0)),BinOp(*,Number(3.0),Var(x)))
val e2 = expr { (x: Double, y: Double) => x * y * x }
println(e2) // BinOp(*,BinOp(*,Var(x),Var(y)),Var(x))
// val e3 = expr { (x: Double) => x.toInt } // Fails during compilation
}
}
你们很亲近。您现在只需要匹配 showRaw
转储的表达式。
这里是完整的 solution:
object ExpressionImplicits {
def expr(expr: AnyRef): Expression = macro expr_impl
def expr_impl(c: blackbox.Context)(expr: c.Expr[AnyRef]): c.Expr[Expression] = {
import c.universe._
def treeToExpression(functionBody: c.Tree): c.Expr[Expression] = {
functionBody match {
case Apply(Select(leftTree, operator), List(rightTree)) =>
val operatorName = Constant(operator.toString)
c.Expr[Expression](q"sk.ygor.Whosebug.q53326545.macros.BinOp($operatorName, ${treeToExpression(leftTree)}, ${treeToExpression(rightTree)})")
case Ident(TermName(varName)) =>
c.Expr[Expression](q"sk.ygor.Whosebug.q53326545.macros.Var($varName)")
case Literal(Constant(num)) if num.isInstanceOf[java.lang.Number] =>
c.Expr[Expression](q"sk.ygor.Whosebug.q53326545.macros.Number(${num.asInstanceOf[java.lang.Number].doubleValue()})")
case unsupported =>
sys.error("Unsupported function body: " + unsupported);
}
}
expr.tree match {
case Function(_, body) => treeToExpression(body)
case unsupported =>
sys.error("Only functions are accepted. Got: " + unsupported);
}
}
}
你应该试着去理解,这是怎么回事:
- 我们正在通过模式匹配和递归进行树遍历。 Link已在评论中提供:https://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html#traversing-trees
- 第一个匹配只检查顶层树是函数的定义
- 我们在函数体上递归匹配
List(rightTree)
意味着,我们期望方法只有一个参数,例如x.foo(y)
、x foo y
、x.+(y)
、x + y
,但不是x.foo()
、x.foo(y, z)
、x.+(y, z)
- 我们使用 Scala 宏构建和组合部分输出树 quasiqotes
- 我们为
BinOp
、Var
和Number
使用完全限定的名称,因此宏的使用者不必导入这些子类