packages feed

hiccup-0.40: TclLib/StringProcs.hs

module TclLib.StringProcs (stringProcs, stringTests) where

import Common
import Util
import qualified Data.ByteString.Char8 as B
import qualified TclObj as T
import TclObj ((.==))
import Data.Char (toLower,toUpper)

import Test.HUnit

stringProcs = makeCmdMap [("string", procString), ("append", procAppend), ("split", procSplit)]

procString = makeEnsemble "string" [
   ("trim", string_Op "trim" T.trim), 
   ("tolower", string_Op "tolower" (B.map toLower)),
   ("toupper", string_Op "toupper" (B.map toUpper)),
   ("reverse", string_Op "reverse" B.reverse),
   ("length", string_length),
   ("match", string_match), ("compare", string_compare),
   ("index", string_index)
 ]

string_Op name op args = case args of
   [s] -> treturn $! op (T.asBStr s)
   _   -> argErr $ "string " ++ name

string_length args = case args of
    [s] -> return $ T.mkTclInt (B.length (T.asBStr s))
    _   -> argErr "string length"

string_compare args = case args of
    [s1,s2] -> case compare (T.asBStr s1) (T.asBStr s2) of
                  LT -> return (T.mkTclInt (-1))
                  GT -> return (T.mkTclInt 1)
                  EQ -> return (T.mkTclInt 0)
    _       -> argErr "string compare"

string_match args = case map T.asBStr args of
   [s1,s2]        -> domatch False s1 s2
   [nocase,s1,s2] -> if nocase == pack "-nocase" then domatch True s1 s2 else argErr "string"
   _              -> argErr "string match"
 where domatch nocase a b = return (T.fromBool (match nocase a b))

string_index args = case args of
                     [s,i] -> do let str = T.asBStr s
                                 ind <- toInd str i
                                 if ind >= (B.length str) || ind < 0 
                                  then ret 
                                  else treturn $ B.take 1 (B.drop ind str)
                     _   -> argErr "string index"
 where toInd s i = (T.asInt i) `orElse` tryEnd s i
       tryEnd s i = if i .== "end" 
                       then return ((B.length s) - 1) 
                       else do let (ip,is) = B.splitAt (length "end-") (T.asBStr i)
                               if ip == pack "end-"
                                  then case B.readInt is of
                                            Just (iv,_) -> return ((B.length s) - (1+iv))
                                            _           -> tclErr "bad index"
                                  else tclErr "bad index"

procAppend args = case args of
            (v:vx) -> do val <- varGet (T.asBStr v) `ifFails` T.empty
                         let cated = oconcat (val:vx)
                         varSet (T.asBStr v) cated
            _  -> argErr "append"
 where oconcat = T.mkTclBStr . B.concat . map T.asBStr

procSplit args = case args of
        [str]       -> dosplit (T.asBStr str)  (pack "\t\n ")
        [str,chars] -> let splitChars = T.asBStr chars 
                       in if B.null splitChars then return $ (T.mkTclList . map (T.mkTclBStr . B.singleton) . unpack) (T.asBStr str)
                                               else dosplit (T.asBStr str) splitChars
        _           -> argErr "split"

 where dosplit str chars = return $ T.mkTclList (map T.mkTclBStr (B.splitWith (\v -> v `B.elem` chars) str))

stringTests = TestList [ matchTests ]
matchTests = TestList [
    "boo" `matches` "boo" 
    ,"" `matches` "" 
    ,"1" `matches` "1" 
    ,"a?c" `matches` "abc" 
    ,"a?c" `doesnt_match` "ab" 
    ,"a??d" `matches` "abcd" 
    ,"f??d" `matches` "feed" 
    ,"b??n" `matches` "been"
    ,"a" `doesnt_match` "ab" 
    ,"ab" `doesnt_match` "a" 
    ,"a*" `matches` "abcde" 
    ,"a*b" `matches` "ab" 
    ,"a*b" `matches` "a OMG b" 
    ,"*b" `matches` "in the land of bob" 
    ,"*" `matches` "in the land of bob" 
    ,"*" `matches` ""
    ,"s\\*r" `matches` "s*r"
  ]
 where matches a b = (show a) ++ " matches " ++ (show b) ~: True ~=? match False (B.pack a) (B.pack b) 
       doesnt_match a b = (show a) ++ " doesn't match " ++ (show b) ~: False ~=? match False (B.pack a) (B.pack b)