mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-25 18:49:50 -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 std::rc::Rc;
|
||||||
|
|
||||||
use simple_error;
|
use simple_error;
|
||||||
|
use rustyline;
|
||||||
|
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
use crate::error;
|
use crate::error;
|
||||||
@ -33,6 +34,8 @@ use crate::parse::parse;
|
|||||||
use crate::build::opcode::translate;
|
use crate::build::opcode::translate;
|
||||||
use crate::build::opcode::Environment;
|
use crate::build::opcode::Environment;
|
||||||
use crate::build::opcode::VM;
|
use crate::build::opcode::VM;
|
||||||
|
use crate::build::opcode::pointer::OpPointer;
|
||||||
|
use crate::build::opcode::translate::PositionMap;
|
||||||
|
|
||||||
pub mod assets;
|
pub mod assets;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
@ -202,6 +205,78 @@ where
|
|||||||
Ok(())
|
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(
|
pub fn eval_input(
|
||||||
&mut self,
|
&mut self,
|
||||||
input: OffsetStrIter,
|
input: OffsetStrIter,
|
||||||
|
@ -33,6 +33,10 @@ impl Stack {
|
|||||||
self.curr.get(name).cloned()
|
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 {
|
pub fn is_bound(&self, name: &str) -> bool {
|
||||||
self.curr.get(name).is_some()
|
self.curr.get(name).is_some()
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,13 @@ pub struct PositionMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PositionMap {
|
impl PositionMap {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
PositionMap {
|
||||||
|
ops: Vec::new(),
|
||||||
|
pos: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.ops.len()
|
self.ops.len()
|
||||||
}
|
}
|
||||||
@ -47,10 +54,7 @@ impl PositionMap {
|
|||||||
|
|
||||||
impl AST {
|
impl AST {
|
||||||
pub fn translate<P: AsRef<Path>>(stmts: Vec<Statement>, root: &P) -> PositionMap {
|
pub fn translate<P: AsRef<Path>>(stmts: Vec<Statement>, root: &P) -> PositionMap {
|
||||||
let mut ops = PositionMap {
|
let mut ops = PositionMap::new();
|
||||||
ops: Vec::new(),
|
|
||||||
pos: Vec::new(),
|
|
||||||
};
|
|
||||||
Self::translate_stmts(stmts, &mut ops, root.as_ref());
|
Self::translate_stmts(stmts, &mut ops, root.as_ref());
|
||||||
return ops;
|
return ops;
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,10 @@ where
|
|||||||
return C(Tuple(flds, pos_list));
|
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> {
|
pub fn run(&mut self) -> Result<(), Error> {
|
||||||
loop {
|
loop {
|
||||||
let op = if let Some(op) = self.ops.next() {
|
let op = if let Some(op) = self.ops.next() {
|
||||||
|
@ -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>)
|
fn test_expr_to_val<'a, O, E>(mut cases: Vec<(Expression, Val)>, mut b: FileBuilder<'a, O, E>)
|
||||||
where
|
where
|
||||||
O: std::io::Write,
|
O: std::io::Write + Clone,
|
||||||
E: std::io::Write,
|
E: std::io::Write + Clone,
|
||||||
{
|
{
|
||||||
for tpl in cases.drain(0..) {
|
for tpl in cases.drain(0..) {
|
||||||
assert_eq!(b.eval_expr(tpl.0).unwrap(), Rc::new(tpl.1));
|
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>> {
|
fn do_repl(import_paths: &Vec<PathBuf>) -> std::result::Result<(), Box<dyn Error>> {
|
||||||
let config = rustyline::Config::builder();
|
let config = rustyline::Config::builder();
|
||||||
let mut editor = rustyline::Editor::<()>::with_config(
|
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(),
|
StdoutWrapper::new(),
|
||||||
StderrWrapper::new(),
|
StderrWrapper::new(),
|
||||||
);
|
);
|
||||||
// loop
|
|
||||||
let mut lines = ucglib::io::StatementAccumulator::new();
|
builder.repl(editor, config_home)?;
|
||||||
println!("Welcome to the UCG repl. Ctrl-D to exit");
|
Ok(())
|
||||||
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()))?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn repl(import_paths: &Vec<PathBuf>) {
|
fn repl(import_paths: &Vec<PathBuf>) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user