mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DEV: Working repl;
This commit is contained in:
parent
d092fba5e8
commit
e058088a17
@ -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,
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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));
|
||||
|
71
src/main.rs
71
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<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>) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user