I would like to create a templating plugin and as the first step convert an arbitrary string to it's "compiled" AST representation (as the scala interpreter does, I guess). So a compiler plugin could e.g assign someString to "HELLO WORLD":
#StringAnnotation("""("hello world").toString.toUpperCase""")
var someString = ""
My current first shot plugin does in short:
runafter parser
create a new representation only compiler and a VirtualFile with the annotation content
compile and print unit.body
see: http://paste.pocoo.org/show/326025/
a)
Right now, "object o{val x = 0}" returns an AST, but e.g. "var x = 1+ 2" doesn't because it wouldn't be a valid .scala file. How can I fix this?
b)
Is onlyPresentation a good choice? Should I instead overriding computeInternalPhases with the appropriate phases or use -Ystop:phase?
c)
Is it possible to bind the environment of the outer compiler to the inner one, so that e.g.
var x = _
(...)
#StringAnnotation("x += 3")
would work?
I found following code[1] using an interpreter and one variable which does something similar:
Interpreter interpreter = new Interpreter(settings);
String[] context = { "FOO" };
interpreter.bind("context", "Array[String]", context);
interpreter
.interpret("de.tutorials.scala2.Test.main(context)");
context[0] = "BAR";
interpreter
.interpret("de.tutorials.scala2.Test.main(context)");
[1] http://www.tutorials.de/java/320639-beispiel-zur-einbindung-des-scala-interpreters-kompilierte-scala-anwendungen.html#post1653884
thanks
Complete Code:
class AnnotationsPI(val global: Global) extends Plugin {
import global._
val name = "a_plugins::AnnotationsPI" //a_ to run before namer
val description = "AST Trans PI"
val components = List[PluginComponent](Component)
private object Component extends PluginComponent with Transform with TypingTransformers with TreeDSL {
val global: AnnotationsPI.this.global.type = AnnotationsPI.this.global
val runsAfter = List[String]("parser");
val phaseName = AnnotationsPI.this.name
def newTransformer(unit: CompilationUnit) = {
new AnnotationsTransformer(unit)
}
val SaTpe = "StringAnnotation".toTypeName
class AnnotationsTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
/** When using <code>preTransform</code>, each node is
* visited before its children.
*/
def preTransform(tree: Tree): Tree = tree match {
case anno#ValDef(Modifiers(_, _, List(Apply(Select(New(Ident(SaTpe)), _), List(Literal(Constant(a))))), _), b, c, d) => //Apply(Select(New(Ident(SaTpe)), /*nme.CONSTRUCTOR*/_), /*List(x)*/x)
val str = a.toString
val strArr = str.getBytes("UTF-8")
import scala.tools.nsc.{ Global, Settings, SubComponent }
import scala.tools.nsc.reporters.{ ConsoleReporter, Reporter }
val settings = new Settings()
val compiler = new Global(settings, new ConsoleReporter(settings)) {
override def onlyPresentation = true
}
val run = new compiler.Run
val vfName = "Script.scala"
var vfile = new scala.tools.nsc.io.VirtualFile(vfName)
val os = vfile.output
os.write(strArr, 0, str.size) // void write(byte[] b, int off, int len)
os.close
new scala.tools.nsc.util.BatchSourceFile(vfName, str)
run.compileFiles(vfile :: Nil)
for (unit <- run.units) {
println("Unit: " + unit)
println("Body:\n" + unit.body)
}
tree
case _ =>
tree
}
override def transform(tree: Tree): Tree = {
super.transform(preTransform(tree))
}
}
}
I don't know if this helps you much, but instead of fiddling with the Interpreter, you can use treeFrom( aString ) which is part of the scala-refactoring project ( http://scala-refactoring.org/ ). doesn't answer your question about cross-bindings, though...
Related
I am interacting with an external Java API which looks like this:
val obj: SomeBigJavaObj = {
val _obj = new SomeBigJavaObj(p1, p2)
_obj.setFoo(p3)
_obj.setBar(p4)
val somethingElse = {
val _obj2 = new SomethingElse(p5)
_obj2.setBar(p6)
_obj2
}
_obj.setSomethingElse(somethingElse)
_obj
}
Basically the Java API exposes bunch of .setXXXX methods which returns void and sets something. I have no control over these external POJOs.
I would therefore like to write a fluent build Scala macro which inspects the object and creates a builder-pattern type .withXXXX() method for each of the void setXXXX() methods which returns this:
val obj: SomeBigJavaObj =
build(new SomeBigJavaObj(p1, p2))
.withFoo(p3)
.withBar(p4)
.withSomethingElse(
build(new SomethingElse(p5))
.withBar(p6)
.result()
)
.result()
Is this possible? I know I cannot generate new top level objects with def macros so open to other suggestions where I would have the similar ergonomics.
It is not complicated to use macros; just unfriendly to IDE (like:code completion;...);
//edit 1 : support multiple arguments
entity:
public class Hello {
public int a;
public String b;
public void setA(int a) {
this.a = a;
}
public void setB(String b) {
this.b = b;
}
public void setAB(int a , String b){
this.a = a;
this.b = b;
}
}
macro code :
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
trait BuildWrap[T] {
def result(): T
}
object BuildWrap {
def build[T](t: T): Any = macro BuildWrapImpl.impl[T]
}
class BuildWrapImpl(val c: whitebox.Context) {
import c.universe._
def impl[T: c.WeakTypeTag](t: c.Expr[T]) = {
val tpe = c.weakTypeOf[T]
//get all set member
val setMembers = tpe.members
.filter(_.isMethod)
.filter(_.name.toString.startsWith("set"))
.map(_.asMethod)
.toList
// temp value ;
val valueName = TermName("valueName")
val buildMethods = setMembers.map { member =>
if (member.paramLists.length > 1)
c.abort(c.enclosingPosition,"do not support Currying")
val params = member.paramLists.head
val paramsDef = params.map(e=>q"${e.name.toTermName} : ${e.typeSignature}")
val paramsName = params.map(_.name)
val fieldName = member.name.toString.drop(3)//drop set
val buildFuncName = TermName(s"with$fieldName")
q"def $buildFuncName(..$paramsDef ) = {$valueName.${member.name}(..$paramsName);this} "
}
val result =
q"""new BuildWrap[$tpe] {
private val $valueName = $t
..${buildMethods}
def result() = $valueName
}"""
// debug
println(showCode(result))
result
}
}
test code :
val hello1: Hello = BuildWrap.build(new Hello).withA(1).withB("b").result()
assert(hello1.a == 1)
assert(hello1.b == "b")
val hello2: Hello = BuildWrap.build(new Hello).withAB(1, "b").result()
assert(hello2.a == 1)
assert(hello2.b == "b")
Not a solution, just a very preliminary mock-up
+---------------------------------------------------------+
| |
| D I S C L A I M E R |
| |
| This is a mock-up. It is not type-safe. It relies on |
| runtime reflection (even worse: it relies on |
| Java-reflection!). Do not use this in production. |
| |
| If you can come up with a type-safe solution, I will |
| definitely take a look at it and upvote your answer. |
| |
+---------------------------------------------------------+
You have said explicitly that type-safety is a must, so the code below cannot count as a solution. However, before investigating any further, maybe you would like to experiment with a purely runtime-reflection based implementation, to understand the requirements better. Here is a very quick-and-dirty mock-up implementation:
import scala.language.dynamics
class DynamicBuilder[X](underConstruction: X) extends Dynamic {
val clazz = underConstruction.getClass
def applyDynamic(name: String)(arg: Any): DynamicBuilder[X] = {
if (name.startsWith("with")) {
val propertyName = name.drop(4)
val setterName = "set" + propertyName
clazz.getDeclaredMethods().
find(_.getName == setterName).
fold(throw new IllegalArgumentException("No method " + setterName)) {
m =>
m.invoke(underConstruction, arg.asInstanceOf[java.lang.Object])
this
}
} else {
throw new IllegalArgumentException("Expected 'result' or 'withXYZ'")
}
}
def result(): X = underConstruction
}
object DynamicBuilder {
def build[A](a: A) = new DynamicBuilder[A](a)
}
Once the build-method is imported
import DynamicBuilder.build
and the definitions of the classes that correspond to POJOs are in scope
class SomethingElse(val p5: String) {
var bar: String = _
def setBar(s: String): Unit = { bar = s }
override def toString = s"SomethingElse[p5 = $p5, bar = $bar]"
}
class SomeBigJavaObj(val p1: Float, val p2: Double) {
var foo: Int = 0
var bar: String = _
var sthElse: SomethingElse = _
def setFoo(i: Int): Unit = { foo = i }
def setBar(s: String): Unit = { bar = s }
def setSomethingElse(s: SomethingElse): Unit = { sthElse = s }
override def toString: String =
s"""|SomeBigJavaObj[
| p1 = $p1, p2 = $p2,
| foo = $foo, bar = $bar,
| sthElse = $sthElse
|]""".stripMargin
}
and also all the required variables p1,...,p6 from your example are defined
val p1 = 3.1415f
val p2 = 12345678d
val p3 = 42
val p4 = "BAR"
val p5 = "P5"
val p6 = "b-a-r"
you can use exactly the syntax from your question:
val obj: SomeBigJavaObj =
build(new SomeBigJavaObj(p1, p2))
.withFoo(p3)
.withBar(p4)
.withSomethingElse(
build(new SomethingElse(p5))
.withBar(p6)
.result()
)
.result()
The result looks as follows:
println(obj)
// Output:
// SomeBigJavaObj[
// p1 = 3.1415, p2 = 1.2345678E7,
// foo = 42, bar = BAR,
// sthElse = SomethingElse[p5 = P5, bar = b-a-r]
// ]
For now, the idea is just to see how badly it fails when you try to use it with a somewhat more realistic example. It might turn out that in reality, everything is a little bit more complicated:
Maybe some setters are generic
Maybe some of them use Java wildcards with Java's strange call-site variance
Maybe instead of setters there are some other methods that take multiple parameters as varargs
Maybe there are overloaded setters with same name but different types of arguments.
etc.
I understand that this cannot be a solution, however, I hope that this might be useful as an additional feasibility check, and that it maybe helps to make the requirements a tiny bit more precise, before investing more time and energy in type-safe macro-based solutions.
If this does roughly what you wanted, I might consider to update the answer. If this is not helpful at all, I'll delete the answer.
I'm looking for a way to convert a Scala singleton object given as a string (for example: package1.Main) to the actual instance of Main, so that I can invoke methods on it.
Example of the problem:
package x {
object Main extends App {
val objectPath: String = io.StdIn.readLine("Give an object: ") // user enters: x.B
// how to convert the objectPath (String) to a variable that references singleton B?
val b1: A = magicallyConvert1(objectPath)
b1.hi()
val b2: B.type = magicallyConvert2(objectPath)
b2.extra()
}
trait A {
def hi() = {}
}
object B extends A {
def extra() = {}
}
}
How can the magicallyConvert1 and magicallyConvert2 functions be implemented?
For a normal class, this can be done using something like:
val b: A = Class.forName("x.B").newInstance().asInstanceOf[A]
But I found a solution for singletons, using Java reflections:
A singleton is accesible in Java under the name:
package.SingletonName$.MODULE$
So you have to append "$.MODULE$", which is a static field.
So we can use standard Java reflections to get it.
So the solution is:
def magicallyConvert1(objectPath: String) = {
val clz = Class.forName(objectPath + "$")
val field = clz.getField("MODULE$")
val b: A = field.get(null).asInstanceOf[A]
b
}
def magicallyConvert2(objectPath: String) = {
val clz = Class.forName(objectPath + "$")
val field = clz.getField("MODULE$")
val b: B.type = field.get(null).asInstanceOf[B.type]
b
}
But it would be interesting to still see a solution with Scala-Reflect en Scala-Meta.
take a look at scalameta http://scalameta.org it does what you want and more
How can I determine whether a Tree object is a subtype of Option[_] in a scalac (2.11) plugin?
Example:
class OptionIfPhase(prev: Phase) extends StdPhase(prev) {
override def name = OptionIf.this.name
override def apply(unit: CompilationUnit) {
for (tree # Apply(TypeApply(Select(rcvr, TermName("map")), typetree), List(param)) <- unit.body;
if rcvr.tpe <:< definitions.OptionClass.tpe)
{
reporter.echo(tree.pos, s"found a map on ${rcvr} with type ${rcvr.tpe} and typelist ${typetree} and arg $param")
}
}
This if-guard is always false, unfortunately. If I remove it completely, it matches all "typed" maps (as expected). Here's a few examples of expressions that I would like to match:
// All of these maps
val x = Some(4).map(_ * 2)
val xo: Option[Int] = x
xo.map(_ * 3)
None.map(i => "foo")
// Not this one
List(1, 3, 5).map(_ + 1) // removing the if guard completely also matches this
// The inner one
List(Some(1), None, Some(3)).flatMap(_.map(_ * -1))
// And from methods:
def foo: Option[Int] = Some(3)
foo.map(_ + 1)
I tried this:
if rcvr.tpe <:< typeOf[Option[_]]
Which works in the REPL (after import scala.reflect.runtime.universe._), but in the plugin it throws an exception: "scala.ScalaReflectionException: class net.obrg.optionif.OptionIf in compiler mirror not found."
What is an appropriate if condition in the for loop to filter out any "map" on an expression that has been determined to be of type Option[_]?
Background: I'm trying to build a scala plugin that converts all x.map(f) expressions, where x is of type Option, to (if (x.isEmpty) None else Some(f(x.get))).
The surrounding plugin boilerplate, so far:
package net.obrg.optionif
import scala.tools.nsc.{Global, Phase}
import scala.tools.nsc.plugins.{Plugin, PluginComponent}
class OptionIf(val global: Global) extends Plugin {
import global._
override val name = "optionif"
override val description = "flattens map operations on options to if statements"
override val components = List[PluginComponent](Component)
private object Component extends PluginComponent {
override val global: OptionIf.this.global.type = OptionIf.this.global
// I'm not 100% on these constraints, but afaik these are appropriate outer bounds.
// The typer phase is, obviously, necessary to determine which objects are of type Option, to begin with.
override val runsAfter = List("typer")
// The refchecks phase seems to do some if-related optimizations ("Eliminate branches in a conditional if the
// condition is a constant"). So put it before this, at least.
override val runsBefore = List("refchecks")
override val phaseName = OptionIf.this.name
override def newPhase(_prev: Phase) = new OptionIfPhase(_prev)
class OptionIfPhase(prev: Phase) extends StdPhase(prev) {
override def name = OptionIf.this.name
override def apply(unit: CompilationUnit) {
for (tree # Apply(TypeApply(Select(rcvr, TermName("map")), typetree), List(param)) <- unit.body;
if rcvr.tpe <:< definitions.OptionClass.tpe)
{
reporter.echo(tree.pos, s"found a map on ${rcvr} with type ${rcvr.tpe} and typelist ${typetree} and arg $param")
}
}
}
}
}
Adapted from http://www.scala-lang.org/old/node/140
EDIT: I just added the test for None.map(x => "foo") and the if guard actually does catch this! Still not the other ones, though.
let say I have 2 classes:
class A {
def sayHello(name: String) {
println("Hi " + name)
}
}
class B {
var methodMaps = Map[String, String => Unit]()
def registerMethod(methodName: String, method: String => Unit) {
methodMaps += (methodName -> method)
}
}
Okay, normally, I will call something like:
val b = new B
val a = new A
b.registerMethod("sayHello", a.sayHello)
But now I want to put the information into a config file, for example:
<method class="A" name="sayHello" />
Now, in the code it need to be something like this:
val b = new B
val className = readFromConfig()
val methodName = readFromConfig()
val aInstance = createInstanceFromReflection(className)
b.registerMethod(methodName, ...)
The problem is I don't know how to get the a.sayHello to pass to registerMethod, I can get the MethodMirror for sayHello, but how can I pass it to the registerMethod?
Thanks.
I guess that in your case, you need plain old reflection. From your example, in the expression b.registerMethod("sayHello", a.sayHello), the Scala compiler will lift the a.sayHello into a function.
When you are using dynamic class data from a file, the compiler can't help you; meaning that you need to do the work yourself. Following your own example, we should have:
val className = readFromConfig()
val methodName = readFromConfig()
val clazz = Class.forName(className)
val method = clazz.getMethods.find(x=>x.getName == "methodName" && x.getParameterTypes().length==1)
val aInstance = clazz.newInstance()
def invoke1[T,U](obj:Any, method:Method)(param:T):U = method.invoke(obj,Seq(param.asInstanceOf[java.lang.Object]):_*).asInstanceOf[U]
Now you should be able to register such construct in your map:
registerMethod(methodName, invoke1(aInstance,method) _ )
(*) This is not tested, but should be in the right direction, we use a similar construct in some part of our system.
Having a trait
trait Persisted {
def id: Long
}
how do I implement a method that accepts an instance of any case class and returns its copy with the trait mixed in?
The signature of the method looks like:
def toPersisted[T](instance: T, id: Long): T with Persisted
This can be done with macros (that are officially a part of Scala since 2.10.0-M3). Here's a gist example of what you are looking for.
1) My macro generates a local class that inherits from the provided case class and Persisted, much like new T with Persisted would do. Then it caches its argument (to prevent multiple evaluations) and creates an instance of the created class.
2) How did I know what trees to generate? I have a simple app, parse.exe that prints the AST that results from parsing input code. So I just invoked parse class Person$Persisted1(first: String, last: String) extends Person(first, last) with Persisted, noted the output and reproduced it in my macro. parse.exe is a wrapper for scalac -Xprint:parser -Yshow-trees -Ystop-after:parser. There are different ways to explore ASTs, read more in "Metaprogramming in Scala 2.10".
3) Macro expansions can be sanity-checked if you provide -Ymacro-debug-lite as an argument to scalac. In that case all expansions will be printed out, and you'll be able to detect codegen errors faster.
edit. Updated the example for 2.10.0-M7
It is not possible to achieve what you want using vanilla scala. The problem is that the mixins like the following:
scala> class Foo
defined class Foo
scala> trait Bar
defined trait Bar
scala> val fooWithBar = new Foo with Bar
fooWithBar: Foo with Bar = $anon$1#10ef717
create a Foo with Bar mixed in, but it is not done at runtime. The compiler simply generates a new anonymous class:
scala> fooWithBar.getClass
res3: java.lang.Class[_ <: Foo] = class $anon$1
See Dynamic mixin in Scala - is it possible? for more info.
What you are trying to do is known as record concatenation, something that Scala's type system does not support. (Fwiw, there exist type systems - such as this and this - that provide this feature.)
I think type classes might fit your use case, but I cannot tell for sure as the question doesn't provide sufficient information on what problem you are trying to solve.
Update
You can find an up to date working solution, which utilizes a Toolboxes API of Scala 2.10.0-RC1 as part of SORM project.
The following solution is based on the Scala 2.10.0-M3 reflection API and Scala Interpreter. It dynamically creates and caches classes inheriting from the original case classes with the trait mixed in. Thanks to caching at maximum this solution should dynamically create only one class for each original case class and reuse it later.
Since the new reflection API isn't that much disclosed nor is it stable and there are no tutorials on it yet this solution may involve some stupid repitative actions and quirks.
The following code was tested with Scala 2.10.0-M3.
1. Persisted.scala
The trait to be mixed in. Please note that I've changed it a bit due to updates in my program
trait Persisted {
def key: String
}
2. PersistedEnabler.scala
The actual worker object
import tools.nsc.interpreter.IMain
import tools.nsc._
import reflect.mirror._
object PersistedEnabler {
def toPersisted[T <: AnyRef](instance: T, key: String)
(implicit instanceTag: TypeTag[T]): T with Persisted = {
val args = {
val valuesMap = propertyValuesMap(instance)
key ::
methodParams(constructors(instanceTag.tpe).head.typeSignature)
.map(_.name.decoded.trim)
.map(valuesMap(_))
}
persistedClass(instanceTag)
.getConstructors.head
.newInstance(args.asInstanceOf[List[Object]]: _*)
.asInstanceOf[T with Persisted]
}
private val persistedClassCache =
collection.mutable.Map[TypeTag[_], Class[_]]()
private def persistedClass[T](tag: TypeTag[T]): Class[T with Persisted] = {
if (persistedClassCache.contains(tag))
persistedClassCache(tag).asInstanceOf[Class[T with Persisted]]
else {
val name = generateName()
val code = {
val sourceParams =
methodParams(constructors(tag.tpe).head.typeSignature)
val newParamsList = {
def paramDeclaration(s: Symbol): String =
s.name.decoded + ": " + s.typeSignature.toString
"val key: String" :: sourceParams.map(paramDeclaration) mkString ", "
}
val sourceParamsList =
sourceParams.map(_.name.decoded).mkString(", ")
val copyMethodParamsList =
sourceParams.map(s => s.name.decoded + ": " + s.typeSignature.toString + " = " + s.name.decoded).mkString(", ")
val copyInstantiationParamsList =
"key" :: sourceParams.map(_.name.decoded) mkString ", "
"""
class """ + name + """(""" + newParamsList + """)
extends """ + tag.sym.fullName + """(""" + sourceParamsList + """)
with """ + typeTag[Persisted].sym.fullName + """ {
override def copy(""" + copyMethodParamsList + """) =
new """ + name + """(""" + copyInstantiationParamsList + """)
}
"""
}
interpreter.compileString(code)
val c =
interpreter.classLoader.findClass(name)
.asInstanceOf[Class[T with Persisted]]
interpreter.reset()
persistedClassCache(tag) = c
c
}
}
private lazy val interpreter = {
val settings = new Settings()
settings.usejavacp.value = true
new IMain(settings, new NewLinePrintWriter(new ConsoleWriter, true))
}
private var generateNameCounter = 0l
private def generateName() = synchronized {
generateNameCounter += 1
"PersistedAnonymous" + generateNameCounter.toString
}
// REFLECTION HELPERS
private def propertyNames(t: Type) =
t.members.filter(m => !m.isMethod && m.isTerm).map(_.name.decoded.trim)
private def propertyValuesMap[T <: AnyRef](instance: T) = {
val t = typeOfInstance(instance)
propertyNames(t)
.map(n => n -> invoke(instance, t.member(newTermName(n)))())
.toMap
}
private type MethodType = {def params: List[Symbol]; def resultType: Type}
private def methodParams(t: Type): List[Symbol] =
t.asInstanceOf[MethodType].params
private def methodResultType(t: Type): Type =
t.asInstanceOf[MethodType].resultType
private def constructors(t: Type): Iterable[Symbol] =
t.members.filter(_.kind == "constructor")
private def fullyQualifiedName(s: Symbol): String = {
def symbolsTree(s: Symbol): List[Symbol] =
if (s.enclosingTopLevelClass != s)
s :: symbolsTree(s.enclosingTopLevelClass)
else if (s.enclosingPackageClass != s)
s :: symbolsTree(s.enclosingPackageClass)
else
Nil
symbolsTree(s)
.reverseMap(_.name.decoded)
.drop(1)
.mkString(".")
}
}
3. Sandbox.scala
The test app
import PersistedEnabler._
object Sandbox extends App {
case class Artist(name: String, genres: Set[Genre])
case class Genre(name: String)
val artist = Artist("Nirvana", Set(Genre("rock"), Genre("grunge")))
val persisted = toPersisted(artist, "some-key")
assert(persisted.isInstanceOf[Persisted])
assert(persisted.isInstanceOf[Artist])
assert(persisted.key == "some-key")
assert(persisted.name == "Nirvana")
assert(persisted == artist) // an interesting and useful effect
val copy = persisted.copy(name = "Puddle of Mudd")
assert(copy.isInstanceOf[Persisted])
assert(copy.isInstanceOf[Artist])
// the only problem: compiler thinks that `copy` does not implement `Persisted`, so to access `key` we have to specify it manually:
assert(copy.asInstanceOf[Artist with Persisted].key == "some-key")
assert(copy.name == "Puddle of Mudd")
assert(copy != persisted)
}
While it's not possible to compose an object AFTER it's creation, you can have very wide tests to determine if the object is of a specific composition using type aliases and definition structs:
type Persisted = { def id: Long }
class Person {
def id: Long = 5
def name = "dude"
}
def persist(obj: Persisted) = {
obj.id
}
persist(new Person)
Any object with a def id:Long will qualify as Persisted.
Achieving what I THINK you are trying to do is possible with implicit conversions:
object Persistable {
type Compatible = { def id: Long }
implicit def obj2persistable(obj: Compatible) = new Persistable(obj)
}
class Persistable(val obj: Persistable.Compatible) {
def persist() = println("Persisting: " + obj.id)
}
import Persistable.obj2persistable
new Person().persist()