summaryrefslogtreecommitdiff
path: root/assignment-8
diff options
context:
space:
mode:
Diffstat (limited to 'assignment-8')
-rw-r--r--assignment-8/skeleton8.icl752
1 files changed, 376 insertions, 376 deletions
diff --git a/assignment-8/skeleton8.icl b/assignment-8/skeleton8.icl
index 562561e..5b5eb58 100644
--- a/assignment-8/skeleton8.icl
+++ b/assignment-8/skeleton8.icl
@@ -1,376 +1,376 @@
-module skeleton8
-
-// Camil Staps, s4498062
-
-/**
- * Advanved Progrmming 2017, Assignment 8
- * Pieter Koopman, pieter@cs.ru.nl
- */
-
-import StdBool
-import StdEnum
-from StdFunc import const, flip, id, o
-import StdList
-import StdString
-import StdTuple
-
-import Control.Applicative
-import Control.Monad
-from Control.Monad.State import :: StateT(StateT), gets, modify, runStateT,
- instance Functor (StateT s m), instance Applicative (StateT s m),
- instance Monad (StateT s m)
-import Data.Bifunctor
-import Data.Error
-from Data.Func import $, on, `on`
-import Data.Functor
-import Data.List
-import qualified Data.Map as M
-import Data.Maybe
-import qualified Data.Set as S
-import Data.Tuple
-from Text import <+, class Text(concat), instance Text String
-
-from iTasks import
- class iTask, class toPrompt, class Publishable, instance Publishable (Task a),
- instance toPrompt String, instance Functor Task,
- class TApplicative, instance TApplicative Task,
- generic gEq, generic gDefault, generic JSONDecode, generic JSONEncode,
- generic gText, generic gEditor,
- :: JSONNode, :: TextFormat, :: Editor, :: Task,
- :: ViewOption(..), :: UpdateOption(..), :: EnterOption(..),
- :: Title(Title), instance toPrompt Title,
- class tune, <<@, :: UIAttributes, instance tune UIAttributes Editor,
- instance tune ArrangeHorizontal Task,
- :: ArrangeHorizontal(..), directionAttr, :: UIDirection(..),
- updateInformation, viewInformation, enterInformation, @, -||, startEngine
-import qualified iTasks
-
-(>>>=) :== 'iTasks'.tbind
-
-:: 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
- = Logical Logical
- | If Logical Stmt Stmt
- | For Ident Set Stmt
- | Expression Expression
-
-:: Set :== Expression
-:: Elem :== Expression
-:: Ident :== String
-
-// === State
-
-:: Val
- = VElem Int
- | VSet ('S'.Set Int)
-
-:: State :== 'M'.Map Ident Val
-
-:: Sem a :== StateT State (MaybeError String) a
-// Or define :: Sem a = Sem (s -> MaybeErrorString (a,s)) and copy the relevant
-// instances from Control.Monad.State
-
-// The types of store, read and fail are more general so that these functions
-// can be used with the Check monad below as well.
-store :: Ident v -> StateT ('M'.Map Ident v) m v | Monad m
-store i v = modify ('M'.put i v) $> v
-
-read :: Ident -> StateT ('M'.Map Ident v) (MaybeError String) v
-read i = gets ('M'.get i) >>= \v -> case v of
- Nothing -> fail $ "unknown variable '" <+ i <+ "'"
- Just v -> pure v
-
-fail :: String -> StateT s (MaybeError String) a
-fail e = StateT \_ -> Error e
-
-class eval f t :: f -> Sem t
-
-evalE :: (Expression -> Sem Val)
-evalE = eval
-
-instance eval Expression Val
-where
- eval :: Expression -> Sem Val
- eval (New xs) = pure $ VSet $ 'S'.fromList xs
- eval (Elem x) = pure $ VElem x
- eval (Variable i) = read i
- eval (Size s) = eval s >>= \xs -> case xs of
- VSet xs -> pure $ VElem $ 'S'.size xs
- _ -> fail "Cannot apply Size to Elem"
- eval (a +. b) = (liftA2 valAdd `on` eval) a b
- where
- valAdd (VElem i) (VElem j) = VElem $ i + j
- valAdd (VElem i) (VSet xs) = VSet $ 'S'.insert i xs
- valAdd (VSet xs) (VElem i) = VSet $ 'S'.insert i xs
- valAdd (VSet xs) (VSet ys) = VSet $ 'S'.union xs ys
- eval (a -. b) = (liftA2 tuple `on` eval) a b >>= uncurry valSub
- where
- valSub (VElem i) (VElem j) = pure $ VElem $ i - j
- valSub (VElem i) (VSet xs) = fail "Cannot subtract Set from Elem"
- valSub (VSet xs) (VElem i) = pure $ VSet $ 'S'.delete i xs
- valSub (VSet xs) (VSet ys) = pure $ VSet $ 'S'.difference xs ys
- eval (a *. b) = (liftA2 tuple `on` eval) a b >>= uncurry valMul
- where
- valMul (VElem i) (VElem j) = pure $ VElem $ i * j
- valMul (VElem i) (VSet xs) = pure $ VSet $ 'S'.mapSet ((*) i) xs
- valMul (VSet xs) (VElem i) = fail "Cannot multiply Elem with Set"
- valMul (VSet xs) (VSet ys) = pure $ VSet $ 'S'.intersection xs ys
- eval (n =. e) = eval e >>= store n
-
-// === semantics
-
-evalB :: (Logical -> Sem Bool)
-evalB = eval
-
-instance eval Logical Bool
-where
- eval :: Logical -> Sem Bool
- eval TRUE = pure True
- eval FALSE = pure False
- eval (e In s) = (liftA2 tuple `on` eval) e s >>= \t -> case t of
- (VElem i, VSet xs) -> pure $ 'S'.member i xs
- _ -> fail "Can only apply In to Elem and Set"
- eval (a ==. b) = (liftA2 tuple `on` eval) a b >>= \t -> case t of
- (VElem i, VElem j) -> pure $ i == j
- (VSet xs, VSet ys) -> pure $ 'S'.isSubsetOf xs ys && 'S'.size xs == 'S'.size ys
- // Cannot use == due to name clash
- _ -> fail "Cannot apply == to Elem and Set"
- eval (a <=. b) = (liftA2 tuple `on` eval) a b >>= \t -> case t of
- (VElem i, VElem j) -> pure $ i <= j
- (VSet xs, VSet ys) -> pure $ 'S'.isSubsetOf xs ys
- _ -> fail "Cannot apply <= to Elem and Set"
- eval (Not b) = not <$> eval b
- eval (a ||. b) = (liftA2 (||) `on` eval) a b
- eval (a &&. b) = (liftA2 (&&) `on` eval) a b
-
-:: Result
- = RElem Int
- | RSet ('S'.Set Int)
- | RBool Bool
-
-evalS :: (Stmt -> Sem Result)
-evalS = eval
-
-/**
- * For `For`, I use the following semantics:
- * - `For x [e1,e2,...] body`
- * Consecutively assigns e1, e2, ... to x and evaluates body to an element.
- * The elements are placed in a set, which is the result.
- * - `For x n body`
- * Sugar for `For x [0..n-1] body`, or an error if n < 0.
- */
-instance eval Stmt Result
-where
- eval :: Stmt -> Sem Result
- eval (If b t e) = eval b >>= \b -> eval $ if b t e
- eval (For v s e) = eval s >>= \s -> case s of
- VElem n -> if (n < 0)
- (fail "Cannot iterate until a negative value")
- (eval $ For v (New [0..n-1]) e)
- VSet xs -> sequence [store v (VElem x) >>| eval e \\ x <- xs`] >>= \xs ->
- if (all (\x -> x=:(RElem _)) xs)
- (pure $ RSet $ 'S'.fromList [x \\ RElem x <- xs])
- (fail "Not all results of For loop were Elems")
- with xs` = 'S'.toList xs
- eval (Expression e) = vtor <$> eval e
- where
- vtor (VElem x) = RElem x
- vtor (VSet xs) = RSet xs
- eval (Logical l) = RBool <$> eval l
-
-// === printing
-
-class print a :: a [String] -> [String]
-
-printToString :: (a -> String) | print a
-printToString = concat o flip print []
-
-instance print Int where print i st = [toString i:st]
-instance print String where print s st = [s:st]
-
-instance print Expression
-where
- print (New xs) st = ["[":intersperse "," (map toString xs)] ++ ["]":st]
- print (Elem x) st = print x st
- print (Variable i) st = print i st
- print (Size s) st = ["(Size ":"(":print s ["))":st]]
- print (a +. b) st = ["(":print a ["+":print b [")":st]]]
- print (a -. b) st = ["(":print a ["-":print b [")":st]]]
- print (a *. b) st = ["(":print a ["*":print b [")":st]]]
- print (v =. e) st = print v [" = ":print e st]
-
-instance print Logical
-where
- print TRUE st = ["TRUE":st]
- print FALSE st = ["FALSE":st]
- print (x In xs) st = ["(":print x [" In ":print xs [")":st]]]
- print (a ==. b) st = ["(":print a ["==":print b [")":st]]]
- print (a <=. b) st = ["(":print a ["<=":print b [")":st]]]
- print (Not b) st = ["~(":print b [")":st]]
- print (a ||. b) st = ["(":print a ["||":print b [")":st]]]
- print (a &&. b) st = ["(":print a ["&&":print b [")":st]]]
-
-instance print Stmt
-where
- print (If b t e) st = ["If ":print b [" {":print t ["} {":print e ["}":st]]]]
- print (For v s e) st = ["For ":print v [" {":print s ["} {":print e ["}":st]]]]
- print (Expression e) st = ["Expression (":print e [")":st]]
- print (Logical l) st = ["Logical (":print l [")":st]]
-
-// === simulation
-
-derive class iTask Expression, Logical, Stmt, Val, Result, 'S'.Set
-
-simulate :: Stmt -> Task Stmt
-simulate stmt =
- updateInformation (Title "Program") [UpdateUsing id (flip const) stmtEditor] stmt
- -|| 'iTasks'.allTasks
- [ viewInformation (Title "String representation") [ViewAs printToString] stmt
- , viewInformation (Title "Type") [ViewAs showtype] stmt
- , viewInformation (Title "Execution") [ViewAs execute] stmt
- ] <<@ ArrangeHorizontal
-where
- execute = fmap (bifmap showResult (map (appSnd showVal) o 'M'.toList))
- o flip runStateT 'M'.newMap o evalS
- showtype = fmap fst o flip runStateT 'M'.newMap o type
-
- showVal :: Val -> String
- showVal (VElem i) = toString i
- showVal (VSet s) = concat (intersperse ", " [toString i \\ i <- 'S'.toList s])
-
- showResult :: Result -> String
- showResult (RElem i) = toString i
- showResult (RSet s) = concat (intersperse ", " [toString i \\ i <- 'S'.toList s])
- showResult (RBool b) = toString b
-
-// NB: I want the input fields to be put vertically but don't see how that is
-// possible; neither this nor this with Vertical nor
-// <<@ Arrange{Horizontal,Vertical} on the task works.
-stmtEditor :: Editor Stmt
-stmtEditor = gEditor{|*|} <<@ directionAttr Horizontal
-
-// === type checking
-
-:: Type
- = TElem
- | TSet
- | TBool
-
-:: Check a :== StateT ('M'.Map Ident Type) (MaybeError String) a
-
-derive class iTask Type
-
-class type a :: a -> Check Type
-
-instance type Expression
-where
- type (New _) = pure TSet
- type (Elem _) = pure TElem
- type (Variable i) = read i
- type (Size e) = type e >>= \e -> case e of
- TSet -> pure TElem
- _ -> fail "Can only apply Size to Set"
- type (a +. b) = (liftA2 tuple `on` type) a b >>= \ts -> case ts of
- (TElem, TElem) -> pure TElem
- (TBool, _) -> fail "Cannot apply + to a boolean"
- (_, TBool) -> fail "Cannot apply + to a boolean"
- _ -> pure TSet
- type (a -. b) = (liftA2 tuple `on` type) a b >>= \ts -> case ts of
- (TElem, TElem) -> pure TElem
- (TBool, _) -> fail "Cannot apply - to a boolean"
- (_, TBool) -> fail "Cannot apply - to a boolean"
- (TElem, TSet) -> fail "Cannot apply - to Elem and Set"
- _ -> pure TSet
- type (a *. b) = (liftA2 tuple `on` type) a b >>= \ts -> case ts of
- (TElem, TElem) -> pure TElem
- (TBool, _) -> fail "Cannot apply * to a boolean"
- (_, TBool) -> fail "Cannot apply * to a boolean"
- (TSet, TElem) -> fail "Cannot apply * to Set and Elem"
- _ -> pure TSet
- type (v =. e) = type e >>= \t -> case t of
- TBool -> fail "Cannot assign a boolean to a variable"
- _ -> modify ('M'.put v t) $> t
-
-instance type Logical
-where
- type TRUE = pure TBool
- type FALSE = pure TBool
- type (x In xs) = (liftA2 tuple `on` type) x xs >>= \ts -> case ts of
- (TElem, TSet) -> pure TBool
- _ -> fail "Can only apply In to Elem and Set"
- type (a ==. b) = (liftA2 tuple `on` type) a b >>= \ts -> case ts of
- (TElem, TElem) -> pure TBool
- (TSet, TSet) -> pure TBool
- _ -> fail "Cannot apply == to Elem and Set"
- type (a <=. b) = (liftA2 tuple `on` type) a b >>= \ts -> case ts of
- (TElem, TElem) -> pure TBool
- (TSet, TSet) -> pure TBool
- _ -> fail "Cannot apply <= to Elem and Set"
- type (Not b) = type b
- type (a ||. b) = type a >>| type b
- type (a &&. b) = type a >>| type b
-
-/**
- * The cases for If and For are tricky. To type-check, we need the then and
- * else block to have the same type. To run, this is not necessary, as long as
- * the type of the used block is correct.
- * We require the For block to have type Elem here, but at runtime it's OK if
- * it has another type as long as the block is not used (the iterator is 0 or
- * empty).
- */
-instance type Stmt
-where
- type (If b t e) = type b >>| type t >>= \t -> type e >>= \e ->
- if (t === e) (pure t) (fail "Types of then and else blocks must match")
- type (For v s e) = type s >>| store v TElem >>| type e >>= \e -> case e of
- TElem -> pure TSet
- _ -> fail "Results of For loop should be Elem"
- type (Expression e) = type e
- type (Logical l) = type l
-
-/*
-// === Testing the type checker
-// Because of the reason explained at the instance type Stmt, we cannot check
-// whether type-check-ok iff run-ok. So we check that every program that can be
-// type-checked correctly can be run correctly instead.
-from Gast import
- quietnm, aStream, generic ggen, generic genShow,
- class TestArg, class Testable, instance Testable (a -> Bool), instance Testable Bool,
- :: GenState, :: RandomStream
-
-derive ggen Stmt, Expression, Logical
-derive genShow Stmt, Expression, Logical
-derive bimap []
-
-test :: Stmt -> (MaybeErrorString Result, MaybeErrorString Type)
-test stm = (fst <$> runStateT (evalS stm) 'M'.newMap, fst <$> runStateT (type stm) 'M'.newMap)
-
-check :: Stmt -> Bool
-check stm = isOk result || isError type
-where (result,type) = test stm
-
-Start _ = quietnm 100000 20 aStream check
-*/
-
-Start w = startEngine (enterInformation (Title "Program") [EnterUsing id stmtEditor] >>>= sim) w
-where
- sim :: Stmt -> Task Stmt
- sim st = simulate st >>>= sim
+module skeleton8
+
+// Camil Staps, s4498062
+
+/**
+ * Advanved Progrmming 2017, Assignment 8
+ * Pieter Koopman, pieter@cs.ru.nl
+ */
+
+import StdBool
+import StdEnum
+from StdFunc import const, flip, id, o
+import StdList
+import StdString
+import StdTuple
+
+import Control.Applicative
+import Control.Monad
+from Control.Monad.State import :: StateT(StateT), gets, modify, runStateT,
+ instance Functor (StateT s m), instance Applicative (StateT s m),
+ instance Monad (StateT s m)
+import Data.Bifunctor
+import Data.Error
+from Data.Func import $, on, `on`
+import Data.Functor
+import Data.List
+import qualified Data.Map as M
+import Data.Maybe
+import qualified Data.Set as S
+import Data.Tuple
+from Text import <+, class Text(concat), instance Text String
+
+from iTasks import
+ class iTask, class toPrompt, class Publishable, instance Publishable (Task a),
+ instance toPrompt String, instance Functor Task,
+ class TApplicative, instance TApplicative Task,
+ generic gEq, generic gDefault, generic JSONDecode, generic JSONEncode,
+ generic gText, generic gEditor,
+ :: JSONNode, :: TextFormat, :: Editor, :: Task,
+ :: ViewOption(..), :: UpdateOption(..), :: EnterOption(..),
+ :: Title(Title), instance toPrompt Title,
+ class tune, <<@, :: UIAttributes, instance tune UIAttributes Editor,
+ instance tune ArrangeHorizontal Task,
+ :: ArrangeHorizontal(..), directionAttr, :: UIDirection(..),
+ updateInformation, viewInformation, enterInformation, @, -||, startEngine
+import qualified iTasks
+
+(>>>=) :== 'iTasks'.tbind
+
+:: 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
+ = Logical Logical
+ | If Logical Stmt Stmt
+ | For Ident Set Stmt
+ | Expression Expression
+
+:: Set :== Expression
+:: Elem :== Expression
+:: Ident :== String
+
+// === State
+
+:: Val
+ = VElem Int
+ | VSet ('S'.Set Int)
+
+:: State :== 'M'.Map Ident Val
+
+:: Sem a :== StateT State (MaybeError String) a
+// Or define :: Sem a = Sem (s -> MaybeErrorString (a,s)) and copy the relevant
+// instances from Control.Monad.State
+
+// The types of store, read and fail are more general so that these functions
+// can be used with the Check monad below as well.
+store :: Ident v -> StateT ('M'.Map Ident v) m v | Monad m
+store i v = modify ('M'.put i v) $> v
+
+read :: Ident -> StateT ('M'.Map Ident v) (MaybeError String) v
+read i = gets ('M'.get i) >>= \v -> case v of
+ Nothing -> fail $ "unknown variable '" <+ i <+ "'"
+ Just v -> pure v
+
+fail :: String -> StateT s (MaybeError String) a
+fail e = StateT \_ -> Error e
+
+class eval f t :: f -> Sem t
+
+evalE :: (Expression -> Sem Val)
+evalE = eval
+
+instance eval Expression Val
+where
+ eval :: Expression -> Sem Val
+ eval (New xs) = pure $ VSet $ 'S'.fromList xs
+ eval (Elem x) = pure $ VElem x
+ eval (Variable i) = read i
+ eval (Size s) = eval s >>= \xs -> case xs of
+ VSet xs -> pure $ VElem $ 'S'.size xs
+ _ -> fail "Cannot apply Size to Elem"
+ eval (a +. b) = (liftA2 valAdd `on` eval) a b
+ where
+ valAdd (VElem i) (VElem j) = VElem $ i + j
+ valAdd (VElem i) (VSet xs) = VSet $ 'S'.insert i xs
+ valAdd (VSet xs) (VElem i) = VSet $ 'S'.insert i xs
+ valAdd (VSet xs) (VSet ys) = VSet $ 'S'.union xs ys
+ eval (a -. b) = (liftA2 tuple `on` eval) a b >>= uncurry valSub
+ where
+ valSub (VElem i) (VElem j) = pure $ VElem $ i - j
+ valSub (VElem i) (VSet xs) = fail "Cannot subtract Set from Elem"
+ valSub (VSet xs) (VElem i) = pure $ VSet $ 'S'.delete i xs
+ valSub (VSet xs) (VSet ys) = pure $ VSet $ 'S'.difference xs ys
+ eval (a *. b) = (liftA2 tuple `on` eval) a b >>= uncurry valMul
+ where
+ valMul (VElem i) (VElem j) = pure $ VElem $ i * j
+ valMul (VElem i) (VSet xs) = pure $ VSet $ 'S'.mapSet ((*) i) xs
+ valMul (VSet xs) (VElem i) = fail "Cannot multiply Elem with Set"
+ valMul (VSet xs) (VSet ys) = pure $ VSet $ 'S'.intersection xs ys
+ eval (n =. e) = eval e >>= store n
+
+// === semantics
+
+evalB :: (Logical -> Sem Bool)
+evalB = eval
+
+instance eval Logical Bool
+where
+ eval :: Logical -> Sem Bool
+ eval TRUE = pure True
+ eval FALSE = pure False
+ eval (e In s) = (liftA2 tuple `on` eval) e s >>= \t -> case t of
+ (VElem i, VSet xs) -> pure $ 'S'.member i xs
+ _ -> fail "Can only apply In to Elem and Set"
+ eval (a ==. b) = (liftA2 tuple `on` eval) a b >>= \t -> case t of
+ (VElem i, VElem j) -> pure $ i == j
+ (VSet xs, VSet ys) -> pure $ 'S'.isSubsetOf xs ys && 'S'.size xs == 'S'.size ys
+ // Cannot use == due to name clash
+ _ -> fail "Cannot apply == to Elem and Set"
+ eval (a <=. b) = (liftA2 tuple `on` eval) a b >>= \t -> case t of
+ (VElem i, VElem j) -> pure $ i <= j
+ (VSet xs, VSet ys) -> pure $ 'S'.isSubsetOf xs ys
+ _ -> fail "Cannot apply <= to Elem and Set"
+ eval (Not b) = not <$> eval b
+ eval (a ||. b) = (liftA2 (||) `on` eval) a b
+ eval (a &&. b) = (liftA2 (&&) `on` eval) a b
+
+:: Result
+ = RElem Int
+ | RSet ('S'.Set Int)
+ | RBool Bool
+
+evalS :: (Stmt -> Sem Result)
+evalS = eval
+
+/**
+ * For `For`, I use the following semantics:
+ * - `For x [e1,e2,...] body`
+ * Consecutively assigns e1, e2, ... to x and evaluates body to an element.
+ * The elements are placed in a set, which is the result.
+ * - `For x n body`
+ * Sugar for `For x [0..n-1] body`, or an error if n < 0.
+ */
+instance eval Stmt Result
+where
+ eval :: Stmt -> Sem Result
+ eval (If b t e) = eval b >>= \b -> eval $ if b t e
+ eval (For v s e) = eval s >>= \s -> case s of
+ VElem n -> if (n < 0)
+ (fail "Cannot iterate until a negative value")
+ (eval $ For v (New [0..n-1]) e)
+ VSet xs -> sequence [store v (VElem x) >>| eval e \\ x <- xs`] >>= \xs ->
+ if (all (\x -> x=:(RElem _)) xs)
+ (pure $ RSet $ 'S'.fromList [x \\ RElem x <- xs])
+ (fail "Not all results of For loop were Elems")
+ with xs` = 'S'.toList xs
+ eval (Expression e) = vtor <$> eval e
+ where
+ vtor (VElem x) = RElem x
+ vtor (VSet xs) = RSet xs
+ eval (Logical l) = RBool <$> eval l
+
+// === printing
+
+class print a :: a [String] -> [String]
+
+printToString :: (a -> String) | print a
+printToString = concat o flip print []
+
+instance print Int where print i st = [toString i:st]
+instance print String where print s st = [s:st]
+
+instance print Expression
+where
+ print (New xs) st = ["[":intersperse "," (map toString xs)] ++ ["]":st]
+ print (Elem x) st = print x st
+ print (Variable i) st = print i st
+ print (Size s) st = ["(Size ":"(":print s ["))":st]]
+ print (a +. b) st = ["(":print a ["+":print b [")":st]]]
+ print (a -. b) st = ["(":print a ["-":print b [")":st]]]
+ print (a *. b) st = ["(":print a ["*":print b [")":st]]]
+ print (v =. e) st = print v [" = ":print e st]
+
+instance print Logical
+where
+ print TRUE st = ["TRUE":st]
+ print FALSE st = ["FALSE":st]
+ print (x In xs) st = ["(":print x [" In ":print xs [")":st]]]
+ print (a ==. b) st = ["(":print a ["==":print b [")":st]]]
+ print (a <=. b) st = ["(":print a ["<=":print b [")":st]]]
+ print (Not b) st = ["~(":print b [")":st]]
+ print (a ||. b) st = ["(":print a ["||":print b [")":st]]]
+ print (a &&. b) st = ["(":print a ["&&":print b [")":st]]]
+
+instance print Stmt
+where
+ print (If b t e) st = ["If ":print b [" {":print t ["} {":print e ["}":st]]]]
+ print (For v s e) st = ["For ":print v [" {":print s ["} {":print e ["}":st]]]]
+ print (Expression e) st = ["Expression (":print e [")":st]]
+ print (Logical l) st = ["Logical (":print l [")":st]]
+
+// === simulation
+
+derive class iTask Expression, Logical, Stmt, Val, Result, 'S'.Set
+
+simulate :: Stmt -> Task Stmt
+simulate stmt =
+ updateInformation (Title "Program") [UpdateUsing id (flip const) stmtEditor] stmt
+ -|| 'iTasks'.allTasks
+ [ viewInformation (Title "String representation") [ViewAs printToString] stmt
+ , viewInformation (Title "Type") [ViewAs showtype] stmt
+ , viewInformation (Title "Execution") [ViewAs execute] stmt
+ ] <<@ ArrangeHorizontal
+where
+ execute = fmap (bifmap showResult (map (appSnd showVal) o 'M'.toList))
+ o flip runStateT 'M'.newMap o evalS
+ showtype = fmap fst o flip runStateT 'M'.newMap o type
+
+ showVal :: Val -> String
+ showVal (VElem i) = toString i
+ showVal (VSet s) = concat (intersperse ", " [toString i \\ i <- 'S'.toList s])
+
+ showResult :: Result -> String
+ showResult (RElem i) = toString i
+ showResult (RSet s) = concat (intersperse ", " [toString i \\ i <- 'S'.toList s])
+ showResult (RBool b) = toString b
+
+// NB: I want the input fields to be put vertically but don't see how that is
+// possible; neither this nor this with Vertical nor
+// <<@ Arrange{Horizontal,Vertical} on the task works.
+stmtEditor :: Editor Stmt
+stmtEditor = gEditor{|*|} <<@ directionAttr Horizontal
+
+// === type checking
+
+:: Type
+ = TElem
+ | TSet
+ | TBool
+
+:: Check a :== StateT ('M'.Map Ident Type) (MaybeError String) a
+
+derive class iTask Type
+
+class type a :: a -> Check Type
+
+instance type Expression
+where
+ type (New _) = pure TSet
+ type (Elem _) = pure TElem
+ type (Variable i) = read i
+ type (Size e) = type e >>= \e -> case e of
+ TSet -> pure TElem
+ _ -> fail "Can only apply Size to Set"
+ type (a +. b) = (liftA2 tuple `on` type) a b >>= \ts -> case ts of
+ (TElem, TElem) -> pure TElem
+ (TBool, _) -> fail "Cannot apply + to a boolean"
+ (_, TBool) -> fail "Cannot apply + to a boolean"
+ _ -> pure TSet
+ type (a -. b) = (liftA2 tuple `on` type) a b >>= \ts -> case ts of
+ (TElem, TElem) -> pure TElem
+ (TBool, _) -> fail "Cannot apply - to a boolean"
+ (_, TBool) -> fail "Cannot apply - to a boolean"
+ (TElem, TSet) -> fail "Cannot apply - to Elem and Set"
+ _ -> pure TSet
+ type (a *. b) = (liftA2 tuple `on` type) a b >>= \ts -> case ts of
+ (TElem, TElem) -> pure TElem
+ (TBool, _) -> fail "Cannot apply * to a boolean"
+ (_, TBool) -> fail "Cannot apply * to a boolean"
+ (TSet, TElem) -> fail "Cannot apply * to Set and Elem"
+ _ -> pure TSet
+ type (v =. e) = type e >>= \t -> case t of
+ TBool -> fail "Cannot assign a boolean to a variable"
+ _ -> modify ('M'.put v t) $> t
+
+instance type Logical
+where
+ type TRUE = pure TBool
+ type FALSE = pure TBool
+ type (x In xs) = (liftA2 tuple `on` type) x xs >>= \ts -> case ts of
+ (TElem, TSet) -> pure TBool
+ _ -> fail "Can only apply In to Elem and Set"
+ type (a ==. b) = (liftA2 tuple `on` type) a b >>= \ts -> case ts of
+ (TElem, TElem) -> pure TBool
+ (TSet, TSet) -> pure TBool
+ _ -> fail "Cannot apply == to Elem and Set"
+ type (a <=. b) = (liftA2 tuple `on` type) a b >>= \ts -> case ts of
+ (TElem, TElem) -> pure TBool
+ (TSet, TSet) -> pure TBool
+ _ -> fail "Cannot apply <= to Elem and Set"
+ type (Not b) = type b
+ type (a ||. b) = type a >>| type b
+ type (a &&. b) = type a >>| type b
+
+/**
+ * The cases for If and For are tricky. To type-check, we need the then and
+ * else block to have the same type. To run, this is not necessary, as long as
+ * the type of the used block is correct.
+ * We require the For block to have type Elem here, but at runtime it's OK if
+ * it has another type as long as the block is not used (the iterator is 0 or
+ * empty).
+ */
+instance type Stmt
+where
+ type (If b t e) = type b >>| type t >>= \t -> type e >>= \e ->
+ if (t === e) (pure t) (fail "Types of then and else blocks must match")
+ type (For v s e) = type s >>| store v TElem >>| type e >>= \e -> case e of
+ TElem -> pure TSet
+ _ -> fail "Results of For loop should be Elem"
+ type (Expression e) = type e
+ type (Logical l) = type l
+
+/*
+// === Testing the type checker
+// Because of the reason explained at the instance type Stmt, we cannot check
+// whether type-check-ok iff run-ok. So we check that every program that can be
+// type-checked correctly can be run correctly instead.
+from Gast import
+ quietnm, aStream, generic ggen, generic genShow,
+ class TestArg, class Testable, instance Testable (a -> Bool), instance Testable Bool,
+ :: GenState, :: RandomStream
+
+derive ggen Stmt, Expression, Logical
+derive genShow Stmt, Expression, Logical
+derive bimap []
+
+test :: Stmt -> (MaybeErrorString Result, MaybeErrorString Type)
+test stm = (fst <$> runStateT (evalS stm) 'M'.newMap, fst <$> runStateT (type stm) 'M'.newMap)
+
+check :: Stmt -> Bool
+check stm = isOk result || isError type
+where (result,type) = test stm
+
+Start _ = quietnm 100000 20 aStream check
+*/
+
+Start w = startEngine (enterInformation (Title "Program") [EnterUsing id stmtEditor] >>>= sim) w
+where
+ sim :: Stmt -> Task Stmt
+ sim st = simulate st >>>= sim