As everyone has already said haskell-src-meta
provides
parsePat :: String -> Either String Pat
parseExp :: String -> Either String Exp
parseType :: String -> Either String Type
parseDecs :: String -> Either String [Dec]
where Pat
, Exp
, Type
, and Dec
are the same as from Language.Haskell.TH.Syntax
.
Why doesn't GHC expose its own parser?
It does. Fire up GHCi with ghci -package ghc
(ghc
is a hidden package by default) and you can import the Parser
. It has functions to parse String
into preliminary ASTs (whose data declarations are in HsSyn
) for patterns, expressions, types, and declarations.
OK, then why does there not exist a library that uses this parser and converts its output to be the AST from template-haskell
(the one in Language.Haskell.TH.Syntax
)?
Looking inside HsSyn
, its obvious that the AST isn't quite the same as the one in Language.Haskell.TH.Syntax
. Open up both HsExpr
and Exp
and side by side you'll see that the latter is filled with types like PostTc id <some-other-type>
and PostRn id <some-other-type>
. As the AST is passed from the parser to the renamer to the type checker, these bits and pieces are all slowly filled in. For example, we don't even know the fixities of operators until we get to type-checking!
In order to make the functions we want, we would need to run much more than just the parser (at least the renamer and type checker too, maybe more). Imagine that: every time you want to parse even a small expression like "1 + 2"
you'll still have to type check a bunch of imports. Even then, converting back to the Language.Haskell.TH.Syntax
wouldn't be a walk in the park: GHC has a variety of peculiarities like its own special global way of storing names and identifiers.
Hmmm... but what does GHC do with quasi-quotes?
That's the cool part! Unlike Exp
, HsExpr
has HsSplice
for representing splices. Look at the types for the first two constructors:
HsTypedSplice :: id -> LHsExpr id -> HsSplice id. -- things like [|| 1 + 2 ||]
HsUntypedSplice :: id -> LHsExpr id -> HsSplice id -- things like [| 1 + 2 |]
Notice that they aren't storing String
, they are storing an AST already! Splices get parsed at the same time as the rest of the AST. And just like the rest of the AST, the splices will get passed along to the renamer, type checker, etc. where missing information will be filled in.
So is it fundamentally impossible to use GHC's parser
Probably not. But extricating it from the rest of GHC may be quite difficult. If to use GHC's parser we have to also run the type-checker and the renamer, it may be more elegant and simple to just use a standalone parser like haskell-src-exts
(which is what Haskell-src-meta
depends on) that is able to do everything in one pass (fixities, for example, are one of the things you have to give ahead of time to this parser).