2013-05-07 2 views
0

(pour TL; DR, allez à la partie de gras)utilisant def-macros pour capturer le code source

J'ai un système de classe de type fermé propre avec sérialisation (détaché de maux de sérialisation POJO). Par exemple:

trait Expr 
case class Const(i: Int) extends Expr 
case class BinOp(a: Expr, b: Expr, op: Int) extends Expr 

Mais dans les situations j'ai besoin de capturer une fermeture. Par exemple:

case class Map(a: Expr, fun: Expr => Expr) extends Expr 

Maintenant, je l'avais résolu cette fois avec sérialisation POJO (ObjectOutputStream etc.) pour la fun. J'ai été gravement mordu dans les pieds, parce que je ne pouvais pas lire dans Scala 2.10 ce que j'avais sérialisé en 2.9. Et dans ce cas, j'ai vraiment besoin de m'assurer que je peux récupérer mon contenu indépendamment de la version Scala. J'ai pensé que je pourrais utiliser une macro pour faire une "sauvegarde" du code source, de sorte que si la désérialisation POJO échoue, je peux régénérer la fonction à partir de la source (en utilisant un compilateur in-situ /interprète).

Mon idée serait

object Map { 
    def apply(a: Expr, fun: Expr => Expr): Map = macro applyImpl 
    private def applyImpl = ??? 

    def unapply(m: Map): Option[(Expr, Expr => Expr)] = Some(m.a -> m.fun) 
} 
trait Map extends Expr { 
    def a: Expr 
    def fun: Expr => Expr 
} 

implicit class ExprOps(val ex: Expr) extends AnyVal { 
    def map(fun: Expr => Expr) = Map(ex, fun) 
} 

Est-il possible de capturer facilement la source d'un appel comme

//   |------------- source of this -------------| 
someExpr.map { case Const(i) => Const(i*i); case x => x } 

(Je pense aux besoins def-macro pour être déjà dans la map fonction de ExprOps).

+2

Je ne pense pas que vous pouvez obtenir la source, mais vous pouvez obtenir 'Tree':' def applyImpl (c: Contexte) (a: c.Expr [Expr], amusant: c.Expr [Expr => Expr]): c.Expr [Map] = {val source = c.universe.show (fun.tree); ...} ' – senia

+0

@senia merci pour le lien et le commentaire. Je n'ai pas besoin du code source original, en effet, car il ne sera pas montré (probablement) à l'utilisateur, je veux juste une source que je peux recompiler dans le même arbre, même si la version sérialisée de dire 'Tree' changements (donc je ne veux pas sérialiser l'arbre directement). Donc, je vais jeter un oeil à ce 'universe.show', qui pourrait en effet être suffisant. –

Répondre

0

Les macros de remplacement de texte sont géniales pour ce genre de choses. Scala ne vient pas avec eux, mais pensez à écrire le vôtre! Transformer par ex.

{# case Const(i) => Const(i*i); case x => x #} 

à

({ case Const(i) => Const(i*i); case x => x }, """case Const(i) => Const(i*i); case x => x""") 

devrait être assez facile; alors il vous suffit de prétraiter avant de compiler. Si vous souhaitez que votre IDE ne soit pas perturbé par des longueurs de ligne différentes, vous pouvez stocker les chaînes dans un objet séparé, par ex.

{# _+7 #}/*STORE*/ 

va à

({_+7}, Store.aW4) 

... 

object Store { 
    val aW4 = """_+7""" 
} 
//(EOF) 

(Pour obtenir les meilleurs résultats, base 64 encode le texte capturé. Nesting fonctionnera bien aussi longtemps que vous travaillez récursive et sont conscients que l'imbrication peut se produire.)

+0

Je dois être capable de faire cela à l'exécution avec un interpréteur intégré. Dans mon projet de compilation/IDE, j'ai déjà accès au code source :) Je pourrais bien sûr capturer le texte de l'interpréteur (et je l'ai déjà fait), mais ensuite je dois me débrouiller pour trouver les sources (sauf si bien sûr, j'ajoute des caractères d'échappement comme avec le '#'). –

Questions connexes