This is an old revision of the document!
Using the REPL
The Relision library provides a generic read, evaluate, print loop (a REPL) that is available through the `relision::repl::Repl` struct. This provides the following services.
- Loading and saving of command history
- Reading lines from the prompt with history
- Detecting and executing commands, including providing help
- Word wrapping of the output
- Coloring of error and other message types.
The REPL support a concept of commands. These are indicated by a leading colon, such as :help.
Simplest Use
The minimum that must be done is to make a new instance of the Repl struct and then invoke its run method. This will not load or save any history, and it will provide the default set of commands and will, by default, just echo other lines.
The one wrinkle is that you must provide some data to the constructor. Why this is so will become clear later. For now, let's just provide &() to the constructor.
use relision::repl; fn main() { let mut repl = repl::Repl::new(()); repl.run(); }
We can try this.
Commands start with a colon (:). Enter :help for help. > :help Commands can be abbreviated to the first few characters that uniquely identify them. :help Access the help system. :history Provide help on commands. :info Provide system information. :quit Quit the REPL. > :fred Error: Command fred not found. > fred > :quit
Adding Line Evaluation
Let's add a custom evaluator for lines. Our “evaluation” will consist of just echoing the line, converted to all uppercase and if the line is “QUIT” then we will also exit the REPL.
To do this we need to create a function that takes a &String and a relision::repl::ReplContext. The &String is the line to evaluate, and the ReplContext packages information from the REPL that we might need. The return is an Option<String> and a Boolean. The return value is interpreted by the REPL as follows.
- If the returned
Option<String>is notNonethen it is output. - If the Boolean is
truethen the REPL continues, and if it isfalsethe REPL terminates.
The function we want looks like this.
fn echo(line: &String, _context: ReplContext) -> (Option<String>, bool) { let answer = line.to_uppercase(); let cont = answer != "QUIT"; (Some(answer), cont) }
We install it as the evaluation function after creating the instance and before invoking run.
fn main() { let mut repl = repl::Repl::new(&()); repl.set_eval(echo); repl.run(); }
Let's try this.
Commands start with a colon (:). Enter :help for help. > :help Commands can be abbreviated to the first few characters that uniquely identify them. :help Access the help system. :history Provide help on commands. :info Provide system information. :quit Quit the REPL. > :fred Error: Command fred not found. > fred FRED > quit QUIT
Adding a New Command
We can add a new command to the REPL using the add_command method. We need to provide the command name, the help text, and a callback for the command. The callback has the same form as for the evaluation method.
We will use our echo function for this, but we don't want to quit when the user enters quit, so we just modify the returned Boolean to always be true. Again, we need to install this command after creating the REPL instance and before invoking run. While we are at it, let's also install a “no-op” command that does nothing. We could do this with a closure, as before: |_,_| (None, true), but let's use a function pointer, instead.
We will also change the prompt, using set_prompt.
fn noop(_line: &String, _context: ReplContext) -> (Option<String>, bool) { (None, true) } fn main() { let mut repl = repl::Repl::new(&()); repl.set_eval(echo); // Install a custom command (using a closure). repl.add_command( "echo".to_string(), "Echo the rest of the line in uppercase.".to_string(), "Usage: :echo [line]\n\ Echo the provided line, after converting it to all upper case." .to_string(), |line, context| (echo(line, context).0, true), ); // Install a custom colon command (using a function pointer). repl.add_command( "noop".to_string(), "Perform no action.".to_string(), "Usage: :noop\n\ Ignore any arguments and perform no action. Could be a comment." .to_string(), noop, ); // Set the prompt and start the REPL. repl.set_prompt("repl> ".to_string()); repl.run(); }
Again, let's try this.
Commands start with a colon (:). Enter :help for help. repl> :help Commands can be abbreviated to the first few characters that uniquely identify them. :echo Echo the rest of the line in uppercase. :help Access the help system. :history Provide help on commands. :info Provide system information. :noop Perform no action. :quit Quit the REPL. repl> :echo I'm not shouting. I'M NOT SHOUTING. repl> :noop This is ignored. repl> :echo quit QUIT repl> quit QUIT
User Data
Our functions will probably want to do something a little more sophisticated, so let's take a look at ReplContext. We get some important things packaged in this struct.
- The line editor (provided by the rustyline crate) being used by the REPL, which gives us access to line history as well as the ability to read lines ourselves and have those lines be part of the same history.
- The terminal (provided by the console crate) that lets us provide a consistent output, though this is not essential.
- The current value of the “quiet” flag, that lets us decide whether we should provide output. If we simply return some string, that string is always printed.
- Some user data, which can hold anything we want.