2012-05-26 11 views
6

Sto progettando un DSL in Clojure che viene utilizzato per pilotare un generatore di codice (in questo caso per sintesi di immagini procedurali - clisk) e sto avendo problemi a elaborare la migliore rappresentazione per i valori intermedi .Rappresentazione intermedia per DSL Lisp/Clojure

Originariamente il DSL era costituito da funzioni che restituivano uno o più moduli, ad es. (illustrativo)

(v+ 1.0 [1.0 'y]) 
=> ['(+ 1.0 1.0) '(+ 1.0 y)] 

Queste funzioni possono quindi essere composte per creare blocchi di codice più grandi.

Questo era semplice ei moduli risultanti potevano essere inseriti direttamente nel generatore di codice. Tuttavia ora ho identificato quelli che sembrano essere alcuni punti deboli con questo approccio, ad esempio se è necessario passare alcuni dati ausiliari (ad esempio oggetti che non possono essere codificati in forme come BufferedImages, metadati utili per l'ottimizzazione ecc.).

Sono sicuro che questo è un problema risolto nel mondo Lisp - Quale sarebbe in genere la migliore rappresentazione intermedia per questo tipo di DSL?

risposta

7

Ogni volta che hai bisogno di una rappresentazione intermedia che verrà utilizzata per generare codice, la cosa più ovvia che mi viene in mente è un albero sintattico astratto (AST). La tua rappresentazione di esempio sono elenchi, che nella mia esperienza non sono flessibili come un modulo. Per qualcosa di più della generazione di codice banale, non vorrei battere la testa, e basta andare con una rappresentazione AST in piena regola. Usando le liste, si spinge più lavoro verso la generazione per analizzare le informazioni come i tipi e il significato del primo oggetto. Passare a una rappresentazione AST ti darà più flessibilità e disaccoppia maggiormente il sistema, a costo di più lavoro dal lato dell'analisi (o più lavoro dalle funzioni che generano i moduli). Anche la generazione farà più lavoro, ma molte di queste componenti possono essere disaccoppiate dal momento che i loro input saranno più strutturati.

In termini di ciò che deve l'aspetto AST come, vorrei copiare sia enlive di Christophe Grand, dove egli utilizza {:tag <tag name> :attrs <map of attrs> :content <some collection>}

o quello che lo script utilizza clojure, {:op <some operator> :children <some collection>}.

Questo lo rende abbastanza generale da quando è possibile definire gli escursionisti arbitrari che fanno capolino in :children e può attraversare qualsiasi struttura senza sapere esattamente su ciò che sono le :op 's o :tag' s.

Quindi per i componenti atomici, è possibile avvolgerli in una mappa e dargli alcune informazioni sul tipo (rispetto alla semantica del DSL) che è indipendente dal tipo effettivo dell'oggetto. {:atom <the object> :type :background-image}.

Sul lato di generazione del codice, quando si incontra un atomo, il codice può quindi essere inviato allo :type e quindi, se lo si desidera, ulteriore invio al tipo effettivo dell'oggetto. Anche la generazione dai moduli di raccolta è semplice, spedita su: op /: tag e poi si ripresenta con i bambini. Per quale raccolta utilizzare per i bambini, vorrei leggere di più sulla discussione sui gruppi di google. Le loro conclusioni sono state illuminanti per me.

https://groups.google.com/forum/#!topic/clojure-dev/vZLVKmKX0oc/discussion

In sintesi, per i bambini, se non ci fossero semantica importanza ordinamento come ad esempio in un'istruzione if, quindi utilizzare una mappa {:conditional z :then y :else x}. Se fosse solo un elenco di argomenti, allora potresti usare un vettore.

+0

molte grazie - solo il tipo di informazioni che stavo cercando! – mikera

1

Credo di non capire. Vorrei solo usare liste o strutture me stesso.

In Lisp, le liste possono contenere, beh, qualsiasi cosa. Devo dire che una cellula CONS può indicare qualsiasi cosa e quindi la lista può contenere qualsiasi cosa. Così può praticamente qualsiasi altra struttura di dati (strutture, matrici, mappe, ecc.).

Ora, queste strutture non possono essere renderizzabili, mediante STAMPA o rese disponibili a qualcosa di leggibile (da READ), ma ciò non significa che non possano essere memorizzate e manipolate.

C'è qualche motivo per esternare questa rappresentazione?

+0

ultimamente voglio compilare i moduli utilizzando per es. usando qualcosa come '(eval \' (fn [xyz] ~ form-to-compile)) - questo però non funziona con gli oggetti incorporati (vedi http://stackoverflow.com/questions/10735701/embedding-arbitrary- object-in-clojure-code) – mikera

1

Non proprio una risposta, perché non so come Clojure funzioni in questo senso, ma in CL ci sono macro di lettori progettate specificamente per questo caso: cioè si definirà la funzione per stampare oggetti non stampabili + una macro lettore che li legge dal modo in cui li hai stampati. Per definire il modo in cui gli oggetti vengono stampati, definirai un nuovo metodo print-object che si specializza sul tipo di oggetto di cui hai bisogno e set-macro-character per aggiungere una funzione al programma che sa leggere l'oggetto del tuo progetto.

Ci sono molte cose da tenere a mente, ma alcune che normalmente si comportano come bombe temporizzate sono i casi in cui gli oggetti sono autorizzati a fare ricorsivamente riferimento a se stessi, nel qual caso la stampa deve considerare oggetti stampati in precedenza.