Skip to content

Instantly share code, notes, and snippets.

@qingwei91
Created September 4, 2017 14:12
Show Gist options
  • Select an option

  • Save qingwei91/ca02e5450df1348084e835ccc559ad22 to your computer and use it in GitHub Desktop.

Select an option

Save qingwei91/ca02e5450df1348084e835ccc559ad22 to your computer and use it in GitHub Desktop.
object CacheMacroImpl {
/**
*
* @param fnTypeParams - Type params of annotation instance, remember our cache macro is generic `class cache[K, V]`,
this will capture Seq(K, V)
* @param cacheExpr - Argument pass to `cache` macro, should be type of `CacheBackEnd[K, V]`
* @param annotatedDef - Methods that is annotated
*/
def expand(fnTypeParams: Seq[Type], cacheExpr: Term.Arg, annotatedDef: Defn.Def): Term = {
val cache = Term.Name(cacheExpr.syntax) // convert Term.Arg to Term.Name
annotatedDef match {
// Another Quasiquote pattern match
// - `..$_` match any modifier
// - `def` match only method
// - `$methodName` bind method name to $methodName
// - `[..$tps]` match some type params of method and bind to $tps
// - `(..$nonCurriedParams)` match non-curried argument list and binf to $nonCurriedParams
// - `$rtType` bind return type to $rtType
// - `$expr` bind method's body to $expr
case q"..$_ def $methodName[..$tps](..$nonCurriedParams): $rtType = $expr" =>
// here is the trick to handle different arg size
if (nonCurriedParams.size == 1) {
// if only 1 arg, use the arg as key of cache
val paramAsArg = Term.Name(nonCurriedParams.head.name.value)
q"""
// here we are generating code that call the CacheBackend
val result: ${rtType} = $cache.get($paramAsArg) match {
case Some(v) => v
case None =>
val value = ${expr}
$cache.put($paramAsArg, value)
value
}
result
"""
} else {
val paramAsArg = nonCurriedParams.map(p => Term.Name(p.name.value))
q"""
// if there are multiple arg, convert them in tuple, as use the tuple as key
val result: ${rtType} = $cache.get((..$paramAsArg)) match {
case Some(v) => v
case None =>
val value = ${expr}
$cache.put((..$paramAsArg), value)
value
}
result
"""
}
case other => abort(s"Expected non-curried method, got $other")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment