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.
Wielkie dzięki - właśnie tego rodzaju spostrzeżenia, których szukałem! – mikera