深入扩展Parser库的方法

时间:2014-01-18 03:47:32

标签: parsing haskell functional-programming parser-combinators

我发现的解析器组合器上的材料涵盖了通过组合构建复杂的解析器,但我想知道是否有任何好的方法来通过调整库的组合解析器来定义解析器而不完全复制原始库的逻辑。

例如,这是Real world Haskell

中定义的简化CSV解析器
import Text.ParserCombinators.Parsec

csvFile = endBy line eol
line = sepBy cell (char ',')
cell = many (noneOf ",\n")
eol = char '\n'

假设在一个库中定义了csvFile,另一个库可以使用自定义版本的cell解析器创建自己的CSV解析器,而无需重写line和{{1}解析器也是?可以重写源库以使其成为可能吗?这对于CSV解析器来说非常简单,但我对广泛适用的解决方案感兴趣。

1 个答案:

答案 0 :(得分:9)

通常,您需要抽象出要替换的组件的签名。例如,在CSV示例中,我们需要扩展csvFile的类型,以允许我们插入自定义cell

line cell = sepBy cell (char ',')
csvFile cell = endBy (line cell) eol

显然这很快变得笨拙。我们可以将所需的所有扩展点打包到字典中,然后将其传递给

data LanguageDefinition =
  LanguageDefinition { cell :: Parser Cell 
                     , ...
                     }

然后我们通过LanguageDefinition

参数化整个解析器组合库
data Parsers = Parsers { line :: Parser Line, csvFile :: Parser [Line], ... }

mkParsers :: LanguageDefinition -> Parsers

这正是Parsec的通用令牌解析模块所采用的方法:请参阅Text.Parsec.TokenText.Parsec.Language


可以采用更通用的方法,将越来越多的东西抽象到字典中。实际上,这成为面向对象或OCaml模块的组织代码的方法,并且非常有效。

民间表达问题表明在引入更多功能和引入更多变体之间存在紧张关系。在这种情况下,您需要一个新的变体,因此您需要修复功能(并将其全部列在字典中)。这将开辟一条引入新变种的道路。

相关问题