-- http://www.codewars.com/kata/5470c635304c127cad000f0d
{-# LANGUAGE OverloadedStrings #-}
module RegExpParser
       ( RegExp(..)
       , parseRegExp
       ) where

import Data.Attoparsec.ByteString.Char8
import qualified Data.ByteString.Char8 as B
import Control.Applicative

data RegExp = Normal Char       -- ^ A single regular character
            | Any               -- ^ Any charater
            | ZeroOrMore RegExp -- ^ Zero or more occurances of the same regexp
            | Or RegExp RegExp  -- ^ A choice between 2 regexps
            | Str [RegExp]      -- ^ A sequence of regexps.
  deriving (Show, Eq)

parseRegExp :: String -> Maybe RegExp
parseRegExp = either (const Nothing) Just . parseOnly (pRegExp <* endOfInput) . B.pack where
  pRegExp     = pOr <|> pRegExp1
  pOr         = Or <$> pRegExp1 <* char '|' <*> pRegExp
  pRegExp1    = wrapStr <$> many1 (pZeroOrMore <|> pChar <|> pAny <|> pBracket)
  pBracket    = char '(' *> pRegExp <* char ')'
  pZeroOrMore = ZeroOrMore <$> (pChar <|> pAny <|> char '(' *> pRegExp <* char ')') <* char '*'
  pChar       = Normal <$> satisfy (`B.notElem` ".*|()")
  pAny        = const Any <$> char '.'

wrapStr :: [RegExp] -> RegExp
wrapStr [x] = x
wrapStr xs = Str xs

