Bee*_*tle 17 parsing haskell types parsec indentation
该缩进包Haskell的秒差距提供了一种方法来解析缩进式语言(比如Haskell和Python).它重新定义了Parser类型,那么如何使用Parsec Text.Parsec.Token模块导出的令牌解析器函数,这些函数属于普通Parser类型?
Text.ParserCombinators.Parsec.IndentParser和Text.ParserCombinators.Parsec.IndentParser.TokenText.Parsec.IndentParsec附带了大量模块.他们中的大多数导出一堆有用的解析器(例如newlinefrom Text.Parsec.Char,解析换行符)或解析器组合器(例如, count n p从Text.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的工作原理是某种一元状态魔法的是什么柱位的源代码工作的,所以它并不需要修改令牌解析器像whiteSpace从Text.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帮我解决这个问题.