微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用最新版本的 Parsec.Indent 库?

如何解决如何使用最新版本的 Parsec.Indent 库?

这个问题似乎与 this question 重复,但是 Parsec 或 Indent 库自 2012 年以来发生了变化,而且我为 indent 库找到的旧示例都没有与最新版本。

我想为一种编程语言制作一个解析器,其中缩进是语法的一部分(用于指示范围),为了实现这一点,我想使用 Text.Parsec.Indent 库,但我在如何使用它的损失。我很清楚必须进行一些修改/自定义解析器类型,但我对 State monad 的有限知识和对解析的表面级理解似乎还不够。

假设您想为一个简单的整数列表创建一个解析器,如下所示。如何实现这一目标?

mylist
    fstitem
    snditem

我尝试基于互联网上流传的一些旧示例创建一个简单的解析器看起来像这样,但它显然会产生一些类型错误

import Control.Monad.State

import Text.Parsec hiding (State)
import Text.Parsec.Indent
import Text.Parsec.Pos

type IParser a = ParsecT String () (State SourcePos) a

parseInt :: IParser Integer
parseInt = read <$> many1 digit

parseIndentedInt :: IParser Integer
parseIndentedInt = indented *> parseInt

特别是这些:

Frontend/Parser.hs:14:20: error:
    • Couldn't match type ‘Control.Monad.Trans.Reader.ReaderT
                             Text.Parsec.Indent.Internal.Indentation m0’
                     with ‘StateT SourcePos Data.Functor.Identity.Identity’
      Expected type: IParser Integer
        Actual type: ParsecT String () (IndentT m0) Integer
    • In the expression: indented *> parseInt
      In an equation for ‘parseIndentedInt’:
          parseIndentedInt = indented *> parseInt
   |
14 | parseIndentedInt = indented *> parseInt
   |                    ^^^^^^^^^^^^^^^^^^^^

Frontend/Parser.hs:14:32: error:
    • Couldn't match type ‘StateT
                             SourcePos Data.Functor.Identity.Identity’
                     with ‘Control.Monad.Trans.Reader.ReaderT
                             Text.Parsec.Indent.Internal.Indentation m0’
      Expected type: ParsecT String () (IndentT m0) Integer
        Actual type: IParser Integer
    • In the second argument of ‘(*>)’,namely ‘parseInt’
      In the expression: indented *> parseInt
      In an equation for ‘parseIndentedInt’:
          parseIndentedInt = indented *> parseInt
   |
14 | parseIndentedInt = indented *> parseInt
   |                                ^^^^^^^^
Failed,no modules loaded.

解决方法

好的,在深入研究源代码并查看缩进 GitHub 存储库中的测试后,我设法创建了一个工作示例。

以下代码可以解析一个简单的缩进列表:

import Text.Parsec        as Parsec
import Text.Parsec.Indent as Indent

data ExampleList = ExampleList String [ExampleList] 
                 deriving (Eq,Show)

plistItem :: Indent.IndentParser String () String
plistItem = Parsec.many1 Parsec.lower <* Parsec.spaces

pList :: Indent.IndentParser String () ExampleList
pList = Indent.withPos (ExampleList <$> plistItem <*> Parsec.many (Indent.indented *> pList))

useParser :: Indent.IndentParser String () a -> String -> a
useParser p src = helper res
                where res = Indent.runIndent $ Parsec.runParserT (p <* Parsec.eof) () "<test>" src
                      helper (Left err) = error "Parse error"
                      helper (Right ok) = ok

示例用法:

*Main> useParser pList "mylist\n\tfstitem\n\tsnditem"
ExampleList "mylist" [ExampleList "fstitem" [],ExampleList "snditem" []]

请注意,useParser 函数执行了一些实际操作,实际上是从任一个 monad 中获取结果,并将文件解析器的结尾放在所提供的解析器后面。根据您的应用程序,您可能想要更改此设置。

此外,类型签名可以用这样的方式缩短:

type IParser a = Indent.IndentParser String () a

plistItem :: IParser String
pList :: IParser ExampleList
useParser :: IParser a -> String -> a

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。