正如其他人已经说过的一样,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为什么不公开其自己的解析器?
GHC确实公开了它自己的解析器。使用ghci -package ghc
(默认情况下,ghc
是一个隐藏包)启动GHCi,您可以导入Parser
。它具有将String
解析为模式、表达式、类型和声明的初步AST(其数据声明在HsSyn
中)的函数。
好吧,那么为什么不存在一个库,该库使用此解析器并将其输出转换为template-haskell
中的AST(Language.Haskell.TH.Syntax
中的AST)?
查看HsSyn
,很明显其AST与Language.Haskell.TH.Syntax
中的AST不完全相同。打开HsExpr
和Exp
,并将它们并排放置,您会发现后者充满了例如PostTc id <some-other-type>
和PostRn id <some-other-type>
这样的类型。随着AST从解析器传递到重命名器再到类型检查器,这些部分都会慢慢填充。例如,在我们进行类型检查之前,我们甚至不知道运算符的固定性!
为了实现我们想要的功能,我们需要运行比解析器更多的操作(至少还需要重命名和类型检查等)。想象一下:每次你想解析一个像
"1 + 2"
这样的小表达式,你仍然需要检查一堆导入。即使这样,转换回
Language.Haskell.TH.Syntax
也不是一件轻松的事情:GHC有各种奇怪的特性,比如它自己的特殊全局名称和标识符存储方式。
嗯... GHC对准引用做了什么?
那就是很酷的部分!与
Exp
不同,
HsExpr
具有
HsSplice
来表示splice。看一下前两个构造函数的类型:
HsTypedSplice :: id -> LHsExpr id -> HsSplice id. -- things like [|| 1 + 2 ||]
HsUntypedSplice :: id -> LHsExpr id -> HsSplice id -- things like [| 1 + 2 |]
请注意,他们不是存储
String
,而是已经存储了AST!插值在与其余AST同时解析。就像其余AST一样,插值将被传递给重命名器、类型检查器等,其中缺少的信息将被填充。
那么使用GHC的解析器基本上是不可能的吗?
可能不是。但从GHC中分离出来可能会非常困难。如果我们必须使用GHC的解析器,还必须运行类型检查器和重命名器,那么使用一个独立的解析器(例如
haskell-src-exts
,这是
Haskell-src-meta
依赖的内容)可能更优雅和简单,它能够一次完成所有操作(例如,优先级是必须提前给出的)。
[|(1+)|]
并完全能够将其转换为(InfixE _)
。那么为什么需要第三方包,而且可能无法正确解析呢?或者我是否误解了,这也是 GHC 使用的规范代码?或者 GHC 根本没有公开此功能?如果您能提供一些关于这个问题的明确解释,我将不胜感激。 :) - WizekLanguage.Haskell.TH.Parser.parse :: String -> Q Exp
,不是更加优雅吗? - Wizekghc
和template-haskell
包之间遇到循环依赖问题。 - Carl