Scala Macro: get param default value - scala-macros

I have the next code, and i would like to extract the default parametr from value.
//
def extractor[T] = macro extractorImpl[T]
def extractorImpl[T: c.WeakTypeTag](c: Context) = {
//first i got a type contructor
???
}
i try with attachments but attachments.all return a Set[Any] with (for example) SymbolSourceAttachment(val name: String = "new name")
SymbolSourceAttachment contain ValDef but i do not know how to extract from SymbolSourceAttachment ValDef.
By the way i should to get a Map[String, String]("name" -> "new name")
Example:
case class Person(name: String = "new name")
object Macro {
def extractor[T] = macro extractorImpl[T]
def extractorImpl[T: c.WeakTypeTag](c: Context) = {
import c.universe._
c.weakTypeOf[T].declarations.collect {
case a: MethodSymbol if a.isConstructor =>
a.paramss.collect {
case b => b.collect {
case c =>
c.attachments.all {
case d => println(showRaw(d)) // => SymbolSourceAttachment(val name: String = "new name")
}
}
}
}
}
}
And macro should return Map("name" -> "new name")

Since you're seeing SymbolSourceAttachment, I assume you're using macro paradise (because it's an internal attachment used only in paradise), so I'll feel free to use quasiquotes :)
There's no easy way to get values of default parameters in Scala reflection API. Your best shot would be reverse-engineering the names of methods that are created to calculate default values and then referring to those.
SymbolSourceAttachment would sort of work if your macro is expanding in the same compilation run that compiles the case class, but it would break under separate compilation (attachments aren't saved in class files), and it wouldn't work in vanilla Scala (because this attachment is exclusive to paradise).
=== Macros.scala ===
import scala.reflect.macros.Context
import scala.language.experimental.macros
object Macros {
def impl[T](c: Context)(T: c.WeakTypeTag[T]): c.Expr[Map[String, Any]] = {
import c.universe._
val classSym = T.tpe.typeSymbol
val moduleSym = classSym.companionSymbol
val apply = moduleSym.typeSignature.declaration(newTermName("apply")).asMethod
// can handle only default parameters from the first parameter list
// because subsequent parameter lists might depend on previous parameters
val kvps = apply.paramss.head.map(_.asTerm).zipWithIndex.flatMap{ case (p, i) =>
if (!p.isParamWithDefault) None
else {
val getterName = newTermName("apply$default$" + (i + 1))
Some(q"${p.name.toString} -> $moduleSym.$getterName")
}
}
c.Expr[Map[String, Any]](q"Map[String, Any](..$kvps)")
}
def extractor[T]: Map[String, Any] = macro impl[T]
}
=== Test.scala ===
case class C(x: Int = 2, y: String, z: Boolean = true)(t: String = "hello")
object Test extends App {
println(Macros.extractor[C])
}
17:10 ~/Projects/Paradise2103/sandbox/src/main/scala (2.10.3)$ scalac Macros.scala && scalac Test.scala && scala Test
Map(x -> 2, z -> true)

Related

Accessing Custom Options from .desc file in Protobuf

I have a proto with the following definitions.
import "google/protobuf/descriptor.proto";
extend google.protobuf.FieldOptions{
optional bool is_key = 50002;
}
message Foo{
int64 id = 1 [(is_key) = true];
}
I generated a .desc file for the above. I was able to access all the Fields and Message defined by the FieldDescriptorProto and DescriptorProto types but not sure how to access the options defined and the value provided to it in this case is_key.
Could anyone provide me with a java version that could access the options from the .desc file
Not sure If this is the right way but all I was trying to do was read custom options from a descriptor_set.desc file.
I parsed the desc file, the problem is that it returns fileDescriptorProto Types. So I parsed them and built a dependencies graph. This helped me to create FileDescriptor Types.
Through FileDescriptor Types I was able to get Extensions and add them to extensionRegistry.
Post Which I parsed the .desc file once again but this time with extensionRegistry.
val filePathToFileDescriptorMap = mutableMapOf <String, FileDescriptor>(DescriptorProtos.getDescriptor().file.name to
DescriptorProtos.getDescriptor())
val extensionRegistry = ExtensionRegistry.newInstance()
fun buildDependencies(fileDescriptorProto: DescriptorProtos.FileDescriptorProto,
filePathToFileDescriptorProtoMap: Map<String, DescriptorProtos.FileDescriptorProto>) : FileDescriptor {
val dependencies = fileDescriptorProto.dependencyList.map { dependency ->
if(filePathToFileDescriptorMap.containsKey(dependency)) {
filePathToFileDescriptorMap[dependency]
}
else if(!filePathToFileDescriptorProtoMap.containsKey(dependency)) {
throw Exception("dependency not found $dependency")
}
else {
buildDependencies(filePathToFileDescriptorProtoMap[dependency]!!, filePathToFileDescriptorProtoMap)
}
}
filePathToFileDescriptorMap[fileDescriptorProto.name] =
FileDescriptor.buildFrom(fileDescriptorProto, dependencies.toTypedArray())
filePathToFileDescriptorMap[fileDescriptorProto.name]!!.extensions.forEach {
if(it.type == Descriptors.FieldDescriptor.Type.MESSAGE){
extensionRegistry.add(it, DynamicMessage.newBuilder(it.messageType).build())
}else{
extensionRegistry.add(it)
}
}
return filePathToFileDescriptorMap[fileDescriptorProto.name]!!
}
fun registerExtensions(){
val fileDescriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(
FileInputStream("src/generated/resources/desc_set.desc"),
)
val filePathToFileDescriptorProtoMap = fileDescriptorSet.fileList.associateBy { it.name }
fileDescriptorSet.fileList.forEach { fileDescriptorProto ->
if(!filePathToFileDescriptorMap.containsKey(fileDescriptorProto.name)){
buildDependencies(fileDescriptorProto, filePathToFileDescriptorProtoMap)
}
}
val fileDescriptorSetWithOptions = DescriptorProtos.FileDescriptorSet.parseFrom(
FileInputStream("src/generated/resources/desc_set.desc"),
extensionRegistry
)
println(fileDescriptorSetWithOptions)
}

Adding a property to an existing enum in Kotlin

Given the following enum defined in an external api.
public enum Status {
COMPLETE,
RUNNING,
WAITING
}
I would like a way to add a int flag to each enum value. I know that I can extend the enum:
fun Status.flag(): Int {
when(this) {
RUNNING -> return 1;
WAITING -> return 2;
else -> return 0;
}
}
However I would like to define those int flag values as constants. Maybe a companion object, but I don't think I can extend an existing enum and add a companion object.
Any ideas?
Unless you are using a field that already exists in the original enum (like ordinal), you won't be able to do what you're asking without wrapping the external enum in your own enum.
Sure you could use ordinal, but a newer version of the external API may change the order of the items in the enum, so I wouldn't recommend it. But, if you REALLY want to, you could do something like this (again, this is NOT recommended):
val Status.flag: Int
get() = this.ordinal
But I'd definitely recommend wrapping it. That way you guarantee that the flag integers you define won't change.
enum class MyStatus(val status: Status, val flag: Int) {
COMPLETE(Status.COMPLETE, 0),
RUNNING(Status.RUNNING, 1),
WAITING(Status.WAITING, 2);
companion object {
private val STATUS_TO_MYSTATUS = values().associateBy { it.status }
fun fromStatus(status: Status): MyStatus {
return STATUS_TO_MYSTATUS[status] ?: throw Exception("No MyStatus found for status ${status.name}")
}
}
}
You can then convert Status to MyStatus by using MyStatus.fromStatus(...). Or you can add an extension function to Status to easily convert to MyStatus.
fun Status.toMyStatus() = MyStatus.fromStatus(this)
You can add extension properties/methods to the companion object of enum/class/etc. if one exists:
val Status.Companion.COMPLETE_INT = 0
val Status.Companion.RUNNING_INT = 1
but indeed you can't currently "create" the companion object if it doesn't. So just put the constants into your own non-companion object:
object StatusFlags {
const val COMPLETE_INT = 0
const val RUNNING_INT = 1
const val WAITING_INT = 2
}
fun Status.flag(): Int {
when(this) {
RUNNING -> return StatusFlags.RUNNING_INT
...
}
}

kotlin safe conversion from string to enum

I need to convert strings to Enum values, but want a function which returns null if the string is not an enum.
enum class Colors{
Red, Green, Blue
}
I can used Colors.valueOf(testString) provided testString is value, but there will be an exception if it is not valid, and I want a null in that case.
Because I want to this often, an extension function would be ideal. But the extension needs to operate on the class Colors, and not an object of type Colors.
Anyone know how to write such an extension? Ideally one that is generic for any enum class.
It is simple to write a top level function, but I am seeking one that acts as the standard 'method' does
// instead of
val willGetAnException = Colors.valueOf("Yellow") // standard existing fun
val willGetNull = Colors.valueOrNullOf("Orange") // new fun i seek
And ideally one that is generic and works for any enum
You don't want an extension since they must be invoked on an existing object. You want a top-level function. There is a built in one You can use:
/**
* Returns an enum entry with specified name.
*/
#SinceKotlin("1.1")
public inline fun <reified T : Enum<T>> enumValueOf(name: String): T
You can call it by inferring the type, or explicitly:
val a : MyEnumClass = enumValueOf("A")
val b = enumValueOf<MyEnumClass>("B")
However this method is not nullable: it throws java.lang.IllegalArgumentException on unknown values.
But it's easy to mimick it's behavior and have it work for nullable enums with a top level function:
inline fun <reified T : Enum<*>> enumValueOrNull(name: String): T? =
T::class.java.enumConstants.firstOrNull { it.name == name }
Colors.values().find { it.name == "Yellow" }
You can use something like this :
inline fun <reified T : Enum<T>> String.asEnumOrDefault(defaultValue: T? = null): T? =
enumValues<T>().firstOrNull { it.name.equals(this, ignoreCase = true) } ?: defaultValue
Then: "Yellow".asEnumOrDefault(Colors.Green)
Or, if you it can't be infered: "Yellow".asEnumOrDefault<Colors>()
enum class Colors {
BLACK, WHITE, UNKNOWN;
companion object {
// Verbose for illustrative purposes
fun fromOrdinal(ordinal: Int): Colors = values()[ordinal]
fun fromOrdinalOrNull(ordinal: Int): Colors? = values().getOrNull(ordinal)
fun fromOrdinalOrDefault(ordinal: Int): Colors = values().getOrElse(ordinal) { UNKNOWN }
fun fromName(name: String): Colors = valueOf(name.uppercase())
fun fromNameOrNull(name: String): Colors? = values().find { it.name == name.uppercase() }
fun fromNameOrDefault(name: String): Colors = values().find { it.name == name.uppercase() } ?: UNKNOWN
}
}
Given the fact it's not easy to access the Enum value safely in Kotlin, I published a library enum-or-null-kt to provide a collection of shorthand functions which makes you can write code like below:
class Example {
enum class Direction(val az: Int) {
NORTH(0),
EAST(90),
SOUTH(180),
WEST(240)
}
fun printAz01(name: String = "EAST") {
val direction = enumValueOrNull<Direction>(name) ?: Direction.EAST
println("az01=${direction.az}")
}
fun printAz02(name: String = "EAST") {
val direction = name.toEnumOrNull<Direction>() ?: Direction.EAST
println("az02=${direction.az}")
}
fun printName01(az: Int = 0) {
val direction = enumValueOrNull<Direction> {
it.az == az
} ?: Direction.NORTH
println("name03=${direction.name}")
}
fun printName02(ordinal: Int = 0) {
val direction = enumValueOrNull<Direction> {
it.ordinal == ordinal
} ?: Direction.NORTH
println("name03=${direction.name}")
}
}
With it, not only can you access the Enum value with names, but also you can pass an arbitrary higher-order function as a predicate clause. That is convenient when you need to deal with a custom conversion such as JPA attribute converters.
The Kotlin API does not work by simply using <reified T: Enum<T>>. It throws an exception of the type InvocationTargetException. So I pass directly to type: Class<T> by parameter.
private fun <T> enumValueOf(type: Class<T>, enum: String) : T {
return type.enumConstants.first { it.toString() == enum }
}
Using
if (type.isEnum) enumValueOf(#Field.type, value as String)

Bug in Scalajs when compiling pattern matching?

I currently cannot reproduce this in a minimum working example, but I'm working on it.
Adding a line in a dead code causes a crash at run-time.
Here are the relevant code snippets, first from the scala file:
// We are inside an object. Expr is a trait with many case classes.
case class Problem(in: Expr, out: Expr)
type RepairOutput = Either[String, Stream[Expr]]
private sealed trait RepairFinalTransform
...
private sealed trait TransformExtend extends RepairFinalTransform {
def continuation(repair: Problem => RepairOutput)
: Expr => RepairOutput
}
private case class TransformSolutionAndContinue(f: Expr => (Problem, Expr => Expr)) extends TransformExtend {
def continuation(repair: Problem => RepairOutput): Expr => RepairOutput = (x: Expr) => f(x) match {
case (p, rewriter) => repair(p) andMap rewriter
}
}
private case class TransformAndContinue(f: Expr => RepairOutput) extends TransformExtend {
def continuation(repair: Problem => RepairOutput): Expr => RepairOutput = f
}
The following code snippet
case c: TransformExtend =>
r = r andThen c.continuation(_repair)
, when working, is compiled as:
if ($is_Lexample_LambdaCalculus$TransformExtend(x1)) {
var x5 = $as_Lexample_LambdaCalculus$TransformExtend(x1);
var e$1 = r;
r = new $c_Lexample_LambdaCalculus$Retransformer().init___s_util_Either(e$1).andThen__F1__s_util_Either(x5.continuation__F1__F1(new $c_sjsr_AnonFunction1().init___sjs_js_Function1((function($this) {
return (function(original_problem$2) {
var original_problem = $as_Lexample_LambdaCalculus$Problem(original_problem$2);
return $m_Lexample_LambdaCalculus$().$$undrepair__Lexample_LambdaCalculus$Problem__s_util_Either(original_problem)
})
})(this))))
}
However, as soon as I add one occurrence of TransformAndContinue(???) (I did not have any before) in a place that is currently never executed, the above code becomes:
if ($is_Lexample_LambdaCalculus$TransformExtend(x1)) {
var x5 = $as_Lexample_LambdaCalculus$TransformExtend(x1);
var e$1 = r;
r = new $c_Lexample_LambdaCalculus$Retransformer().init___s_util_Either(e$1).andThen__F1__s_util_Either(x5.f$1)
}
Note that x5 is supposed to be a type cast to TransformExtend, which does not have the field f, only the continue method. Since f exists in both classes, its return type differs. Besides, the case class TransformSolutionAndContinue was used in many other places, so there is no reason why it calls f instead of continue.
So the translation to Javascript seems wrong (and actually, the errors fail on this point). I'm using scalajs 0.6.19 and compile using fastOptJS and scalatest.
Can you tell me what to change in my code so that the compilation is correct? Is it a known bug?

Threading `Try`s through for-comprehension

Triggered by another question (which has been subsequently edited away though), I wanted to try out how easy it would be to chain calls to Scala 2.10's Try construct (cf. this presentation), using for-comprehensions.
The idea is to have a list of tokens and match them against a sequence of patterns, then return the first error or the successfully matched pattern. I arrived at the following pretty awkward version, and I wonder if this can be made simpler and nicer:
import util.Try
trait Token
case class Ident (s: String) extends Token
case class Keyword(s: String) extends Token
case class Punct (s: String) extends Token
case object NoToken extends Token
case class FunctionDef(id: Ident)
case class Expect[A](expectation: String)(pattern: PartialFunction[Token, A]) {
def unapply(tup: (Try[_], Token)) = Some(tup._1.map { _ =>
pattern.lift(tup._2).getOrElse(throw new Exception(expectation))
})
}
Now construct the expectations for Keyword("void") :: Ident(id) :: Punct("(") :: Punct(")") :: tail
val hasVoid = Expect("function def starts with void") { case Keyword("void") => }
val hasIdent = Expect("expected name of the function") { case id: Ident => id }
val hasOpen = Expect("expected opening parenthesis" ) { case Punct("(") => }
val hasClosed = Expect("expected closing parenthesis" ) { case Punct(")") => }
Construct a full test case:
def test(tokens: List[Token]) = {
val iter = tokens.iterator
def next(p: Try[_]) = Some(p -> (if (iter.hasNext) iter.next else NoToken))
def first() = next(Try())
val sq = for {
hasVoid (vd) <- first()
hasIdent (id) <- next(vd)
hasOpen (op) <- next(id)
hasClosed(cl) <- next(op)
} yield cl.flatMap(_ => id).map(FunctionDef(_))
sq.head
}
The following verifies the test mehod:
// the following fail with successive errors
test(Nil)
test(Keyword("hallo") :: Nil)
test(Keyword("void" ) :: Nil)
test(Keyword("void" ) :: Ident("name") :: Nil)
test(Keyword("void" ) :: Ident("name") :: Punct("(") :: Nil)
// this completes
test(Keyword("void" ) :: Ident("name") :: Punct("(") :: Punct(")") :: Nil)
Now especially the additional flatMap and map in yield seems horrible, as well as the need to call head on the result of the for comprehension.
Any ideas? Is Try very badly suited for for comprehensions? Shouldn't either Either or Try be "fixed" to allow for this type of threading (e.g. allow Try as a direct result type of unapply)?
The trick seems to be to not create Try instances in the inner structure, but instead let that throw exceptions and construct one outer Try.
First, let's get rid of the Try[Unit]'s:
case class Expect(expectation: String)(pattern: PartialFunction[Token, Unit]) {
def unapply(token: Token) =
pattern.isDefinedAt(token) || (throw new Exception(expectation))
}
case class Extract[A](expectation: String)(pattern: PartialFunction[Token, A]) {
def unapply(token: Token) = Some(
pattern.lift(token).getOrElse(throw new Exception(expectation))
)
}
Then the checks become:
val hasVoid = Expect ("function def starts with void") { case Keyword("void") => }
val getIdent = Extract("expected name of the function") { case id: Ident => id }
val hasOpen = Expect ("expected opening parenthesis" ) { case Punct("(") => }
val hasClosed = Expect ("expected closing parenthesis" ) { case Punct(")") => }
And the test method:
def test(tokens: List[Token]) = Try {
val iter = tokens.iterator
def next() = Some(if (iter.hasNext) iter.next else NoToken)
(for {
hasVoid() <- next()
getIdent(id) <- next()
hasOpen() <- next()
hasClosed() <- next()
} yield FunctionDef(id)).head // can we get rid of the `head`?
}

Resources