use std::io::{self, StdoutLock, Write, stdout}; use std::rc::Rc; use libopenbirch::environment::Environment; use libopenbirch::node::constant::Constant; use libopenbirch::node::empty::Empty; use libopenbirch::node::{Node, NodeEnum}; use libopenbirch::parser::{Lexer, Parser, ParserError}; use malachite::base::num::basic::traits::Zero; use malachite::rational::Rational; #[cfg(feature = "async")] use termion::AsyncReader; use termion::color; use termion::event::Key; use termion::input::TermReadEventsAndRaw; use termion::raw::{IntoRawMode, RawTerminal}; pub struct Input { #[cfg(not(feature = "async"))] stdin: std::io::Stdin, stdout: RawTerminal>, buffer: String, current_char: usize, } impl Input { pub fn new() -> Self { #[cfg(not(feature = "async"))] let stdin = std::io::stdin(); let stdout = stdout().lock().into_raw_mode().unwrap(); Self { stdin, stdout, buffer: "".into(), current_char: 0, } } #[inline] fn draw_line( buffer: &String, stdout: &mut RawTerminal>, current_char: usize, ) { print!("\r{}> {}", termion::clear::CurrentLine, buffer,); let left_diff = buffer.len() - current_char; if left_diff > 0 { print!("{}", termion::cursor::Left(left_diff.try_into().unwrap())); } let _ = stdout.flush(); } /// Gets input from `io::stdin`. /// Returns `None` when the user presses or types `:quit` pub fn get(&mut self) -> Result, io::Error> { Self::draw_line(&self.buffer, &mut self.stdout, self.current_char); loop { for b in (&mut self.stdin).events_and_raw() { let (event, _slice) = b?; match event { termion::event::Event::Key(key) => match key { Key::Char(char) if char == '\n' => { if &self.buffer == ":quit" { println!("\r"); return Err(io::Error::from(io::ErrorKind::Interrupted)); } let r = self.buffer.clone(); self.buffer.clear(); self.current_char = 0; print!("\n\r"); return Ok(Some(r)); } Key::Backspace => { if self.current_char == 0 { continue; } self.current_char -= 1; self.buffer.remove(self.current_char); Self::draw_line(&self.buffer, &mut self.stdout, self.current_char); let _ = self.stdout.flush(); } Key::Delete => { if self.current_char == self.buffer.len() { continue; } self.buffer.remove(self.current_char); Self::draw_line(&self.buffer, &mut self.stdout, self.current_char); let _ = self.stdout.flush(); } Key::Left => { if self.current_char == 0 { continue; } self.current_char -= 1; print!("{}", termion::cursor::Left(1)); let _ = self.stdout.flush(); } Key::Right => { if self.current_char == self.buffer.len() { continue; } self.current_char += 1; print!("{}", termion::cursor::Right(1)); let _ = self.stdout.flush(); } Key::Char(char) => { self.buffer.insert(self.current_char, char); self.current_char += 1; Self::draw_line(&self.buffer, &mut self.stdout, self.current_char); } Key::Ctrl(c) if c == 'c' => { println!("\r"); return Err(io::Error::from(io::ErrorKind::Interrupted)); } _ => { #[cfg(debug_assertions)] return Err(io::Error::other(format!( "Key {key:?} is not implemented" ))); } }, termion::event::Event::Mouse(mouse_event) => {} termion::event::Event::Unsupported(items) => {} } } } } pub fn disable_raw(&mut self) -> io::Result<()> { self.stdout.suspend_raw_mode() } pub fn enable_raw(&mut self) -> io::Result<()> { self.stdout.activate_raw_mode() } } fn print_err(i: usize, len: usize, exp: String) { println!( "\r{}{}{}", " ".repeat(i + 3 - len), color::Fg(color::Yellow), "^".repeat(len) ); println!( "\r{}{}{}", color::Fg(color::Red), exp, color::Fg(color::Reset) ); } fn print(args: &Vec>, env: &mut Environment) -> Result, String> { for expr in args { println!("\r{}", expr.as_string(Some(env))); } Ok(Empty::new("")) } fn main() -> Result<(), io::Error> { let mut input = Input::new(); let mut env = Environment::new(); env.define_native_function("print", print); while let Some(source) = input.get()? { input.disable_raw()?; let mut lexer = Lexer::new(&source); let tokens_result = lexer.lex(); if tokens_result.is_err() { match tokens_result.err().unwrap() { libopenbirch::parser::LexerError::UnexpectedChar(i, exp) => print_err(i, 1, exp), } continue; } let tokens = tokens_result.unwrap(); let mut parser = Parser::new(tokens, &mut env); let nodes = match parser.parse() { Ok(nodes) => nodes, Err(err) => { match err { ParserError::UnexpectedEndOfTokens(exp) => print_err(source.len(), 1, exp), ParserError::UnexpectedToken(i, len, exp) | ParserError::Unimplemented(i, len, exp) => print_err(i, len, exp), ParserError::UnexpectedNode(i, exp) => print_err(i, 1, exp), } continue; } }; print!("{}", color::Fg(color::Blue)); for node in nodes { let evaluated = node.evaluate(&mut env); match evaluated { Ok(result) => println!("\r\t{}", result.as_string(Some(&env))), Err(exp) => print_err(0, 1, exp), } } input.enable_raw()?; print!("{}", color::Fg(color::Reset)); // println!("\t{}{source}{}", termion::color::Fg(termion::color::Blue), termion::color::Fg(termion::color::Reset)); } Ok(()) }