let prelude = import! "std/prelude.glu"
let io = import! "std/io.glu"
let map = import! "std/map.glu"
let { Bool } = import! "std/bool.glu"
let { Option } = import! "std/option.glu"
let { Result } = import! "std/result.glu"
let string = import! "std/string.glu"
let { Map } = map
let { wrap, (*>) } = prelude.make_Applicative io.applicative
let { (>>=) } = prelude.make_Monad io.monad
let { Eq } = prelude
let { append = (++) } = string.semigroup
let ord_map = map.make string.ord
let { singleton, find } = ord_map
let for = (prelude.make_Traversable ord_map.traversable).for io.applicative
let { (<>) } = prelude.make_Semigroup ord_map.semigroup
let { empty } = ord_map.monoid
let load_file filename : String -> IO String =
let last_slash =
match string.rfind filename "/" with
| None -> 0
| Some i -> i + 1
let modulename = string.slice filename last_slash (string.len filename - 3)
let read_result =
io.catch (io.read_file_to_string filename >>= \x -> wrap (Ok x)) (\err -> wrap (Err err))
read_result
>>= \result ->
match result with
| Ok expr -> io.load_script modulename expr
| Err msg -> wrap msg
type Cmd = { info : String, action : String -> IO Bool }
let commands : Map String Cmd =
let print_result result =
match result with
| Ok x -> io.println x
| Err x -> io.println x
let commands = ref empty
let cmds =
singleton "q" { info = "Quit the REPL", action = \_ -> wrap False } <> singleton "t" {
info = "Prints the type with an expression",
action = \arg -> repl_prim.type_of_expr arg >>= print_result *> wrap True
}
<> singleton "i" {
info = "Prints information about the given name",
action = \arg -> repl_prim.find_info arg >>= print_result *> wrap True
}
<> singleton "k" {
info = "Prints the kind with the given type",
action = \arg -> repl_prim.find_kind arg >>= print_result *> wrap True
}
<> singleton "l" {
info = "Loads the file at \'folder/module.ext\' and stores it at \'module\'",
action = \arg -> load_file arg >>= io.println *> wrap True
}
<> singleton "h" {
info = "Print this help",
action = \_ ->
let print_header = io.println "Available commands\n"
let print_cmd cmd : { key : String, value : Cmd } -> IO () =
io.println (" :" ++ cmd.key ++ " " ++ cmd.value.info)
print_header *> for (load commands) print_cmd
*> wrap True
}
commands <- cmds
load commands
let do_command line : String -> IO Bool =
if string.len line >= 2 then
let cmd = string.slice line 1 2
let arg =
if string.len line >= 3
then string.trim (string.slice line 3 (string.len line))
else ""
match find cmd commands with
| Some command -> command.action arg
| None -> io.println ("Unknown command \'" ++ cmd ++ "\'") *> wrap True
else
io.println "Expected a command such as `:h`"
*> wrap True
let store line : String -> IO Bool =
let line = string.trim line
match string.find line " " with
| Some bind_end ->
let binding = string.slice line 0 bind_end
let expr = string.slice line bind_end (string.len line)
io.load_script binding expr *> wrap True
| None -> io.println "Expected binding in definition" *> wrap True
let loop editor : Editor -> IO () =
let run_line line =
if string.is_empty (string.trim line)
then wrap True
else
if string.starts_with line ":"
then do_command line
else io.catch (repl_prim.eval_line line) wrap >>= io.println *> wrap True
rustyline.readline editor "> "
>>= \line_opt ->
match line_opt with
| None -> wrap ()
| Some line -> run_line line >>= \continue -> if continue then loop editor else wrap ()
let run x : () -> IO () =
io.println "gluon (:h for help, :q to quit)"
*> loop (rustyline.new_editor ())
run