I am on Scala 2.11.1, I have an annotation
case class MyAnnotation(id: String, message: String) extends StaticAnnotation
I would like to create a macro MessageFromMyAnnotation that transform the following code
class Foo {
#MyAnnotation("001", "James Bond 001")
def foox = {
... // my messy code
val x = MessageFromMyAnnotation("001")
... // my messy code
}
}
to
class Foo {
#MyAnnotation("001", "James Bond 001")
def foox = {
... // my messy code
val x = "Hello world, James Bond 001"
... // my messy code
}
}
In brief, the macro find, on its enclosing element, a message of #MyAnnotation whose id = "001" and return "Hello world, " + message
This is the macro
object MessageFromMyAnnotation {
def apply(id: String) = macro impl
def impl(c: Context)(id: c.Expr[String]): c.Expr[String] = {
c.internal.enclosingOwner.annotations.filter( anno =>
anno.tpe =:= c.universe.typeOf[MyAnnotation] &&
anno.asInstanceOf[MyAnnotation].id == id.value //this does not work
) match {
case anno :: Nil => c.universe.reify("Hello world, " + ...)
case x => c.abort(c.enclosingPosition, c.universe.showRaw(x))
}
}
}
I want to convert anno of type cuniverse.Annotation to MyAnnotation and compare its id with the argument id of type c.Expr[String], but the anno.asInstanceOf[MyAnnotation] yields a ClassCastException and id.value gives me an error message
cannot use value except for signatures of macro implementations
So, please help me with 2 questions:
How to convert anno of type cuniverse.Annotation to MyAnnotation
How to compare its id with the argument id of type c.Expr[String]
I have successfully made it thanks to #Imm's suggestion:
You don't have an instance of MyAnnotation - this is compile time, you only
have an AST that represents the call. You can get the Expr that's the
parameter given to the cuniverse.Annotation, and either splice it, or pattern
match it as a String literal and then take the value out of that.
And here is the code
object MessageFromMyAnnotation {
def apply(id: String) = macro impl
def impl(c: Context)(id: c.Expr[String]): c.Expr[String] = {
import c.universe._
id match { case Expr(Literal(Constant(idVal: String))) =>
(for { Annotation(tpe,
Literal(Constant(annoIdVal: String)) ::
Literal(Constant(annoMessageVal: String)) ::
Nil, _) <- c.internal.enclosingOwner.annotations
if tpe =:= typeOf[MyAnnotation] && idVal == annoIdVal
} yield (annoIdVal, annoMessageVal)
) match {
case (annoIdVal, annoMessageVal) :: Nil =>
reify(c.literal("Hello world, " + annoMessageVal).splice)
case matchedMore :: thanOne :: Nil => c.abort(c.enclosingPosition, "Found more than one #MyAnnotation with the same id")
case x => c.abort(c.enclosingPosition, "Not Found #MyAnnotation with the specified id")
}
}
}
}
Related
I want to generate a bunch of objects at compile time that follow a simple pattern, so I wrote the following macro:
object MyMacro {
def readWrite[T](taName: String, readParse: String => T, label: String, format: T => String): Any = macro readWriteImpl[T]
def readWriteImpl[T: c.WeakTypeTag](c: Context)(taName: c.Expr[String], readParse: c.Expr[String => T], label: c.Expr[String], format: c.Expr[T => String]): c.Expr[Any] = {
import c.universe._
def termName(s: c.Expr[String]): TermName = s.tree match {
case Literal(Constant(s: String)) => TermName(s)
case _ => c.abort(c.enclosingPosition, "Not a string literal")
}
c.Expr[Any](q"""
object ${termName(taName)} extends TypeAdapter.=:=[${implicitly[c.WeakTypeTag[T]].tpe}] {
def read[WIRE](path: Path, reader: Transceiver[WIRE], isMapKey: Boolean = false): ${implicitly[c.WeakTypeTag[T]].tpe} =
reader.readString(path) match {
case null => null.asInstanceOf[${implicitly[c.WeakTypeTag[T]].tpe}]
case s => Try( $readParse(s) ) match {
case Success(d) => d
case Failure(u) => throw new ReadMalformedError(path, "Failed to parse "+${termName(label)}+" from input '"+s+"'", List.empty[String], u)
}
}
def write[WIRE](t: ${implicitly[c.WeakTypeTag[T]].tpe}, writer: Transceiver[WIRE], out: Builder[Any, WIRE]): Unit =
t match {
case null => writer.writeNull(out)
case _ => writer.writeString($format(t), out)
}
}
""")
}
}
I'm not sure I have the return value for readWrite and readWriteImpl correct--the compiler complains mightily about some assertion failure!
I'm also not sure how to actually consume this macro. First I tried (in a separate compilation unit):
object TimeFactories {
MyMacro.readWrite[Duration](
"DurationTypeAdapterFactory",
(s: String) => Duration.parse(s),
"Duration",
(t: Duration) => t.toString)
}
Didn't work. If I tried to reference TimeFactories.DurationTypeAdapterFactory I got an error saying it wasn't found. Next I thought I'd try assigning it to a val...didn't work either:
object Foo {
val duration = MyMacro.readWrite[Duration](
"DurationTypeAdapterFactory",
(s: String) => Duration.parse(s),
"Duration",
(t: Duration) => t.toString).asInstanceOf[TypeAdapterFactory]
}
How can I wire this up so I get generated code compiled like this:
object TimeFactories{
object DurationTypeAdapterFactory extends TypeAdapter.=:=[Duration] {
def read[WIRE](path: Path, reader: Transceiver[WIRE], isMapKey: Boolean = false): Duration =
reader.readString(path) match {
case null => null.asInstanceOf[Duration]
case s => Try( Duration.parse(s) ) match {
case Success(d) => d
case Failure(u) => throw new ReadMalformedError(path, "Failed to parse Duration from input 'Duration'", List.empty[String], u)
}
}
def write[WIRE](t: Duration, writer: Transceiver[WIRE], out: Builder[Any, WIRE]): Unit =
t match {
case null => writer.writeNull(out)
case _ => writer.writeString(t.toString, out)
}
}
// ... More invocations of the readWrite macro with other types for T
}
I don't think, that you can generate new identifiers using macros and than use them publicly.
Instead, try to replace object ${termName(taName)} extends TypeAdapter simply with new TypeAdapter and assign invocation of the macro to a val (as in your second example). You will then reference an anonymous (and generated) class stored in a val. Parameter taName becomes redundant.
I am playing with scalameta and I want to have a generic measurement annotation which sends measurements about how long the method execution took.
I used Qing Wei's cache annotation demo.
https://www.cakesolutions.net/teamblogs/scalameta-tut-cache
It works for non async methods but my attribute doesn't match on methods which return Future due to the ExecutionContext argument list.
My annotation looks like this:
package measurements
import scala.concurrent.Future
import scala.meta._
class measure(name: String) extends scala.annotation.StaticAnnotation {
inline def apply(defn: Any): Any = meta {
defn match {
case defn: Defn.Def => {
this match {
case q"new $_($backendParam)" =>
val body: Term = MeasureMacroImpl.expand(backendParam, defn)
defn.copy(body = body)
case x =>
abort(s"Unrecognized pattern $x")
}
}
case _ =>
abort("This annotation only works on `def`")
}
}
}
object MeasureMacroImpl {
def expand(nameExpr: Term.Arg, annotatedDef: Defn.Def): Term = {
val name: Term.Name = Term.Name(nameExpr.syntax)
annotatedDef match {
case q"..$_ def $methodName[..$tps](..$nonCurriedParams): $rtType = $expr" => {
rtType match {
case f: Future[Any] => q"""
val name = $name
println("before " + name)
val future: ${rtType} = ${expr}
future.map(result => {
println("after " + name)
result
})
"""
case _ => q"""
val name = $name
println("before " + name)
val result: ${rtType} = ${expr}
println("after " + name)
result
"""
}
}
case _ => abort("This annotation only works on `def`")
}
}
}
I use the annotation like this:
#measure("A")
def test(x: String): String = x
#measure("B")
def testMultipleArg(x: Int, y: Int): Int = x + y
I would like to use it with async methods like this:
#measure("C")
def testAsync(x: String)(implicit ec: ExecutionContext) : Future[String] = {
Future(test(x))
}
but I get the following error:
exception during macro expansion:
scala.meta.internal.inline.AbortException: This annotation only works on `def`
I assume the issue is MeasureMacroImpl matching but I am not sure how to match on multiple argument groups. Could you guys help me? Any ideas or sample code would be greatly appreciated. I am pretty new to scala and scala meta so apologies if I asked a trivial question.
You are getting error because MeasureMacroImpl does not match curried parameters.
It's fairly trivial to match curried params, simply use
scala
case q"..$_ def $methodName[..$tps](...$nonCurriedParams): $rtType = $expr"
Notice the ...$nonCurriedParams instead of ..$nonCurriedParams
def control(x: String): Option[String] = macro controlImpl
def controlImpl(c: Context)(x: c.Expr[String]): c.Expr[Option[String]] = {
import c.universe._
val result = x.tree match {
case Match(expr, cases) =>
val matchz = Match(q"""List("hello")""", cases)
q"Some(List(5)).map{a => $matchz}"
case a => a
}
c.Expr[Option[String]](result)
}
This macro crashes during macro expansion with the following error:
java.lang.IllegalArgumentException: Could not find proxy for val o7: Some in List(value o7, method apply, <$anon: Function1>, value f, method apply, <$anon: Function1>, method apply, <$anon: Function0>, value <local ApplicativeTest>, object ApplicativeTest, package scalaz, package <root>) (currentOwner= value one )
This is the macro application point:
val f = IdiomBracket.control {
a.get match {
case List(one) => one
case _ => ""
}
}
What is odd is that if you substitute q"Some(List(5)).map{a => $matchz}" with q"Some($matchz)" then the compiler crash goes away.
"Simple macro" is an oxymoron.
The crash is in lambda lift, which indicates a bad symbol owner.
You can inspect the tree you produce with -Ybrowse:typer.
Consider what you'd like to produce:
class X {
//def g = List("hello") match { case List(x) => x case _ => "" }
def f = Some(List(5)).map{a => List("hello") match { case List(x) => x case _ => "" } }
}
The var in the case is owned by the anonfun you want to create:
Symbol: [has] value x
Symbol owner: value $anonfun
Your actual result:
Symbol: [has] value x
Symbol owner: value <local Controlled>
where Controlled is my sample enclosing object for your code.
You might have to rebuild the cases, as opposed to just cases map (_.untypecheck).
https://github.com/scala/scala/blob/v2.11.5/src/reflect/scala/reflect/api/Trees.scala#L1100
I'm trying to make a very simple parser with parser combinators (to parse something similar to BNF). I've checked several blog posts that explain the matter (the ones top-ranked at Google (for me)) and I think I understand it but the tests say otherwise.
I've checked the questions in StackOverflow and while some could maybe be applied and useful whenever I try to apply them something else breaks, so best way to to is going through an specific example:
This is my main:
def main(args: Array[String]) {
val parser: BaseParser = new BaseParser
val eol = sys.props("line.separator")
val test = s"a = b ${eol} a = c ${eol}"
System.out.println(test)
parser.parse(test)
}
This is the parser:
import com.github.trylks.tests.parser.ParserClasses._
import scala.util.parsing.combinator.syntactical._
import scala.util.parsing.combinator.ImplicitConversions
import scala.util.parsing.combinator.PackratParsers
class BaseParser extends StandardTokenParsers with ImplicitConversions with PackratParsers {
val eol = sys.props("line.separator")
lexical.delimiters += ("=", "|", "*", "[", "]", "(", ")", ";", eol)
def rules = rep1sep(rule, eol) ^^ { Rules(_) }
def rule = id ~ "=" ~ repsep(expression, "|") ^^ flatten3 { (e1: ID, _: Any, e3: List[Expression]) => Rule(e1, e3) }
def expression: Parser[Expression] = (element | parenthesized | optional) ^^ { x => x } // and sequence and repetition, but that's another problem...
def parenthesized: Parser[Expression] = "(" ~> expression <~ ")" ^^ { x => x }
def optional: Parser[Expression] = "[" ~> expression <~ "]" ^^ { Optional(_) }
def element: Parser[Element] = (id | constant) ^^ { x => x }
def constant: Parser[Constant] = stringLit ^^ { Constant(_) }
def id: Parser[ID] = ident ^^ { ID(_) }
def parse(text: String): Option[Rules] = {
val s = rules(new lexical.Scanner(text))
s match {
case Success(res, next) => {
println("Success!\n" + res.toString)
Some(res)
}
case Error(msg, next) => {
println("error: " + msg)
None
}
case Failure(msg, next) => {
println("failure: " + msg)
None
}
}
}
}
These are the classes that you are missing from the previous part of the code:
object ParserClasses {
abstract class Element extends Expression
case class ID(value: String) extends Element {
override def toString(): String = value
}
case class Constant(value: String) extends Element {
override def toString(): String = value
}
abstract class Expression
case class Optional(value: Expression) extends Expression {
override def toString() = s"[$value]"
}
case class Rule(head: ID, body: List[Expression]) {
override def toString() = s"$head = ${body.mkString(" | ")}"
}
case class Rules(rules: List[Rule]) {
override def toString() = rules.mkString("\n")
}
}
The problem is: as the code is now, it doesn't work, it parses only one rule (not both). If I replace eol with ";" (in the main and the parser) then it works (at least for this test).
Most people seem to prefer regex parsers, every blog explaining parser combinators doesn't get into details about the traits that could be extended or not, so I have no idea about those differences or why there are several (I say this because it may be important to understand why the code doesn't work). The problem is: If I try to use regex parsers then I get errors for all the strings that I have specified in the parsers "=", "*", etc.
I have been searching the forum and Google for answers to type erasure issues for Scala. However, I cannot find anything that answers my question.
I struggling with pattern matching on objects that match the type parameter of ParamClass. I need to pattern match on the type of incoming objects to the bar method. I have seen solutions such as
bar[X](a : X)(implicit m : Manifest[X])
which would solve my problem, but I cannot use this as the bar method is an overridden method. (Actually is the receive partial function in the Akka actor framework). The code is given below and should be self explanatory:
class ParamClass[A : Manifest] {
def bar(x : Any) = x match {
case a: A => println("Found A: " + a)
case _ => println("No match: " + x)
}
}
object ErasureIssue {
def main(args: Array[String]) {
val clz = new ParamClass[Int]
clz.bar("faf")
clz.bar(2.3)
clz.bar(12) // this should match, but does not
}
}
ErasureIssue.main(null)
Any help on solving this issue is greatly appreciated. I'm using Scala 2.9.1, BTW.
-J
In theory you could check in bar like this: x.getClass == implicitly[Manifest[A]].erasure, but that fails for primitive types such as Int for which the manifest correctly erases to Int, but bar is called with boxed type java.lang.Integer ... :-(
You could require A to be an AnyRef in order to get the boxed manifest:
class ParamClass[A <: AnyRef : Manifest] {
def bar(x : Any) = x match {
case _ if x.getClass == implicitly[Manifest[A]].erasure =>
println("Found A: " + x.asInstanceOf[A])
case _ => println("No match: " + x)
}
}
object ErasureIssue {
def main(args: Array[String]) {
val clz = new ParamClass[Integer] // not pretty...
clz.bar("faf")
clz.bar(2.3)
clz.bar(12) // ok
}
}
ErasureIssue.main(null)
Given your requirement to construct primitive arrays, you could store directly the boxed class, independently of the unboxed manifest:
object ParamClass {
def apply[A](implicit mf: Manifest[A]) = {
val clazz = mf match {
case Manifest.Int => classOf[java.lang.Integer] // boxed!
case Manifest.Boolean => classOf[java.lang.Boolean]
case _ => mf.erasure
}
new ParamClass[A](clazz)
}
}
class ParamClass[A] private[ParamClass](clazz: Class[_])(implicit mf: Manifest[A]) {
def bar(x : Any) = x match {
case _ if x.getClass == clazz =>
println("Found A: " + x.asInstanceOf[A])
case _ => println("No match: " + x)
}
def newArray(size: Int) = new Array[A](size)
override def toString = "ParamClass[" + mf + "]"
}
val pi = ParamClass[Int]
pi.bar("faf")
pi.bar(12)
pi.newArray(4)
val ps = ParamClass[String]
ps.bar("faf")
ps.bar(12)
ps.newArray(4)
If you try to compile with -unchecked, you immediately get the warning.
test.scala:3: warning: abstract type A in type pattern A is unchecked
since it is eliminated by erasure
case a: A => println("Found A: " + a)
If you now want to go deeper, you can use scalac -print
[[syntax trees at end of cleanup]]// Scala source: test.scala
package <empty> {
class ParamClass extends java.lang.Object with ScalaObject {
def bar(x: java.lang.Object): Unit = {
<synthetic> val temp1: java.lang.Object = x;
if (temp1.$isInstanceOf[java.lang.Object]())
{
scala.this.Predef.println("Found A: ".+(temp1))
}
else
{
scala.this.Predef.println("No match: ".+(x))
}
};
def this(implicit evidence$1: scala.reflect.Manifest): ParamClass = {
ParamClass.super.this();
()
}
};
final object ErasureIssue extends java.lang.Object with ScalaObject {
def main(args: Array[java.lang.String]): Unit = {
val clz: ParamClass = new ParamClass(reflect.this.Manifest.Int());
clz.bar("faf");
clz.bar(scala.Double.box(2.3));
clz.bar(scala.Int.box(12))
};
def this(): object ErasureIssue = {
ErasureIssue.super.this();
()
}
}
}
Now seeing this code you can see that your A has turned into a java.lang.Object, which cause all the parameters to match the clause