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

给定以下类型和函数,意味着将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到底有什么用呢?

最佳答案
详细阐述@Daniel Wagner的答案……解析器通常使用Parsec构建,你可以从解析特定字符(例如,加号或数字)的低级解析器开始,然后使用组合器在它们之上构建解析器(就像many1组合器,将读取单个数字的解析器转换为读取一个或多个数字的解析器,或者解析“一个或多个数字”后跟“加号”后跟“一个或多个数字”的monadic解析“).

但是,每个解析器,无论是低级数字解析器还是更高级别的“加法表达式”解析器,都可以直接应用于同一输入流.

你通常不会做的是编写一个解析器,它吞噬输入流的一大块来生成一个String和另一个解析String(而不是原始输入流)的解析器并尝试组合它们.这是Parsec不直接支持的那种“垂直构图”,看起来不自然且非单一.

正如评论中所指出的,在某些情况下,垂直构图是最干净的整体方法(例如,当您将一种语言嵌入到另一种语言的组件或表达式中时),但这不是Parsec解析器采用的常用方法.

应用程序的底线是,只生成String的单元格解析器太专业化而无法使用.一个更有用的CSV文件Parsec框架将是:

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'

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

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

并解析CSV文件:

> parse (csv customCell) "" "1,2,3\n4,5,6\n"
Right [[1,2,3],[4,5,6]]
>

这里,“cell”是一个隐式上下文,在其中调用提供的单元格解析器来解析底层输入流,而不是让一个单元子析构程序显式地将逗号分隔的单元格解析为一个字符串以供给另一个解析器.在输入流中间的行中间可以预期逗号分隔的单元格的适当点.

转载注明原文:解析 – 生成解析器,在另一个解析器的输出上运行接收的解析器,并单独连接结果 - 代码日志