type ArrayIndex = Int
class (Typeable a) => ArrayClass a where
array_iType :: a -> Type
array_iType = const $ mkType "Array"
array_fetch :: a -> Eval VArray
array_fetch av = do
size <- array_fetchSize av
mapM (array_fetchVal av) [0..size-1]
array_store :: a -> VArray -> Eval ()
array_store av list = do
forM_ ([0..] `zip` list) $ \(idx, val) -> do
sv <- array_fetchElem av idx
writeIVar sv val
array_storeSize av (length list)
array_fetchKeys :: a -> Eval [ArrayIndex]
array_fetchKeys av = do
svList <- array_fetch av
return $ zipWith const [0..] svList
array_fetchElem :: a -> ArrayIndex -> Eval (IVar VScalar) -- autovivify
array_fetchElem av idx = do
return $ proxyScalar (array_fetchVal av idx) (array_storeVal av idx)
array_storeElem :: a -> ArrayIndex -> IVar VScalar -> Eval () -- binding
array_storeElem av idx sv = do
val <- readIVar sv
array_storeVal av idx val
array_fetchVal :: a -> ArrayIndex -> Eval Val
array_fetchVal av idx = do
rv <- array_existsElem av idx
if rv then readIVar =<< array_fetchElem av idx
else return undef
array_storeVal :: a -> ArrayIndex -> Val -> Eval ()
array_storeVal av idx val = do
sv <- array_fetchElem av idx
writeIVar sv val
array_fetchSize :: a -> Eval ArrayIndex
array_fetchSize av = do
vals <- array_fetch av
return $ length vals
array_storeSize :: a -> ArrayIndex -> Eval ()
array_storeSize av sz = do
size <- array_fetchSize av
case size `compare` sz of
GT -> mapM_ (const $ array_pop av) [size .. sz-1]
EQ -> return () -- no need to do anything
LT -> mapM_ (\idx -> array_storeElem av idx lazyUndef) [size .. sz-1]
array_extendSize :: a -> ArrayIndex -> Eval ()
array_extendSize _ 0 = return ()
array_extendSize av sz = do
size <- array_fetchSize av
when (size < sz) $ do
mapM_ (\idx -> array_storeElem av idx lazyUndef) [size .. sz-1]
array_deleteElem :: a -> ArrayIndex -> Eval ()
array_deleteElem av idx = do
size <- array_fetchSize av
let idx' = if idx < 0 then idx `mod` size else idx
case (size - 1) `compare` idx' of
GT -> return () -- no such index
EQ -> array_storeSize av (size - 1) -- truncate
LT -> array_storeElem av idx' lazyUndef -- set to undef
array_existsElem :: a -> ArrayIndex -> Eval VBool
array_existsElem av idx = do
size <- array_fetchSize av
return $ size > (if idx < 0 then idx `mod` size else idx)
array_clear :: a -> Eval ()
array_clear av = array_storeSize av 0
array_push :: a -> [Val] -> Eval ()
array_push av vals = do
size <- array_fetchSize av
forM_ ([size..] `zip` vals) $ \(idx, val) -> do
array_storeElem av idx (lazyScalar val)
array_pop :: a -> Eval Val
array_pop av = do
size <- array_fetchSize av
if size == 0
then return undef
else do
sv <- array_fetchElem av $ size - 1
array_storeSize av $ size - 1
readIVar sv
array_shift :: a -> Eval Val
array_shift av = do
vals <- array_splice av 0 1 []
return $ last (undef:vals)
array_unshift :: a -> [Val] -> Eval ()
array_unshift av vals = do
array_splice av 0 0 vals
return ()
array_splice :: a -> ArrayIndex -> ArrayIndex -> [Val] -> Eval [Val]
array_splice av off len vals = do
size <- array_fetchSize av
let off' = if off < 0 then off + size else off
len' = if len < 0 then len + size - off' else len
result <- mapM (array_fetchElem av) [off' .. off' + len' - 1]
let off = if off' > size then size else off'
len = if off + len' > size then size - off else len'
cnt = length vals
case cnt `compare` len of
GT -> do
-- Move items up to make room
let delta = cnt - len
array_extendSize av (size + delta)
(`mapM_` reverse [off + len .. size - 1]) $ \idx -> do
val <- array_fetchElem av idx
array_storeElem av (idx + delta) val
LT -> do
let delta = len - cnt
(`mapM_` [off + len .. size - 1]) $ \idx -> do
val <- array_fetchElem av idx
array_storeElem av (idx - delta) val
array_storeSize av (size - delta)
_ -> return ()
forM_ ([0..] `zip` vals) $ \(idx, val) -> do
array_storeElem av (off + idx) (lazyScalar val)
mapM readIVar result
instance ArrayClass IArraySlice where
array_iType = const $ mkType "Array::Slice"
array_store av vals = mapM_ (uncurry writeIVar) (zip av vals)
array_fetchSize = return . length
array_fetchElem av idx = getIndex idx Nothing (return av) Nothing
array_storeSize _ _ = return () -- XXX error?
array_storeElem _ _ _ = retConstError undef
instance ArrayClass IArray where
array_store av vals = do
let svList = map lazyScalar vals
liftSTM $ writeTVar av $ IntMap.fromAscList ([0..] `zip` svList)
array_fetchSize av = do
avMap <- liftSTM $ readTVar av
return $ IntMap.size avMap
array_storeSize av sz = do
liftSTM $ modifyTVar av $ \avMap ->
let size = IntMap.size avMap in
case size `compare` sz of
GT -> fst $ IntMap.split sz avMap
EQ -> avMap
LT -> IntMap.union avMap $
IntMap.fromAscList ([size .. sz-1] `zip` repeat lazyUndef)
array_shift av = do
svList <- liftSTM $ fmap IntMap.elems $ readTVar av
case svList of
(sv:rest) -> do
liftSTM $ writeTVar av $ IntMap.fromAscList ([0..] `zip` rest)
readIVar sv
_ -> return undef
array_unshift av vals = do
liftSTM $ modifyTVar av $ \avMap ->
let svList = IntMap.elems avMap in
IntMap.fromAscList ([0..] `zip` ((map lazyScalar vals) ++ svList))
array_pop av = do
avMap <- liftSTM $ readTVar av
if IntMap.null avMap
then return undef
else do
let idx = IntMap.size avMap - 1
liftSTM $ writeTVar av $ IntMap.delete idx avMap
readIVar . fromJust $ IntMap.lookup idx avMap
array_push av vals = do
liftSTM $ modifyTVar av $ \avMap ->
let svList = IntMap.elems avMap in
IntMap.fromAscList ([0..] `zip` (svList ++ (map lazyScalar vals)))
array_extendSize _ 0 = return ()
array_extendSize av sz = do
liftSTM $ modifyTVar av $ \avMap ->
let size = IntMap.size avMap in
case size `compare` sz of
GT -> avMap
EQ -> avMap
LT -> IntMap.union avMap $
IntMap.fromAscList ([size .. sz-1] `zip` repeat lazyUndef)
array_fetchVal av idx = do
readIVar =<< getMapIndex idx (Just $ constScalar undef)
(liftSTM $ readTVar av)
Nothing -- don't bother extending
array_fetchKeys av = do
avMap <- liftSTM $ readTVar av
return $ IntMap.keys avMap
array_fetchElem av idx = do
sv <- getMapIndex idx Nothing
(liftSTM $ readTVar av)
(Just (array_extendSize av $ idx+1))
if refType (MkRef sv) == mkType "Scalar::Lazy"
then do
val <- readIVar sv
sv' <- newScalar val
liftSTM . modifyTVar av $ \avMap ->
let idx' = idx `mod` IntMap.size avMap in
IntMap.adjust (const sv') idx' avMap
return sv'
else return sv
array_existsElem av idx | idx < 0 = array_existsElem av (abs idx - 1)
array_existsElem av idx = do
avMap <- liftSTM $ readTVar av
return $ IntMap.member idx avMap
array_deleteElem av idx = do
liftSTM . modifyTVar av $ \avMap ->
let size = IntMap.size avMap
idx' | idx < 0 = idx `mod` IntMap.size avMap -- XXX wrong; wraparound
| otherwise = idx in
case (size - 1) `compare` idx' of
LT -> avMap
EQ -> IntMap.delete idx' avMap
GT -> IntMap.adjust (const lazyUndef) idx' avMap
array_storeElem av idx sv = do
liftSTM . modifyTVar av $ \avMap ->
let size = IntMap.size avMap
idx' | idx < 0 = idx `mod` IntMap.size avMap -- XXX wrong; wraparound
| otherwise = idx in
if size > idx'
then IntMap.adjust (const sv) idx' avMap
else IntMap.union avMap $
IntMap.fromAscList ([size .. idx'] `zip` (sv:repeat lazyUndef))
instance ArrayClass VArray where
array_iType = const $ mkType "Array::Const"
array_store [] _ = return ()
array_store _ [] = return ()
array_store (VUndef:as) (_:vs) = array_store as vs
array_store (a:as) vals@(v:vs) = do
env <- ask
ref <- fromVal a
if isaType (envClasses env) "List" (refType ref)
then writeRef ref (VList vals)
else do
writeRef ref v
array_store as vs
array_fetch = return
array_fetchSize = return . length
array_fetchVal av idx = getIndex idx (Just undef) (return av) Nothing
array_fetchElem av idx = do
val <- array_fetchVal av idx
return $ constScalar val
array_storeVal _ _ _ = retConstError undef
array_storeElem _ _ _ = retConstError undef
instance ArrayClass (IVar VPair) where
array_iType = const $ mkType "Pair"
array_fetch pv = do
(k, v) <- readIVar pv
return [k, v]
array_existsElem _ idx = return (idx >= -2 || idx <= 1)
array_fetchSize = const $ return 2
array_fetchVal pv (-2) = return . fst =<< readIVar pv
array_fetchVal pv (-1) = return . snd =<< readIVar pv
array_fetchVal pv 0 = return . fst =<< readIVar pv
array_fetchVal pv 1 = return . snd =<< readIVar pv
array_fetchVal _ _ = return undef
array_storeVal a _ _ = retConstError $ VStr $ show a
array_storeElem a _ _ = retConstError $ VStr $ show a
array_deleteElem a _ = retConstError $ VStr $ show a
perl5EvalApply :: String -> [PerlSV] -> Eval Val
perl5EvalApply code args = do
env <- ask
rv <- liftIO $ do
envSV <- mkVal env
subSV <- evalPerl5 code envSV (enumCxt cxtItemAny)
invokePerl5 subSV nullSV args envSV (enumCxt cxtItemAny)
return $ case rv of
[sv] -> PerlSV sv
_ -> VList (map PerlSV rv)
instance ArrayClass PerlSV where
array_iType = const $ mkType "Array::Perl"
array_fetchVal sv idx = do
idxSV <- fromVal $ castV idx
perl5EvalApply "sub { $_[0]->[$_[1]] }" [sv, idxSV]
array_clear sv = do
perl5EvalApply "sub { undef @{$_[0]} }" [sv]
return ()
array_storeVal sv idx val = do
idxSV <- fromVal $ castV idx
valSV <- fromVal val
perl5EvalApply "sub { $_[0]->[$_[1]] = $_[2] }" [sv, idxSV, valSV]
return ()
array_deleteElem sv idx = do
idxSV <- fromVal $ castV idx
perl5EvalApply "sub { delete $_[0]->[$_[1]] }" [sv, idxSV]
return ()