DEV: Working repl;

This commit is contained in:
Jeremy Wall 2019-09-03 18:38:06 -05:00
parent d092fba5e8
commit e058088a17
6 changed files with 96 additions and 74 deletions

View File

@ -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,

View File

@ -33,6 +33,10 @@ impl Stack {
self.curr.get(name).cloned()
}
pub fn remove_symbol(&mut self, name: &str) -> Option<(Rc<Value>, Position)> {
self.curr.remove(name)
}
pub fn is_bound(&self, name: &str) -> bool {
self.curr.get(name).is_some()
}

View File

@ -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<P: AsRef<Path>>(stmts: Vec<Statement>, 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;
}

View File

@ -140,6 +140,10 @@ where
}
return C(Tuple(flds, pos_list));
}
pub fn remove_symbol(&mut self, sym: &str) -> Option<(Rc<Value>, Position)> {
self.symbols.remove_symbol(sym)
}
pub fn run(&mut self) -> Result<(), Error> {
loop {

View File

@ -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));

View File

@ -400,10 +400,6 @@ fn env_help() {
);
}
fn print_repl_help() {
println!(include_str!("help/repl.txt"));
}
fn do_repl(import_paths: &Vec<PathBuf>) -> std::result::Result<(), Box<dyn Error>> {
let config = rustyline::Config::builder();
let mut editor = rustyline::Editor::<()>::with_config(
@ -442,70 +438,9 @@ fn do_repl(import_paths: &Vec<PathBuf>) -> std::result::Result<(), Box<dyn Error
StdoutWrapper::new(),
StderrWrapper::new(),
);
// 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
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") {
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<PathBuf>) {