我想在megaparsec模块中使用Read的派生实例。 如何在“解析器a”中使用“ Text.Read.read”或“ Text.Read.readEither”?
它不必很快,但易于维护和扩展。 megaparsec模块用于通过CLI测试我的应用程序,因此必须解析许多不同的数据类型。
它将以以下方式工作:
import Text.Megaparsec
readableDatatype :: Read a => Parser a
readableDatatype =
-- This is wrong, but describes how it shall work
-- liftA read chunkToTokens
expr' :: Parser UserControlExpr
expr' = timeExpr
<|> timeEventExpr
<|> digiInExpr
<|> quitExpr
digiInExpr :: Parser UserControlExpr
digiInExpr = do
cmdword "digiIn"
inElement <- (readableDatatype :: Parser TI_I)
return $ UserDigiIn inElement
我必须写什么,以便对三个函数进行类型检查,尤其是readableDataype
?
答案 0 :(得分:0)
您可以将getInput :: MonadParsec e s m => m s
和setInput :: MonadParsec e s m => s -> m ()
与reads :: Read a => String -> [(a, String)]
一起使用。 getInput
和setInput
仅获取并设置解析器正在处理的输入流,而reads
则获取字符串,并返回可能解析的列表以及输入的剩余未使用部分。我们还需要告诉解析器输入中的新偏移量,否则错误位置是错误的。我们可以使用getOffset
和setOffset
来做到这一点。
-- For equality constraint (~)
{-# LANGUAGE TypeFamilies #-}
import Text.Megaparsec
import Text.Read (reads)
readableDatatype :: (Read a, MonadParsec e s m, s ~ String) => m a
readableDatatype = do
input <- getInput
offset <- getOffset
choice $
(\(a, input') -> a <$ setInput input'
<* setOffset (offset + length input - length input'))
<$> reads input
如果您输入的内容不是String
,则必须在String
之后和getInput
之前在该输入和setInput
之间进行转换。
以下是有关性能问题的信息,因此与您的问题并不真正相关,但也许是有教育意义的,对其他可能需要性能良好的解决方案的人可能有用。
在解析过程中,始终在String
和其他类型之间转换整个输入,这对于较大的输入来说是一个很大的性能瓶颈。此外,在这里使用length
来计算新的偏移量也不是很好。
要解决这两个问题,需要某种方法才能知道读取解析器实际消耗了多少输入,以便我们可以从原始输入中删除该部分,而不必转换整个输入未使用的零件恢复为原始输入类型。但是Read
类没有。可以尝试解析输入的更长前缀,这在使用Read
进行的解析比整个输入的长度短的情况下可能会更快。您还可以使用unsafePerformIO
来向IORef
写入读取解析器实际上强制了多少输入,这将是最快但不是那么好的解决方案。
我实现了后者here。随时使用它,但要注意,它不是经过很好的测试。但是,它确实解决了上述方法的所有问题。
答案 1 :(得分:0)
做到了。谢谢!同时,我通过将构造函数定义为字符串并解析它们(不使用read)来“保守”地解决了该问题。这样做的好处是,您得到了megaparsec令人印象深刻的错误消息,告诉您缺少哪些符号。
read
的示例:
1:8:
|
1 | digiIn TI_I_Signal1 DirA Dectivated
| ^
unknown parse error
(“停用”中仅缺少“ a”)
带有手写解析器的示例,用于数据类型:
1:19:
|
1 | digiIn TI_I_Signal1 Dectivated
| ^^^^^^^^
unexpected "Dectivat"
expecting "active", "inactive", '0', or '1'
我想我会在将来的数据类型中使用您的代码块。 非常感谢你!