2013-10-24 3 views
13

Se ho una macro che tranforms codice come:Perché l'universo riflettente runtime e l'universo macro creano due alberi diversi per scala.None?

(src: a.b.c.TestEntity) => 
    { 
     z.y.TestTable(None) 
    } 

per abbinare l'Nessuno parte di quella AST posso usare un estrattore come ad esempio:

object NoneExtractor { 
    def unapply(t: Tree): Boolean = t match { 
     case Select(Ident(scala), none) if scala.encoded == "scala" && none.encoded == "None" => true 
     case _ => false 
    } 
    } 

Come il showRaw della Nessuno parte della AST assomiglia:

Select(Ident(scala), None) 

Eppure, se voglio scrivere uno unit test del NoneExtractor io non voglio compilare e ricostruire i macro e ospitare il test nel progetto che la macro sta compilando. Voglio unit test l'estrattore nel progetto del macro che suggerisce runtime riflessione è la strada da percorrere con:

val t = reify { 

    (src: a.b.c.TestEntity) => 
    { 
     z.y.TestTable(None) 
    } 

}.tree 

Ma l'albero è totalmente diverso e nel showRaw di quell'albero l'Nessuno assomiglia:

Ident(scala.None) 

Questa è una cattiva notizia per la scrittura di test negativi e il controllo della gestione degli errori della mia macro. Non è possibile scrivere test negativi per una macro utilizzando la macro da un altro progetto poiché il codice non verrà compilato (e non è possibile eseguire il debug del test negativo con errori di compilazione).

Perché le rappresentazioni di qualcosa di fondamentale come None così diverse tra riflessione del tempo di compilazione e riflessione di runtime? C'è un modo per creare i frammenti di albero testabili all'interno del progetto macro, che è lo stesso AST che sarebbe stato consegnato alla macro durante la riflessione del tempo di compilazione?

+0

Scegliere un valore specifico basato sulla sua rappresentazione ad albero è sempre un po 'complicato, anche se sei interamente in macro-terra. Puoi fare qualcosa come 't.tpe =: = typeOf [None.type]' invece? –

+0

Ho davvero bisogno di fare structual match AST con le dichiarazioni di match annidate. Sono nuovo al funzionale quindi ho iniziato con if/then/else/call codice imperativo stile. Quello era circa cinque volte il codice ora che sto rifattando alla corrispondenza funzionale/caso/partita/caso/.... L'api universo macro ha un'enorme quantità di estrattori integrati. Quindi mi rendo conto che gli estrattori e la corrispondenza annidata sono il modo canonico per farlo. La mia domanda è come testare quel modo canonico piuttosto che una domanda su come prendere completamente un approccio diverso. – simbo1905

risposta

0

Per aggirare questa incoerenza, è possibile utilizzare l'imminente quasiqoutes nella corrispondenza del modello. Essi astrarre l'AST e quindi funzionerà con entrambe le rappresentazioni (l'AST è compilatore specifico in ogni caso, Scala è un linguaggio unico compilatore per ora, ma non è così bello fare affidamento sulla rappresentazione interna del compilatore):

case q"_root_.scala.None" => ... 

corrisponderebbe a entrambi gli AST. Inoltre è possibile creare l'albero con q"_root_.scala.None" in modo da non doversi preoccupare della rappresentazione. Reify diventerà obsoleto quando vengono rilasciate quasi quotes con scala 2.11. Per utilizzare quasiquotes con scala 2.10 è possibile utilizzare macro paradise.

Here is a nice WIP guide on scala quasiquotes.

Problemi correlati