Шаблон 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 19

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), который способен сделать все за один проход (например, фиксированность-это одна из вещей, которые вы должны дать заранее этому парсеру).