summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2016-05-07 20:29:51 +0200
committerGregor Kleen <gkleen@yggdrasil.li>2016-05-07 20:29:51 +0200
commit2e662e12f5fd6d26eee9e74b06b9cf3d176df07e (patch)
tree611f99b31f76129f384e842674d16666c98d26b4
parent39de6510e0830df7ca36ec0da2f4aaf485be0283 (diff)
download2017-01-16_17:13:37-2e662e12f5fd6d26eee9e74b06b9cf3d176df07e.tar
2017-01-16_17:13:37-2e662e12f5fd6d26eee9e74b06b9cf3d176df07e.tar.gz
2017-01-16_17:13:37-2e662e12f5fd6d26eee9e74b06b9cf3d176df07e.tar.bz2
2017-01-16_17:13:37-2e662e12f5fd6d26eee9e74b06b9cf3d176df07e.tar.xz
2017-01-16_17:13:37-2e662e12f5fd6d26eee9e74b06b9cf3d176df07e.zip
First prototype
-rw-r--r--src/Main.hs127
-rw-r--r--src/Types.hs57
2 files changed, 184 insertions, 0 deletions
diff --git a/src/Main.hs b/src/Main.hs
new file mode 100644
index 0000000..eff9a92
--- /dev/null
+++ b/src/Main.hs
@@ -0,0 +1,127 @@
1{-# LANGUAGE FlexibleContexts, ConstraintKinds, ViewPatterns, OverloadedStrings #-}
2
3import Data.PQueue.Prio.Max (MaxPQueue)
4import qualified Data.PQueue.Prio.Max as MaxPQueue
5
6import Data.Sequence (Seq, (|>), (<|), viewl, viewr, ViewL(..), ViewR(..))
7import qualified Data.Sequence as Seq
8
9import Data.List (delete)
10import Data.Bool
11
12import Data.Monoid (Monoid(..), (<>))
13
14import Data.Default.Class
15
16import System.Console.Readline (readline)
17
18import Control.Monad.State.Lazy (MonadState(..), runStateT, modify)
19import Control.Monad.IO.Class (MonadIO(..))
20
21import Control.Monad (void, join)
22
23import Control.Lens hiding ((|>), (:<))
24
25import Data.Text (Text)
26import qualified Data.Text as Text
27import qualified Data.Text.IO as Text
28
29import Text.Read (readMaybe)
30
31import Data.CaseInsensitive ( CI )
32import qualified Data.CaseInsensitive as CI
33
34import System.Exit (exitSuccess)
35
36
37import Types
38
39type SequenceM m = (MonadIO m, MonadState SequenceState m)
40
41prompt :: String
42prompt = "→ "
43
44main :: IO ()
45main = void $ runStateT acceptCmd def
46
47acceptCmd :: SequenceM m => m ()
48acceptCmd = join $ liftIO (readline prompt) >>= handleCmd
49
50terminate :: MonadIO m => m a
51terminate = liftIO exitSuccess
52
53handleCmd :: SequenceM m => Maybe String -> m (m ())
54handleCmd Nothing = return terminate -- EOF
55handleCmd (Just "") = return acceptCmd -- Empty input
56handleCmd (Just (Text.words . Text.pack -> (CI.mk -> cmd) : args)) -- Input containing at least one word
57 | cmd `elem` ["exit", "quit"] = return $ return ()
58 | cmd `elem` ["insert", "add"] = acceptCmd <$ case args of
59 (name : (readMaybe . Text.unpack -> Just val) : comment) -> bool (liftIO $ Text.putStrLn "Did not insert new entity") (return ()) =<< withNewName (\e -> modify $ update (Insert e val, parseComment comment)) name
60 _ -> liftIO . Text.putStrLn $ "Malformed arguments to insert.\nExpecting: insert <name> <sequence value> [<comment> […]]"
61 | cmd == "dump" = acceptCmd <$ (get >>= liftIO . print)
62 | otherwise = acceptCmd <$ do
63 liftIO $ Text.putStrLn ("No such command known: " <> CI.original cmd)
64handleCmd _ = error "Could not parse input String to Text"
65
66askBool :: MonadIO m => Text -> m Bool
67askBool question = do
68 str <- liftIO $ readline (Text.unpack question <> " (Yes/No)?\n" <> prompt)
69 case str of
70 Nothing -> terminate
71 Just (CI.mk -> "yes") -> return True
72 Just (CI.mk -> "y") -> return True
73 Just (CI.mk -> "no") -> return False
74 Just (CI.mk -> "n") -> return False
75 _ -> askBool question
76
77withNewName :: SequenceM m => (Entity -> m ()) -> Text -> m Bool
78withNewName callback name = do
79 -- names <- getNames <$> use history
80 entity <- getEntity
81 maybe False (const True) <$> maybe (return Nothing) (\e -> Just <$> callback e) entity
82 where
83 number :: MaxPQueue Integer Entity -> MaxPQueue Integer Entity
84 number = fst . MaxPQueue.foldrWithKey accum (MaxPQueue.empty, 0)
85 accum key val o@(queue, maxNmbr)
86 | (Entity (CI.mk -> val') _) <- val
87 , val' == CI.mk name = (MaxPQueue.insert key (Entity (CI.original val') (Just $ succ maxNmbr)) queue, succ maxNmbr)
88 | otherwise = o
89 getEntity = do
90 names <- MaxPQueue.elems <$> use queue -- we have semantic guarantees that names is a set
91 let
92 takenNumbers = map (\(Entity _ n) -> n) $ filter (\(Entity (CI.mk -> id) _) -> id == CI.mk name) names
93 case takenNumbers of
94 [] -> return . Just $ Entity name Nothing
95 [Nothing] -> do
96 liftIO $ Text.putStrLn ("Name »" <> name <> "« is already taken.")
97 useNumber <- liftIO $ askBool "Introduce a numbering scheme"
98 if useNumber
99 then queue <~ (number) >> getEntity
100 else return Nothing
101 (maximum -> (Just maxNmbr)) -> do
102 let
103 entity = Entity name (Just $ succ maxNmbr)
104 liftIO $ Text.putStrLn ("Changed name to »" <> showEntity entity <> "«")
105 return $ Just entity
106
107parseComment :: [Text] -> Maybe Comment
108parseComment (Text.unwords -> c)
109 | c == "" = Nothing
110 | otherwise = Just c
111
112update :: (Alteration, Maybe Comment) -> SequenceState -> SequenceState
113update loggable@(alt, _) = affect alt . over history (|> loggable)
114
115affect :: Alteration -> SequenceState -> SequenceState
116affect (Modify id by) = over queue $ affect' (\(k, v) -> if v == id then Just (k + by, id) else Just (k, v))
117affect (Override id new) = over queue $ affect' (\(k, v) -> if v == id then Just (new, id) else Just (k, v))
118affect (Drop id) = over queue $ MaxPQueue.filter (/= id)
119affect (Insert id v) = over queue $ MaxPQueue.insert v id
120affect _ = error "Modification not implemented yet"
121
122affect' :: ((Integer, Entity) -> Maybe (Integer, Entity)) -> MaxPQueue Integer Entity -> MaxPQueue Integer Entity
123affect' f = MaxPQueue.foldrWithKey accum MaxPQueue.empty
124 where
125 accum key val
126 | (Just (key', val')) <- f (key, val) = MaxPQueue.insert key' val'
127 | otherwise = id
diff --git a/src/Types.hs b/src/Types.hs
new file mode 100644
index 0000000..2dff0f0
--- /dev/null
+++ b/src/Types.hs
@@ -0,0 +1,57 @@
1{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
2
3module Types
4 ( Entity(..)
5 , showEntity
6 , Comment
7 , Alteration(..)
8 , SequenceState, queue, history
9 ) where
10
11import Data.PQueue.Prio.Max (MaxPQueue)
12import qualified Data.PQueue.Prio.Max as MaxPQueue
13
14import Data.Sequence (Seq)
15import qualified Data.Sequence as Seq
16
17import Data.Monoid (Monoid(..), (<>))
18
19import Control.Monad.State.Lazy (MonadState)
20import Control.Monad.IO.Class (MonadIO)
21
22import Data.Text (Text)
23import qualified Data.Text as Text
24import qualified Data.Text.IO as Text
25
26import Control.Lens.TH
27
28import Data.Default.Class
29
30data Entity = Entity Text (Maybe Integer)
31 deriving (Eq, Ord, Show)
32
33showEntity :: Entity -> Text
34showEntity (Entity t Nothing) = t
35showEntity (Entity t (Just n)) = t <> " " <> (Text.pack $ show n)
36
37type Comment = Text
38
39data Alteration = Modify Entity Integer
40 | Override Entity Integer
41 | Drop Entity
42 | Insert Entity Integer
43 | Rename Entity Entity
44 deriving (Show)
45
46data SequenceState = SequenceState
47 { _queue :: MaxPQueue Integer Entity -- Current state
48 , _history :: Seq (Alteration, Maybe Comment) -- Most recent last
49 }
50 deriving (Show)
51makeLenses ''SequenceState
52
53instance Default SequenceState where
54 def = SequenceState
55 { _queue = MaxPQueue.empty
56 , _history = Seq.empty
57 }