+module a8
+
+/*
+ Advanced Progrmming 2018, Assignment 8
+ Pieter Koopman, pieter@cs.ru.nl
+*/
+import StdEnv
+
+import iTasks => qualified return, >>=, >>|, sequence, forever, :: Set
+
+import Control.Applicative
+import Control.Monad
+import Control.Monad.State
+import Control.Monad.Trans
+import Data.Func
+import Data.Functor
+import Data.Either
+import Data.Maybe
+
+import Text => qualified join
+
+import qualified Data.List as List
+import qualified Data.Map as Map
+
+:: Expression
+ = New [Int]
+ | Elem Int
+ | Variable Ident
+ | Size Set
+ | (+.) infixl 6 Expression Expression
+ | (-.) infixl 6 Expression Expression
+ | (*.) infixl 7 Expression Expression
+ | (=.) infixl 2 Ident Expression
+
+:: Logical
+ = TRUE | FALSE
+ | (In) infix 4 Elem Set
+ | (==.) infix 4 Expression Expression
+ | (<=.) infix 4 Expression Expression
+ | Not Logical
+ | (||.) infixr 2 Logical Logical
+ | (&&.) infixr 3 Logical Logical
+
+:: Stmt
+ = Expression Expression
+ | Logical Logical
+ | For Ident Set Stmt
+ | If Logical Stmt Stmt
+
+:: Set :== Expression
+:: Elem :== Expression
+:: Ident :== String
+
+// === State
+:: Val :== Either Int [Int]
+:: SemState :== 'Map'.Map String Val
+
+:: Sem a :== StateT SemState (Either String) a
+store :: Ident Val -> Sem Val
+store k v = modify ('Map'.put k v) *> pure v
+
+read :: Ident -> Sem Val
+read k = gets ('Map'.get k) >>= maybe (fail "Unknown ident") pure
+
+fail :: String -> Sem a
+fail s = liftT (Left s)
+
+// === semantics
+isset :: (Sem Val) -> Sem [Int]
+isset s = s >>= either (\_->fail "Expected Set, got Elem") pure
+
+iselem :: (Sem Val) -> Sem Int
+iselem s = s >>= either pure (\_->fail "Expected Elem, got Set")
+
+evalExpr :: Expression -> Sem Val
+evalExpr (New s) = pure $ Right s
+evalExpr (Elem i) = pure $ Left i
+evalExpr (Variable i) = read i
+evalExpr (Size s) = isset (evalExpr s) >>= pure o Left o length
+evalExpr (l +. r) = evalExpr l >>= \x->evalExpr r >>= \y->case (x, y) of
+ (Left x, Left y) = pure $ Left (x + y)
+ (Left x, Right y) = pure $ Right $ 'List'.union [x] y
+ (Right x, Left y) = pure $ Right $ 'List'.union x [y]
+ (Right x, Right y) = pure $ Right $ 'List'.union x y
+evalExpr (l -. r) = evalExpr l >>= \x->evalExpr r >>= \y->case (x, y) of
+ (Left x, Left y) = pure $ Left (x - y)
+ (Left x, Right y) = fail "Elem -. Set is illegal"
+ (Right x, Left y) = pure $ Right $ 'List'.intersect x [y]
+ (Right x, Right y) = pure $ Right $ 'List'.intersect x y
+evalExpr (l *. r) = evalExpr l >>= \x->evalExpr r >>= \y->case (x, y) of
+ (Left x, Left y) = pure $ Left (x * y)
+ (Left x, Right y) = pure $ Right [x*y\\y<-y]
+ (Right x, Left y) = fail "Set *. Elem is illegal"
+ (Right x, Right y) = pure $ Right $ 'List'.intersect x y
+evalExpr (v =. b) = evalExpr b >>= store v
+
+evalLogic :: Logical -> Sem Bool
+evalLogic TRUE = pure True
+evalLogic FALSE = pure False
+evalLogic (e In s) = liftM2 'List'.elem (iselem (evalExpr e)) (isset (evalExpr s))
+evalLogic (e1 ==. e2) = liftM2 (===) (evalExpr e1) (evalExpr e2)
+evalLogic (e1 <=. e2) = evalExpr e1 >>= \x->evalExpr e2 >>= \y->case (x, y) of
+ (Left e1, Left e2) = pure $ e1 < e2
+ (Left e1, Right e2) = pure $ True
+ (Right e1, Left e2) = pure $ False
+ (Right e1, Right e2) = pure $ length e1 < length e2
+evalLogic (Not l) = not <$> evalLogic l
+evalLogic (l1 ||. l2) = liftM2 (||) (evalLogic l1) (evalLogic l2)
+evalLogic (l1 &&. l2) = liftM2 (&&) (evalLogic l1) (evalLogic l2)
+
+evalStmt :: Stmt -> Sem ()
+evalStmt (Expression e) = evalExpr e >>| pure ()
+evalStmt (Logical l) = evalLogic l >>| pure ()
+evalStmt (For i e s) = (evalExpr e >>= store i) *> evalStmt s
+evalStmt (If l s1 s2) = evalLogic l >>= \b->if b (evalStmt s1) (evalStmt s2)
+
+printExpr :: Expression [String] -> [String]
+printExpr (New s) c = ["[":'List'.intersperse "," $ map toString s] ++ ["]":c]
+printExpr (Elem i) c = [toString i:c]
+printExpr (Variable i) c = [i:c]
+printExpr (Size s) c = ["size(":printExpr s [")":c]]
+printExpr (l +. r) c = ["(":printExpr l ["+.":printExpr r [")":c]]]
+printExpr (l -. r) c = ["(":printExpr l ["-.":printExpr r [")":c]]]
+printExpr (l *. r) c = ["(":printExpr l ["*.":printExpr r [")":c]]]
+printExpr (l =. r) c = [l,"=.":printExpr r c]
+
+printLogic :: Logical [String] -> [String]
+printLogic TRUE c = ["True":c]
+printLogic FALSE c = ["False":c]
+printLogic (e In s) c = printExpr e [" in ":printExpr s c]
+printLogic (e1 ==. e2) c = ["(":printExpr e1 ["==.":printExpr e2 [")":c]]]
+printLogic (e1 <=. e2) c = ["(":printExpr e1 ["<=.":printExpr e2 [")":c]]]
+printLogic (Not l) c = ["not (":printLogic l [")":c]]
+printLogic (l1 ||. l2) c = ["(":printLogic l1 ["||.":printLogic l2 [")":c]]]
+printLogic (l1 &&. l2) c = ["(":printLogic l1 ["&&.":printLogic l2 [")":c]]]
+
+printStmt :: Stmt [String] -> [String]
+printStmt (Expression e) c = printExpr e c
+printStmt (Logical l) c = printLogic l c
+printStmt (For i e s) c = ["For ",i,"=":printExpr e ["in":printStmt s ["Rof":c]]]
+printStmt (If l s1 s2) c = ["If":printLogic l ["then":printStmt s1 ["else":printStmt s2 ["Fi":c]]]]
+
+// === simulation
+stateShared :: Shared SemState
+stateShared = sharedStore "sharedSemState" 'Map'.newMap
+
+derive class iTask Expression, Logical, Stmt
+
+main :: Task SemState
+main = 'iTasks'.forever $
+ enterInformation "Enter a statement" []
+ -|| viewSharedInformation "Old state" [ViewAs printState] stateShared
+ >&^ viewSharedInformation "Print" [ViewAs viewer]
+ >&> viewSharedInformation "New state" [ViewAs $ fmap printState] o mapRead evaler o ((>*<) stateShared)
+ >>* [OnAction (Action "Execute") $ ifValue (\e->e=:(Right _)) $ \(Right s)->set s stateShared]
+where
+ viewer = maybe
+ "No expression selected"
+ ('Text'.join " " o flip printStmt [])
+ evaler (s, t) = maybe
+ (Left "No expression selected")
+ (\e->execStateT (evalStmt e) s) t
+ printState m = [k +++ "=" +++ toSingleLineText v\\(k,v)<-'Map'.toList m]
+
+Start w = doTasks main w