Skip to content

Commit

Permalink
Repl UX improvements
Browse files Browse the repository at this point in the history
- Use `"> "` as repl prompt
- Add basic help message at startup
- Skips parsing if an empty line is entered
- Adds previous expressions to linenoise history so that you can get back to them using the arrow keys
- Quit repl on ^C and ^D
  • Loading branch information
brendanzab committed Jul 17, 2016
1 parent 76f6bf0 commit 2ed0a35
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 37 deletions.
13 changes: 11 additions & 2 deletions src/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,18 @@ fn find_info(args: WithVM<RootStr>) -> IO<Result<String, String>> {
}

fn compile_repl(vm: &Thread) -> Result<(), Box<StdError + Send + Sync>> {
fn input(s: &str) -> IO<Option<String>> {
IO::Value(::linenoise::input(s))
fn input(prompt: &str) -> IO<Option<String>> {
let input = ::linenoise::input(prompt);

if let Some(ref input) = input {
if !input.trim().is_empty() {
::linenoise::history_add(input);
}
}

IO::Value(input)
}

try!(vm.define_global("repl_prim",
record!(
type_of_expr => primitive!(1 type_of_expr),
Expand Down
72 changes: 37 additions & 35 deletions std/repl.glu
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ let prelude = import "std/prelude.glu"
let map = import "std/map.glu"
let string = import "std/string.glu"
let { Map } = map
let { (>>=), return, (>>), join, map = fmap, lift2, forM_ } = prelude.make_Monad prelude.monad_IO
let { Eq, Option, Result, Monoid } = prelude
let { (==) } = string.eq
let (++) = string.monoid.(<>)
let { singleton, find, insert, monoid, to_list }
= map.make string.ord
let { (>>=), return, (>>), forM_ } = prelude.make_Monad prelude.monad_IO
let { Eq, Result, Monoid } = prelude
let { (<>) = (++) } = string.monoid
let { singleton, find, monoid, to_list } = map.make string.ord
let { (<>), empty } = monoid


Expand All @@ -23,35 +21,31 @@ let load_file filename: String -> IO String =
| Ok expr -> io.load_script modulename expr
| Err msg -> return msg

type Cmd = { info: String, action: String -> IO Bool }
type Cmd = {
info: String,
action: String -> IO Bool
}

let commands: Map String Cmd =
let print_result result =
match result with
| Ok x -> io.print x
| Err x -> io.print x

let commands = ref empty
let cmds =
singleton "q" { info = "Quit the REPL", action = \_ -> return False }
<> singleton "t" {
info = "Prints the type with an expression",
action = \arg -> repl_prim.type_of_expr arg >>= \result ->
match result with
| Ok x -> io.print x
| Err x -> io.print x
>> return True
action = \arg -> repl_prim.type_of_expr arg >>= print_result >> return True
}
<> singleton "i" {
info = "Prints information about the given name",
action = \arg -> repl_prim.find_info arg >>= \result ->
match result with
| Ok x -> io.print x
| Err x -> io.print x
>> return True
action = \arg -> repl_prim.find_info arg >>= print_result >> return True
}
<> singleton "k" {
info = "Prints the kind with the given type",
action = \arg -> repl_prim.find_kind arg >>= \result ->
match result with
| Ok x -> io.print x
| Err x -> io.print x
>> return True
action = \arg -> repl_prim.find_kind arg >>= print_result >> return True
}
<> singleton "l" {
info = "Loads the file at 'folder/module.ext' and stores it at 'module'",
Expand All @@ -71,7 +65,7 @@ let commands: Map String Cmd =
commands <- cmds
load commands

let do_command line: String -> IO Bool =
let do_command line: String -> IO Bool =
let cmd = string.slice line 1 2
let arg = if string.length line >= 3 then string.trim (string.slice line 3 (string.length line)) else ""
match find cmd commands with
Expand All @@ -81,25 +75,33 @@ let do_command line: String -> IO Bool =
let store line: String -> IO Bool =
let line = string.trim line
match string.find line " " with
| Some bind_end ->
| Some bind_end ->
let binding = string.slice line 0 bind_end
let expr = string.slice line bind_end (string.length line)
io.load_script binding expr >> return True
| None -> io.print "Expected binding in definition" >> return True

let loop x: () -> IO () = repl_prim.input ">>" >>= \line_opt ->
match line_opt with
| None -> loop ()
| Some line ->
(if string.starts_with line ":" then
let loop x: () -> IO () =
let run_line line =
if string.is_empty (string.trim line) then
return True
else if string.starts_with line ":" then
do_command line
else if string.starts_with line "def " then
store (string.slice line 4 (string.length line))
else
io.catch (io.run_expr line) return >>= io.print >> return True) >>= \continue ->
if continue then
loop ()
else
return ()
io.catch (io.run_expr line) return
>>= io.print
>> return True

repl_prim.input "> " >>= \line_opt ->
match line_opt with
| None -> return ()
| Some line -> run_line line >>= \continue ->
if continue then loop () else return ()

let run x: () -> IO () =
io.print "gluon (:h for help, :q to quit)"
>>= loop

loop
run

0 comments on commit 2ed0a35

Please sign in to comment.