【问题标题】:Template Haskell: Is there a function (or special syntax) that parses a String and returns Q Exp?模板 Haskell:是否有解析字符串并返回 Q Exp 的函数(或特殊语法)?
【发布时间】:2016-12-06 08:05:15
【问题描述】:

我正在尝试学习一些 Template Haskell 和 Quasi Quotation,我正在寻找一个接受 String 并将其解析为 Q Exp 的函数,因此类型为:

String -> Q Exp

尝试搜索 hoogle,但我看到的结果与将字符串文字提升到 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+)")

编辑(2015-07-25):我已经开始使用haskell-src-meta,到目前为止它似乎运行良好。然而,cabal install 确实需要相当长的时间(在我的机器上大约需要 10 分钟)。真可惜,我的包实际上很小,我想安装是否可以快速。任何人都知道具有较小依赖性的解决方案?

【问题讨论】:

  • 我相信haskell-src-meta 提供了这个
  • @luqui 我对那个包有点困惑。它在描述中写道“还没有 100% 完成”。 GHC 中不应该已经存在此功能吗?一定是,因为它需要[|(1+)|],并且完全有能力将其转换为(InfixE _)。那么为什么需要一个可能正确解析也可能不正确解析的第三方包呢?还是我误解了,这也是 GHC 使用的规范代码?或者 GHC 可能根本没有公开这个功能?我将不胜感激。 :)
  • AFAIU GHC 不公开此代码,但我不是 TH 专家
  • @luqui 如果是这样的话,在例如Language.Haskell.TH.Parser.parse :: String -> Q Exp 下公开它不是更优雅吗?
  • 如果 GHC 暴露它会方式更优雅,但它不能有那种类型。它需要一个包含所有可能影响解析的选项的类型。当它到达那里时,您最终会在 ghctemplate-haskell 包之间遇到循环依赖问题。

标签: haskell template-haskell


【解决方案1】:

正如大家已经说过的haskell-src-meta提供了

parsePat :: String -> Either String Pat
parseExp :: String -> Either String Exp
parseType :: String -> Either String Type
parseDecs :: String -> Either String [Dec]

其中PatExpTypeDec 与来自 Language.Haskell.TH.Syntax 的相同。


为什么 GHC 不公开自己的解析器?

确实如此。使用ghci -package ghc 启动GHCi(ghc 默认为隐藏包),您可以导入Parser。它具有将 String 解析为模式、表达式、类型和声明的初步 AST(其数据声明在 HsSyn 中)的功能。

好的,那么为什么不存在使用此解析器并将其输出转换为来自template-haskellLanguage.Haskell.TH.Syntax 中的那个)的 AST 的库?

查看HsSyn 内部,很明显AST 与Language.Haskell.TH.Syntax 中的不完全相同。打开HsExprExp 并排你会看到后者充满了PostTc id <some-other-type>PostRn id <some-other-type> 这样的类型。随着 AST 从解析器传递到重命名器再到类型检查器,这些点点滴滴都被慢慢填充。例如,在进行类型检查之前,我们甚至不知道运算符的固定性!

为了实现我们想要的功能,我们需要运行的不仅仅是解析器(至少还有重命名器和类型检查器,也许更多)。想象一下:每次你想解析一个像"1 + 2" 这样的小表达式时,你仍然需要对一堆导入进行类型检查。即便如此,转换回 Language.Haskell.TH.Syntax 也不是在公园里散步:GHC 有很多特性,比如它自己特殊的全局存储名称和标识符的方式。

嗯...但是 GHC 对准引号有什么作用?

这是最酷的部分!与Exp 不同,HsExprHsSplice 用于表示拼接。查看前两个构造函数的类型:

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 所依赖的)这样的独立解析器可能更优雅和简单一次性完成所有事情(例如,固定性是您必须提前将此解析器提供的事情之一。

【讨论】:

  • 为什么直到类型检查才知道固定性?从概念上讲,这不是解析器上的一层以产生更完整的 AST 吗?为什么在那之前会发生奇特的事情?
  • @dfeuer Fixities 在重命名阶段(发生在类型检查之前)填充,因此类型检查是完全存在fixities 的第一个阶段。我可能不明白你的问题......
  • 我认为重命名器与类型检查器绑定在一起;也许那是错误的。
  • @dfeuer 不,你理解正确 - 重命名器和类型检查器确实相互反馈,但仅在处理顶级拼接时。重点仍然是特定块的固定性直到重命名结束时才知道(因此与中缀运算符may need to be rearranged 相关的 AST)。
  • 我在stackoverflow.com/questions/45674757/… 遇到了完全相同的问题 TH 是如何工作的?将 AST 转换为文字源代码,然后将其输入“常规 GHC”,还是 GHC 直接使用 AST?如果是前者,那么 haskell-src-meta 不是一个相当低效的解决方案,其中相同的代码被解析两次——一次由 haskell-src-meta 解析,一次由实际 GHC 解析?
猜你喜欢
  • 2011-05-08
  • 2018-08-28
  • 2011-04-11
  • 2015-11-04
  • 1970-01-01
  • 2011-06-10
  • 2021-07-19
  • 2015-11-28
  • 2017-07-16
相关资源
最近更新 更多