module DynamicShell import StdEnv, StdParsComb, StdDynamic, StdDynamicFileIO, Debug :: Void = Void :: Try a = Ok a | Raise String :: Expression = (Apply) Expression Expression | Name String | Value Dynamic Start :: *World -> *World Start world = case writeFunctions shellFunctions world of Ok world -> shell "Dynamic shell (enter empty line to exit)" world Raise error -> abort error writeFunctions :: [(String, Dynamic)] *World -> Try *World writeFunctions [(name, dyn):functions] world # (ok, world) = writeDynamic name dyn world | not ok = Raise ("Cannot write dynamic \"" +++ name +++ "\"") = writeFunctions functions world writeFunctions _ world = Ok world shellFunctions :: [(String, Dynamic)] shellFunctions = [ ("shell", dynamic shell "New Dynamic Shell" :: *World -> *World) , ("if", dynamic \x y z -> if x y z :: A.a: Bool a a -> a) , ("==", dynamic (==) :: Int Int -> Bool) , ("+", dynamic (+) :: Int Int -> Int) , ("-", dynamic (-) :: Int Int -> Int) ] shell :: String *World -> *World shell message world # (console, world) = stdio world console = fwrites (message +++ "\n> ") console (cmdline, console) = freadline console (_, world) = fclose console world | cmdline == "" || cmdline == "\n" = world = case parse cmdline of Ok expression # (try, world) = resolve expression world -> case try of Ok expression -> case compose expression of Ok dyn # (result, world) = evaluate dyn world -> shell (valueOf result +++ " :: " +++ typeOf result) world Raise error -> shell error world Raise error -> shell error world Raise error -> shell error world parse :: String -> Try Expression parse cmdline = case begin (sp shellParser <& sp endOfLine) (fromString cmdline) of [([], expression)] -> Ok expression _ -> Raise "Syntax error" where endOfLine = (symbol '\n') &> eof resolve :: Expression *World -> (Try Expression, *World) resolve (e1 Apply e2) world # (try1, world) = resolve e1 world (try2, world) = resolve e2 world = case (try1, try2) of (Ok e1, Ok e2) -> (Ok (e1 Apply e2), world) (Raise error, _) -> (Raise error, world) (_, Raise error) -> (Raise error, world) resolve (Name name) world # (ok, dyn, world) = readDynamic name world | ok = (Ok (Value dyn), world) = (Raise ("Cannot read dynamic \"" +++ name +++ "\""), world) resolve other world = (Ok other, world) compose :: Expression -> Try Dynamic compose (e1 Apply e2) = case (compose e1, compose e2) of (Ok d1, Ok d2) -> apply d1 d2 (Raise error, _) -> Raise error (_, Raise error) -> Raise error compose (Value dyn) = Ok dyn compose _ = Raise "Cannot compose expression" apply :: Dynamic Dynamic -> Try Dynamic apply (f :: a -> b) (x :: a) = Ok (dynamic f x :: b) apply df dx = Raise ("Cannot apply: " +++ typeOf df +++ " to: " +++ typeOf dx) evaluate :: Dynamic *World -> (Dynamic, *World) evaluate (x :: A.a: a) world = (dynamic x :: A.a: a, world) evaluate (x :: A.a b: a -> b) world = (dynamic x :: A.a b: a -> b, world) evaluate (x :: A.a: a -> a) world = (dynamic x :: A.a: a -> a, world) evaluate (x :: A.a c: a -> (b, c)) world = (dynamic x :: A.a c: a -> (b, c), world) evaluate (x :: A.a: a -> (b, a)) world = (dynamic x :: A.a: a -> (b, a), world) evaluate d=:(f :: *World -> *(a, *World)) world # (x, world) = f world = (dynamic x :: a, world) evaluate d=:(f :: *World -> *World) world # world = f world = (dynamic Void :: Void, world) evaluate (x :: a) world = (dynamic x :: a, world) typeOf :: Dynamic -> String typeOf d = toString (typeCodeOfDynamic d) valueOf :: Dynamic -> String valueOf dyn = foldl (+++) "" (debugShowWithOptions [DebugMaxChars 79, DebugTerminator ""] (fst (valueAndType dyn))) where valueAndType :: !Dynamic -> (a, TypeCode) valueAndType _ = code inline { pop_a 0 } shellParser :: CParser Char Expression t shellParser = expression where expression = simple <&> \e -> <*> (sp simple) <@ \es -> foldl (Apply) e es simple = symbol '(' &> sp expression <& spsymbol ')' value <@ Value name <@ Name name = <+> (satisfy (\c -> not (isMember c [' \t\n\r']))) <@ toString value = bool char real int string where bool = token ['True'] <@ const (dynamic True :: Bool) token ['False'] <@ const (dynamic False :: Bool) char = symbol '\'' &> character '\'' <& symbol '\'' <@ (\c -> dynamic c :: Char) int = (symbol '+') &> nat <@ (\i -> dynamic i :: Int) symbol '-' &> nat <@ (\i -> dynamic ~ i :: Int) real = (realWithExp realWithoutExp) <@ \cs -> dynamic toReal (toString cs) :: Real where realWithoutExp = int <++> symbol '.' <:&> nat realWithExp = int <++> ( (symbol '.' <:&> nat) <@ flatten) <++> symbol 'E' <:&> int int = symbol '-' <:&> nat (symbol '+') &> nat nat = <+> (satisfy (\c -> isMember c ['0'..'9'])) string = symbol '"' &> <*> (character '"') <& symbol '"' <@ (\cs -> dynamic toString cs :: String) character sep = satisfy ((<>) sep) symbol '\\' &> satisfy (const True)