diff --git a/Cargo.lock b/Cargo.lock index f9c8d7e..3442e87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "calc" version = "0.1.0" dependencies = [ "signal-hook", - "termcolor", + "termion", ] [[package]] @@ -16,6 +22,30 @@ version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_termios" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" +dependencies = [ + "redox_syscall", +] + [[package]] name = "signal-hook" version = "0.3.15" @@ -36,41 +66,13 @@ dependencies = [ ] [[package]] -name = "termcolor" -version = "1.2.0" +name = "termion" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "659c1f379f3408c7e5e84c7d0da6d93404e3800b6b9d063ba24436419302ec90" dependencies = [ - "winapi-util", + "libc", + "numtoa", + "redox_syscall", + "redox_termios", ] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index d8f637e..7e40d36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,5 @@ panic = "abort" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -#dialoguer = "0.10.3" -#colored = "2.0.0" -termcolor = "1.2.0" -signal-hook = "0.3.15" \ No newline at end of file +signal-hook = "0.3.15" +termion = "2.0.1" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 5376072..073be58 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,110 +1,103 @@ -use std::io; -use std::io::Write; -//use std::io::Read; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; - - - -use termcolor::{ - Color, - ColorChoice, - ColorSpec, - StandardStream, - WriteColor -}; +use std::io::{Write, stdout, stdin}; +use termion::event::Key; +use termion::input::TermRead; +use termion::raw::IntoRawMode; +use termion::raw::RawTerminal; +use termion::{color, style}; mod parser; -use crate::parser::Token; +//use crate::parser::Token; //use crate::parser::ParserError; use crate::parser::LineLocation; -const PROMPT_PREFIX: &str = "==> "; -/// Show a prompt and save trimmed input to `input`. -/// -/// # Arguments: -/// -/// * `stdout`: Where we should write the prompt -/// * `input`: Where we should save user input -/// -/// # Example usage: -/// ``` -/// let mut input = String::new(); -/// prompt(&mut stdout, &mut input)?; -/// ``` -fn prompt( - stdout: &mut StandardStream, - input: &mut String -) -> Result<(), std::io::Error> { +fn draw_line(stdout: &mut RawTerminal, s: &String) -> Result<(), std::io::Error> { + write!( + stdout, + "\r{}{}==>{}{} {s} {}", + style::Bold, + color::Fg(color::Blue), + color::Fg(color::Reset), + style::Reset, - // Print colored prompt prefix - stdout.set_color(ColorSpec::new().set_fg(Some(Color::Blue)))?; - write!(*stdout, "{PROMPT_PREFIX}")?; - stdout.reset()?; // reset colors - stdout.flush()?; // flush, we didn't print a full line yet. + // Our string can change by at most one character each iteration. + // Clearing is done by inserting an extra space, then moving the cursor left. + termion::cursor::Left(1) + )?; + stdout.flush()?; - // Ask for input - io::stdin().read_line(input)?; - - // If this input doesn't end with a newline, - // the user terminated this prompt with ctrl-d. - // Add a newline to keep spacing consistent, - // and clear the input. - if match input.chars().last() { - Some(val) => val != '\n', - None => true - } { - write!(*stdout, "\n")?; - input.clear(); - } else { - (*input) = input.trim().to_string(); - } - - Ok(()) + return Ok(()); } - fn main() -> Result<(), std::io::Error> { + let mut stdout = stdout().into_raw_mode().unwrap(); - let mut stdout = StandardStream::stdout(ColorChoice::Always); + //let size = termion::terminal_size().unwrap(); + //write!(stdout, "{:?}", size).unwrap(); - let term = Arc::new(AtomicBool::new(false)); - signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&term))?; - while !term.load(Ordering::Relaxed) { - let mut input = String::with_capacity(64); - prompt(&mut stdout, &mut input).expect("Could not show prompt"); - let input = input; + let mut s: String = String::with_capacity(64); - // Ignore empty input - if input == "" { - stdout.flush()?; - continue; + 'outer: loop { + + s.clear(); + draw_line(&mut stdout, &s)?; + + let stdin = stdin(); + for c in stdin.keys() { + if let Key::Char(q) = c.as_ref().unwrap() { + match q { + '\n' => { + let s = s.trim().to_string(); + if s == "" { write!(stdout, "\r\n")?; break; } + + RawTerminal::suspend_raw_mode(&stdout)?; + write!(stdout, "\n")?; + let g = parser::parse(&s); + RawTerminal::activate_raw_mode(&stdout)?; + + match g { + Ok(g) => { + RawTerminal::suspend_raw_mode(&stdout)?; + writeln!(stdout, "Tokenized: {g:#?}")?; + RawTerminal::activate_raw_mode(&stdout)?; + }, + Err((l, e)) => { + let LineLocation{pos, len} = l; + write!( + stdout, + "{}{}{} {e:?}{}\r\n", + color::Fg(color::Red), + " ".repeat(pos + 4), + "^".repeat(len), + color::Fg(color::Reset), + )?; + } + }; + + break; + }, + '/' => { s.push('÷'); }, + '*' => { s.push('×'); }, + _ => { s.push(*q); } + }; + } else { + match c.unwrap() { + Key::Backspace => { s.pop(); }, + Key::Delete => { s.pop(); }, + Key::Left => {}, + Key::Right => {}, + Key::Up => {}, + Key::Down => {}, + + Key::Ctrl('d') | + Key::Ctrl('c') => { break 'outer; }, + _ => {} + }; + }; + draw_line(&mut stdout, &s)?; } - - // Parse input. - // Fail if we encounter invalid characters. - let g: Token = match parser::parse(&input) { - Ok(g) => g, - Err((l, e)) => { - let LineLocation{pos, len} = l; - - let s = " "; - let m = "^"; - println!("{}{} {:?}", s.repeat(pos + 4), m.repeat(len), e); - stdout.flush()?; - continue; - } - }; - - stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; - write!(stdout, "\n => ")?; - stdout.reset()?; - write!(stdout, "Got {input}\n\n\n")?; - - writeln!(stdout, "Tokenized: {g:#?}")?; } - writeln!(stdout, "Exiting.")?; - Ok(()) + write!(stdout, "\r\n")?; + return Ok(()); } \ No newline at end of file diff --git a/src/parser/tokenize.rs b/src/parser/tokenize.rs index 1b3fea2..3cb847e 100644 --- a/src/parser/tokenize.rs +++ b/src/parser/tokenize.rs @@ -211,15 +211,18 @@ pub fn tokenize(input: &String) -> Result { // Operator // Always one character - '+' | '*' | '/' | '^' | '%' => { + + '*'|'×'| + '/'|'÷'| + '+'|'%'|'^' => { push_token(g_now, i, t)?; t = Some(Token::PreOperator( LineLocation{pos: i, len: 1}, match c { '^' => Operators::Power, '%' => Operators::Modulo, - '*' => Operators::Multiply, - '/' => Operators::Divide, + '*'|'×' => Operators::Multiply, + '/'|'÷' => Operators::Divide, '+' => Operators::Add, _ => panic!() }