生成在另一个解析器的输出上运行接收的解析器的解析器,并单独加入结果

时间:2017-09-12 22:31:18

标签: parsing haskell monads monad-transformers parsec

给出以下类型和函数,意味着将CSV字段的字段解析为字符串:

type Parser resultType = ParsecT String () Identity resultType
cell :: Parser String 

我已实现以下功能:

customCell :: String -> Parser res  -> Parser res
customCell typeName subparser = 
  cell
    >>= either (const $ unexpected typeName) 
               return . parse (subparser <* eof) ""

虽然我不能不再想到我没有尽可能多地使用Monad概念,并且最终有更好的方法将内部结果与外部解析器合并,特别是关于它的失败。

有人知道我该怎么做,或者这段代码是什么意思?

PS - 我现在意识到我的类型简化可能不合适,也许我想要的是用Either Monad替换底层的Identity Monad ....不幸的是,我对Monad变换器还不够熟悉。

PS2 - 无论如何,潜在的monad到底有什么用呢?

2 个答案:

答案 0 :(得分:4)

阐述@Daniel Wagner的答案......解析器通常使用Parsec构建的方式,你从解析特定字符(例如,加号或数字)的低级解析器开始,然后构建使用组合器的解析器(如many1组合器,将读取单个数字的解析器转换为读取一个或多个数字的解析器,或者解析器&#34;一个或多个数字&#34)的monadic解析器34;然后是&#34;加号&#34;后跟&#34;一个或多个数字&#34;)。

然而,每个解析器,无论是低级数字解析器还是更高级别的附加表达式&#34;解析器,旨在直接应用于同一输入流。

通常做的事情就是编写一个解析器来吞噬输入流的一大块来生成一个String和另一个解析 String (而不是原始输入流)并尝试将它们组合在一起。这是一种&#34;垂直构图&#34; Parsec并没有直接支持它,看起来不自然和非monadic。

正如评论中所指出的那样,某些情况下垂直构图是最干净的整体方法(比如当你将一种语言嵌入到另一种语言的组件或表达式中时),但它&# 39;不是Parsec解析器采用的常用方法。

您的应用程序的底线是仅生成cell的{​​{1}}解析器太专业化而无法使用。一个更有用的CSV文件Parsec框架是:

String

现在,您可以编写一个解析正整数的自定义单元格解析器:

import Text.Parsec
import Text.Parsec.String

-- | `csv cell` parses a CSV file each of whose elements is parsed by `cell`
csv :: Parser a -> Parser [[a]]
csv cell = many (row cell)

-- | `row cell` parses a newline-terminated row of comma separated
--   `cell`-expressions
row :: Parser a -> Parser [a]
row cell = sepBy cell (char ',') <* char '\n'

并解析CSV文件:

customCell :: Parser Int
customCell = read <$> many1 digit

这里,&#34; cell&#34;不是让一个> parse (csv customCell) "" "1,2,3\n4,5,6\n" Right [[1,2,3],[4,5,6]] > 子分析器明确地将逗号分隔的单元格解析为一个字符串,而是将其输入到另一个解析器中。是一个隐式上下文,在其中调用提供的单元格解析器以在适当的位置解析基础输入流,在该位置可以预期输入流中间的行中间有逗号分隔的单元格。

答案 1 :(得分:2)

遗憾的是,我知道Haskell没有支持垂直解析器组合的解析器库或解析器生成器。像你所写的东西一样好。荡!