首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何将Megaparsec与Text.Read (派生读取实例)结合使用

如何将Megaparsec与Text.Read (派生读取实例)结合使用
EN

Stack Overflow用户
提问于 2019-01-27 10:49:02
回答 2查看 168关注 0票数 2

我想在megaparsec模块中使用Read的派生实例。如何在“解析器a”中使用“Text.Read.read”或“Text.Read.readEither”?

它不需要很快,但要易于维护和扩展。megaparsec模块用于通过CLI测试我的应用程序,因此必须解析许多不同的数据类型。

它的工作方式如下:

代码语言:javascript
复制
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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-01-28 02:02:33

为此,您可以将getInput :: MonadParsec e s m => m ssetInput :: MonadParsec e s m => s -> m ()reads :: Read a => String -> [(a, String)]一起使用。getInputsetInput只获取并设置解析器正在处理的输入流,reads接受一个字符串并返回一个可能的解析列表,以及输入中剩余的未使用部分。我们还需要告诉解析器输入中的新偏移量,否则错误位置是错误的。我们可以使用getOffsetsetOffset来做到这一点。

代码语言:javascript
复制
-- 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,则必须在getInput之后和setInput之前在它和String之间进行转换。

以下是关于性能的问题,所以与您的问题不太相关,但可能具有教育意义,可能对需要性能良好的解决方案的其他人有用。

对于较大的输入,在解析期间一直在String和其他类型之间转换整个输入是一个相当大的性能瓶颈。此外,在这里使用length计算新的偏移量也不是很好。

要解决这两个问题,需要某种方法能够知道读取解析器实际消耗了多少输入,这样我们就可以从原始输入中删除该部分,而不必将整个未使用的部分转换回原始输入类型。但是Read类没有这样的功能。可以尝试逐步解析输入的较长前缀,如果使用Read完成的解析与整个输入的长度相比较短,则可能会更快。您还可以使用unsafePerformIOIORef写入多少输入实际上是由读取解析器强制执行的,这将是最快但不太美观的解决方案。

我实现了后一个here。你可以随意使用它,但要知道它并没有经过很好的测试。然而,它确实解决了上述方法的所有问题。

票数 0
EN

Stack Overflow用户

发布于 2019-01-29 07:01:27

这就完成了。谢谢!同时,我对这个问题做了一个“保守”的解决方案,将构造函数定义为字符串并对其进行解析,而不使用read。这有一个好处,那就是你得到了令人印象深刻的兆秒错误信息,它告诉你丢失了什么符号。

使用read的示例

代码语言:javascript
复制
1:8:
  |
1 | digiIn TI_I_Signal1 DirA Dectivated
  |        ^
unknown parse error

(“停用”中只缺了一个'a‘)

使用数据类型的手写解析器的示例:

代码语言:javascript
复制
1:19:
  |
1 | digiIn TI_I_Signal1 Dectivated
  |                     ^^^^^^^^
unexpected "Dectivat"
expecting "active", "inactive", '0', or '1'

我想我会在将来的数据类型中使用你的代码块。非常感谢!

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54384656

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档