parsec:将一个解析器的输出提供给另一个解析器
我使用(滥用)解析器进行一些字符串转换,例如normalizeWS :: Parser String
删除重复的空格,并且normalizeCase
将特定字符串映射为小写。我使用分析器,因为输入数据有一些结构,例如文字字符串必须保持不变。有没有一种优雅的方式来提供一个解析器的输出作为下一个输入,从而形成一个转换管道? normalizeWS . normalizeCase
(这当然不起作用)的东西?parsec:将一个解析器的输出提供给另一个解析器
非常感谢提前!
我用这个方法解决了这个问题...也许还有一个更优雅的方式
preprocessor :: Parser String
preprocessor = normalizeCase `feeds` expandKettensatz `feeds` normalizeWs
feeds :: Parser String -> Parser String -> Parser String
feeds p1 p2 = do
s <- p1
setInput s
p2
这不会构成好:'feed p1 p2 >> p3'不像'消耗p1'消耗的东西,将消耗的结果传递给'p2',然后解析剩余的未消耗的输入为'p3'会“ 。要想解决这个问题并不容易,不幸的是,当然,修复这种特殊情况的最明显的'getInput' /'setInput'体操只会让bug更加微妙,而不是在所有情况下实际工作。 –
如果你有功能类似于
normalizeWhitespace :: Stream s m Char => ParsecT s u m String
normalizeCase :: Stream s m Char => Set String -> Parsec s u m String
你可以使用runParser
和>>=
把它们结合在一起:
runBoth :: Stream s Identity Char => Set String -> SourceName -> s -> Either ParseError String
runBoth wordSet src input = do
input <- runParser normalizeWhitespace() src input
runParser (normalizeCase wordSet)() src input
但是这不会给你一个解析器,你可以和其他解析器一起链接。
这并不令人惊讶,因为Parsec中的解析器组成都是关于构成在相同流上操作的解析器的 ,而这些解析器在 上操作不同的流。
拥有多个不同的流也很常见 - 使用a tokenization or lexing pass as input to parsing的输出可以使得该流程更容易理解 ,但Parsec is a little easier to use out of the box as a direct parser (without lexing/tokenization)。
我不认为你可以用这种方式编写'Parser's,因为它们都会从底层流中读取。我认为你可能会更好地将每个定义为'String - > String',并且当你有一个'Parser String'时,你想规范化你可以'fmap(normalizeWS。normalizeCase)'。 – ryachza