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

Bee*_*tle 17 parsing haskell types parsec indentation

缩进包Haskell的秒差距提供了一种方法来解析缩进式语言(比如Haskell和Python).它重新定义了Parser类型,那么如何使用Parsec Text.Parsec.Token模块导出的令牌解析器函数,这些函数属于普通Parser类型?

背景

Parsec附带了大量模块.他们中的大多数导出一堆有用的解析器(例如newlinefrom Text.Parsec.Char,解析换行符)或解析器组合器(例如, count n pText.Parsec.Combinator,运行解析器p,n次)

但是,该模块Text.Parsec.Token希望导出由用户参数化的函数和正在解析的语言的特征,例如,该braces p函数将在解析'{'之后和解析'}'之前运行解析器p,忽略评论之类的东西,其语法取决于你的语言.

Text.Parsec.Token实现这一目标的方法是它导出一个函数makeTokenParser,你调用它,给它你特定语言的参数(就像评论的样子),并返回一个包含所有函数的记录Text.Parsec.Token,适合你的语言.指定.

当然,在缩进式语言中,这些需要进一步调整(也许?这里是我不确定的地方 - 我稍后会解释)所以我注意到(可能是过时的)IndentParser包提供了一个模块Text.ParserCombinators.Parsec.IndentParser.Token这看起来是替补Text.Parsec.Token.

我应该在某些时候提到所有Parsec解析器都是monadic函数,所以它们用状态做神奇的事情,这样错误消息可以说出源文件中的哪一行和哪一行出现了错误

我的问题

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

这样看来,我认为(新)Text.Parsec.Indent的工作原理是某种一元状态魔法的是什么柱位的源代码工作的,所以它并不需要修改令牌解析器像whiteSpaceText.Parsec.Token,这可能是为什么它不提供更换模块.但是我遇到类型问题.

你看,没有Text.Parsec.Indent,我的所有解析器都是类型Parser Something,其中Something是返回类型,并且Parser是Text.Parsec.String中定义的类型别名

type Parser = Parsec String ()
Run Code Online (Sandbox Code Playgroud)

但是,我使用自己的定义Text.Parsec.Indent,而不是导入Text.Parsec.String

type Parser a = IndentParser String () a
Run Code Online (Sandbox Code Playgroud)

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

如果现在这没有多大意义,那是因为我有点失落.这里讨论了类型问题.


我得到的错误是我尝试用另一个替换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 ()
Run Code Online (Sandbox Code Playgroud)

链接

遗憾的是,上面的示例都没有像Text.Parsec.Token中那样使用令牌解析器.

Bee*_*tle 14

你想做什么?

听起来你想让你的解析器在任何地方被定义为类型

Parser Something

(其中Something是返回类型)并通过隐藏和重新定义Parser通常从Text.Parsec.String类似或类似方式导入的类型来实现此功能.你仍然需要导入一些Text.Parsec.String,使Stream成为monad的一个实例; 用这条线做到这一点:

import Text.Parsec.String ()
Run Code Online (Sandbox Code Playgroud)

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

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

type Parser = ParsecT String () (State SourcePos)
Run Code Online (Sandbox Code Playgroud)

并且可能取消import Text.Parsec.Indent (IndentParser)显示此定义的文件中的内容.

错误,墙上的错误

您的问题是您正在查看编译器错误消息的错误部分.你专注于

Couldn't match expected type `State SourcePos' with actual type `Identity'
Run Code Online (Sandbox Code Playgroud)

当你应该专注于

Expected type: P.GenTokenParser ...
  Actual type: P.TokenParser ...
Run Code Online (Sandbox Code Playgroud)

它汇编!

你从哪里"导入"解析器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
Run Code Online (Sandbox Code Playgroud)

......但是a P.LanguageDef st只是a GenLanguageDef String st Identity,而a P.TokenParser ()确实是a 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
Run Code Online (Sandbox Code Playgroud)

......就是这样!这将允许您的"导入"令牌解析器具有类型,而不是(这是别名),您的代码现在应该编译.ParsecT String () (State SourcePos) SomethingParsec String () SomethingParsecT String () Identity Something

(为了最大限度的一般性,我假设您可能Parser在文件中定义类型,该文件与您定义实际解析器函数的文件分开并导入.因此,这两个重复的import语句.)

谢谢

非常感谢Daniel Fischer帮我解决这个问题.