Tuesday, May 26, 2009

Solutions to Chapter 8 (p. 211)

#1. Contrary to the authors' claim, this one is pretty simple -- if you define a helper function. Here, the function prepend either prepends a string to a Right value, or propagates unchanged a Left value. We then use it to update all equations defining globToRegex' and charClass. The main function, globToRegex, is also updated to handle either case.

(Note: the update is applied to the original version of globToRegex, not to the modified versions created in previous exercises.)

module GlobRegex (globToRegex, matchesGlob) where

import Text.Regex.Posix ((=~))

type GlobError = String

globToRegex :: String -> Either GlobError String
globToRegex cs = case internal of
    Left error -> Left error
    Right val  -> Right ('^' : val ++ "$")
  where internal = globToRegex' cs

globToRegex' :: String -> Either GlobError String
globToRegex' "" = Right ""

globToRegex' ('*':cs) = prepend ".*" (globToRegex' cs)
globToRegex' ('?':cs) = prepend "." (globToRegex' cs)

globToRegex' ('[':'!':c:cs) = prepend ("[^" ++ [c]) (charClass cs)
globToRegex' ('[':c:cs) = prepend ['[', c] (charClass cs)
globToRegex' ('[':_) = Left "unterminated character class"

globToRegex' (c:cs) = prepend (escape c) (globToRegex' cs)

escape :: Char -> String
escape c | c `elem` regexChars = '\\' : [c]
         | otherwise = [c]
  where regexChars = "\\+()^$.{}]"

charClass :: String -> Either GlobError String
charClass (']':cs) = prepend "]" (globToRegex' cs)
charClass (c:cs) = prepend [c] (charClass cs)
charClass _ = Left "unterminated character class"

prepend :: String -> Either GlobError String -> Either GlobError String
prepend prefix (Left error) = Left error
prepend prefix (Right str)  = Right (prefix ++ str)

#2. namesMatching never uses globToRegex directly; it uses matchesGlob, which we update thus:

matchesGlob :: FilePath -> String -> Bool
f `matchesGlob` g = case (globToRegex g) of
    Right regex -> f =~ regex
    Left err    -> False

In other words, a bad glob expression simply matches nothing.

No comments:

Post a Comment