FEATURE: Repl commands.

Adds: #help and #del

Closes #46
This commit is contained in:
Jeremy Wall 2019-05-28 20:28:40 -05:00
parent 879a8d2100
commit c5d608216a
4 changed files with 62 additions and 1 deletions

View File

@ -295,6 +295,10 @@ where
self.scope.lookup_sym(&key, true) self.scope.lookup_sym(&key, true)
} }
pub fn scope_mut(&mut self) -> &mut Scope {
&mut self.scope
}
/// Puts the builder in validation mode. /// Puts the builder in validation mode.
/// ///
/// Among other things this means that assertions will be evaluated and their results /// Among other things this means that assertions will be evaluated and their results

18
src/help/repl.txt Normal file
View File

@ -0,0 +1,18 @@
UCG repl help
The UCG repl allows you to enter ucg statements interactively. You can import
files create named bindings and see the result of UCG expressions.
The repl understands some non-ucg command as well. Any command that starts with
a '#' character is treated as a ucg repl command and is not parsed as a ucg
statement. Repl commands are invalid inside a ucg statement and will result
in a ucg parse error.
Currently supported commands are:
* #help
- prints out this help text
* #del <name>
- deletes a previously created named binding

View File

@ -28,6 +28,10 @@ impl StatementAccumulator {
self.acc.len() + 1 self.acc.len() + 1
} }
pub fn last_line(&self) -> Option<&String> {
self.acc.last()
}
/// Tells you if the latest line ends in the statement terminator. /// Tells you if the latest line ends in the statement terminator.
/// ///
/// Returns None if it wasn't a terminated statement and leaves the /// Returns None if it wasn't a terminated statement and leaves the

View File

@ -513,6 +513,10 @@ fn env_help() {
); );
} }
fn print_repl_help() {
println!(include_str!("help/repl.txt"));
}
fn do_repl<C: Cache>( fn do_repl<C: Cache>(
import_paths: &Vec<PathBuf>, import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<C>>, cache: Rc<RefCell<C>>,
@ -552,10 +556,41 @@ fn do_repl<C: Cache>(
// loop // loop
let mut lines = ucglib::io::StatementAccumulator::new(); let mut lines = ucglib::io::StatementAccumulator::new();
println!("Welcome to the UCG repl. Ctrl-D to exit"); println!("Welcome to the UCG repl. Ctrl-D to exit");
println!("Type '#help' for help.");
println!(""); println!("");
loop { loop {
// print prompt // print prompt
lines.push(editor.readline(&format!("{}> ", lines.next_line()))?); let line = editor.readline(&format!("{}> ", lines.next_line()))?;
// TODO check for a repl command.
// repl commands are only valid while not accumulating a statement;
let trimmed = line.trim();
if trimmed.starts_with("#") {
// handle the various commands.
if trimmed.starts_with("#help") {
print_repl_help();
} else if trimmed.starts_with("#del") {
// remove a named binding from the builder output.
let args: Vec<&str> = trimmed.split(" ").skip(1).collect();
if args.len() != 1 {
// print usage of the #del command
eprintln!("The '#del' command expects a single argument specifying \nthe binding to delete.");
} else {
let key = ucglib::ast::PositionedItem {
pos: ucglib::ast::Position::new(0, 0, 0),
val: args[0].to_string(),
};
if let None = builder.scope_mut().build_output.remove(&key) {
eprintln!("No such binding {}", key.val);
}
}
} else {
eprintln!("Invalid repl command...");
eprintln!("");
print_repl_help();
}
continue;
}
lines.push(line);
// check to see if that line is a statement // check to see if that line is a statement
loop { loop {
// read a statement // read a statement