2011-10-17 17 views
35

In un plugin per compilatore Scala, sto provando a creare una nuova classe che implementa un tratto preesistente. Finora il mio codice assomiglia a questo:Come aggiungere una nuova classe in un plugin per compilatore Scala?

def trait2Impl(original: ClassDef, newName: String): ClassDef = { 
    val impl = original.impl 
    // Seems OK to have same self, but does not make sense to me ... 
    val self = impl.self 
    // TODO: implement methods ... 
    val body = impl.body 
    // We implement original 
    val parents = original :: impl.parents 
    val newImpl = treeCopy.Template(impl, parents, self, body) 
    val name = newTypeName(newName) 
    // We are a syntheic class, not a user-defined trait 
    val mods = (original.mods | SYNTHETIC) &~ TRAIT 
    val tp = original.tparams 
    val result = treeCopy.ClassDef(original, mods, name, tp, newImpl) 
    // Same Package? 
    val owner = original.symbol.owner 
    // New symbol. What's a Position good for? 
    val symbol = new TypeSymbol(owner, NoPosition, name) 
    result.setSymbol(symbol) 
    symbol.setFlag(SYNTHETIC) 
    symbol.setFlag(ABSTRACT) 
    symbol.resetFlag(INTERFACE) 
    symbol.resetFlag(TRAIT) 
    owner.info.decls.enter(symbol) 
    result 
} 

Ma non sembra essere aggiunto al pacchetto. Sospetto che sia perché effettivamente il pacchetto è "attraversato" prima del tratto che causa la generazione, e/o perché il metodo "override def transform (tree: Tree): Tree" di TypingTransformer può restituire solo un albero ogni albero che riceve, quindi non può effettivamente produrre un nuovo albero, ma modificarne solo uno.

Quindi, come si aggiunge una nuova classe a un pacchetto esistente? Forse funzionerebbe se trasformassi il pacchetto quando "transform (Tree)" lo ottiene, ma io quel punto non conosco ancora il contenuto del pacchetto, quindi non posso generare la nuova classe così presto (o potrei?) . O forse è legato al parametro "Posizione" del Simbolo?

Finora ho trovato diversi esempi in cui gli alberi vengono modificati, ma nessuno in cui una classe completamente nuova viene creata in un plug-in compilatore.

+0

avete risolto il problema? Ne sto avendo uno simile, quindi mi stavo chiedendo ... Penso che sia necessario abbinare per il genitore della tua caratteristica nel metodo di trasformazione, sfortunatamente. Cioè, fai attenzione a PackageDef e Template e trova tutte le occorrenze del tuo tratto nei loro corpi. Quindi puoi restituire un PackageDef e un Template trasformati. –

+0

Sì, ho ricevuto l'aiuto da una mailing list di Scala. Ma non ci sono ancora, dato che posso solo creare la classe in * lo stesso pacchetto *. Cercherò di pubblicare una risposta alla mia domanda quando avrò risolto le cose. –

+0

Per favore rispondi alla tua domanda ORA! Perché ho la stessa [domanda] (https://github.com/iron9light/autoguice) – iron9light

risposta

2

Il codice sorgente completo è qui: https://gist.github.com/1794246

Il trucco è quello di memorizzare le appena creati ClassDef s e li usa quando si crea una nuova PackageDef. Si noti che è necessario gestire sia i simboli che gli alberi: un simbolo del pacchetto è solo un handle. Per generare codice, è necessario generare un AST (proprio come per una classe, in cui il simbolo contiene il nome e il tipo della classe, ma il codice si trova negli alberi ClassDef).

Come si è notato, le definizioni dei pacchetti sono più in alto dell'albero rispetto alle classi, quindi è necessario prima recurse (supponendo che si generi la nuova classe da una classe esistente). Quindi, una volta attraversati i sottoalberi, è possibile preparare un nuovo PackageDef (ogni unità di compilazione ha una definizione di pacchetto, che per impostazione predefinita è il pacchetto vuoto) con le nuove classi.

Nel esempio, supponendo che il codice sorgente è

class Foo { 
    def foo { 
    "spring" 
    } 
} 

il compilatore avvolge in

package <empty> { 
    class Foo { 
    def foo { 
     "spring" 
    } 
    } 
} 

e il plugin trasforma in

package <empty> { 
    class Foo { 
    def foo { 
     "spring" 
    } 
    } 
    package mypackage { 
    class MyClass extends AnyRef 
    } 
} 
+0

Grazie! Non l'ho provato, ma visto che sei uno dei "Guru", devi aver capito bene. Vorrei solo aggiungere questo, e questo non ha niente a che fare con te in particolare, non ho usato Scala in mesi e posso riguardare persone che si lamentano che la comunità di Scala è ostile/scostante/esclusiva quando domande di base (questo è un no-brain in un plugin per compilatore Java) riceve risposta solo dopo * mesi * quando * qualcuno mette una taglia su di essi "* Ho chiesto sulla mailing list della lingua di Scala e non ho nemmeno ricevuto un aiuto utile lì ... Prendilo personalmente, sono grato per la tua risposta! –

+2

Capisco il tuo punto di vista, ma per favore non giudicare la comunità di Scala sulla base di questa domanda.Molte domande di Scala ottengono risposte veloci e di qualità, ma questo non è sicuramente un ' no brain. "Richiede a qualcuno che lavora sul compilatore di conoscere la risposta (la maggior parte delle persone nel team di Scala non legge SOV molto spesso), ed è qualcosa che" scalac "non fa mai, quindi è impossibile trovare un esempio. anche se la soluzione è breve, mi ci è voluta circa un'ora per ottenere tutto funziona correttamente. –

+0

Grazie ancora! Per dirla in breve, quando qualcuno ha una "domanda Java", possono chiedere qui o in dozzina di altri posti, e sono sicuri di trovare una risposta (se ce n'è una), ma quando una domanda di Scala non viene qui né risposta né sul Le mailing list di Scala, allora è tutto, sei da solo, e questo è davvero deprimente. Vorrei tanto che qualcuno scrivesse un libro "hardcore alla Scala"; "Scala in profondità" non è ancora terminato, e se sarà lungo circa 200 pagine, sarà solo graffiare la superficie di tutti su "cose ​​difficili". Ma le cose difficili sono molto più difficili di Java e non ci sono abbastanza potenziali acquirenti. –

Problemi correlati