Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Domain-specific names: an application of Scala macros

Domain-specific names: an application of Scala macros

Talk at ScalaSyd April 2013

Tony Sloane

August 21, 2014
Tweet

More Decks by Tony Sloane

Other Decks in Programming

Transcript

  1. Domain-specific names: an application of Scala macros Anthony M. Sloane

    Programming Languages Research Group Department of Computing Macquarie University [email protected] [email protected] @inkytonik April 11, 2013
  2. Simple expression language abstract syntax abstract class Exp case class

    Num (i : Int) extends Exp case class Var (s : String) extends Exp case class Add (l : Exp , r : Exp) extends Exp case class Mul (l : Exp , r : Exp) extends Exp Add (Num (1), Mul (Num (2), Var ("i")))
  3. Domain objects: Kiama attributes val isconst : Exp => Boolean

    = attr { case Num (_) => true case Var (_) => false case Add (l, r) => isconst (l) && isconst (r) case Mul (l, r) => isconst (l) && isconst (r) }
  4. Domain objects: Kiama rewriting strategies def rule (f : Any

    ==> Any) : Strategy = new Strategy { ... } val mulbytwoToAdd = rule { case Mul (Num (2), x) => Add (x, x) } val simplifiers = mulbytwoToAdd <+ mulbyzeroToZero val simplify = everywherebu (simplifiers)
  5. Domain object names: error messages val badattr : Exp =>

    Int = attr { case n : Num => 1 + badattr (n) } badattr (Num (3)) Cycle detected in attribute evaluation ’badattr ’ at Num (3)
  6. Domain object names: profiling By strategy for StratEval: Total Total

    Count Count ms % % 21 30.5 1 0.1 obfuscateProgram 20 28.1 150 13.4 all 20 28.0 148 13.2 topdown 12 17.3 1 0.1 obfuscateUses 10 14.5 150 13.4 attempt 10 14.1 1 0.1 obfuscateDecls 3 5.5 75 6.7 <+ 1 1.9 71 6.3 obfuscateNormalUse 1 1.6 75 6.7 obfuscateDecl 0 0.8 127 11.3 id 0 0.5 75 6.7 preservePredefinedUse
  7. User-specified names val mulbytwoToAdd = rule (" mulbytwoToAdd") { case

    Mul (Num (2), x) => Add (x, x) } val isconst : Exp => Boolean = attr ("isconst") { case Num (_) => true case Var (_) => false case Add (l, r) => isconst (l) && isconst (r) case Mul (l, r) => isconst (l) && isconst (r) } But now we are specifying the names twice!
  8. Pass the name to the builder method using a macro

    def rule (f : Any ==> Any) : Strategy = new Strategy { ... } becomes def rule (f : Any ==> Any) : Strategy = macro RewriterCoreMacros .ruleMacro def rule (name : String , f : Any ==> Any) : Strategy = new Strategy (name) { ... }
  9. dsname library def makeCallWithName [T] (c : Context , methodName

    : String) : c.Expr[T] c is the macro Context methodname is the name of the method to call “foobar”: call a method in the caller’s scope “MyObj.foobar”: call a method of some object “this.foobar”: call a method of the receiver
  10. Strategy builder macro: rule def ruleMacro (c : Context) (f

    : c.Expr[Any ==> Any]) : c.Expr[Strategy] = makeCallWithName (c, "rule") means that val mulbytwoToAdd = rule { ... } becomes val mulbytwoToAdd = rule ("mulbytwoToAdd", { ... })
  11. Strategy builder: <+ abstract class Strategy { def <+ (q

    : Strategy) : Strategy = macro RewriterCoreMacros .detchoiceMacro def <+ (name : String , q : => Strategy) : Strategy = ... }
  12. Strategy builder macro: <+ def detchoiceMacro (c : Context) (q

    : c.Expr[Strategy ]) : c.Expr[Strategy] = makeCallWithName (c, "this.<+") means that val simplifiers = mulbytwoToAdd <+ mulbyzeroToZero becomes val simplifiers = mulbytwoToAdd <+ ("simplifiers", mulbyzeroToZero )
  13. makeCallWithName: finding the name Get the method that encloses the

    macro invocation. Search the method body for a value definition whose right-hand side is the macro invocation and return the name from that definition. Otherwise, if the invocation computes the value of the method, return the name of the method. Otherwise, return the name of the macro. If there is no enclosing method, get the enclosing template (class, trait or object). Search the template body for a value definition whose right-hand side is the macro invocation. Otherwise, report an error.
  14. makeCallWithName Simple implementation: about 90 lines of code. The macro

    invocation is detected by source code position. Detects zero to two argument lists including type application. It’s easy to extend to more but the current version of dsname uses custom patterns to match. We are trying to generalise. We still have issues with detecting values declared inside closures. . . Non-local returns from methods probably don’t work either. . .
  15. Future work Default method name will be the macro name

    Support for passing other data such as the source position More control over how the data will be passed, not just as the first argument of the first argument list