--- /dev/null
+module a9
+
+/*
+ Advanced Progrmming 2018, Assignment 8
+ Pieter Koopman, pieter@cs.ru.nl
+*/
+import StdEnv
+
+
+import Control.Applicative
+import Control.Monad => qualified join
+import Control.Monad.State
+import Control.Monad.Trans
+import Data.Func
+import Data.Functor
+import Data.Either
+import Data.Maybe
+import Data.List
+
+import Text
+
+import qualified Data.List as List
+import qualified Data.Map as Map
+
+:: Set :== Sem [Int]
+:: Elem :== Sem Int
+:: Ident :== String
+
+// === State
+:: Val :== Either Int [Int]
+:: SemState :== 'Map'.Map String Val
+
+:: Sem a =
+ { evaluator :: StateT SemState (Either String) a
+ , printer :: [String] -> [String]
+ }
+
+fail :: String -> StateT SemState (Either String) a
+fail s = liftT (Left s)
+
+//Sets
+integer :: Int -> Elem
+integer i = {evaluator=pure i,printer = \c->[toString i:c]}
+
+set :: [Int] -> Set
+set i =
+ { evaluator = pure (removeDup i)
+ , printer = \c->["[":intersperse "," (map toString i)] ++ ["]":c]
+ }
+
+size :: Set -> Elem
+size s =
+ { evaluator = length <$> s.evaluator
+ , printer = \c->["size(":s.printer [")"]]
+ }
+
+class variable a :: String -> a
+instance variable Set
+where
+ variable i =
+ { evaluator
+ = gets ('Map'.get i) >>= \v->case v of
+ Nothing = fail ("No set with identifier " +++ i)
+ Just (Left v) = fail (i +++ " is an element")
+ Just (Right v) = pure v
+ , printer = \c->[i:c]
+ }
+instance variable Elem
+where
+ variable i =
+ { evaluator
+ = gets ('Map'.get i) >>= \v->case v of
+ Nothing = fail ("No element with identifier " +++ i)
+ Just (Right v) = fail (i +++ " is a set")
+ Just (Left v) = pure v
+ , printer = \c->[i:c]
+ }
+class (+.) infixl 6 a b ~c :: a b -> c
+instance +. Elem Elem Elem
+where
+ +. a b =
+ { evaluator = (+) <$> a.evaluator <*> b.evaluator
+ , printer = \c->a.printer ["+":b.printer c]
+ }
+instance +. Elem Set Set
+where
+ +. a b =
+ { evaluator = 'List'.union <$> pure <$> a.evaluator <*> b.evaluator
+ , printer = \c->a.printer ["+":b.printer c]
+ }
+instance +. Set Elem Set
+where
+ +. a b =
+ { evaluator = 'List'.union <$> a.evaluator <*> (pure <$> b.evaluator)
+ , printer = \c->a.printer ["+":b.printer c]
+ }
+instance +. Set Set Set
+where
+ +. a b =
+ { evaluator = 'List'.union <$> a.evaluator <*> b.evaluator
+ , printer = \c->a.printer ["+":b.printer c]
+ }
+class (-.) infixl 6 a b ~c :: a b -> c
+instance -. Elem Elem Elem
+where
+ -. a b =
+ { evaluator = (-) <$> a.evaluator <*> b.evaluator
+ , printer = \c->a.printer ["-":b.printer c]
+ }
+instance -. Set Elem Set
+where
+ -. a b =
+ { evaluator = 'List'.difference <$> a.evaluator <*> (pure <$> b.evaluator)
+ , printer = \c->a.printer ["-":b.printer c]
+ }
+instance -. Set Set Set
+where
+ -. a b =
+ { evaluator = 'List'.difference <$> a.evaluator <*> b.evaluator
+ , printer = \c->a.printer ["-":b.printer c]
+ }
+class (*.) infixl 6 a b ~c :: a b -> c
+instance *. Elem Elem Elem
+where
+ *. a b =
+ { evaluator = (*) <$> a.evaluator <*> b.evaluator
+ , printer = \c->a.printer ["*":b.printer c]
+ }
+instance *. Elem Set Set
+where
+ *. a b =
+ { evaluator = a.evaluator >>= \x->map ((*)x) <$> b.evaluator
+ , printer = \c->a.printer ["*":b.printer c]
+ }
+instance *. Set Set Set
+where
+ *. a b =
+ { evaluator = 'List'.intersect <$> a.evaluator <*> b.evaluator
+ , printer = \c->a.printer ["*":b.printer c]
+ }
+
+class (=.) infixl 2 a :: Ident a -> a
+instance =. Set
+where
+ =. n s =
+ { evaluator = s.evaluator >>= \v->modify ('Map'.put n (Right v)) *> pure v
+ , printer = \c->[n,"=":s.printer c]
+ }
+instance =. Elem
+where
+ =. n s =
+ { evaluator = s.evaluator >>= \v->modify ('Map'.put n (Left v)) *> pure v
+ , printer = \c->[n,"=":s.printer c]
+ }
+
+//Logicals
+:: Logical :== Sem Bool
+true :: Logical
+true = {evaluator = pure True, printer = \c->["True":c]}
+
+false :: Logical
+false = {evaluator = pure False, printer = \c->["False":c]}
+
+In :: Elem Set -> Logical
+In e s =
+ { evaluator = elem <$> e.evaluator <*> s.evaluator
+ , printer = \c->e.printer ["In":s.printer c]
+ }
+
+class (==.) infix 4 a :: a a -> Logical
+instance ==. Elem
+where
+ ==. a b =
+ {evaluator = (==) <$> a.evaluator <*> b.evaluator
+ ,printer = \c->a.printer ["==":b.printer c]
+ }
+instance ==. Set
+where
+ ==. a b =
+ {evaluator = (==) <$> a.evaluator <*> b.evaluator
+ ,printer = \c->a.printer ["==":b.printer c]
+ }
+
+class (<=.) infixl 6 a b :: a b -> Logical
+instance <=. Elem Elem
+where
+ <=. a b =
+ {evaluator = (<=) <$> a.evaluator <*> b.evaluator
+ ,printer = \c->a.printer ["<=":b.printer c]
+ }
+instance <=. Set Set
+where
+ <=. a b =
+ {evaluator = ((<=) `on` length) <$> a.evaluator <*> b.evaluator
+ ,printer = \c->a.printer ["<=":b.printer c]
+ }
+
+Not :: Logical -> Logical
+Not a = {evaluator = not <$> a.evaluator, printer = \c->["not":a.printer c]}
+
+(||.) infixr 2 :: Logical Logical -> Logical
+(||.) a b =
+ {evaluator = (||) <$> a.evaluator <*> b.evaluator
+ ,printer = \c->a.printer ["||":b.printer c]
+ }
+
+(&&.) infixr 2 :: Logical Logical -> Logical
+(&&.) a b =
+ {evaluator = (&&) <$> a.evaluator <*> b.evaluator
+ ,printer = \c->a.printer ["&&":b.printer c]
+ }
+
+//Stmts
+:: Stmt :== Sem ()
+expression :: Set -> Stmt
+expression e = {evaluator = e.evaluator *> pure (), printer = e.printer}
+
+logical :: Logical -> Stmt
+logical e = {evaluator = e.evaluator *> pure (), printer = e.printer}
+
+For :: String Set Stmt -> Stmt
+For i e s =
+ { evaluator = (i =. e).evaluator *> s.evaluator
+ , printer = \c->["For",i,"=":e.printer ["In":s.printer c]]
+ }
+
+If :: Logical Stmt Stmt -> Stmt
+If l s1 s2 =
+ { evaluator = l.evaluator >>= \b->if b s1.evaluator s2.evaluator
+ , printer = \c->["If":l.printer ["then":s1.printer ["else":s2.printer c]]]
+ }
+
+evalSem :: (Sem a) -> (SemState -> Either String a)
+evalSem s = evalStateT s.evaluator
+
+printSem :: (Sem a) -> ([String] -> [String])
+printSem s = s.printer
+
+Start =
+ ( evalSem (integer 42 +. integer 42 +. set [] +. integer 4 +. set [42]) 'Map'.newMap
+ , join " " $ printSem (integer 42 +. integer 42 +. set [] +. integer 4 +. set [42]) []
+ )