6

使用 Parsec,我可以String -> Maybe MyType相对轻松地编写类型函数。我现在想Read基于此为我的类型创建一个实例;但是,我不明白readsPrec它是如何工作的或它应该做什么。

我现在最好的猜测readsPrec是用于从头开始构建递归解析器以遍历字符串,在 Haskell 中构建所需的数据类型。但是,我已经有一个非常强大的解析器,它可以为我做这件事。那么如何告诉readsPrec我使用我的解析器呢?它采用的“运算符优先级”参数是什么,在我的上下文中它有什么好处?

如果有帮助,我在 Github 上创建了一个最小示例。它包含一个类型、一个解析器和一个空白的 Read 实例,并且很好地反映了我遇到的问题。

(背景:真正的解析器是针对 Scheme 的。)

4

1 回答 1

3

但是,我已经有一个非常强大的解析器,它可以为我做这件事。

它实际上不是那么健壮,你的解析器有多余括号的问题,它不会解析

((1) (2))

例如,它会在一些格式错误的输入上抛出异常,因为

singleP = Single . read <$> many digit

可以使用read "" :: Int.

顺便说一句,优先级参数用于确定在某些地方是否需要括号,例如,如果你有

infixr 6 :+:

data a :+: b = a :+: b

data C = C Int

data D = D C

您不需要将括号括起来C 12作为 的参数(:+:),因为应用程序的优先级高于 的(:+:),但是您需要将括号括起来C 12作为 的参数D

所以你通常会有类似的东西

readsPrec p = needsParens (p >= precedenceLevel) someParser

wheresomeParser解析输入中的值而不包含括号,并needsParens True thing解析thing括号之间的 a,同时needsParens False thing解析thing 可选的括在括号中的 a [您应该始终接受比必要更多的括号,((((((1))))))应该解析为Int]。

由于readsPrec p解析器用于在读取列表、元组等时将输入的部分解析为值的一部分,因此它们不仅必须返回解析的值,还必须返回输入的剩余部分。

parsec这样,将解析器转换为解析器的简单方法readsPrec

withRemaining :: Parser a -> Parser (a, String)
withRemaining p = (,) <$> p <*> getInput

parsecToReadsPrec :: Parser a -> Int -> ReadS a
parsecToReadsPrec parsecParser prec input
    = case parse (withremaining $ needsParens (prec >= threshold) parsecParser) "" input of
        Left _ -> []
        Right result -> [result]

但是,如果您使用 GHC,则最好使用ReadPrec / ReadP解析器(使用 构建Text.ParserCombinators.ReadP[rec])而不是parsec解析器,并使用定义readPrec而不是readsPrec.

于 2013-01-25T15:06:28.943 回答