hiccup-0.40: VarName.hs
{-# LANGUAGE BangPatterns,OverloadedStrings #-}
module VarName (parseVarName,
nsTail,
nsQualifiers,
parseNSTag,
parseProc,
VarName(..),
arrName,
isArr,
showVN,
NSQual(..),
NSTag(..),
asGlobal,
isGlobalQual,
noNsQual,
splitWith,
nsSep,
varNameTests) where
import Util
import qualified Data.ByteString.Char8 as B
import Data.ByteString (findSubstrings)
import Test.HUnit
data NSQual a = NSQual !(Maybe NSTag) !a deriving (Eq,Show)
data NSTag = NS !Bool ![BString] deriving (Eq,Show,Ord)
data VarName = VarName { vnName :: !BString, vnInd :: Maybe BString } deriving (Eq,Show)
class BStringable a where
toBStr :: a -> BString
instance BStringable NSTag where
toBStr (NS g lst) = B.append (if g then nsSep else B.empty) (B.intercalate nsSep lst)
arrName !an !ind = VarName an (Just ind)
{-# INLINE arrName #-}
isArr (VarName _ (Just _)) = True
isArr _ = False
nsSep :: BString
nsSep = "::"
isGlobalQual (Just (NS v _)) = v
isGlobalQual _ = False
{-# INLINE isGlobalQual #-}
noNsQual nst = case nst of
Nothing -> True
(Just (NS False [])) -> True
_ -> False
{-# INLINE noNsQual #-}
asGlobal (Just (NS _ lst)) = Just (NS True lst)
asGlobal Nothing = Just (NS True [])
parseNSQual ns = case parseNSTag ns of
Nothing -> NSQual Nothing ""
Just (NS False [s]) -> NSQual Nothing s
Just (NS gq []) -> NSQual (Just (NS gq [])) ""
Just (NS gq nsl) -> NSQual (Just (NS gq (init nsl))) (last nsl)
parseNSTag ns = toNSTag (ns `splitWith` nsSep) where
toNSTag nl = if isAbs then return (NS True (tail nl))
else return (NS False nl)
isAbs = nsSep == B.take 2 ns
parseVarName name =
case parseArrRef name of
(str,ind) -> case parseNSQual str of
NSQual nst n -> NSQual nst (VarName n ind)
parseProc :: BString -> NSQual BString
parseProc = parseNSQual
showVN :: VarName -> String
showVN (VarName name Nothing) = show name
showVN (VarName name (Just i)) = "\"" ++ unpack name ++ "(" ++ unpack i ++ ")\""
parseArrRef str = case B.elemIndex '(' str of
Nothing -> (str, Nothing)
Just start -> if (start /= 0) && B.last str == ')'
then let (pre,post) = B.splitAt start str
in (pre, Just (B.tail (B.init post)))
else (str, Nothing)
nsTail (NS _ []) = error "Malformed NSTag"
nsTail (NS _ nsl) = last nsl
nsTail_ x = case parseNSTag x of
Nothing -> error "FAIL"
Just v -> nsTail v
nsQualifiers str = case findSubstrings nsSep str of
[] -> B.empty
lst -> B.take (last lst) str
splitWith :: BString -> BString -> [BString]
splitWith str sep =
case findSubstrings sep str of
[] -> [str]
il -> extract il str
where slen = B.length sep
extract [] !s = [s]
extract (i:ix) !s = let (b,a) = B.splitAt i s
in b : extract (map (\v -> v - (i+slen)) ix) (B.drop slen a)
{-# INLINE splitWith #-}
varNameTests = TestList [splitWithTests, testArr, testParseVarName, testParseNS, testNSTail,
testNsQuals,testInvert, testParseNSQual,
testParseProc] where
bp = pack
splitWithTests = TestList [
("one::two","::") `splitsTo` ["one","two"]
,("::x","::") `splitsTo` ["","x"]
,("wonderdragon","::") `splitsTo` ["wonderdragon"]
,("","::") `splitsTo` [""]
,("::","::") `splitsTo` ["", ""]
]
where splitsTo (a,b) r = map bp r ~=? ((bp a) `splitWith` (bp b))
testParseNSQual = TestList [
"boo" `parses_to` (Nothing, "boo")
,"::boo" `parses_to` (mkns True [], "boo")
,"::boo::flag" `parses_to` (mkns True ["boo"], "flag")
,"foo::bar" `parses_to` (mkns False ["foo"], "bar")
]
where parses_to a (b1,b2) = ("parseNSQual " ++ show a) ~: NSQual b1 (bp b2) ~=? parseNSQual a
mkns g v = Just (NS g v)
testParseNS = TestList [
"boo" `parses_to` (NS False ["boo"])
,"::boo" `parses_to` (NS True ["boo"])
,"::" `parses_to` (NS True [""])
,"foo::boo" `parses_to` NS False ["foo", "boo"]
,"::foo::boo" `parses_to` NS True ["foo", "boo"]
,"woo::foo::boo" `parses_to` NS False ["woo", "foo", "boo"]
]
where parses_to a nst = ("parseNSTag " ++ show a) ~: (Just nst) ~=? parseNSTag a
testParseVarName = TestList [
parseVarName "x" ~=? NSQual Nothing (VarName "x" Nothing)
,parseVarName "x(a)" ~=? NSQual Nothing (VarName "x" (Just "a"))
,parseVarName "boo::x(::)" ~=? NSQual (Just (NS False ["boo"])) (VarName "x" (Just "::"))
,parseVarName "::x" ~=? NSQual (Just (NS True [])) (VarName "x" Nothing)
]
testNSTail = TestList [
nsTail_ "boo" ~=? "boo"
,nsTail_ (bp "::boo") ~=? (bp "boo")
,nsTail_ (bp "baby::boo") ~=? (bp "boo")
,nsTail_ (bp "::baby::boo") ~=? (bp "boo")
,nsTail_ (bp "::") ~=? (bp "")
]
testNsQuals = TestList [
"boo" `should_be` ""
,"::" `should_be` ""
,"a::b" `should_be` "a"
,"a::b::c" `should_be` "a::b"
,"::a::b::c" `should_be` "::a::b"
]
where should_be b a = nsQualifiers (bp b) ~=? (bp a)
testParseProc = TestList [
"boo" `parses_to` (NSQual Nothing "boo")
,"::boo" `parses_to` (NSQual (Just (NS True [])) "boo")
,"::some::boo" `parses_to` (NSQual (Just (NS True ["some"])) "boo")
]
where parses_to a b = b ~=? parseProc (bp a)
testInvert = TestList (map tryInvert vals)
where vals = map bp ["boo", "::boo", "::", "::a::b", "a::b", ""]
tryInvert v = v ~=? toBStr (up (parseNSTag v))
up (Just x) = x
up _ = error "Error in testInvert"
testArr = TestList [
"december" `should_be` Nothing
,"dec(mber" `should_be` Nothing
,"dec)mber" `should_be` Nothing
,"(cujo)" `should_be` Nothing
,"de(c)mber" `should_be` Nothing
,"a(1)" ?=> ("a","1")
,"boo(4)" ?=> ("boo","4")
,"xx(september)" ?=> ("xx","september")
,"arr(3,4,5)" ?=> ("arr","3,4,5")
,"arr()" ?=> ("arr","")
]
where (?=>) a b@(b1,b2) = (a ++ " -> " ++ show b) ~: parseArrRef (bp a) ~=? ((bp b1), Just (bp b2))
should_be x _ = (x ++ " should be " ++ show (bp x)) ~: parseArrRef (bp x) ~=? (bp x, Nothing)