如何将 Scala 代码块转换为字符串?
How to convert scala code block to string?
我需要实现一个测试函数来检查“splain”插件的编译时错误信息,该函数的一部分需要将代码块转换为字符串,例如:
def convert(fn: => Unit): String
// for testing
val code = convert {
object I extends Seq {}
}
assert(code == "object I extends Seq {}")
是否可以使用标准 Scala 功能?非常感谢您的建议。
此功能将能够验证复杂代码的编译时消息,这些代码需要 IDE 经常
进行索引和重构
是,有可能。
李昊一的微距Text
from sourcecode
def text[T: c.WeakTypeTag](c: Compat.Context)(v: c.Expr[T]): c.Expr[sourcecode.Text[T]] = {
import c.universe._
val fileContent = new String(v.tree.pos.source.content)
val start = v.tree.collect {
case treeVal => treeVal.pos match {
case NoPosition ⇒ Int.MaxValue
case p ⇒ p.startOrPoint
}
}.min
val g = c.asInstanceOf[reflect.macros.runtime.Context].global
val parser = g.newUnitParser(fileContent.drop(start))
parser.expr()
val end = parser.in.lastOffset
val txt = fileContent.slice(start, start + end)
val tree = q"""${c.prefix}(${v.tree}, $txt)"""
c.Expr[sourcecode.Text[T]](tree)
}
几乎可以满足您的要求:
def convert[A](fn: => Text[A]): String = fn.source
convert(10 + 20 +
30
)
//10 + 20 +
// 30
不幸的是,
if you have multiple statements in a {}
block, sourcecode.Text
will only capture the source code for the last expression that gets returned.
而且由于 { object I extends Seq {} }
实际上是 { object I extends Seq {}; () }
宏在这种情况下将不起作用。
所以让我们编写自己的简单宏
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def convert(fn: => Any): String = macro convertImpl
def convertImpl(c: blackbox.Context)(fn: c.Tree): c.Tree = {
import c.universe._
val pos = fn.pos
val res = new String(pos.source.content).slice(pos.start, pos.end)
Literal(Constant(res))
}
用法:
trait Seq
convert {
val i: Int = 1
object I extends Seq {}
10 + 20 + 30
convert(1)
}
//{
// val i: Int = 1
// object I extends Seq {}
// 10 + 20 + 30
// convert(1)
// }
注意 def 宏的参数在宏扩展之前进行了类型检查(因此 convert { val i: Int = "a" }
、convert { object I extends XXX }
没有定义 XXX
、convert { (; }
等将无法编译)。
我需要实现一个测试函数来检查“splain”插件的编译时错误信息,该函数的一部分需要将代码块转换为字符串,例如:
def convert(fn: => Unit): String
// for testing
val code = convert {
object I extends Seq {}
}
assert(code == "object I extends Seq {}")
是否可以使用标准 Scala 功能?非常感谢您的建议。
此功能将能够验证复杂代码的编译时消息,这些代码需要 IDE 经常
进行索引和重构是,有可能。
李昊一的微距Text
from sourcecode
def text[T: c.WeakTypeTag](c: Compat.Context)(v: c.Expr[T]): c.Expr[sourcecode.Text[T]] = {
import c.universe._
val fileContent = new String(v.tree.pos.source.content)
val start = v.tree.collect {
case treeVal => treeVal.pos match {
case NoPosition ⇒ Int.MaxValue
case p ⇒ p.startOrPoint
}
}.min
val g = c.asInstanceOf[reflect.macros.runtime.Context].global
val parser = g.newUnitParser(fileContent.drop(start))
parser.expr()
val end = parser.in.lastOffset
val txt = fileContent.slice(start, start + end)
val tree = q"""${c.prefix}(${v.tree}, $txt)"""
c.Expr[sourcecode.Text[T]](tree)
}
几乎可以满足您的要求:
def convert[A](fn: => Text[A]): String = fn.source
convert(10 + 20 +
30
)
//10 + 20 +
// 30
不幸的是,
if you have multiple statements in a
{}
block,sourcecode.Text
will only capture the source code for the last expression that gets returned.
而且由于 { object I extends Seq {} }
实际上是 { object I extends Seq {}; () }
宏在这种情况下将不起作用。
所以让我们编写自己的简单宏
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def convert(fn: => Any): String = macro convertImpl
def convertImpl(c: blackbox.Context)(fn: c.Tree): c.Tree = {
import c.universe._
val pos = fn.pos
val res = new String(pos.source.content).slice(pos.start, pos.end)
Literal(Constant(res))
}
用法:
trait Seq
convert {
val i: Int = 1
object I extends Seq {}
10 + 20 + 30
convert(1)
}
//{
// val i: Int = 1
// object I extends Seq {}
// 10 + 20 + 30
// convert(1)
// }
注意 def 宏的参数在宏扩展之前进行了类型检查(因此 convert { val i: Int = "a" }
、convert { object I extends XXX }
没有定义 XXX
、convert { (; }
等将无法编译)。