Dziedzina DSLExecutora: założenia

Jak pisałem wcześniej, tworzone przeze mnie narzędzie będzie przetwarzało wyrażenia i funkcje. Zanim zajmę się szczegółami tego przetwarzania, skupię się na zdefiniowaniu przetwarzanych bytów – wyrażeń i funkcji właśnie. Byty te razem tworzą dziedzinę, w której DSLExecutor będzie się poruszał.

Wszystko jest wyrażeniem

Zacznijmy od podstawowych stwierdzeń dotyczących sposobu, w jaki będę traktował tekstowy zapis operacji (czyli wejściowy DSL):

  1. Cały taki zapis jest wyrażeniem.
  2. Każde wyrażenie ma przypisany typ wartości zwracanej.
  3. Wyrażenie może zawierać podwyrażenia – które też są wyrażeniami.
  4. Zbiór wyrażeń to też wyrażenie.
    • Zbiór wyrażeń zawiera jedno wyrażenie wyróżnione – to, którego wartość zwracana stanie się wartością zwracaną całego zbioru.

Takie podejście pozwoli mi traktować wszystkie elementy DSLa w jednolity sposób. Spójrzmy na przykłady. Za składnię DSL przyjmijmy pseudokod przypominający C#: (typ wartości zwracanej każdego z poniższych wyrażeń to <tt>int</tt>.)

  • Literał jest wyrażeniem:
    • 1
  • Wywołanie funkcji jest wyrażeniem:
    • GetActiveUsersNumber()
  • Wywołanie funkcji parametrycznej jest wyrażeniem zawierającym podwyrażenia reprezentujące argumenty tej funkcji – w tym przypadku dwa (zaczerpnięte z przykładów powyżej):
    • Add(GetActiveUsersNumber(), 1)
  • Zbiór wyrażeń jest wyrażeniem zawierającym podwyrażenia – w tym przypadku dwa wywołania funkcji. Przyjmijmy, że wyróżnionym (zwracającym) wyrażeniem jest wyrażenie ostatnie w zbiorze:
    • RegisterUser("manisero")
      Add(GetActiveUsersNumber(), 1)

Ameryki nie odkryłem – zbliżone podejście obowiązuje w chyba wszystkich znanych mi językach obiektowych, funkcyjnych i skryptowych. W następnym poście pokażę, jak te założenia przekładają się na implementację klas należących do dziedziny DSLExecutora – zapraszam!