Шаблон Haskell: существует ли функция (или специальный синтаксис), которая анализирует строку и возвращает Q Exp?
Я пытаюсь изучить немного шаблона Хаскелла и Квазикавычки, и я ищу функцию, которая берет String
и разбирает его на Q Exp
, поэтому тип:
String -> Q Exp
Попробовал поискать хугла, но результаты, которые я увидел, были связаны с подъемом строковых литералов к Q Exp
, и ближе всего я нашел Language.Haskell.TH.dyn
, который делает то, что я хочу, но только для одной переменной.
Есть ли другие варианты? Например, особый синтаксис? Я просто нахожусь в процессе ознакомления с [||]
и $()
, так, может быть, есть что-то и для этой цели?
Пример того, как я себе это представляю:
runQ (parse "(1+)") == InfixE (Just (LitE (IntegerL 1))) (VarE GHC.Num.+) Nothing
Кроме того, я знаю об этом
runQ [| (1+) |] == InfixE (Just (LitE (IntegerL 1))) (VarE GHC.Num.+) Nothing
Но это не будет работать с переменными строками, потому что-понятно-строка внутри берется как литерал.
runQ [| "(1+)" |] == LitE (StringL "(1+)")
Edit (2015-07-25): я начал использовать haskell-src-meta
, и до сих пор это, кажется, работает хорошо. Однако это займет довольно много времени, чтобы cabal install
(около 10 минут на моей машине). Что обидно, мой пакет на самом деле довольно мал, и я хотел бы, чтобы установка могла быть быстрой. Кто-нибудь знает решение, которое имеет меньшие зависимости?
1 ответ:
Как все уже говорили
haskell-src-meta
обеспечиваетparsePat :: String -> Either String Pat parseExp :: String -> Either String Exp parseType :: String -> Either String Type parseDecs :: String -> Either String [Dec]
Где
Pat
,Exp
,Type
, иDec
такие же, как изLanguage.Haskell.TH.Syntax
.
Почему GHC не выставляет свой собственный парсер?
Так и есть. Запустите GHCi с помощью
ghci -package ghc
(ghc
является скрытым пакетом по умолчанию) и вы можете импортироватьParser
. Он имеет функции для разбораString
на предварительные ASTs (объявления данных которых находятся вHsSyn
) для шаблонов, выражений, типы и объявления.Хорошо, тогда почему не существует библиотеки, которая использует этот синтаксический анализатор и преобразует его выходные данные в AST из
template-haskell
(тот, что вLanguage.Haskell.TH.Syntax
)?Заглядывая внутрь
HsSyn
, очевидно, что АСТ не совсем то же самое, что и вLanguage.Haskell.TH.Syntax
. Откройте обаHsExpr
и ещеExp
и рядом вы увидите, что последний заполнен типами типаPostTc id <some-other-type>
иPostRn id <some-other-type>
. Как АСТ передается от парсер к переименователю к проверке типа, эти биты и части все медленно заполняются. Например, мы даже не знаем фиксированности операторов, пока не доберемся до проверки типа!Для того, чтобы сделать функции, которые мы хотим, нам нужно было бы запустить гораздо больше, чем просто синтаксический анализатор (по крайней мере, переименователь и проверка типов тоже, может быть, больше). Представьте себе, что каждый раз, когда вы хотите разобрать даже небольшое выражение, такое как
"1 + 2"
, вам все равно придется вводить check кучу импорта. Даже тогда, обращая вернемся к делу.Language.Haskell.TH.Syntax
не было бы прогулки в парке: GHC имеет множество особенностей, таких как свой собственный особый глобальный способ хранения имен и идентификаторов.Хммм... но что делает GHC с квазикавычками?
Вот это круто! В отличие от
Exp
,HsExpr
имеетHsSplice
для представления сращиваний. Рассмотрим типы для первых двух конструкторов:Обратите внимание, что они не хранятHsTypedSplice :: id -> LHsExpr id -> HsSplice id. -- things like [|| 1 + 2 ||] HsUntypedSplice :: id -> LHsExpr id -> HsSplice id -- things like [| 1 + 2 |]
String
, они уже хранят АСТ! Сращивания получить анализ в то же время, как и остальная часть AST. И так же, как и остальная часть AST, сращивания будут передаваться на переименователь, проверку типов и т. д. где будет заполнена недостающая информация.Так что принципиально невозможно использовать парсер GHC
Вероятно, нет. Но извлечь его из остальной части GHC может быть довольно трудно. Если для использования парсера GHC мы должны также запустить type-checker и renamer, он может быть более элегантным и простым в использовании автономный парсер, такой как
haskell-src-exts
(от которого зависитHaskell-src-meta
), который способен сделать все за один проход (например, фиксированность-это одна из вещей, которые вы должны дать заранее этому парсеру).