使用Scala宏实现非Scala源代码位置

4

我有一个Scala宏,它依赖一个任意的XML文件,这个文件由一个包含其位置的静态字符串指定。

def myMacro(path: String) = macro myMacroImpl

def myMacroImpl(c: Context)(path: c.Expr[String]): c.Expr[Any] = {
  // load file specified by path and generate some code
  ...
}

这意味着,如果xml文件格式不正确,宏就无法展开。目前,我正在提供一个错误消息,其中包含xml文件中出错位置的文本表示。但是,这显然不是最好的解决方案。
是否可以为生成的代码提供来自不同(可能非Scala)文件的源位置,以便错误指向包含xml文件的scala文件而不是scala文件本身?我不知道如何在不修改现有内容的情况下创建位置。
1个回答

4

这个应用场景绝对非常有趣,看起来应该在反射API中得到支持。不幸的是,目前没有公开的API可以实现这一点,即使内部已经有了相应的机制,但也属于比较低级的。

import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
import scala.reflect.io.AbstractFile
import scala.reflect.internal.util.BatchSourceFile
import scala.reflect.internal.util.OffsetPosition

class Impl(val c: Context) {
  def impl: c.Tree = {
    val filePath = "foo.txt"
    val af = AbstractFile.getFile(filePath)
    val content = scala.io.Source.fromFile(filePath).mkString
    val sf = new BatchSourceFile(af, content)
    val pos = new OffsetPosition(sf, 3).asInstanceOf[c.universe.Position]
    c.abort(pos, "it works")
  }
}

object Macros {
  def foo: Any = macro Impl.impl
}

object Test extends App {
  Macros.foo
}

在一个简单的文本文件上运行此代码会产生以下结果:
20:56 ~/Projects/Master/sandbox (master)$ cat foo.txt
hello
world
20:56 ~/Projects/Master/sandbox (master)$ scalac Test.scala
foo.txt:1: error: it works
hello
   ^
one error found

请注意,此解决方案涉及scala.reflect.internal和一个强制转换(两者都使我们为scala-reflect.jar提供的所有兼容性保证无效),因此我不建议在生产代码中使用。

太棒了,非常感谢你的回答。 :) 这将帮助我玩弄这个宏,直到公共API支持类似的功能。我会提交一个工单。 - Martin Ring
谢谢您的工单! - Eugene Burmako
供日后参考:该问题的工单编号为SI-8526 - Martin Ring

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接