Tom Phoenix wrote:
On 8/30/06, Chris Kuklewicz <[EMAIL PROTECTED]> wrote:

> -- Simulate "(a?|b+|c*)*d" regular expression

But 'go' seems to not terminate with the leading 'star'

Unless I'm missing something... The part of the pattern inside the
parentheses should successfully match at least the empty string at the
beginning of the string. Since it's regulated by the second (outer)
'star', it will keep matching as long as it keeps succeeding; since it
keeps matching the empty string, it keeps matching forever in the same
spot.

To solve this problem, your implementation of 'star' could perhaps be
changed to answer "no more matches" rather than "infinitely many
matches" once the body fails to consume any characters.

Hope this helps!

--Tom Phoenix

And that is indeed the solution. But then I wanted $ end-of-line anchors (easy) and ^ begin-of-line anchors (annoying). But it works now:

-- | Using ReadP to simulate regular expressions, finding the longest match
-- by Chris Kuklewicz, public domain
import Control.Monad
import Data.Set(Set,member)
import Data.Maybe(maybe)
import Text.ParserCombinators.ReadP

type R = Char -> ReadP (Int,Char)

dot :: R
dot _ = do x <- get
           return (1,x)

anyOf :: Set Char -> R
anyOf s _ = do
  x <- satisfy (`member` s)
  return (1,x)

noneOf :: Set Char -> R
noneOf s _ = do
  x <- satisfy (not.(`member` s))
  return (1,x)

c :: Char -> R
c x _ = char x >> return (1,x)

cs :: String -> R
cs [] prev = return (0,prev)
cs xs _ = string xs >> return (length xs,last xs)

atBOL prev =
  case prev of '\n' -> return (0,prev)
               _ -> pfail

atEOL prev = do
  rest <- look
  case rest of [] -> return (0,prev)
               ('\n':_) -> return (0,prev)
               _ -> pfail

-- Consume like x? x+ x* and return the length
quest,plus,star :: R -> R
quest x = x <|> (\prev -> return (0,prev))
plus  x = x +> star x
star  x prev = until0 0 prev
  where until0 t prev' = do
          (len,prev'') <- quest x prev'
          if (0==len)
            then return (t,prev'')
            else let tot = t + len
                 in seq tot (until0 tot prev'')

upToN :: Int -> R -> R
upToN n x = helper n
  where helper 0 prev t = return (t,prev)
        helper i prev t = do
          (len,prev') <- x prev
          if 0==len
            then return (t,prev')
            else helper (pred i) prev' $! t+len

ranged 0 Nothing x = star x
ranged 0 (Just n) x | n>0 = upToN n x
ranged m n x | (m>=0) && maybe True (\n'->n'>=m) n =
  doSeq (replicate m x) +> (ranged 0 (fmap (subtract m) n) x)
             | otherwise = (\prev -> return (0,prev))

infixr 6 +>
infixr 5 <|>
(+>),(<|>) :: R -> R -> R
(+>) x y = (\prev -> do
  (lenX,prev') <- x prev
  (lenY,prev'') <- y prev'
  let tot = lenX + lenY
  seq tot (return (tot,prev''))
 )

(<|>) x y = (\prev -> (x prev) +++ (y prev))

orSeq,doSeq :: [R] -> R
orSeq [] prev = return (0,prev)
orSeq xs prev = foldr1 (<|>) xs $ prev

doSeq [] prev = return (0,prev)
doSeq xs prev = foldr1 (+>) xs $ prev


-- Simulate "(^a|b+|c*|^.)*(d|_rest_)$" regular expression
test = star (orSeq [quest (c 'a')
                   ,plus (c 'b')
                   ,star (c 'c')
                   ,atBOL +> dot ]) +> doSeq [c 'd' <|> cs "_rest_",atEOL]

go foo = case readP_to_S (gather (test '\n')) foo of
           [] -> Nothing
           xs -> Just (last xs)



_______________________________________________
Haskell-Cafe mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to