219 lines
7.4 KiB
Rust
219 lines
7.4 KiB
Rust
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<StdoutLock<'static>>,
|
|
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<StdoutLock<'static>>,
|
|
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 <C-c> or types `:quit`
|
|
pub fn get(&mut self) -> Result<Option<String>, 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<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, 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(())
|
|
}
|