mod ast;
mod eval;
mod lexer;
mod parser;
use std::fmt::{Binary, Display, LowerHex};
use num_traits::{CheckedRem, CheckedShl, CheckedShr, PrimInt, ToBytes, Zero};
use reedline::{DefaultPrompt, Reedline, Signal};
use crate::{ast::Expr, parser::parse};
enum Mode {
U8,
U16,
U32,
U64,
}
fn main() {
let mut line_editor = Reedline::create();
let prompt = DefaultPrompt::new(
reedline::DefaultPromptSegment::Empty,
reedline::DefaultPromptSegment::Empty,
);
let mut mode = Mode::U64;
println!("Welcome to bitcalc!");
println!("Type 'help' for available operations.");
loop {
let sig = line_editor.read_line(&prompt);
match sig {
Ok(Signal::Success(buffer)) => {
if buffer == "h" || buffer == "help" {
println!("Special commands:");
println!(" h, help display this help message");
println!(" bits=N set the number of bits to work on (8, 16, 32, 64)");
println!();
println!("Arithmetic operators:");
println!(" + add");
println!(" - subtract");
println!(" * multiply");
println!(" / divide");
println!(" % remainder");
println!(" ** exponentiation");
println!();
println!("Bit manipulation operators:");
println!(" ^ bitwise exclusive or");
println!(" | bitwise or");
println!(" & bitwise and");
println!(" << shift left");
println!(" >> shift right");
println!(" rotl rotate left");
println!(" rotr rotate right");
println!();
println!("Order of operations:");
println!(" **");
println!(" *, /, %");
println!(" +, -");
println!(" <<, >>, rotl, rotr");
println!(" &");
println!(" ^");
println!(" |");
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, &expr);
}
Err(err) => {
print_error(&buffer, err);
}
}
}
Ok(Signal::CtrlD) | Ok(Signal::CtrlC) => {
break;
}
x => {
println!("Event: {:?}", x);
}
}
}
}
fn do_eval(mode: &Mode, expr: &Expr) {
println!("Parsed:");
println!(" {expr}");
println!();
match mode {
Mode::U8 => eval_and_print::<u8>(expr),
Mode::U16 => eval_and_print::<u16>(expr),
Mode::U32 => eval_and_print::<u32>(expr),
Mode::U64 => eval_and_print::<u64>(expr),
}
}
fn eval_and_print<
I: Zero
+ PrimInt
+ TryFrom<u64>
+ TryInto<u32>
+ TryInto<usize>
+ CheckedRem
+ CheckedShl
+ CheckedShr
+ ToBytes
+ Display
+ LowerHex
+ Binary,
>(
expr: &Expr,
) {
let res = eval::eval::<I>(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 printed_bin: Vec<_> = x_bytes
.as_ref()
.iter()
.map(|x| format!("{x:08b}"))
.collect();
let printed_bin = printed_bin.join("_");
println!("Result:");
println!(" Dec: {x}");
println!(" Hex: 0x{printed_hex}");
println!(" Bin: 0b{printed_bin}");
}
Err(e) => {
println!("Error: {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();
}