Parsec:如何在库中简单地创建一个新的解析器

时间:2018-11-08 09:34:35

标签: parsing haskell parsec

假设我只想创建自己的解析器,该解析器与Parsec中的char完全相同,但是在运行时

import Text.Parsec
char1 c = char c

它给了我

? Non type-variable argument in the constraint: Stream s m Char
  (Use FlexibleContexts to permit this)
? When checking the inferred type
    char1 :: forall s (m :: * -> *) u.
             Stream s m Char =>
             Char -> ParsecT s u m Cha

我该如何解决?我还应该包括其他进口商品吗?谢谢

1 个答案:

答案 0 :(得分:2)

Parsec解析器由于其灵活性而具有相当复杂的类型。您可以通过两种方式解决此问题:

  1. {-# Language FlexibleContexts #-}放在源文件的顶部。这种语用告诉GHC做一些额外的类型推断。

  2. (推荐)给char1一个显式类型。

    char1 :: Char -> Parsec String () Char   
    char1 c = char c
    

建议这样做,因为您应始终为任何顶级声明提供显式类型。如果您不这样做,那么所有编译器都可以告诉您代码中某处存在类型不匹配(它将告诉您它在哪里找到了矛盾,但这不太可能是错误所在)。使用显式类型声明,编译器可以缩小范围。

在这种情况下,Parsec将Parsec类型定义为

type Parsec s u = ParsecT s u Identity

您将从错误消息中识别出ParsecTParsecT是monad转换器:它使解析器在识别文本时让解析器在其他monad(例如IO)中执行操作。在这种情况下,我们只需要一个解析器,因此我们使用Identity monad,它什么都不做。

s参数是输入流。在这种情况下,我们将说解析器正在从String获取输入。

u参数是一个状态。这使您可以在遇到变量时做一些事情,例如在符号表中记录变量名。在这种情况下,我们不使用状态,因此仅使用()

如果您正在编写一个真正的解析器,则通常会定义自己的类型同义词,例如

type FooParser a = Parsec Text FooState a

char1 :: Char -> FooParser Char
char1 = char