From e058088a174945a38f3c3756e2fb59421b8b72e7 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Tue, 3 Sep 2019 18:38:06 -0500 Subject: [PATCH] DEV: Working repl; --- src/build/mod.rs | 75 +++++++++++++++++++++++++++++++++++ src/build/opcode/scope.rs | 4 ++ src/build/opcode/translate.rs | 12 ++++-- src/build/opcode/vm.rs | 4 ++ src/build/test.rs | 4 +- src/main.rs | 71 ++------------------------------- 6 files changed, 96 insertions(+), 74 deletions(-) diff --git a/src/build/mod.rs b/src/build/mod.rs index 968919f..d342465 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -24,6 +24,7 @@ use std::path::PathBuf; use std::rc::Rc; use simple_error; +use rustyline; use crate::ast::*; use crate::error; @@ -33,6 +34,8 @@ use crate::parse::parse; use crate::build::opcode::translate; use crate::build::opcode::Environment; use crate::build::opcode::VM; +use crate::build::opcode::pointer::OpPointer; +use crate::build::opcode::translate::PositionMap; pub mod assets; pub mod format; @@ -202,6 +205,78 @@ where Ok(()) } + pub fn repl(&mut self, mut editor: rustyline::Editor<()>, config_home: PathBuf) -> BuildResult { + // loop + let mut lines = crate::io::StatementAccumulator::new(); + println!("Welcome to the UCG repl. Ctrl-D to exit"); + println!("Type '#help' for help."); + println!(""); + // Initialize VM with an empty OpPointer + let mut vm = VM::new(Rc::new(PositionMap::new()), self.environment.clone(), &self.working_dir); + loop { + // print prompt + let line = editor.readline(&format!("{}> ", lines.next_line()))?; + // 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") { + println!(include_str!("../help/repl.txt")); + } 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 = args[0].to_string(); + if let None = vm.remove_symbol(&key) { + eprintln!("No such binding {}", key); + } + } + } else { + eprintln!("Invalid repl command..."); + eprintln!(""); + println!(include_str!("../help/repl.txt")); + } + continue; + } + lines.push(line); + // check to see if that line is a statement + loop { + // read a statement + if let Some(stmt) = lines.get_statement() { + // if it is then + // eval statement + let stmts = parse(OffsetStrIter::new(&stmt), None)?; + let ops = translate::AST::translate(stmts, &self.working_dir); + vm = vm.to_new_pointer(OpPointer::new(Rc::new(ops))); + match vm.run() { + // print the result + Err(e) => eprintln!("{}", e), + Ok(_) => { + match vm.last { + Some((ref val, _)) => { + println!("{}", val); + vm.last = None; + } + None => { + } + } + editor.history_mut().add(stmt); + editor.save_history(&config_home)?; + } + } + // start loop over at prompt. + break; + } + // if not then keep accumulating lines without a prompt + lines.push(editor.readline(&format!("{}> ", lines.next_line()))?); + } + } + } + + // TODO(jwall): The repl is going to have to be in here. pub fn eval_input( &mut self, input: OffsetStrIter, diff --git a/src/build/opcode/scope.rs b/src/build/opcode/scope.rs index 58b8a47..54893b2 100644 --- a/src/build/opcode/scope.rs +++ b/src/build/opcode/scope.rs @@ -33,6 +33,10 @@ impl Stack { self.curr.get(name).cloned() } + pub fn remove_symbol(&mut self, name: &str) -> Option<(Rc, Position)> { + self.curr.remove(name) + } + pub fn is_bound(&self, name: &str) -> bool { self.curr.get(name).is_some() } diff --git a/src/build/opcode/translate.rs b/src/build/opcode/translate.rs index 296d823..2993f2d 100644 --- a/src/build/opcode/translate.rs +++ b/src/build/opcode/translate.rs @@ -31,6 +31,13 @@ pub struct PositionMap { } impl PositionMap { + pub fn new() -> Self { + PositionMap { + ops: Vec::new(), + pos: Vec::new(), + } + } + pub fn len(&self) -> usize { self.ops.len() } @@ -47,10 +54,7 @@ impl PositionMap { impl AST { pub fn translate>(stmts: Vec, root: &P) -> PositionMap { - let mut ops = PositionMap { - ops: Vec::new(), - pos: Vec::new(), - }; + let mut ops = PositionMap::new(); Self::translate_stmts(stmts, &mut ops, root.as_ref()); return ops; } diff --git a/src/build/opcode/vm.rs b/src/build/opcode/vm.rs index c211034..7aa838e 100644 --- a/src/build/opcode/vm.rs +++ b/src/build/opcode/vm.rs @@ -140,6 +140,10 @@ where } return C(Tuple(flds, pos_list)); } + + pub fn remove_symbol(&mut self, sym: &str) -> Option<(Rc, Position)> { + self.symbols.remove_symbol(sym) + } pub fn run(&mut self) -> Result<(), Error> { loop { diff --git a/src/build/test.rs b/src/build/test.rs index 977c490..39ee240 100644 --- a/src/build/test.rs +++ b/src/build/test.rs @@ -23,8 +23,8 @@ use std::rc::Rc; fn test_expr_to_val<'a, O, E>(mut cases: Vec<(Expression, Val)>, mut b: FileBuilder<'a, O, E>) where - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, { for tpl in cases.drain(0..) { assert_eq!(b.eval_expr(tpl.0).unwrap(), Rc::new(tpl.1)); diff --git a/src/main.rs b/src/main.rs index dc0b2cd..8dbdd0d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -400,10 +400,6 @@ fn env_help() { ); } -fn print_repl_help() { - println!(include_str!("help/repl.txt")); -} - fn do_repl(import_paths: &Vec) -> std::result::Result<(), Box> { let config = rustyline::Config::builder(); let mut editor = rustyline::Editor::<()>::with_config( @@ -442,70 +438,9 @@ fn do_repl(import_paths: &Vec) -> std::result::Result<(), Box ", lines.next_line()))?; - // 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(), - }; - // FIXME(jwall): handle this in an actual repl driver? - //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 - if let Some(stmt) = lines.get_statement() { - // if it is then - // eval statement - match builder.eval_string(&stmt) { - // print the result - Err(e) => eprintln!("{}", e), - Ok(v) => { - if builder.out.is_some() { - builder.out = None; - } else { - println!("{}", v); - editor.history_mut().add(stmt); - editor.save_history(&config_home)?; - } - } - } - // start loop over at prompt. - break; - } - // if not then keep accumulating lines without a prompt - lines.push(editor.readline(&format!("{}> ", lines.next_line()))?); - } - } + + builder.repl(editor, config_home)?; + Ok(()) } fn repl(import_paths: &Vec) {