use crate::parser::ast::*; use crate::parser::parser::*; use crate::atom_table::*; use crate::forms::*; use crate::iterators::*; use crate::machine::heap::*; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; use crate::machine::machine_state::MachineState; use crate::machine::streams::*; use crate::parser::char_reader::*; use crate::repl_helper::Helper; use crate::types::*; use fxhash::FxBuildHasher; use indexmap::IndexSet; use rustyline::error::ReadlineError; use rustyline::{Config, Editor}; use std::collections::VecDeque; use std::io::{Cursor, Error, ErrorKind, Read}; type SubtermDeque = VecDeque<(usize, usize)>; impl MachineState { pub(crate) fn devour_whitespace( &mut self, mut inner: Stream, ) -> Result { let mut parser = Parser::new(inner, self); parser.devour_whitespace()?; inner.add_lines_read(parser.lines_read()); parser.eof() } pub(crate) fn read( &mut self, mut inner: Stream, op_dir: &OpDir, ) -> Result { let (term, num_lines_read) = { let prior_num_lines_read = inner.lines_read(); let mut parser = Parser::new(inner, self); parser.add_lines_read(prior_num_lines_read); let term = parser.read_term(&CompositeOpDir::new(op_dir, None)) .map_err(CompilationError::from)?; (term, parser.lines_read() - prior_num_lines_read) }; inner.add_lines_read(num_lines_read); write_term_to_heap(&term, &mut self.heap, &mut self.atom_tbl) } } static mut PROMPT: bool = false; const HISTORY_FILE: &'static str = ".scryer_history"; pub(crate) fn set_prompt(value: bool) { unsafe { PROMPT = value; } } #[inline] fn get_prompt() -> &'static str { unsafe { if PROMPT { "?- " } else { "" } } } #[derive(Debug)] pub struct ReadlineStream { rl: Editor, pending_input: Cursor, add_history: bool, } impl ReadlineStream { #[inline] pub fn new(pending_input: &str, add_history: bool) -> Self { let config = Config::builder().check_cursor_position(true).build(); let helper = Helper::new(); let mut rl = Editor::with_config(config); rl.set_helper(Some(helper)); if let Some(mut path) = dirs_next::home_dir() { path.push(HISTORY_FILE); if path.exists() && rl.load_history(&path).is_err() { println!("Warning: loading history failed"); } } // rl.bind_sequence(KeyEvent::from('\t'), Cmd::Insert(1, "\t".to_string())); ReadlineStream { rl, pending_input: Cursor::new(pending_input.to_owned()), add_history: add_history, } } pub fn set_atoms_for_completion(&mut self, atoms: *const IndexSet) { let helper = self.rl.helper_mut().unwrap(); helper.atoms = atoms; } #[inline] pub fn reset(&mut self) { self.pending_input.get_mut().clear(); self.pending_input.set_position(0); } fn call_readline(&mut self) -> std::io::Result { match self.rl.readline(get_prompt()) { Ok(text) => { *self.pending_input.get_mut() = text; self.pending_input.set_position(0); unsafe { if PROMPT { self.rl.history_mut().add(self.pending_input.get_ref()); self.save_history(); PROMPT = false; } } if self.pending_input.get_ref().chars().last() != Some('\n') { *self.pending_input.get_mut() += "\n"; } Ok(self.pending_input.get_ref().len()) } Err(ReadlineError::Eof) => Ok(0), Err(e) => Err(Error::new(ErrorKind::InvalidInput, e)), } } fn save_history(&mut self) { if !self.add_history { return; }; if let Some(mut path) = dirs_next::home_dir() { path.push(HISTORY_FILE); if path.exists() { if self.rl.append_history(&path).is_err() { println!("Warning: couldn't append history (existing file)"); } } else if self.rl.save_history(&path).is_err() { println!("Warning: couldn't save history (new file)"); } } } pub(crate) fn peek_byte(&mut self) -> std::io::Result { loop { match self.pending_input.get_ref().bytes().next() { Some(0) => { return Ok(0); } Some(b) => { return Ok(b); } None => match self.call_readline() { Err(e) => { return Err(e); } Ok(0) => { self.pending_input.get_mut().push('\u{0}'); return Ok(0); } _ => { set_prompt(false); } }, } } } } impl Read for ReadlineStream { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { match self.pending_input.read(buf) { Ok(0) => { self.call_readline()?; self.pending_input.read(buf) } result => result } } } impl CharRead for ReadlineStream { fn peek_char(&mut self) -> Option> { loop { let pos = self.pending_input.position() as usize; match self.pending_input.get_ref()[pos ..].chars().next() { Some('\u{0}') => { return Some(Ok('\u{0}')); } Some(c) => { return Some(Ok(c)); } None => { match self.call_readline() { Err(e) => { return Some(Err(e)); } Ok(0) => { self.pending_input.get_mut().push('\u{0}'); return Some(Ok('\u{0}')); } _ => { set_prompt(false); } } } } } } fn consume(&mut self, nread: usize) { let offset = self.pending_input.position() as usize; self.pending_input.set_position((offset + nread) as u64); } fn put_back_char(&mut self, c: char) { let offset = self.pending_input.position() as usize; self.pending_input.set_position((offset - c.len_utf8()) as u64); } } #[inline] pub(crate) fn write_term_to_heap( term: &Term, heap: &mut Heap, atom_tbl: &mut AtomTable, ) -> Result { let term_writer = TermWriter::new(heap, atom_tbl); term_writer.write_term_to_heap(term) } #[derive(Debug)] struct TermWriter<'a, 'b> { heap: &'a mut Heap, atom_tbl: &'b mut AtomTable, queue: SubtermDeque, var_dict: HeapVarDict, } #[derive(Debug)] pub struct TermWriteResult { pub heap_loc: usize, pub var_dict: HeapVarDict, } impl<'a, 'b> TermWriter<'a, 'b> { #[inline] fn new(heap: &'a mut Heap, atom_tbl: &'b mut AtomTable) -> Self { TermWriter { heap, atom_tbl, queue: SubtermDeque::new(), var_dict: HeapVarDict::with_hasher(FxBuildHasher::default()), } } #[inline] fn modify_head_of_queue(&mut self, term: &TermRef<'a>, h: usize) { if let Some((arity, site_h)) = self.queue.pop_front() { self.heap[site_h] = self.term_as_addr(term, h); if arity > 1 { self.queue.push_front((arity - 1, site_h + 1)); } } } #[inline] fn push_stub_addr(&mut self) { let h = self.heap.len(); self.heap.push(heap_loc_as_cell!(h)); } fn term_as_addr(&mut self, term: &TermRef<'a>, h: usize) -> HeapCellValue { match term { &TermRef::Cons(..) => list_loc_as_cell!(h), &TermRef::AnonVar(_) | &TermRef::Var(..) => heap_loc_as_cell!(h), &TermRef::CompleteString(_, _, ref src) => if src.as_str().is_empty() { empty_list_as_cell!() } else if self.heap[h].get_tag() == HeapCellValueTag::CStr { heap_loc_as_cell!(h) } else { pstr_loc_as_cell!(h) }, &TermRef::PartialString(..) => pstr_loc_as_cell!(h), &TermRef::Literal(_, _, literal) => HeapCellValue::from(*literal), &TermRef::Clause(_,_,_,subterms) if subterms.len() == 0 => heap_loc_as_cell!(h), &TermRef::Clause(..) => str_loc_as_cell!(h), } } fn write_term_to_heap(mut self, term: &'a Term) -> Result { let heap_loc = self.heap.len(); for term in breadth_first_iter(term, RootIterationPolicy::Iterated) { let h = self.heap.len(); match &term { &TermRef::Cons(Level::Root, ..) => { self.queue.push_back((2, h + 1)); self.heap.push(list_loc_as_cell!(h + 1)); self.push_stub_addr(); self.push_stub_addr(); continue; } &TermRef::Cons(..) => { self.queue.push_back((2, h)); self.push_stub_addr(); self.push_stub_addr(); } &TermRef::Clause(Level::Root, _, name, subterms) => { if subterms.len() > MAX_ARITY { return Err(CompilationError::ExceededMaxArity); } self.heap.push(if subterms.len() == 0 { heap_loc_as_cell!(heap_loc + 1) } else { str_loc_as_cell!(heap_loc + 1) }); self.queue.push_back((subterms.len(), h + 2)); let named = atom_as_cell!(name, subterms.len()); self.heap.push(named); for _ in 0..subterms.len() { self.push_stub_addr(); } continue; } &TermRef::Clause(_, _, name, subterms) => { self.queue.push_back((subterms.len(), h + 1)); let named = atom_as_cell!(name, subterms.len()); self.heap.push(named); for _ in 0..subterms.len() { self.push_stub_addr(); } } &TermRef::AnonVar(Level::Root) | &TermRef::Literal(Level::Root, ..) => { let addr = self.term_as_addr(&term, h); self.heap.push(addr); } &TermRef::Var(Level::Root, _, ref var_ptr) => { let addr = self.term_as_addr(&term, h); self.var_dict.insert(var_ptr.clone(), heap_loc_as_cell!(h)); self.heap.push(addr); } &TermRef::AnonVar(_) => { if let Some((arity, site_h)) = self.queue.pop_front() { if arity > 1 { self.queue.push_front((arity - 1, site_h + 1)); } } continue; } &TermRef::CompleteString(_, _, ref src) => { put_complete_string(self.heap, src.as_str(), self.atom_tbl); } &TermRef::PartialString(lvl, _, ref src, _) => { if let Level::Root = lvl { // Var tags can't refer directly to partial strings, // so a PStrLoc cell must be pushed. self.heap.push(pstr_loc_as_cell!(heap_loc + 1)); } allocate_pstr(self.heap, src.as_str(), self.atom_tbl); let h = self.heap.len(); self.queue.push_back((1, h - 1)); if let Level::Root = lvl { continue; } } &TermRef::Var(_, _, ref var) => { if let Some((arity, site_h)) = self.queue.pop_front() { if let Some(addr) = self.var_dict.get(var).cloned() { self.heap[site_h] = addr; } else { self.var_dict.insert(var.clone(), heap_loc_as_cell!(site_h)); } if arity > 1 { self.queue.push_front((arity - 1, site_h + 1)); } } continue; } _ => {} }; self.modify_head_of_queue(&term, h); } Ok(TermWriteResult { heap_loc, var_dict: self.var_dict, }) } }