Haskell的Parsec的缩进包提供了一种解析缩进风格的语言(如Haskell和Python)的方法。它重新定义了Parser类型,那么如何使用由Parsec的Text.Parsec.Token模块导出的、属于普通Parser类型的令牌解析器函数呢?
背景
Text.ParserCombinators.Parsec.IndentParser.TokenText.Parsec.Indent的新程序包,都是Text.ParserCombinators.Parsec.IndentParser
Parsec与a load of modules一起提供。它们中的大多数导出一组有用的解析器(例如,来自Text.Parsec.Char的newline,它解析换行符)或解析器组合符(例如,来自Text.Parsec.Combinator的count 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中定义的类型别名
type Parser = Parsec String ()但是在Text.Parsec.Indent中,我没有导入Text.Parsec.String,而是使用自己的定义
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中的一个令牌解析器时,我得到了编译错误
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 ()链接
的新Text.Parsec.Indent
遗憾的是,上面的两个示例都没有使用与Text.Parsec.Token中相同的令牌解析器。
发布于 2013-03-12 00:19:06
你想做什么?
听起来您想让您的解析器在任何地方都定义为
Parser Something
(其中某项是返回类型),并通过隐藏和重新定义通常从Text.Parsec.String或类似类型导入的Parser类型来实现此功能。您仍然需要导入一些Text.Parsec.String,以使Stream成为monad的实例;使用以下代码行完成此操作:
import Text.Parsec.String ()您对Parser的定义是正确的。或者或者等效地(对于那些在评论中关注聊天的人),您可以使用
import Control.Monad.State
import Text.Parsec.Pos (SourcePos)
type Parser = ParsecT String () (State SourcePos)并可能删除出现此定义的文件中的import Text.Parsec.Indent (IndentParser)。
错误,墙上的错误
您的问题是您看到的编译器错误消息的错误部分。你关注的是
Couldn't match expected type `State SourcePos' with actual type `Identity'当你应该专注于
Expected type: P.GenTokenParser ...
Actual type: P.TokenParser ...它编译了!
在从Text.Parsec.Token“导入”解析器时,您实际要做的当然(正如您简要提到的)首先是定义一个记录您的语言参数,然后将其传递给函数makeTokenParser,该函数返回一个包含令牌解析器的记录。
因此,您必须有一些类似于以下内容的行:
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。
必须将类型声明更改为以下内容:
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在这方面对我的帮助。
https://stackoverflow.com/questions/15315624
复制相似问题