Parsing Haskell/Parsec:如何将Text.Parsec.Token与Text.Parsec.Indent一起使用(来自indents包)

Parsing Haskell/Parsec:如何将Text.Parsec.Token与Text.Parsec.Indent一起使用(来自indents包),parsing,haskell,types,indentation,parsec,Parsing,Haskell,Types,Indentation,Parsec,Haskell的Parsec的indents包提供了一种解析缩进风格语言(如Haskell和Python)的方法。它重新定义了解析器类型,那么如何使用由Parsec模块导出的令牌解析器函数,这些函数属于正常的解析器类型 背景 是一个解析器组合器库,不管这意味着什么 是一个提供两个模块和 是提供单个模块的新软件包 Parsec随附。它们中的大多数导出一组有用的解析器(例如,newlinefrom,用于解析新行)或解析器组合器(例如,countnpfrom,用于运行解析器p,n次) 但是,模块希

Haskell的Parsec的indents包提供了一种解析缩进风格语言(如Haskell和Python)的方法。它重新定义了
解析器
类型,那么如何使用由Parsec模块导出的令牌解析器函数,这些函数属于正常的
解析器
类型

背景
  • 是一个解析器组合器库,不管这意味着什么
  • 是一个提供两个模块和
  • 是提供单个模块的新软件包
Parsec随附。它们中的大多数导出一组有用的解析器(例如,
newline
from,用于解析新行)或解析器组合器(例如,
countnp
from,用于运行解析器p,n次)

但是,模块希望导出由用户使用所分析语言的功能参数化的函数,例如,
大括号p
函数将在解析“{”之后和解析“}”之前运行解析器p,忽略注释之类的内容,注释的语法取决于您的语言

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

当然,在缩进风格的语言中,这些需要进一步调整(也许?这里我不确定-我稍后会解释),所以我注意到,它提供了一个模块,看起来可以替代

我应该在某一点上提到,所有的Parsec解析器都是一元函数,因此它们对state做了一些神奇的事情,这样错误消息就可以说错误出现在源文件的哪一行和哪一列

我的问题 出于几个小原因,我觉得它或多或少是IndentParser的当前版本,但是它没有提供类似的模块,它只提供
Text.Parsec.Indent
,因此我想知道如何从
Text.Parsec.token
获取所有令牌解析器(比如
reserved“something”
解析保留关键字“something”,或者像我前面提到的
brages

在我看来,(新的)
Text.Parsec.Indent
通过某种一元状态魔法来计算源代码的列位,因此它不需要修改像
whiteSpace
from这样的标记解析器,这可能就是它不提供替换模块的原因。但我在类型方面有问题

你看,如果没有
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()类型的解析器都成为了某种东西,其中IndentParser是在Text.Parsec.Indent中定义的。但是我从
Text.Parsec.token
中得到的令牌解析器的类型是错误的

如果到目前为止这还没有什么意义,那是因为我有点迷路了。类型问题是


我得到的错误是,我试图用另一个替换上面的
解析器
的一个定义,但是当我试图使用
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中那样使用令牌解析器。

您想做什么? 听起来你想让你的解析器在任何地方都被定义为

Parser Something

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

import Text.Parsec.String ()
您对
解析器的定义是正确的。或者,您可以使用

import Control.Monad.State
import Text.Parsec.Pos (SourcePos)

type Parser = ParsecT String () (State SourcePos)
并可能在出现此定义的文件中删除导入Text.Parsec.Indent(IndentParser)

错误,墙上的错误 您的问题是您看到的编译器错误消息的错误部分

无法将预期类型“State SourcePos”与实际类型“Identity”匹配
当你应该专注于

预期类型:P.GenTokenParser。。。
实际类型: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字符串st标识
,而
p.TokenParser()
实际上是一个
GenTokenParser字符串()标识

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

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
的别名),您的代码现在应该可以编译了