2012-05-26 11 views
6

Projektuję DSL w Clojure, który służy do sterowania generatorem kodu (w tym przypadku do syntezy obrazu proceduralnego - clisk) i mam problem z wypracowaniem najlepszej reprezentacji dla wartości pośrednich .Półprodukt reprezentatywny dla Lisp/Clojure DSL

Oryginalnie DSL składał się z funkcji, które zwróciły jedną lub więcej form, np. (ilustracja)

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

Funkcje te można następnie komponować w celu tworzenia większych bloków kodu.

To było proste, a otrzymane formularze można było wprowadzić bezpośrednio do generatora kodu. Zidentyfikowaliśmy jednak coś, co wydaje się być słabym punktem w tym podejściu, na przykład, jeśli istnieje potrzeba przekazania pewnych danych pomocniczych (np. Obiektów, które nie mogą być zakodowane w formach takich jak BufferedImages, metadane przydatne do optymalizacji itp.).

Jestem pewien, że jest to problem rozwiązany w świecie Lisp - co zwykle byłoby najlepszą reprezentacją pośrednią tego rodzaju DSL?

Odpowiedz

7

Za każdym razem, gdy potrzebujesz pośredniej reprezentacji, która zostanie użyta do wygenerowania kodu, najbardziej oczywistą rzeczą, która przychodzi mi na myśl, jest abstrakcyjne drzewo składniowe (AST). Twoja przykładowa reprezentacja to listy, które z mojego doświadczenia nie są tak elastyczne w formie. Jeśli chodzi o coś więcej niż trywialne generowanie kodu, nie pokonałbym krzaków i po prostu z pełnym przedstawicielem AST. Używając list, przenosisz więcej pracy na stronę generacji, aby przeanalizować informacje takie jak typy i co oznacza pierwszy element. Przejście do reprezentacji AST daje większą elastyczność i uniezależnia więcej systemu, kosztem więcej pracy od strony analizowania (lub więcej pracy z funkcji generujących formularze). Strona pokoleniowa będzie również wykonywać więcej pracy, ale wiele z tych komponentów może zostać oddzielonych od produkcji, ponieważ ich dane wejściowe będą bardziej uporządkowane.

chodzi o to, co powinien wygląd jak AST, chciałbym skopiować albo Christophe GRAND za enlive, gdzie wykorzystuje {:tag <tag name> :attrs <map of attrs> :content <some collection>}

lub co clojure skrypt wykorzystuje {:op <some operator> :children <some collection>}.

To sprawia, że ​​jest to dość ogólne, ponieważ można zdefiniować dowolnych spacerowiczów, którzy zaglądają do :children i mogą przemierzać dowolną strukturę bez znajomości szczegółów na temat :op lub :tag.

Następnie dla komponentów atomowych można zawinąć go w mapę i podać kilka informacji o typie (w odniesieniu do semantyki twojego DSL), która jest niezależna od faktycznego typu obiektu. {:atom <the object> :type :background-image}.

Po stronie generowania kodu, po napotkaniu atomu, twój kod może następnie wysłać na :type, a następnie, jeśli chcesz, dalszą wysyłkę na faktycznym typie obiektu. Generowanie z formularzy kolekcji jest również łatwe, wyślij na: op /: tag, a następnie powróć do dzieci. W przypadku jakiej kolekcji używać dzieci, przeczytałem więcej o dyskusji w grupach google. Ich wnioski były dla mnie pouczające.

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

Podsumowując, dla dzieci, gdyby nie było semantyczne znaczenie zamówień takich jak w instrukcji if, a następnie użyć mapy {:conditional z :then y :else x}. Jeśli była to tylko lista argumentów, możesz użyć wektora.

+0

Wielkie dzięki - właśnie tego rodzaju spostrzeżenia, których szukałem! – mikera

1

Chyba nie rozumiem. Sam bym po prostu używał list lub struktur.

W Lispie listy mogą zawierać, cóż, wszystko. Powinienem powiedzieć, że komórka CONS może wskazywać na wszystko, a zatem lista może zawierać cokolwiek. Podobnie może być z każdą inną strukturą danych (strukturami, tablicami, mapami itp.).

Teraz te struktury nie mogą być renderowalne, PRINT lub renderowane do czegoś czytelnego (przez READ), ale to nie znaczy, że nie mogą być przechowywane i manipulowane.

Czy jest jakiś powód, dla którego należy przekazać tę reprezentację na zewnątrz?

+0

ostatecznie chcę skompilować formularze za pomocą np. używając czegoś takiego jak '(eval \' (fn [xyz] ~ form-to-compile)) '- to nie działa z obiektami osadzonymi (zobacz http://stackoverflow.com/questions/10735701/embedding-arbitrary- object-in-clojure-code) – mikera

1

Naprawdę nie jest to odpowiedź, ponieważ nie wiem, jak działa Clojure w tym zakresie, ale w CL są makra czytnika zaprojektowane specjalnie dla tego przypadku: tzn. Zdefiniowałbyś swoją funkcję drukowania niedrukowalnych obiektów + makro czytnika czyta je od sposobu, w jaki je wydrukowałeś. Aby zdefiniować sposób drukowania obiektów, należy zdefiniować nową metodę print-object, która specjalizuje się w typie obiektu, którego potrzebujesz i set-macro-character, aby dodać funkcję do tabeli, która wie, jak odczytać obiekt projektu.

Jest wiele rzeczy, o których należy pamiętać, ale niektóre, które zwykle działają jak bomby zegarowe, są przypadkami, gdy obiekty mogą rekurencyjnie odwoływać się do siebie, w takim przypadku drukowanie musi uwzględniać wcześniej wydrukowane obiekty.