Plantilla Haskell: ¿Existe una función(o sintaxis especial) que analiza una cadena y devuelve Q Exp?
template-haskell (1)
Como todo el mundo ya ha dicho haskell-src-meta
proporciona
parsePat :: String -> Either String Pat
parseExp :: String -> Either String Exp
parseType :: String -> Either String Type
parseDecs :: String -> Either String [Dec]
donde Pat
, Exp
, Type
y Dec
son los mismos que para Language.Haskell.TH.Syntax
.
¿Por qué GHC no expone su propio analizador?
Lo hace. Inicie GHCi con ghci -package ghc
( ghc
es un paquete oculto de forma predeterminada) y puede importar el Parser
. Tiene funciones para analizar String
en AST preliminares (cuyas declaraciones de datos están en HsSyn
) para patrones, expresiones, tipos y declaraciones.
Bien, entonces, ¿por qué no existe una biblioteca que use este analizador y convierta su salida a AST desde
template-haskell
(la deLanguage.Haskell.TH.Syntax
)?
Mirando dentro de HsSyn
, es obvio que el AST no es exactamente el mismo que en Language.Haskell.TH.Syntax
. Abra tanto HsExpr
como Exp
y de lado a lado verá que este último está lleno de tipos como PostTc id <some-other-type>
y PostRn id <some-other-type>
. A medida que el AST pasa del analizador al renombrador y al comprobador de tipos, estos bits y piezas se rellenan lentamente. Por ejemplo, ¡ni siquiera conocemos los aspectos básicos de los operadores hasta que llegamos a la comprobación de tipos!
Para realizar las funciones que queremos, tendríamos que ejecutar mucho más que solo el analizador (al menos también el renombrador y el comprobador de tipos, quizás más). Imagínese eso: cada vez que desee analizar incluso una expresión pequeña como "1 + 2"
, todavía tendrá que escribir y revisar un montón de importaciones. Incluso entonces, volver a utilizar Language.Haskell.TH.Syntax
no sería una caminata en el parque: GHC tiene una variedad de peculiaridades, como su propia forma global especial de almacenar nombres e identificadores.
Hmmm ... pero, ¿qué hace GHC con las cuasi cotizaciones?
Esa es la parte genial! A diferencia de Exp
, HsExpr
tiene HsSplice
para representar empalmes. Mira los tipos para los dos primeros constructores:
HsTypedSplice :: id -> LHsExpr id -> HsSplice id. -- things like [|| 1 + 2 ||]
HsUntypedSplice :: id -> LHsExpr id -> HsSplice id -- things like [| 1 + 2 |]
Observe que no están almacenando String
, ¡ya están almacenando un AST! Los empalmes se analizan al mismo tiempo que el resto de AST. Y al igual que el resto del AST, los empalmes se pasarán al renombrador, al tipo de comprobación, etc. donde se completará la información faltante.
Entonces, ¿es fundamentalmente imposible usar el analizador de GHC?
Probablemente no. Pero sacarlo del resto de GHC puede ser bastante difícil. Si para usar el analizador de GHC también tenemos que ejecutar el comprobador de tipos y el renombrador, puede ser más elegante y simple usar un analizador independiente como haskell-src-exts
(que es de lo que depende Haskell-src-meta
) que es capaz de hacer todo en una sola pasada (las reparaciones, por ejemplo, son una de las cosas que debe dar a este analizador por adelantado ).
Estoy tratando de aprender un poco de Template Haskell y Quasi Quotation, y estoy buscando una función que tome un String
y lo analice en Q Exp
, por lo que el tipo es:
String -> Q Exp
Intenté buscar en Google, pero los resultados que vi tenían que ver con levantar los literales de String a Q Exp
, y el más cercano que encontré fue Language.Haskell.TH.dyn
que hace exactamente lo que quiero, pero solo para una variable.
¿Hay otras opciones? Por ejemplo, una sintaxis especial? Estoy solo en el proceso de familiarizarme con [||]
y $()
, ¿entonces quizás también haya algo para este propósito?
Un ejemplo de cómo imagino que funcionaría:
runQ (parse "(1+)") == InfixE (Just (LitE (IntegerL 1))) (VarE GHC.Num.+) Nothing
Además, soy consciente de esto
runQ [| (1+) |] == InfixE (Just (LitE (IntegerL 1))) (VarE GHC.Num.+) Nothing
pero esto no funcionará con cadenas variables porque, comprensiblemente, la cadena interna se toma como un literal.
runQ [| "(1+)" |] == LitE (StringL "(1+)")
Editar (2015-07-25): comencé a usar haskell-src-meta
, y parece funcionar bien hasta ahora. Sin embargo, lleva bastante tiempo cabal install
(aproximadamente 10 minutos en mi máquina). Lo que es una pena, mi paquete es bastante pequeño y me gustaría que la instalación fuera rápida. ¿Alguien sabe de una solución que tenga dependencias más pequeñas?