From c5d608216a5090723e795beb6aa53af460c60f07 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Tue, 28 May 2019 20:28:40 -0500 Subject: [PATCH] FEATURE: Repl commands. Adds: #help and #del Closes #46 --- src/build/mod.rs | 4 ++++ src/help/repl.txt | 18 ++++++++++++++++++ src/io/mod.rs | 4 ++++ src/main.rs | 37 ++++++++++++++++++++++++++++++++++++- 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/help/repl.txt diff --git a/src/build/mod.rs b/src/build/mod.rs index 92c2da7..54b437e 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -295,6 +295,10 @@ where self.scope.lookup_sym(&key, true) } + pub fn scope_mut(&mut self) -> &mut Scope { + &mut self.scope + } + /// Puts the builder in validation mode. /// /// Among other things this means that assertions will be evaluated and their results diff --git a/src/help/repl.txt b/src/help/repl.txt new file mode 100644 index 0000000..da4ac5f --- /dev/null +++ b/src/help/repl.txt @@ -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 + - deletes a previously created named binding + \ No newline at end of file diff --git a/src/io/mod.rs b/src/io/mod.rs index 7fcbb58..df38f55 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -28,6 +28,10 @@ impl StatementAccumulator { 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. /// /// Returns None if it wasn't a terminated statement and leaves the diff --git a/src/main.rs b/src/main.rs index 8d98967..f2fd49a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -513,6 +513,10 @@ fn env_help() { ); } +fn print_repl_help() { + println!(include_str!("help/repl.txt")); +} + fn do_repl( import_paths: &Vec, cache: Rc>, @@ -552,10 +556,41 @@ fn do_repl( // loop let mut lines = ucglib::io::StatementAccumulator::new(); println!("Welcome to the UCG repl. Ctrl-D to exit"); + println!("Type '#help' for help."); println!(""); loop { // 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 loop { // read a statement