Haskell Text Parser组合器像正则表达式范围符号一样贪婪地解析范围

时间:2017-04-07 13:10:04

标签: regex parsing haskell

在正则表达式中,您可以通过执行\d{1,5}之类的操作来获取一系列解析,这些数据会贪婪地解析数字1至5次。或者你\d{1,5}?让它变得懒惰。

你如何在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 个答案:

答案 0 :(得分:1)

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

======

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

定义一个返回Maybes的左侧偏向版option

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

然后,我们可以使用replicateM来完成n的某些事情:

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"