mod ast;
mod eval;
mod lexer;
mod parser;
use std::fmt::{Binary, Display, LowerHex};
use num_traits::{CheckedRem, CheckedShl, CheckedShr, PrimInt, ToBytes, Zero};
use rustyline::DefaultEditor;
use rustyline::error::ReadlineError;
use crate::{ast::Expr, parser::parse};
mod colors {
use std::fmt::Display;
pub const RED: Seq = Seq(31);
pub const GREEN: Seq = Seq(32);
pub const YELLOW: Seq = Seq(33);
pub const BLUE: Seq = Seq(34);
pub const PURPLE: Seq = Seq(35);
pub const RESET: Seq = Seq(0);
pub const GRAY: Seq = Seq(2);
pub const BOLD: Seq = Seq(1);
pub const UNDERLINE: Seq = Seq(4);
pub struct Seq(u8);
impl Display for Seq {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\x1B[{}m", self.0)
}
}
}
use colors::*;
enum Mode {
U8,
U16,
U32,
U64,
}
fn main() {
let mut line_editor = DefaultEditor::new().unwrap();
let mut mode = Mode::U64;
println!("Welcome to bitcalc!");
println!("Type 'help' for available operations.");
let mut answers = Vec::new();
loop {
let prompt = format!("{GRAY}[{:>3}]{RESET} {BOLD}{GREEN}>{RESET} ", answers.len());
let line = line_editor.readline(&prompt);
match line {
Ok(buffer) => {
if buffer.trim().is_empty() {
continue;
}
if buffer == "q" || buffer == "quit" {
break;
}
if buffer == "h" || buffer == "help" {
println!("{UNDERLINE}{BOLD}Special commands{RESET}");
println!(" {BLUE}q{RESET}, {BLUE}quit{RESET} quit the application");
println!(" {BLUE}h{RESET}, {BLUE}help{RESET} display this help message");
println!(
" {BLUE}bits=N{RESET} set the number of bits to work on (8, 16, 32, 64)"
);
println!();
println!("{UNDERLINE}{BOLD}History values{RESET}");
println!(" {BLUE}_{RESET} result of the last successful calculation");
println!(
" {BLUE}_N{RESET} get a value from the history with N being the index"
);
println!();
println!("{UNDERLINE}{BOLD}Arithmetic operators{RESET}");
println!(" {BLUE}+{RESET} add");
println!(" {BLUE}-{RESET} subtract");
println!(" {BLUE}*{RESET} multiply");
println!(" {BLUE}/{RESET} divide");
println!(" {BLUE}%{RESET} remainder");
println!(" {BLUE}**{RESET} exponentiation");
println!();
println!("{UNDERLINE}{BOLD}Bit manipulation operators{RESET}");
println!(" {BLUE}^{RESET} bitwise exclusive or");
println!(" {BLUE}|{RESET} bitwise or");
println!(" {BLUE}&{RESET} bitwise and");
println!(" {BLUE}<<{RESET} shift left");
println!(" {BLUE}>>{RESET} shift right");
println!(" {BLUE}rotl{RESET} rotate left");
println!(" {BLUE}rotr{RESET} rotate right");
println!();
println!("{UNDERLINE}{BOLD}Order of operations{RESET}");
println!(" {BLUE}**{RESET}");
println!(" {BLUE}*{RESET}, {BLUE}/{RESET}, {BLUE}%{RESET}");
println!(" {BLUE}+{RESET}, {BLUE}-{RESET}");
println!(
" {BLUE}<<{RESET}, {BLUE}>>{RESET}, {BLUE}rotl{RESET}, {BLUE}rotr{RESET}"
);
println!(" {BLUE}&{RESET}");
println!(" {BLUE}^{RESET}");
println!(" {BLUE}|{RESET}");
println!();
println!("Parentheses can be used to group operations.");
continue;
}
if let Some(rest) = buffer.strip_prefix("bits=") {
match rest.parse() {
Ok(b) => {
mode = match b {
8 => Mode::U8,
16 => Mode::U16,
32 => Mode::U32,
64 => Mode::U64,
_ => {
println!("Error: number of bits must be 8, 16, 32 or 64.");
continue;
}
};
}
Err(e) => {
println!("Error: {e}");
}
}
continue;
}
let parsed = parse(&buffer);
match parsed {
Ok(expr) => {
do_eval(&mode, &mut answers, &expr);
}
Err(err) => {
print_error(&buffer, err);
}
}
}
Err(ReadlineError::Interrupted | ReadlineError::Eof) => {
break;
}
x => {
println!("Event: {:?}", x);
}
}
}
}
fn do_eval(mode: &Mode, answers: &mut Vec<u64>, expr: &Expr) {
match mode {
Mode::U8 => eval_and_print::<u8>(answers, expr),
Mode::U16 => eval_and_print::<u16>(answers, expr),
Mode::U32 => eval_and_print::<u32>(answers, expr),
Mode::U64 => eval_and_print::<u64>(answers, expr),
}
}
fn eval_and_print<
I: Zero
+ PrimInt
+ TryFrom<u64>
+ TryInto<u64>
+ TryInto<u32>
+ TryInto<usize>
+ CheckedRem
+ CheckedShl
+ CheckedShr
+ ToBytes
+ Display
+ LowerHex
+ Binary,
>(
answers: &mut Vec<u64>,
expr: &Expr,
) {
let res = eval::eval::<I>(answers, expr);
match res {
Ok(x) => {
let x_bytes = x.to_be_bytes();
let printed_hex: Vec<_> = x_bytes
.as_ref()
.iter()
.map(|x| format!("{:_>8}", format!("{x:02x}")))
.collect();
let printed_hex = printed_hex.join("_");
let (hex_dim, hex_bright) = match printed_hex.find(|c| c != '_' && c != '0') {
Some(x) => printed_hex.split_at(x),
None => (printed_hex.as_ref(), ""),
};
let printed_bin: Vec<_> = x_bytes
.as_ref()
.iter()
.map(|x| format!("{x:08b}"))
.collect();
let printed_bin = printed_bin.join("_");
let (bin_dim, bin_bright) = match printed_bin.find(|c| c != '_' && c != '0') {
Some(x) => printed_bin.split_at(x),
None => (printed_bin.as_ref(), ""),
};
println!();
println!(" {GRAY}{expr}{RESET} = {BLUE}{x}{RESET}");
println!();
println!(" Hex: {GRAY}0x{hex_dim}{RESET}{BLUE}{hex_bright}{RESET}");
println!(" Bin: {GRAY}0b{bin_dim}{RESET}{BLUE}{bin_bright}{RESET}");
println!();
answers.push(x.try_into().ok().unwrap());
}
Err(e) => {
println!("{RED}Error:{RESET} {e}");
}
}
}
fn print_error(input: &str, err: parser::ParseError) {
use ariadne::{ColorGenerator, Label, Report, ReportKind, Source};
let mut colors = ColorGenerator::new();
let a = colors.next();
let mut builder =
Report::build(ReportKind::Error, ("input", 0..0)).with_message(err.kind().to_string());
if let Some(span) = err.span() {
builder = builder.with_label(
Label::new(("input", span.clone()))
.with_message(err.kind().to_string())
.with_color(a),
);
}
builder
.finish()
.print(("input", Source::from(input)))
.unwrap();
}