Комментарии 8
data Exp = Let String Exp Exp | Exp1 Exp1 deriving (Show)
data Exp1 = Plus Exp1 Term | Minus Exp1 Term | Term Term deriving (Show)
data Term = Mul Term Factor | Div Term Factor | Factor Factor deriving (Show)
data Factor = Num Int | Var String | Brack Exp deriving (Show)
А вот это читерство, можно было объявить и один тип Exp(благо что грамматика и так факторизована), тогда и код интерпретатора был сильно проще и уложился бы в одну функцию с паттернматчингом
Это все очень здорово и полезно, учитывая что всякие компиляторы компиляторов примерно все так работают, и даже синтаксис похож у многих. Но на практике такой немудреный интерпретатор проще написать, используя аппликативный парсер навроде parsec.
А вообще в статье было бы очень уместно сказать пару слов про формальные грамматики, потому что фарш, который мы имеем в *.y, без осознания, что такое (расширенная) форма Бэкуса-Наура, совсем может быть непонятно.
Ну и грамматику можно сделать гораздо проще, если задать приоритеты операторов, вместо того, чтобы городить деревья разбора. Про излишнюю стожность типов уже выше сказано.
Плюс работы "вот через это всё" в том, что, в отличие от монадических парсеров, оно ловит на неоднозначности грамматики в момент компиляции, а не удивляет в рантайме своим поведением (в том числе неожиданной неспособностью распарсить что-нибудь на первый взгляд тривиальное или пугающим по своим масштабам бэктрекингом).
Но со временем я понял, что комбинаторные парсеры, например Parsec, проще и гибче чем генераторы парсеров, а эффективность не столь важна.
Пишем (недо)интерпретатор на Haskell с помощью alex и happy