Jednym z większych projektów, które zrobiłem podczas studiów licencjackich, jest niewątpliwie interpreter prostego języka programowania Latte.
Interpreter napisałem w Haskellu, a grmatyka jest parsowana do tego języka za pomocną narzędzia BNFC. Zaimplementowane funkcjonalności są szczegółowo opisane na GitHubie (odnośnik na dole) wraz z dużą liczbą przykładów, więc tutaj przedstawiam skrótowy opis szczegółów implementacji.
Kod języka Latte jest automatycznie parsowany przez BNFC, a następnie przekazany do statycznej analizy typów. Monada reprezentująca stan TypeCheckera wygląda następująco:
type Env = Map.Map Var TType
data TCSt = TCSt {
env :: Env,
whileCnt :: Int,
ret :: Maybe TType
}
Typy zmiennych przechowywane są w środowisku env. Zadaniem whileCnt jest zliczanie napotkanych pętli, co umożliwia poprawną obsługę instrukcji break oraz continue, natomiast ret przechowuje oczekiwany typ zwracany przez aktualną funkcję (uniemożliwia również użycie return poza funkcją).
Po udanej statycznej analizie kodu następuje jego wykonanie. Monada reprezentująca stan Interpretera wygląda nastepująco:
type Env = Map.Map Var Loc
type Store = Map.Map Loc Value
type FStore = Map.Map Loc Fun
data St = St {
env :: Env,
store :: Store,
funcs :: FStore
}
Środowisko env mapuje nazwy zmiennych i funkcji na miejsca w pamięci, store mapuje lokacje zmiennych na odpowiadające im wartości, a funcs mapuje lokacje funkcji na odpowiadające im definicje.