{-# LANGUAGE OverloadedStrings #-} module Exercise04 where import Data.Text (count, dropAround, isInfixOf, isPrefixOf, null, pack, splitOn, stripEnd, tail, unpack) import Prelude hiding (null, tail) {-WETT-} xmlLight :: String -> Bool xmlLight str = null tag && pure "<" `notElem` scanl processTag [""] (tags ++ pure "/>") where tag : tags = splitOn "<" $ dropAround (`notElem` unpack "<>" {-unpack so the ambiguity between String and Text is resolved-} ) $ pack str -- Actually exhaustive, splitOn always produces at least one result (or error) processTag (lastOpenedTag : openedTags) currentTag -- "<" marks invalid tag. This can't occur in an actual tag since I split on it. | null currentTag || count ">" currentTag /= 1 = pure "<" -- Empty Tag (counted as invalid) | isPrefixOf "/" currentTag' && tail currentTag' == lastOpenedTag = openedTags -- Matched closing tag | isPrefixOf "/" currentTag' || isInfixOf " " currentTag' = pure "<" -- Non matched closing tag or new opening tag with space | otherwise = currentTag' : lastOpenedTag : openedTags -- New opening tag where currentTag' = stripEnd $ head $ splitOn ">" currentTag {-TTEW-}