atomo-0.4: src/Atomo/Kernel/List.hs
{-# LANGUAGE QuasiQuotes #-}
module Atomo.Kernel.List (load) where
import Data.List (isPrefixOf)
import qualified Data.Vector as V
import Atomo
load :: VM ()
load = do
eval [$e|operator right .|]
[$p|List new: (... contents)|] =::: [$e|contents|]
[$p|(l: List) length|] =:
liftM (Integer . fromIntegral . V.length) (getVector [$e|l|])
[$p|(l: List) empty?|] =:
liftM (Boolean . V.null) (getVector [$e|l|])
[$p|(l: List) at: (n: Integer)|] =: do
Integer n <- here "n" >>= findInteger
vs <- getVector [$e|l|]
if fromIntegral n >= V.length vs
then here "l" >>= \l -> raise
["out-of-bounds", "for-list"]
[ Integer n
, l
]
else return (vs `V.unsafeIndex` fromIntegral n)
[$p|[] head|] =::: [$e|error: @empty-list|]
[$p|(l: List) head|] =:
liftM V.unsafeHead (getVector [$e|l|])
[$p|[] last|] =::: [$e|error: @empty-list|]
[$p|(l: List) last|] =:
liftM V.unsafeLast (getVector [$e|l|])
-- TODO: handle negative ranges
[$p|(l: List) from: (s: Integer) to: (e: Integer)|] =:::
[$e|l from: s take: (e - s)|]
[$p|(l: List) from: (s: Integer) take: (n: Integer)|] =: do
vs <- getVector [$e|l|]
Integer start <- here "s" >>= findInteger
Integer num <- here "n" >>= findInteger
if start < 0 || num < 0 || (start + num) > fromIntegral (V.length vs)
then here "l" >>= \l -> raise
["invalid-slice", "for-list"]
[ keyParticleN ["from", "take"] [Integer start, Integer num]
, l
]
else return . List $ V.unsafeSlice
(fromIntegral start)
(fromIntegral num)
vs
[$p|[] init|] =::: [$e|error: @empty-list|]
[$p|(l: List) init|] =:
liftM (List . V.unsafeInit) (getVector [$e|l|])
[$p|[] tail|] =::: [$e|error: @empty-list|]
[$p|(l: List) tail|] =:
liftM (List . V.unsafeTail) (getVector [$e|l|])
[$p|(l: List) take: (n: Integer)|] =: do
vs <- getVector [$e|l|]
Integer n <- here "n" >>= findInteger
return . List $ V.take (fromIntegral n) vs
[$p|(l: List) drop: (n: Integer)|] =: do
vs <- getVector [$e|l|]
Integer n <- here "n" >>= findInteger
return . List $ V.drop (fromIntegral n) vs
[$p|v replicate: (n: Integer)|] =: do
v <- here "v"
Integer n <- here "n" >>= findInteger
return . List $ V.replicate (fromIntegral n) v
[$p|(a: List) .. (b: List)|] =: do
as <- getVector [$e|a|]
bs <- getVector [$e|b|]
return . List $ as V.++ bs
[$p|(l: List) reverse|] =:
liftM (List . V.reverse) (getVector [$e|l|])
[$p|(l: List) map: b|] =: do
vs <- getVector [$e|l|]
b <- here "b"
nvs <- V.mapM (\v ->
dispatch (keyword ["call"] [b, v])) vs
return $ List nvs
[$p|(x: List) zip: (... ys) &zipper: @id|] =: do
xs <- getVector [$e|x|]
yss <- getList [$e|ys|] >>= mapM (liftM (\(List v) -> v) . findList)
z <- here "zipper"
let zipped = zipN (xs:yss)
if z == particle "id"
then return (list (map list zipped))
else liftM list $ forM zipped $ \vs ->
dispatch (keyword ["call"] [z, tuple vs])
[$p|(l: List) filter: b|] =: do
vs <- getVector [$e|l|]
b <- here "b"
nvs <- V.filterM (\v -> do
Boolean t <- dispatch (keyword ["call"] [b, v]) >>= findBoolean
return t) vs
return $ List nvs
[$p|[] reduce: b|] =::: [$e|error: @empty-list|]
[$p|(l: List) reduce: b|] =: do
vs <- getVector [$e|l|]
b <- here "b"
V.fold1M (\x acc ->
dispatch (keyword ["call"] [b, tuple [x, acc]])) vs
[$p|(l: List) reduce: b with: v|] =: do
vs <- getVector [$e|l|]
b <- here "b"
v <- here "v"
V.foldM (\x acc ->
dispatch (keyword ["call"] [b, tuple [x, acc]])) v vs
[$p|[] reduce-right: b|] =::: [$e|error: @empty-list|]
[$p|(l: List) reduce-right: b|] =: do
vs <- getVector [$e|l|]
b <- here "b"
foldr1MV (\x acc ->
dispatch (keyword ["call"] [b, tuple [x, acc]])) vs
[$p|(l: List) reduce-right: b with: v|] =: do
vs <- getVector [$e|l|]
b <- here "b"
v <- here "v"
foldrMV (\x acc ->
dispatch (keyword ["call"] [b, tuple [x, acc]])) v vs
[$p|(l: List) concat|] =::: [$e|l reduce: @.. with: []|]
[$p|(l: List) sum|] =::: [$e|l reduce: @+ with: 0|]
[$p|(l: List) product|] =::: [$e|l reduce: @* with: 1|]
[$p|(l: List) maximum|] =::: [$e|l reduce: @max:|]
[$p|(l: List) minimum|] =::: [$e|l reduce: @min:|]
[$p|(l: List) all?: b|] =: do
vs <- getVector [$e|l|]
b <- here "b"
nvs <- V.mapM (\v -> do
Boolean t <- dispatch (keyword ["call"] [b, v]) >>= findBoolean
return t) vs
return $ Boolean (V.and nvs)
[$p|(l: List) any?: b|] =: do
vs <- getVector [$e|l|]
b <- here "b"
nvs <- V.mapM (\v -> do
Boolean t <- dispatch (keyword ["call"] [b, v]) >>= findBoolean
return t) vs
return $ Boolean (V.or nvs)
[$p|(l: List) and|] =::: [$e|l all?: @(== True)|]
[$p|(l: List) or|] =::: [$e|l any?: @(== True)|]
[$p|(l: List) take-while: test|] =: do
t <- here "test"
l <- getList [$e|l|]
let takeWhileM [] = return []
takeWhileM (x:xs) =
ifVM (dispatch (keyword ["call"] [t, x]))
(liftM (x:) (takeWhileM xs))
(return [])
liftM list $ takeWhileM l
[$p|(l: List) drop-while: test|] =: do
t <- here "test"
l <- getList [$e|l|]
let dropWhileM [] = return []
dropWhileM (x:xs) =
ifVM (dispatch (keyword ["call"] [t, x]))
(dropWhileM xs)
(return (x:xs))
liftM list $ dropWhileM l
[$p|v in?: (l: List)|] =::: [$e|l contains?: v|]
[$p|(l: List) contains?: v|] =::: [$e|l any?: @(== v)|]
-- TODO: find
[$p|(l: List) at: (n: Integer) put: v|] =: do
vs <- getVector [$e|l|]
Integer n <- here "n" >>= findInteger
v <- here "v"
if fromIntegral n >= V.length vs
then here "l" >>= \l' -> raise
["out-of-bounds", "for-list"]
[ Integer n
, l'
]
else return (List $ vs V.// [(fromIntegral n, v)])
[$p|v . (l: List)|] =: do
vs <- getVector [$e|l|]
v <- here "v"
return (List $ V.cons v vs)
[$p|(l: List) push: v|] =: do
vs <- getVector [$e|l|]
v <- here "v"
return . List $ V.snoc vs v
[$p|(l: List) cons: v|] =: do
vs <- getVector [$e|l|]
v <- here "v"
return . List $ V.cons v vs
[$p|(l: List) split: (d: List)|] =: do
l <- getList [$e|l|]
d <- getList [$e|d|]
return $ list (map list (splitOn d l))
[$p|(l: List) split-on: d|] =: do
l <- getList [$e|l|]
d <- here "d"
return $ list (map list (splitWhen (== d) l))
[$p|(l: List) sort &comparison: @<=>|] =: do
cmp <- here "comparison"
getList [$e|l|] >>= liftM list . sortVM cmp
[$p|(l: List) sort-by: something &comparison: @<=>|] =: do
vs <- getList [$e|l|]
something <- here "something"
cmp <- here "comparison"
liftM list $ sortByVM cmp something vs
foldr1MV :: (Value -> Value -> VM Value) -> V.Vector Value -> VM Value
foldr1MV f vs = foldrMV f (V.last vs) (V.init vs)
foldrMV :: (Value -> Value -> VM Value) -> Value -> V.Vector Value -> VM Value
foldrMV _ acc vs | V.null vs = return acc
foldrMV f acc vs = do
rest <- foldrMV f acc (V.tail vs)
f (V.head vs) rest
sortVM :: Value -> [Value] -> VM [Value]
sortVM cmp = mergesort gt
where
gt a b = do
Integer t <- dispatch (keyword ["call"] [cmp, tuple [a, b]])
>>= findInteger
return t
sortByVM :: Value -> Value -> [Value] -> VM [Value]
sortByVM cmp by = mergesort gt
where
gt a b = do
x <- dispatch (keyword ["call"] [by, a])
y <- dispatch (keyword ["call"] [by, b])
Integer t <- dispatch (keyword ["call"] [cmp, tuple [x, y]])
>>= findInteger
return t
mergesort :: (Value -> Value -> VM Integer) -> [Value] -> VM [Value]
mergesort cmp = mergesort' cmp . map (: [])
mergesort' :: (Value -> Value -> VM Integer) -> [[Value]] -> VM [Value]
mergesort' _ [] = return []
mergesort' _ [xs] = return xs
mergesort' cmp xss = mergePairs cmp xss >>= mergesort' cmp
mergePairs :: (Value -> Value -> VM Integer) -> [[Value]] -> VM [[Value]]
mergePairs _ [] = return []
mergePairs _ [xs] = return [xs]
mergePairs cmp (xs:ys:xss) = do
z <- merge cmp xs ys
zs <- mergePairs cmp xss
return (z:zs)
merge :: (Value -> Value -> VM Integer) -> [Value] -> [Value] -> VM [Value]
merge _ [] ys = return ys
merge _ xs [] = return xs
merge cmp (x:xs) (y:ys) = do
o <- cmp x y
if o > 0
then do
rest <- merge cmp (x:xs) ys
return (y:rest)
else do
rest <- merge cmp xs (y:ys)
return (x:rest)
splitWhen :: (a -> Bool) -> [a] -> [[a]]
splitWhen f vs' = splitWhen' vs' []
where
splitWhen' [] acc = [acc]
splitWhen' (v:vs) acc
| f v = acc : splitWhen' vs []
| otherwise = splitWhen' vs (acc ++ [v])
splitOn :: Eq a => [a] -> [a] -> [[a]]
splitOn d vs' = splitOn' vs' []
where
splitOn' [] acc = [acc]
splitOn' a@(v:vs) acc
| d `isPrefixOf` a = acc : splitOn' (drop (length d) a) []
| otherwise = splitOn' vs (acc ++ [v])
zipN :: [VVector] -> [[Value]]
zipN vs
| any V.null vs = []
| otherwise = (map V.head vs) : zipN (map V.tail vs)