Text.Parsec.Token
模块导出的令牌解析器函数,它们是普通的Parser类型?
背景
> Parsec是一个解析器组合库,无论如何。
> IndentParser 0.2.1是一个提供两个模块Text.ParserCombinators.Parsec.IndentParser
和Text.ParserCombinators.Parsec.IndentParser.Token
的旧软件包
> indents 0.3.3是提供单模块Text.Parsec.Indent
的新包装
Parsec自带a load of modules.它们大多数导出一大堆有用的解析器(例如,从Text.Parsec.Char
开始解析换行符的换行符)或解析器组合器(例如,运行解析器p的Text.Parsec.Combinator
的np数n次)
然而,模块Text.Parsec.Token
想要导出用户使用被解析语言的特征进行参数化的函数,以便例如,大括号p函数将在解析“{”之后解析“ }’,忽略诸如评论之类的东西,其语法取决于你的语言。
Text.Parsec.Token
实现这一点的方式是,它导出一个单一的功能makeTokenParser
,您调用它,给出您的特定语言的参数(如评论的样子),并返回包含Text.Parsec.Token
中所有功能的记录,适用于你指定的语言
当然,在缩进式语言中,这些需要进一步调整(也许这里是我不确定的地方 – 我稍后会解释一下),所以我注意到the (presumably obsolete) IndentParser package提供了一个模块Text.ParserCombinators.Parsec.IndentParser.Token
,它看起来是一个Text.Parsec.Token
的替代品。
我应该提到在某些时候,所有的Parsec解析器都是一个单一的功能,所以他们做的事情与状态,以便错误消息可以说在源文件中的哪个行和列错误出现
我的问题
由于几个小的原因,我觉得the indents package是现在版本的IndentParser的或多或少的,但是它不提供一个看起来像Text.ParserCombinators.Parsec.IndentParser.Token
的模块,它只提供Text.Parsec.Indent,所以我想知道如何一个关于从Text.Parsec.Token获取所有令牌解析器(如保留“something”解析保留关键字“某事”,或者像前面提到的大括号)。
在我看来,(新的)Text.Parsec.Indent可以通过某种一元状态魔法来处理源代码的哪些列,这样它就不需要修改像whiteSpace这样的令牌解析器Text.Parsec.Token
,这可能是为什么它不提供替换模块。但我有类型的问题。
你看,没有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 ()
> Parsec
> IndentParser(旧包装)
> indents,提供Text.Parsec.Indent(新包)
> some discussion of Parser types用示例代码
> another example of using Text.Parsec.Indent
不幸的是,上面的例子都不使用像Text.Parsec.Token这样的令牌解析器。
解决方法
这听起来像你想让你的解析器在任何地方定义为类型
解析器的东西
(其中Something是返回类型),并通过隐藏和重新定义通常从Text.Parsec.String或类似导入的解析器类型来进行此工作。您仍然需要导入一些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)
并且可能在导出此定义的文件中导入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()身份。
您必须将您的类型声明更改为以下内容:
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帮助我。