首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Haskell/Parsec:如何在Text.Parsec.Indent中使用Text.Parsec.Token (来自缩进包)

Haskell/Parsec:如何在Text.Parsec.Indent中使用Text.Parsec.Token (来自缩进包)
EN

Stack Overflow用户
提问于 2013-03-10 04:34:43
回答 1查看 3.5K关注 0票数 17

Haskell的Parsec的缩进包提供了一种解析缩进风格的语言(如Haskell和Python)的方法。它重新定义了Parser类型,那么如何使用由Parsec的Text.Parsec.Token模块导出的、属于普通Parser类型的令牌解析器函数呢?

背景

  • Parsec是一个解析器组合器库,无论means.
  • IndentParser 0.2.1是提供两个模块的旧程序包,Text.ParserCombinators.Parsec.IndentParser.Token
  • indents 0.3.3是提供单个模块Text.Parsec.Indent

的新程序包,都是Text.ParserCombinators.Parsec.IndentParser

Parsec与a load of modules一起提供。它们中的大多数导出一组有用的解析器(例如,来自Text.Parsec.Charnewline,它解析换行符)或解析器组合符(例如,来自Text.Parsec.Combinatorcount n p,它运行解析器p,n次)

然而,模块Text.Parsec.Token想要导出由用户参数化的函数和被解析语言的特征,这样,例如,braces p函数将在解析'{‘之后和解析'}’之前运行解析器p,忽略诸如注释之类的东西,其语法取决于您的语言。

Text.Parsec.Token实现这一点的方法是导出一个函数makeTokenParser,您可以调用该函数,为其提供特定语言的参数(如注释的外观),并返回一个记录,其中包含Text.Parsec.Token中的所有函数,并根据指定的语言进行调整。

当然,在缩进风格的语言中,这些需要进一步调整(也许?这是我不确定的地方-我稍后会解释)所以我注意到the (presumably obsolete) IndentParser package提供了一个模块Text.ParserCombinators.Parsec.IndentParser.Token,它看起来像是Text.Parsec.Token的临时替代品。

我应该在某种程度上提到,所有的Parsec解析器都是一元函数,所以它们对state做了一些神奇的事情,以便错误消息可以指出错误出现在源文件中的哪一行和哪一列

我的问题

由于一些小的原因,在我看来,the indents package或多或少是IndentParser的当前版本,但是它没有提供一个看起来像Text.ParserCombinators.Parsec.IndentParser.Token的模块,它只提供了Text.Parsec.Indent,所以我想知道如何从something获得所有的令牌解析器(比如解析保留关键字“Text.ParserCombinators.Parsec.IndentParser.Token”的reserved "something",或者像我之前提到的braces )。

在我看来,(新的) Text.Parsec.Indent通过某种单一的状态魔术来确定源代码的列位,这样它就不需要修改令牌解析器,比如Text.Parsec.Token中的whiteSpace,这可能就是它没有提供替代模块的原因。但是我对类型有一个问题。

在没有Text.Parsec.Indent的情况下,我的所有解析器都是Parser Something类型,其中Something是返回类型,Parser是在Text.Parsec.String中定义的类型别名

代码语言:javascript
复制
type Parser = Parsec String ()

但是在Text.Parsec.Indent中,我没有导入Text.Parsec.String,而是使用自己的定义

代码语言:javascript
复制
type Parser a = IndentParser String () a

这使得我所有的解析器类型都是IndentParser String () Something,其中IndentParser是在Text.Parsec.Indent中定义的。但是我从Text.Parsec.Token中的makeTokenParser得到的令牌解析器的类型是错误的。

如果到目前为止这还不是很有意义,那是因为我有点迷路了。类型问题是discussed a bit here

我得到的错误是,我尝试用上面的一个Parser定义替换另一个,但是当我尝试使用Text.Parsec.Token中的一个令牌解析器时,我得到了编译错误

代码语言:javascript
复制
Couldn't match expected type `Control.Monad.Trans.State.Lazy.State
                                Text.Parsec.Pos.SourcePos'
            with actual type `Data.Functor.Identity.Identity'
Expected type: P.GenTokenParser
                 String
                 ()
                 (Control.Monad.Trans.State.Lazy.State Text.Parsec.Pos.SourcePos)
  Actual type: P.TokenParser ()

链接

  • Parsec
  • IndentParser (旧package)
  • indents,提供package)
  • some discussion of Parser types (具有示例code
  • another example of using Text.Parsec.Indent

的新Text.Parsec.Indent

遗憾的是,上面的两个示例都没有使用与Text.Parsec.Token中相同的令牌解析器。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-03-12 00:19:06

你想做什么?

听起来您想让您的解析器在任何地方都定义为

Parser Something

(其中某项是返回类型),并通过隐藏和重新定义通常从Text.Parsec.String或类似类型导入的Parser类型来实现此功能。您仍然需要导入一些Text.Parsec.String,以使Stream成为monad的实例;使用以下代码行完成此操作:

代码语言:javascript
复制
import Text.Parsec.String ()

您对Parser的定义是正确的。或者或者等效地(对于那些在评论中关注聊天的人),您可以使用

代码语言:javascript
复制
import Control.Monad.State
import Text.Parsec.Pos (SourcePos)

type Parser = ParsecT String () (State SourcePos)

并可能删除出现此定义的文件中的import Text.Parsec.Indent (IndentParser)

错误,墙上的错误

您的问题是您看到的编译器错误消息的错误部分。你关注的是

代码语言:javascript
复制
Couldn't match expected type `State SourcePos' with actual type `Identity'

当你应该专注于

代码语言:javascript
复制
Expected type: P.GenTokenParser ...
  Actual type: P.TokenParser ...

它编译了!

在从Text.Parsec.Token“导入”解析器时,您实际要做的当然(正如您简要提到的)首先是定义一个记录您的语言参数,然后将其传递给函数makeTokenParser,该函数返回一个包含令牌解析器的记录。

因此,您必须有一些类似于以下内容的行:

代码语言:javascript
复制
import qualified Text.Parsec.Token as P

beetleDef :: P.LanguageDef st
beetleDef =
    haskellStyle {
        parameters, parameters etc.
        }

lexer :: P.TokenParser ()
lexer = P.makeTokenParser beetleDef

..。但是P.LanguageDef st只是一个GenLanguageDef String st Identity,而P.TokenParser ()实际上是一个GenTokenParser String () Identity

必须将类型声明更改为以下内容:

代码语言:javascript
复制
import Control.Monad.State
import Text.Parsec.Pos (SourcePos)
import qualified Text.Parsec.Token as P

beetleDef :: P.GenLanguageDef String st (State SourcePos)
beetleDef =
    haskellStyle {
        parameters, parameters etc.
        }

lexer :: P.GenTokenParser String () (State SourcePos)
lexer = P.makeTokenParser beetleDef

..。就是这样!这将允许您的“导入”令牌解析器具有类型ParsecT String () (State SourcePos) Something,而不是Parsec String () Something (它是ParsecT String () Identity Something的别名),并且您的代码现在应该已经编译。

(为了获得最大的通用性,我假设您可能在一个文件中定义Parser类型,该文件与定义实际解析器函数的文件不同,并由该文件导入。因此出现了两个重复的import语句。)

谢谢

非常感谢Daniel Fischer在这方面对我的帮助。

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

https://stackoverflow.com/questions/15315624

复制
相关文章

相似问题

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