2017-04-07 80 views
2

在正则表达式中,您可以通过执行类似\d{1,5}这样的操作来获取一个解析范围,该解析器可以贪婪地解析数字1到5次。或者你做\d{1,5}?使它懒惰。Haskell文本解析器组合器解析范围贪婪像正则表达式范围表示法

你如何在Haskell的Text.ParserCombinators.ReadP中做到这一点?

我尝试了这一点:

rangeParse :: Read a => ReadP a -> [Int] -> ReadP [a] 
rangeParse parse ranges = foldr1 (<++) $ fmap (\n -> count n $ parse) ranges 

哪,如果你不喜欢它rangeParse (satisfy isDigit) ([5,4..1])将执行的数字贪婪解析1至5倍。而如果你将数字序列换成[1..5],你会得到一个懒惰的解析。

是否有更好或更习惯的方法来解析器组合?

回答

1

更新:下面是错误的 - 例如 rangeGreedy 2 4 a <* string "aab",正则表达式a{2,4}aab相当于不匹配。提问者的解决方案正确。我不会删除答案,以防止其他人犯同样的错误。

=========

这不是一个完整的答案,只是写的贪婪 版本一个可行的办法。我还没有找到一个很好的方式来做懒惰的版本。

定义的option返回Maybes左偏版本:

greedyOption :: ReadP a -> ReadP (Maybe a) 
greedyOption p = (Just <$> p) <++ pure Nothing 

然后,我们可以做的最多的事情n,其中他们的replicateM

upToGreedy :: Int -> ReadP a -> ReadP [a] 
upToGreedy n p = catMaybes <$> replicateM n (greedyOption p) 

要允许最小计数,分别做强制性部分并附加 它:

rangeGreedy :: Int -> Int -> ReadP a -> ReadP [a] 
rangeGreedy lo hi p = (++) <$> count lo p <*> upToGreedy (hi - lo) p 

其余的我的测试代码,以防万一它对任何人都有用:

module Main where 

import Control.Monad (replicateM) 
import Data.Maybe (catMaybes) 
import Text.ParserCombinators.ReadP 

main :: IO() 
main = mapM_ go ["aaaaa", "aaaab", "aaabb", "aabbb", "abbbb", "bbbbb"] 
    where 
    go = print . map fst . readP_to_S test 

test :: ReadP [String] 
test = ((++) <$> rangeGreedy 2 4 a <*> many aOrB) <* eof 
    where 
    a = char 'a' *> pure "ay" 
    aOrB = (char 'a' +++ char 'b') *> pure "ayorbee"