深入扩展Parser库的方法

Mat*_*ner 3 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'
Run Code Online (Sandbox Code Playgroud)

假设csvFile在一个库中定义,另一个库是否可以使用自定义版本的cell解析器创建自己的CSV解析器,而不必重写linecsvFile解析器?可以重写源库以使其成为可能吗?这对于CSV解析器来说非常简单,但我对广泛适用的解决方案感兴趣.

J. *_*son 9

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

line cell = sepBy cell (char ',')
csvFile cell = endBy (line cell) eol
Run Code Online (Sandbox Code Playgroud)

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

data LanguageDefinition =
  LanguageDefinition { cell :: Parser Cell 
                     , ...
                     }
Run Code Online (Sandbox Code Playgroud)

然后我们通过它参数化整个解析器组合库 LanguageDefinition

data Parsers = Parsers { line :: Parser Line, csvFile :: Parser [Line], ... }

mkParsers :: LanguageDefinition -> Parsers
Run Code Online (Sandbox Code Playgroud)

这正是Parsec的通用Token解析模块采用的方法:参见Text.Parsec.TokenText.Parsec.Language.


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

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