2012-05-26 10 views
6

Ich entwerfe eine DSL in Clojure, die verwendet wird, um einen Code-Generator (in diesem Fall für prozedurale Bildsynthese - clisk) zu fahren und habe Schwierigkeiten, die beste Darstellung für Zwischenwerte zu erarbeiten .Zwischendarstellung für eine Lisp/Clojure DSL

Ursprünglich bestand die DSL aus Funktionen, die eine oder mehrere Formen, z. (illustrativ)

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

Diese Funktionen könnten dann zusammengesetzt werden, um größere Codeblöcke aufzubauen.

Dies war einfach und die resultierenden Formulare konnten direkt in den Codegenerator eingespeist werden. Allerdings habe ich jetzt einige Schwachstellen bei diesem Ansatz identifiziert, zum Beispiel, wenn einige Hilfsdaten weitergegeben werden müssen (z. B. Objekte, die nicht in Formularen wie BufferedImages kodiert werden können, Metadaten, die für die Optimierung usw. nützlich sind).

Ich bin mir sicher, dass dies ein gelöstes Problem in der Lisp-Welt ist - Was wäre normalerweise die beste Zwischenrepräsentation für diese Art von DSL?

Antwort

7

Jedes Mal, wenn Sie eine Zwischenrepräsentation benötigen, die zum Generieren von Code verwendet wird, ist das offensichtlichste, was mir in den Sinn kommt, ein abstrakter Syntaxbaum (AST). Ihre Beispieldarstellung sind Listen, die meiner Erfahrung nach nicht so flexibel sind. Für etwas mehr als triviale Code-Generierung würde ich nicht um den heißen Brei herumreden, und nur mit einer vollständigen AST-Darstellung gehen. Indem Sie Listen verwenden, schieben Sie mehr Arbeit auf die Generierungsseite, um die Informationen wie Typen und was der erste Gegenstand bedeutet, auszuwerten. Wenn Sie zu einer AST-Repräsentation wechseln, erhalten Sie mehr Flexibilität und entkoppeln mehr von dem System auf Kosten von mehr Arbeit von der Parsing-Seite (oder mehr Arbeit von den Funktionen, die die Formulare generieren). Die Erzeugungsseite wird ebenfalls mehr Arbeit leisten, aber die vielen dieser Komponenten können entkoppelt werden, da ihre Eingaben strukturierter sind.

Im Hinblick darauf, was sollte der AST aussehen, ich kopiere entweder enlive Christophe Groß würde, wo er {:tag <tag name> :attrs <map of attrs> :content <some collection>}

oder welche clojure Skript verwendet verwendet, {:op <some operator> :children <some collection>}.

Das macht es ziemlich allgemein, da Sie willkürliche Wanderer definieren können, die in :children spähen und jede Struktur durchqueren können, ohne genau zu wissen, was die :op 's oder :tag' s sind.

Dann für atomare Komponenten, können Sie es in einer Karte umhüllen und geben Sie einige Informationen (in Bezug auf die Semantik Ihrer DSL), die unabhängig von der tatsächlichen Art des Objekts ist. {:atom <the object> :type :background-image}.

Auf der Seite der Code-Generierung, wenn Sie auf ein Atom stoßen, kann Ihr Code dann auf die :type versenden, und dann, wenn Sie möchten, weiter auf den tatsächlichen Typ des Objekts versenden. Die Generierung aus Sammlungsformularen ist ebenfalls einfach, Versand am: op /: tag, und dann wieder mit den Kindern. Für welche Sammlung, die ich für Kinder verwende, habe ich mehr über die Diskussionen in den Google-Gruppen gelesen. Ihre Schlussfolgerungen waren für mich aufschlussreich.

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

Um es zusammenzufassen, für Kinder, wenn es semantischen Ordnungs Bedeutung waren wie in einer if-Anweisung, dann {:conditional z :then y :else x} eine Karte verwenden. Wenn es nur eine Argumentliste wäre, könnten Sie einen Vektor verwenden.

+0

vielen dank - genau die art von einblicken, nach denen ich gesucht habe! – mikera

1

Ich denke, ich verstehe nicht. Ich würde nur Listen oder Strukturen selbst verwenden.

In Lisp können Listen enthalten, nun ja, alles. Ich sollte sagen, eine CONS-Zelle kann auf alles zeigen und somit kann die Liste alles enthalten. So kann so ziemlich jede andere Datenstruktur (Strukturen, Arrays, Karten, etc.).

Nun können diese Strukturen nicht durch PRINT renderbar oder in etwas lesbar (durch READ) gerendert werden, aber das bedeutet nicht, dass sie nicht gespeichert und manipuliert werden können.

Gibt es einen Grund, warum Sie diese Darstellung externalisieren müssen?

+0

schließlich möchte ich die Formulare mit z. benutzt etwas wie '(eval \' (fn [xyz] ~ form-to-compile)) - dies funktioniert jedoch nicht mit eingebetteten Objekten (siehe http://stackoverflow.com/questions/10735701/embedding-arbitrary- objects-in-clojure-code) – mikera

1

Nicht wirklich eine Antwort, weil ich nicht weiß, wie Clojure in dieser Hinsicht funktioniert, aber in CL gibt es Lesemakros speziell für diesen Fall entwickelt: dh Sie würden Ihre Funktion zum Drucken nicht druckbarer Objekte + ein Lesemakro definieren das liest sie von der Art, wie du sie gedruckt hast. Um die Art zu definieren, wie Objekte gedruckt werden, definieren Sie eine neue Methode print-object, die sich auf den von Ihnen benötigten Objekttyp spezialisiert, und set-macro-character, um der lesbaren Tabelle, die weiß, wie Sie das Objekt Ihres Entwurfs lesen können, eine Funktion hinzuzufügen.

Es gibt viele Dinge zu beachten, aber einige, die sich normalerweise wie Timer-Bomben verhalten, sind die Fälle, in denen Objekte rekursiv referenzieren dürfen. In diesem Fall müssen zuvor gedruckte Objekte berücksichtigt werden.

Verwandte Themen