2017-10-19 63 views
2

我使用(滥用)解析器进行一些字符串转换,例如normalizeWS :: Parser String删除重复的空格,并且normalizeCase将特定字符串映射为小写。我使用分析器,因为输入数据有一些结构,例如文字字符串必须保持不变。有没有一种优雅的方式来提供一个解析器的输出作为下一个输入,从而形成一个转换管道? normalizeWS . normalizeCase(这当然不起作用)的东西?parsec:将一个解析器的输出提供给另一个解析器

非常感谢提前!

+0

我不认为你可以用这种方式编写'Parser's,因为它们都会从底层流中读取。我认为你可能会更好地将每个定义为'String - > String',并且当你有一个'Parser String'时,你想规范化你可以'fmap(normalizeWS。normalizeCase)'。 – ryachza

回答

1

我用这个方法解决了这个问题...也许还有一个更优雅的方式

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 
+0

这不会构成好:'feed p1 p2 >> p3'不像'消耗p1'消耗的东西,将消耗的结果传递给'p2',然后解析剩余的未消耗的输入为'p3'会“ 。要想解决这个问题并不容易,不幸的是,当然,修复这种特殊情况的最明显的'getInput' /'setInput'体操只会让bug更加微妙,而不是在所有情况下实际工作。 –

0

如果你有功能类似于

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)