ããããã
http://www.scala-lang.org/node/27499
ã«èŒã£ãŠããæ°æ©èœïŒå®éšçæ©èœãªã¹ãã®äžã§æåŸã«ãªãããã¯ãã§ããã³ã³ãã€ã«æã«ãASTãè§Šã£ãŠäœããã®åŠçãããããšãã§ããŸãã
Macros
http://docs.scala-lang.org/overviews/macros/overview.html
æ¥æ¬èªèš³
http://eed3si9n.com/ja/node/61
ãã®ãã¯ããååã®Scala Reflectionã®æŠèŠã§ããã©ããšåºãŠããŠããŠã
http://docs.scala-lang.org/overviews/reflection/overview.html
ãCompile-time ReflectionããšããŠååã ãæžãããŠããŸããã³ã³ãã€ã«æã«ãASTãæäœã§ããæ©èœã§ãããŒãšã
Scala Reflectionãšéãã®ã¯ãäœçšããã®ãå®è¡æãã³ã³ãã€ã«æãã§ãããã¯ãã¯ã³ã³ãã€ã«æã«äœçšããŸããããASTãè§Šãããšã«ãªãã®ã§ãå®éã«ã¯ããã¡ããé¢åã§ãâŠã
ã§ããã®ãã¯ãã§ããäžèšã®æŠèŠããŒãžã«èŒã£ãŠããµã³ãã«ããããŸãããåãããªãã£ãã®ã§ããã®ä»ã®ããŒãžãåèã«ããªããããããè§Šã£ãŠã¿ãŸããã
åèã«ããã®ã¯ãäžèšããŒãžã«ãèŒã£ãŠãã
Using macros with Maven or SBT
https://github.com/scalamacros/sbt-example
ãåã£æããã«ãã€ã€ãæŠèŠããŒãžãšã«ããã£ãããªããé 匵ã£ãŠã¿ãŸãããšã
ããããããããèŠãæ¹ãããããã
http://eed3si9n.com/ja/metaprogramming-in-scala-210
æåã®ãã¯ã
ãã¯ããäœæããã«ã¯ã
ãšãªãããã®2ã€ã¯åããŠããå¿ èŠããããŸããèŠã¯ããã¯ãã¯å ã«ã³ã³ãã€ã«ããŠãããªããŠã¯ãªããªãããšããããšã§ãã
ã§ã¯ããã¯ããæžããŠã¿ãŸãã
ãã¯ãã䜿çšããã«ã¯ããã¯ããå®çŸ©ããScalaãœãŒã¹ã«
import scala.language.experimental.macros
ãšæžãããã³ã³ãã€ã«ãªãã·ã§ã³ã«
-language:experimental.macros
ãäžããå¿ èŠããããŸããèªåã¯ãimportæã§æžããŸããã
ãŸãã¯ãåŒæ°ãåããã«åçŽã«ãMy First Macroããšè¡šç€ºããã ãã®ãã¯ããæžããŠã¿ãŸãããã¯ãã®å®çŸ©ã¯ãäœãããã®objectã«æžãããã§ãã
import scala.language.experimental.macros import scala.reflect.macros.Context object FirstMacro { }
ããã«ãåŒã³åºããã¯ãã®é¢æ°å®çŸ©ãæžããŸããã€ã³ã¿ãŒãã§ãŒã¹çã«ã¯ãæ®éã®é¢æ°ãšäœãå€ãããŸãããå®çŸ©ãã颿°åã¯ããprintOnlyããšããŸãã
import scala.language.experimental.macros import scala.reflect.macros.Context object FirstMacro { def printOnly: Unit = macro printOnlyImpl }
ãã ãã颿°å®çŸ©ã®å®äœã¯æžããŸãããmacroã«é¢æ°ã®å®äœå®çŸ©ãäžããã ãã«ãªããŸããå®äœã«ååã¯ããprintOnlyImplããšããŸãã
ç¶ããŠãå®äœã®å®çŸ©ã§ãã
def printOnlyImpl(c: Context): c.Expr[Unit] = c.universe.reify(println("My First Macro"))
åŒæ°ã¯ãå¿
ãscala.reflect.macros.Contextã§ãããŸãä»åã¯æ»ãå€ãUnitãªã®ã§ããã®ããã«å®£èšããŸãããContext#Exprãã¬ã€ãã«ååŒæ°ãäžãããã®ãæ»ãå€ãšãªããŸãã
Context#universe#reifyã¯ASTãç°¡åã«äœã£ãŠãããã¡ãœããã§ãã
ã§ã¯ããããã³ã³ãã€ã«ããŠãããŸãã
$ fsc FirstMacro.scala
ç¶ããŠã䜿ãåŽã«ã
object UseMacro { def main(args: Array[String]): Unit = { import FirstMacro._ printOnly } }
ãã¡ãã¯ãæ®éã«ã¡ãœãããšããŠäœ¿ãã°OKã§ãã
ã§ã¯ãã³ã³ãã€ã«ãå®è¡ã
$ fsc UseMacro.scala $ scala UseMacro My First Macro
åããŸãããã
ã³ã³ãã€ã«æã«ã-Ymacro-debug-liteããªãã·ã§ã³ãä»ãããšãè©³çŽ°ãªæ å ±ãšããããASTãèŠããŸãã
$ fsc -Ymacro-debug-lite UseMacro.scala performing macro expansion FirstMacro.printOnly at source-/xxxxx/UseMacro.scala,line-5,offset=89 scala.this.Predef.println("My First Macro") Apply(Select(Select(This(newTypeName("scala")), newTermName("Predef")), newTermName("println")), List(Literal(Constant("My First Macro"))))
ä»åäœæãããã¯ãã®å®å šãªå®çŸ©ã¯ããããªããŸãã
import scala.language.experimental.macros import scala.reflect.macros.Context object FirstMacro { def printOnly: Unit = macro printOnlyImpl def printOnlyImpl(c: Context): c.Expr[Unit] = c.universe.reify(println("My First Macro")) } }
ããã§ã®ãã€ã³ãã¯ã
- ãã¯ããšããã䜿çšããã³ãŒãã®ã³ã³ãã€ã«ã¯å¥ã ã«ããã¯ããå
- ãã¯ãã®å®çŸ©ã¯ãã·ã°ããã£ãšå®äœãåãã
ãšãã£ããšããã§ããããã
åŒæ°ãåãåããã¯ã
ç¶ããŠãåŒæ°ãåããã¯ããæžããŠã¿ãŸãã
ãé¡ã¯ããåŒæ°ã«æååãåãã3åããè¿ãããã®ããŠè¿ãããšããŸããå®äœã¯ãæ®éã«StringOps#*ã§ãããŸããã
以äžãå®çŸ©ã«ãªããŸãã颿°åã¯ãtripleããšããŸãã
import scala.language.experimental.macros import scala.reflect.macros.Context import java.text.SimpleDateFormat import java.util.Date object FirstMacro { def printOnly: Unit = macro printOnlyImpl def printOnlyImpl(c: Context): c.Expr[Unit] = c.universe.reify(println("My First Macro")) def triple(msg: String): String = macro tripleImpl def tripleImpl(c: Context)(msg: c.Expr[String]): c.Expr[String] = { import c.universe._ val Literal(Constant(m: String)) = msg.tree c.Expr(Literal(Constant(m * 3))) // äžèšã¯ã以äžã§ãOK // c.literal(m * 3) // ããã«ãå šéšãŸãšããŠä»¥äžã§ãOK // c.universe.reify(msg.splice * 3) } }
tripleã®ã·ã°ããã£èªäœã¯ãæ®éã«åŒæ°ãåãéåžžã®é¢æ°å®çŸ©ã§ãããã ãåŒæ°ãåã£ãŠãmacroã«é¢æ°å®çŸ©ãæž¡ããšããã¯å€ãããŸããã
ã§ãå®äœã®å®çŸ©ã§ãããããªã£ãŠããŸãã
def tripleImpl(c: Context)(msg: c.Expr[String]): c.Expr[String] = { import c.universe._ val Literal(Constant(m: String)) = msg.tree c.Expr(Literal(Constant(m * 3))) // äžèšã¯ã以äžã§ãOK // c.literal(m * 3) // ããã«ãå šéšãŸãšããŠä»¥äžã§ãOK // c.universe.reify(msg.splice * 3) }
ä»åºŠã¯å€ãè¿ãã®ã§ãæ»ãå€ã®åã
c.Expr[String]
ãšãªã£ãŠããŸãããŸãåŒæ°ã¯ãã«ãªãŒåããäžã§Context#Expråã§åãåãããšã«ãªããŸãã
msg: c.Expr[String]
ã§ãããããåŒæ°ã«æž¡ãããŠããStringãæãåºããããšããã§ããã倿°msgã¯Stringã§ã¯ãªãã®ã§ãåè§£ããŸããContext#Expr#treeã§ASTãååŸã§ããã®ã§ããããšUniverse#LiteralãšUniverse#Constantã䜿ã£ãŠäžã®å€ãååŸããŸãã
import c.universe._ val Literal(Constant(m: String)) = msg.tree
ãã®èŸºãã®ã³ãŒããæžãæã¯ãContext#universeãimportããŠããã®ãéäŸã¿ããã§ãã
ããã§ã倿°mã«åŒæ°ã§æž¡ãããStringãå ¥ããŸãã
ããšã¯ããã«StringOps#*(3)ãããŠãASTãäœã£ãŠExprã§ã©ããããŠè¿ããŸãã
c.Expr(Literal(Constant(m * 3)))
ããã§ãã³ã³ãã€ã«æã«ASTãæäœããŠããããšã«ãªã£ãŠããŸããåŒæ°ãåãåããªãæ¹ã®ãã¯ããåã話ãªã®ã§ããããã¡ãã¯æ»ãå€ããªããã¯ãã ã£ãã®ã§ãæ»ãå€ã®ããå®çŸ©ã§æžããæ¹ãããããªãããšã
ãªãããœãŒã¹ã³ã¡ã³ãã«ãæžããŠããŸãããæåŸã®
c.Expr(Literal(Constant(m * 3)))
ã¯
c.literal(m * 3)
ã«çœ®ãæããããŸãã
ããã«ããããŸã§ã®åŠçãå šéšãŸãšããŠ
c.universe.reify(msg.splice * 3)
ã«ã眮ãæããããŸããExpr#spliceã§äžã®å€ãåããŸãããã ãããã¯Context#universe#reifyãšäžç·ã«äœ¿ãã¹ãã ãããªã
ã§ã¯ãäœã£ããã¯ãã䜿ã£ãŠã¿ãŸãã
object UseMacro { def main(args: Array[String]): Unit = { import FirstMacro._ printOnly println(triple("Hello")) } }
å®è¡ãããš
$ scala UseMacro My First Macro HelloHelloHello
ãšãªããŸããåããŠãŸããã
ã¡ãªã¿ã«ãscalacãšãã§ã-Xprintããšãã§ã³ã³ãã€ã«çµæãèŠããšåãããŸãã
$ scalac -Xprint:jvm UseMacro.scala [[syntax trees at end of jvm]] // UseMacro.scala package <empty> { object UseMacro extends Object { def main(args: Array[String]): Unit = { scala.this.Predef.println("My First Macro"); scala.this.Predef.println("HelloHelloHello") }; def <init>(): UseMacro.type = { UseMacro.super.<init>(); () } } }
ãšãã³ã³ãã€ã«çµæã«ã¯ãã§ã«ãHelloãã3åå ¥ã£ãŠããããšãåãããŸãã
ãããŸã§ããã³ã³ãã€ã«æã«ASTãè§Šã£ãŠããããšãããšããããã€ã³ãã§ãã
ããšãå¯å€é·åŒæ°ãåããã¯ããæžããŠã¿ãŸããã
def varargs(args: Int*): Int = macro varargsImpl def varargsImpl(c: Context)(args: c.Expr[Int]*): c.Expr[Int] = { import c.universe._ val sum = args.toList.map { i => val Literal(Constant(iv: Int)) = i.tree iv }.sum c.literal(sum) }
Intãåãåã£ãŠãåç®ããŠè¿ãã ãã§ããListãè¿ãããšé 匵ã£ãŠã¿ãŸããããã¡ãã£ã𿫿ããŸããã
ãããŸã§ãäž»ã«è§Šã£ãŠããã¯ã©ã¹ããã¬ã€ãã¯
Context
http://www.scala-lang.org/api/current/index.html#scala.reflect.macros.Context
UniverseïŒïŒscala.reflect.macrosããã±ãŒãžã§ãïŒ
http://www.scala-lang.org/api/current/index.html#scala.reflect.macros.Universe
Expr
http://www.scala-lang.org/api/current/index.html#scala.reflect.api.Exprs$Expr
Literal
http://www.scala-lang.org/api/current/index.html#scala.reflect.api.Trees$Literal
Constant
http://www.scala-lang.org/api/current/index.html#scala.reflect.api.Constants$Constant
äžå¿ã«ããã®ã¯ãUniverseãšTreesãªãã§ããããã
http://www.scala-lang.org/api/current/index.html#scala.reflect.api.Trees
ããã®å®çŸ©ã«ããããæ¢èŠæããããŸãâŠã
Scala Reflectionãšããããã¯ããšããã倧å€ã§ããâŠãã©ãã ã䜿ã人ãããã§ããïŒASTè§Šãã®ã¯ãããŒãªãé¢åã
ãšãããããã²ãšãŸãæ°æ©èœãªã¹ãã«èŒã£ãŠãããã®ã¯ãã ãããè§Šã£ãŠã¿ãæãã§ããããèŒã£ãŠãããã®ã¯âŠã
ãªãã±
ãã¯ããæå¹æŽ»çšãããã®ãããŸãæãã€ããªãã£ãã®ã§ãCèšèªã®ã__LINE__ãã¿ãããªãã®ãæžããŠã¿ãŸããã
ææ°èŸŒãã§å§ãããã®ã®ãæå€ãšãã£ããæ å ±ãååŸã§ããŠããŸã£ãŠã¡ãã£ãšããã¯ãªã
import scala.language.experimental.macros import scala.reflect.macros.Context import java.text.SimpleDateFormat import java.util.Date object CLikeMacro { def __LINE__ : Int = macro srcLineImpl def srcLineImpl(c: Context): c.Expr[Int] = c.literal(c.enclosingPosition.line) def __FILE__ : String = macro fileImpl def fileImpl(c: Context): c.Expr[String] = c.literal(c.enclosingUnit.source.file.name) def __METHOD__ : String = macro methodImpl def methodImpl(c: Context): c.Expr[String] = c.literal(c.enclosingMethod.symbol.name.decoded) def __DATE__ : String = macro dateImpl def dateImpl(c: Context): c.Expr[String] = c.literal(new SimpleDateFormat("yyyy/MM/dd").format(new Date)) def __TIME__ : String = macro timeImpl def timeImpl(c: Context): c.Expr[String] = c.literal(new SimpleDateFormat("HH:mm:ss.S").format(new Date)) }
䜿ã£ãŠã¿ãŸãããã
object UseMacro { def main(args: Array[String]): Unit = { import FirstMacro._ printOnly println(triple("Hello")) println(varargs(1, 2, 3, 4, 5)) import CLikeMacro._ println("Current Line => " + __LINE__) println("Current Source => " + __FILE__) println("Current Method => " + __METHOD__) println("Current Line Complied Time => " + __TIME__) println("Current Line Complied Date => " + __DATE__) } }
å®è¡ã
$ scala UseMacroMy First Macro My First Macro HelloHelloHello 15 Current Line => 12 Current Source => UseMacro.scala Current Method => main Current Line Complied Time => 23:09:15.600 Current Line Complied Date => 2013/01/23
æéã¯ãã³ã³ãã€ã«æã«åã蟌ãã§ããã®ã§äœåå®è¡ããŠããåãæéãåºåãããŸãã
ãœãŒã¹é¢é£ã®æ
å ±ã¯ãContextããååŸã§ããŸãã
Position
http://www.scala-lang.org/api/current/index.html#scala.reflect.api.Position
CompilationUnit
http://www.scala-lang.org/api/current/index.html#scala.reflect.macros.Universe$CompilationUnit
TreeContextApi
http://www.scala-lang.org/api/current/index.html#scala.reflect.macros.Universe$TreeContextApi
ãªãã±2
ãã®ãµã³ãã«ãæžããŠããæã«ãããŸã«ããã£ãã®ãfscã§ãã
ãã¯ãã倿ŽããŠåã³ã³ãã€ã«ããæã«ã倿Žãèªèãããªããšããå Žé¢ã«ããééããŸããã仿¹ããªãã®ã§ããã¯ãã倿Žããå Žåã¯
$ fsc -shutdown
ã§1åèœãšããŠã¯ã©ã¹ãã¡ã€ã«ãæ¶ããŠããå床ã³ã³ãã€ã«ããŠãŸããã