Parsing Haskell/Parsec:如何使用Text.Parsec.Indent中的函数?
我很难理解如何使用Haskell包提供的模块中的任何函数,Haskell包是Parsec的一种附加组件 所有这些函数都做什么?如何使用它们 我可以理解Parsing Haskell/Parsec:如何使用Text.Parsec.Indent中的函数?,parsing,haskell,indentation,parsec,Parsing,Haskell,Indentation,Parsec,我很难理解如何使用Haskell包提供的模块中的任何函数,Haskell包是Parsec的一种附加组件 所有这些函数都做什么?如何使用它们 我可以理解withBlock的简短黑线鳕描述,并且我找到了如何使用withBlock、runIndent和IndentParser类型的示例,以及。我还可以理解四个解析器的文档。但是很多事情仍然让我困惑 特别是: 带f a p块的和 do aa <- a pp <- block p return f aa pp do aa因此,第一
withBlock
的简短黑线鳕描述,并且我找到了如何使用withBlock
、runIndent
和IndentParser
类型的示例,以及。我还可以理解四个解析器的文档。但是很多事情仍然让我困惑
特别是:
和
do aa <- a
pp <- block p
return f aa pp
do aa因此,第一个提示是查看IndentParser
type IndentParser s u a = ParsecT s u (State SourcePos) a
也就是说,它是一个ParsecT
额外密切监视的抽象容器,可用于访问当前列号等内容。因此,它可能将当前的“缩进级别”存储在SourcePos
中。这是我对“参考水平”的初步猜测
简而言之,indents
为您提供了一种新的Parsec
,它对上下文特别敏感,对当前缩进非常敏感。我会按顺序回答你的问题
(2) “引用级别”是这个缩进级别开始的当前解析器上下文状态中引用的“信念”。为了更清楚,让我给出一些关于(3)的测试用例
(3) 为了开始试验这些函数,我们将构建一个小的测试运行程序。它将使用我们提供的字符串运行解析器,然后使用我们要修改的initialPos
打开内部状态
部分。编码
import Text.Parsec
import Text.Parsec.Pos
import Text.Parsec.Indent
import Control.Monad.State
testParse :: (SourcePos -> SourcePos)
-> IndentParser String () a
-> String -> Either ParseError a
testParse f p src = fst $ flip runState (f $ initialPos "") $ runParserT p () "" src
(注意这几乎是runIndent
,除了我提供了一个后门来修改initialPos
)
现在我们可以看一下缩进的
。通过检查源代码,我可以看出它做了两件事。首先,如果当前SourcePos
列号小于或等于SourcePos
中存储的State
中的SourcePos
中存储的“参考级别”,它将失败。其次,它有点神秘地将状态
SourcePos
的行计数器(而不是列计数器)更新为当前状态
据我所知,只有第一种行为才是重要的。我们可以看到这里的区别
>>> testParse id indented ""
Left (line 1, column 1): not indented
>>> testParse id (spaces >> indented) " "
Right ()
>>> testParse id (many (char 'x') >> indented) "xxxx"
Right ()
因此,为了使缩进成功,我们需要使用足够的空白(或任何其他!)来将列位置推过“reference”列位置。否则,它将无法说出“未缩进”。接下来的三个函数也存在类似的行为:same
失败,除非当前位置和参考位置在同一行上;sameOrIndented
失败,如果当前列严格小于参考列,除非它们在同一行上,除非当前列和引用列匹配,否则检查缩进将失败
与pos
略有不同。它不仅仅是一个IndentParser
,它是一个IndentParser
-组合器它将输入IndentParser
转换为一个认为“引用列”(处于状态的SourcePos
)正是我们调用with pos
时的位置
顺便说一句,这给了我们另一个提示。它让我们知道我们有权更改引用列
(1)现在,让我们来看看<代码>块和<代码>如何使用我们的新的、低级引用列运算符使用Bug < /Cord>工作。code>withBlock
是根据block
实现的,因此我们将从block
开始
-- simplified from the actual source
block p = withPos $ many1 (checkIndent >> p)
因此,block
将“参考列”重置为当前列的任何内容,然后使用来自p
的至少1个解析,只要每个解析与新设置的“参考列”缩进相同。现在我们可以用block查看
withBlock f a p = withPos $ do
r1 <- a
r2 <- option [] (indented >> block p)
return (f r1 r2)
withBlock f a p=withPos$do
r1(p区)
返回(f r1 r2)
因此,它将“参考列”重置为当前列,解析单个a
parse,尝试解析p
s的缩进的块
,然后使用f
组合结果。您的实现几乎是正确的,只是需要使用with pos
来选择正确的“参考列”
然后,一旦你有了withBlock
,withBlock'=withBlock(\\ubs->bs)
(5) 因此,indented
和friends正是执行此操作的工具:如果解析相对于withPos
选择的“参考位置”缩进不正确,它们将导致解析立即失败
(4) 是的,在你学会如何在baseParsec
中使用解析之前,不要担心这些家伙。它通常是一种更干净、更快、更简单的指定解析的方法。有时它们甚至更强大,但如果你理解Monad,那么它们几乎总是完全相同的
(6) 这是关键。如果您可以使用with pos
描述您的预期缩进,那么到目前为止提到的工具只能导致缩进失败。很快,我认为不可能根据其他解析的成功或失败来指定with pos
。。。所以你必须再深入一层。幸运的是,IndentParser
s工作的机制很明显,它只是一个内部状态
monad,包含SourcePos
。您可以使用lift::MonadTrans t=>ma->tma
来操作此内部状态,并根据需要设置“参考列”
干杯 你看过源代码了吗?(按照文档中类型签名右侧的“源”链接)这至少对您的第一个问题有帮助。对不起,还有一个问题。什么事