-- File created: 2009-08-06 20:47:49 module Haschoo.Evaluator.Standard.Vectors (procedures) where import Control.Applicative ((<$>)) import Data.Array (Array) import Data.Array.IArray ((!), bounds, elems) import Data.Array.IO (IOArray) import Data.Array.MArray ( readArray, writeArray , newArray, newListArray , getBounds, getElems) import Data.Complex (Complex((:+))) import Haschoo.Types (ScmValue(..), listToPair, pairToList) import Haschoo.Utils (ErrOr) import Haschoo.Evaluator.Utils ( tooFewArgs, tooManyArgs, immutable , notInt, notList) procedures :: [(String, ScmValue)] procedures = map (\(a,b) -> (a, ScmFunc a b)) [ ("vector?", return . fmap ScmBool . scmIsVector) , ("make-vector", scmMakeVector) , ("vector", scmVector) , ("vector-length", scmLength) , ("vector-ref", scmRef) , ("vector-set!", scmSet) , ("vector->list", scmToList) , ("list->vector", scmFromList) , ("vector-fill!", scmFill) ] scmIsVector :: [ScmValue] -> ErrOr Bool scmIsVector [ScmMVector _] = Right True scmIsVector [ScmVector _] = Right True scmIsVector [_] = Right False scmIsVector [] = tooFewArgs "vector?" scmIsVector _ = tooManyArgs "vector?" scmMakeVector, scmVector :: [ScmValue] -> IO (ErrOr ScmValue) scmMakeVector (ScmInt l : xs) = case xs of [] -> f (flip newArray (ScmComplex $ 0 :+ 42)) [x] -> f (flip newArray x) _ -> return$ tooManyArgs "make-vector" where f mk = tryToLen "make-vector" l $ \i -> ScmMVector <$> mk (0, i-1) scmMakeVector (_:_:_:_) = return$ tooManyArgs "make-vector" scmMakeVector [] = return$ tooFewArgs "make-vector" scmMakeVector _ = return$ notInt "make-vector" scmVector xs = Right <$> toScmMVector xs scmLength :: [ScmValue] -> IO (ErrOr ScmValue) scmLength [x] = case x of ScmVector s -> Right . ScmInt . toInteger <$> vLen s ScmMVector s -> Right . ScmInt . toInteger <$> mvLen s _ -> return$ notVector "vector-length" scmLength [] = return$ tooFewArgs "vector-length" scmLength _ = return$ tooManyArgs "vector-length" scmRef, scmSet :: [ScmValue] -> IO (ErrOr ScmValue) scmRef [x, ScmInt i] = case x of ScmVector s -> f (vLen s) (return . (s!)) ScmMVector s -> f (mvLen s) (readArray s) _ -> return$ notVector "vector-ref" where f len = tryToIdx "vector-ref" len i scmRef [_,_] = return$ notInt "vector-ref" scmRef (_:_:_) = return$ tooManyArgs "vector-ref" scmRef _ = return$ tooFewArgs "vector-ref" scmSet [v, ScmInt idx, x] = case v of ScmMVector v' -> do tryToIdx "vector-set!" (mvLen v') idx (\i -> writeArray v' i x) return (Right ScmVoid) ScmVector _ -> return$ immutable "vector-set!" _ -> return$ notVector "vector-set!" scmSet [_, _, ScmChar _] = return$ notInt "vector-set!" scmSet (_:_:_:_) = return$ tooManyArgs "vector-set!" scmSet _ = return$ tooFewArgs "vector-set!" scmToList, scmFromList :: [ScmValue] -> IO (ErrOr ScmValue) scmToList [x] = case x of ScmVector v -> f (elems v) ScmMVector v -> f =<< getElems v _ -> return$ notVector "vector->list" where f = fmap (Right . fst) . listToPair scmToList [] = return$ tooFewArgs "vector->list" scmToList _ = return$ tooManyArgs "vector->list" scmFromList [x] = case x of ScmList l -> Right <$> toScmMVector l p@(ScmPair _ _) -> do l <- pairToList p case l of Left _ -> return$ notList "list->vector" Right (ScmList l') -> Right <$> toScmMVector l' _ -> error "list->vector :: the impossible happened" _ -> return$ notList "list->vector" scmFromList [] = return$ tooFewArgs "list->vector" scmFromList _ = return$ tooManyArgs "list->vector" scmFill :: [ScmValue] -> IO (ErrOr ScmValue) scmFill [v, x] = case v of ScmMVector v' -> do (a,b) <- getBounds v' mapM_ (\i -> writeArray v' i x) [a..b] return (Right ScmVoid) ScmVector _ -> return$ immutable "vector-fill!" _ -> return$ notVector "vector-fill!" scmFill (_:_:_) = return$ tooManyArgs "vector-fill!" scmFill _ = return$ tooFewArgs "vector-fill!" ------ notVector :: String -> ErrOr a notVector = fail . ("Nonvector argument to " ++) toScmMVector :: [ScmValue] -> IO ScmValue toScmMVector v = ScmMVector <$> newListArray (0, length v - 1) v tryToLen :: String -> Integer -> (Int -> IO a) -> IO (ErrOr a) tryToLen = tryRange 0 (toInteger (maxBound :: Int)) tryToIdx :: String -> IO Int -> Integer -> (Int -> IO a) -> IO (ErrOr a) tryToIdx s mlen i f = mlen >>= \len -> tryRange 0 (toInteger len - 1) s i f tryRange :: Integer -> Integer -> String -> Integer -> (Int -> IO a) -> IO (ErrOr a) tryRange a b s i f = if i >= a && i <= b then Right <$> f (fromInteger i) else return.Left $ "Out-of-range integer to " ++ s vLen :: Array Int ScmValue -> IO Int vLen = return . (+1) . snd . bounds mvLen :: IOArray Int ScmValue -> IO Int mvLen = fmap ((+1) . snd) . getBounds