diff --git a/Cargo.lock b/Cargo.lock index 35bd457..ecdcdf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,14 +118,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "fairy-ring" -version = "0.1.0" -dependencies = [ - "clap", - "hyphae", -] - [[package]] name = "hashbrown" version = "0.15.4" diff --git a/Cargo.toml b/Cargo.toml index 32c5fb0..df6ee70 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ cargo-features = ["profile-rustflags"] [workspace] resolver = "2" -members = ["mycelium", "decomposer", "hyphae", "organelle", "fairy-ring"] +members = ["mycelium", "decomposer", "hyphae", "organelle"] [profile.release] opt-level = 3 diff --git a/hyphae/build.rs b/hyphae/build.rs index 2502a65..7984d48 100644 --- a/hyphae/build.rs +++ b/hyphae/build.rs @@ -105,21 +105,13 @@ fn main() { let mut isa_fromstr = "impl FromStr for Operation {\n".to_owned(); isa_fromstr += " type Err = &'static str;\n"; isa_fromstr += " fn from_str(v: &str) -> Result {\n"; - isa_fromstr += " let a = v.to_ascii_uppercase();\n"; - isa_fromstr += " let v = a.as_str();\n"; isa_fromstr += " match v {\n"; let mut isa_from_str = "impl TryFrom<&str> for Operation {\n".to_owned(); isa_from_str += " type Error = &'static str;\n"; isa_from_str += " fn try_from(v: &str) -> Result {\n"; - isa_from_str += " let a = v.to_ascii_uppercase();\n"; - isa_from_str += " let v = a.as_str();\n"; isa_from_str += " match v {\n"; - let mut isa_into_str = "impl Display for Operation {\n".to_owned(); - isa_into_str += " fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> {\n"; - isa_into_str += " match self.0 {\n"; - let mut isa_num_args = "impl Operation {\n".to_owned(); isa_num_args += " pub fn num_args(&self) -> Result {\n"; isa_num_args += " match self.0 {\n"; @@ -141,9 +133,6 @@ fn main() { isa_fromstr += format!(" \"{}\" => Ok({}),\n", const_name, const_name).as_str(); - isa_into_str += format!(" {} => write!(f, \"{}\"),\n", - idx, const_name).as_str(); - isa_num_args += format!(" {} => Ok({}),\n", idx, instr.args.len()) .as_str(); @@ -165,11 +154,6 @@ fn main() { isa_fromstr += " }\n"; isa_fromstr += "}\n\n"; - isa_into_str += " _ => panic!(\"illegal instruction\"),\n"; - isa_into_str += " }\n"; - isa_into_str += " }\n"; - isa_into_str += "}\n\n"; - isa_num_args += " _ => Err(\"illegal instruction\"),\n"; isa_num_args += " }\n"; isa_num_args += " }\n"; @@ -179,11 +163,9 @@ fn main() { isa += isa_from_byte.as_str(); isa += isa_from_str.as_str(); isa += isa_fromstr.as_str(); - isa += isa_into_str.as_str(); isa += isa_num_args.as_str(); - write!(&mut output_file, "use core::str::FromStr;\n").unwrap(); - write!(&mut output_file, "use alloc::fmt::{{Display, Formatter, Error as E}};\n\n\n").unwrap(); + write!(&mut output_file, "use core::str::FromStr;\n\n\n").unwrap(); write!(&mut output_file, "{}", isa).unwrap(); write!(&mut output_file, "\n\npub const TOTAL_INSTRUCTIONS: usize = {};", peak) .unwrap(); diff --git a/hyphae/src/heap.rs b/hyphae/src/heap.rs index f02a693..b3d1ce4 100644 --- a/hyphae/src/heap.rs +++ b/hyphae/src/heap.rs @@ -23,8 +23,7 @@ use core::ptr::NonNull; use alloc::{vec, vec::Vec}; use alloc::rc::Rc; use alloc::boxed::Box; -use alloc::fmt::{Error as E, Debug, Formatter, Display}; -use alloc::string::String; +use alloc::fmt::Debug; use organelle::{Number, Fraction, SymbolicNumber, Float, ScientificNotation}; @@ -98,12 +97,6 @@ impl DerefMut for Gc { } } -impl Display for Gc { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> { - write!(f, "{}", **self) - } -} - // takes a pointer to target Rc macro_rules! shallow_copy_rc { ( $src:expr ) => { @@ -271,18 +264,6 @@ impl Index for Cons { } } -impl Display for Cons { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> { - write!(f, "(")?; - self.0.clone() - .and_then(|x| write!(f, "{}", x).into()); - write!(f, " ")?; - self.1.clone() - .and_then(|x| write!(f, "{}", x).into()); - write!(f, ")") - } -} - #[derive(PartialEq, Debug)] pub enum Datum { Number(Number), @@ -295,43 +276,6 @@ pub enum Datum { None } -impl Display for Datum { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> { - match self { - Datum::Number(n) => write!(f, "{}", Into::::into(*n)), - Datum::Bool(b) => write!(f, "{}", b), - Datum::Cons(c) => write!(f, "'{}", c), - Datum::Char(c) => write!(f, "'{}'", c), - Datum::String(s) => write!(f, "\"{}\"", str::from_utf8(&s).unwrap()), - Datum::Vector(v) => { - write!(f, "#(")?; - if v.len() > 0 { - write!(f, "{}", v[0])?; - } - if v.len() > 1 { - for i in &v[1..] { - write!(f, ", {}", i)?; - } - } - write!(f, ")") - }, - Datum::ByteVector(v) => { - write!(f, "#u8(")?; - if v.len() > 0 { - write!(f, "{}", v[0])?; - } - if v.len() > 1 { - for i in &v[1..] { - write!(f, ", {}", i)?; - } - } - write!(f, ")") - }, - Datum::None => write!(f, "nil"), - } - } -} - // implemented by hand to force deep copy on Cons datum impl Clone for Datum { fn clone(&self) -> Datum { diff --git a/hyphae/src/serializer.rs b/hyphae/src/serializer.rs index e0b990c..c3cd48b 100644 --- a/hyphae/src/serializer.rs +++ b/hyphae/src/serializer.rs @@ -20,8 +20,6 @@ use crate::heap::Datum; use alloc::vec::Vec; use alloc::vec; -use alloc::str::FromStr; -use alloc::fmt::{Display, Formatter, Error as E}; use core::ops::Index; use core::mem::transmute; @@ -66,6 +64,12 @@ pub enum Address { Char = 0xfa, // immutable access only } +#[derive(Debug, Clone, PartialEq)] +pub struct Deserializer<'a> { + pub input: &'a [u8], + // TODO: Debug levels for errors +} + #[derive(Debug, Clone, PartialEq)] pub struct Operand(pub Address, pub usize); @@ -142,56 +146,6 @@ impl Into> for Operand { } } -impl FromStr for Operand { - type Err = &'static str; - fn from_str(v: &str) -> Result { - match v { - "$expr" => Ok(Operand(Address::Expr, 0)), - "$oper1" => Ok(Operand(Address::Oper1, 0)), - "$oper2" => Ok(Operand(Address::Oper2, 0)), - "$oper3" => Ok(Operand(Address::Oper3, 0)), - "$oper4" => Ok(Operand(Address::Oper4, 0)), - "true" => Ok(Operand(Address::Bool, 1)), - "false" => Ok(Operand(Address::Bool, 0)), - a if a.chars().nth(0).unwrap() == '%' && - a.len() > 1 && - a[1..].parse::().is_ok() => - Ok(Operand(Address::Stack, a[1..].parse::().unwrap())), - a if a.chars().nth(0).unwrap() == '@' && - a.len() > 1 && - a[1..].parse::().is_ok() => - Ok(Operand(Address::Instr, a[1..].parse::().unwrap())), - a if a.chars().nth(0).unwrap() == '\'' && - a.len() == 3 && - a.chars().nth(2).unwrap() == '\'' => - Ok(Operand(Address::Char, a.chars().nth(1).unwrap() as usize)), - a if a.parse::().is_ok() => - Ok(Operand(Address::Numer, a.parse::().unwrap())), - _ => Err("invalid operand") - } - } -} - -impl Display for Operand { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> { - match self.0 { - Address::Expr => write!(f, "$expr"), - Address::Oper1 => write!(f, "$oper1"), - Address::Oper2 => write!(f, "$oper2"), - Address::Oper3 => write!(f, "$oper3"), - Address::Oper4 => write!(f, "$oper4"), - Address::Bool => - write!(f, "{}", if self.1 > 0 { "true" } else { "false" }), - Address::Stack => write!(f, "%") - .and_then(|_| write!(f, "{}", self.1)), - Address::Instr => write!(f, "@") - .and_then(|_| write!(f, "{}", self.1)), - Address::Numer => write!(f, "{}", self.1), - Address::Char => write!(f, "'{}'", self.1 as u8 as char), - } - } -} - impl Operand { fn byte_length(&self) -> u8 { 1 + self.0.operand_size() @@ -229,46 +183,6 @@ impl Into> for Instruction { } } -impl FromStr for Instruction { - type Err = &'static str; - fn from_str(v: &str) -> Result { - let toks: Vec<&str> = v.trim().split(' ').collect(); - if toks.len() < 1 { - return Err("empty string"); - } - - let oper = Operation::from_str(toks[0])?; - let mut args = vec![]; - if toks.len() == 1 && oper.num_args()? == 0 { - return Ok(Instruction(oper, args)); - } - for i in toks[1..].iter() { - args.push(Operand::from_str(i.trim_matches(','))?); - } - - if oper.num_args()? as usize != args.len() { - return Err("instruction has incorrect number of operands"); - } - - Ok(Instruction(oper, args)) - } -} - -impl Display for Instruction { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> { - write!(f, "{}\t", self.0)?; - if self.1.len() > 0 { - write!(f, "{}", self.1[0])?; - } - if self.1.len() > 1 { - for i in self.1[1..].iter() { - write!(f, ", {}", i)?; - } - } - Ok(()) - } -} - impl Instruction { fn byte_length(&self) -> u8 { self.1.iter() @@ -333,52 +247,6 @@ impl<'a> Index for Program { } } -impl Display for Program { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> { - write!(f, "DATA:\n")?; - for i in self.0.iter() { - write!(f, " {}", i)?; - } - - write!(f, "\nCODE:\n")?; - for i in self.1.iter() { - write!(f, " {}", i)?; - } - - Ok(()) - } -} - -impl FromStr for Program { - type Err = &'static str; - fn from_str(val: &str) -> Result { - //let mut datum = vec![]; - let mut instrs = vec![]; - let lines: Vec<&str> = val.split('\n').collect(); - let mut cur = 0; - let mut toggle = 0; - - while cur < lines.len() { - if toggle == 1 { - instrs.push(lines[cur].parse::()?); - - // TODO: toggle == 2 case for interpreting a DATA chunk - - } else { - match lines[cur] { - "DATA:" => return Err("datum parser unimplemented"), - "CODE:" => toggle = 1, - a => return Err("unknown section in document: "), - } - } - - cur += 1; - } - - Ok(Program(vec![], instrs)) - } -} - impl TryFrom for DeserializerControlCode { type Error = &'static str; fn try_from(value: u8) -> Result { @@ -412,31 +280,6 @@ mod tests { use crate::instr; use super::*; - #[test] - fn test_operand_tofrom_str() { - let cases = vec![ - ("$expr", Operand(Address::Expr, 0)), - ("$oper1", Operand(Address::Oper1, 0)), - ("$oper2", Operand(Address::Oper2, 0)), - ("$oper3", Operand(Address::Oper3, 0)), - ("$oper4", Operand(Address::Oper4, 0)), - ("true", Operand(Address::Bool, 1)), - ("false", Operand(Address::Bool, 0)), - ("%12", Operand(Address::Stack, 12)), - ("%1", Operand(Address::Stack, 1)), - ("@1", Operand(Address::Instr, 1)), - ("@12", Operand(Address::Instr, 12)), - ("1234", Operand(Address::Numer, 1234)), - ("'c'", Operand(Address::Char, 'c' as usize)), - ]; - - for i in cases.iter() { - let a = Operand::from_str(i.0).unwrap(); - assert_eq!(a, i.1); - assert_eq!(i.0, a.to_string().as_str()); - } - } - #[test] fn test_operand_parse() { let bad_addressing = @@ -514,36 +357,7 @@ mod tests { assert_eq!(two_bytes.unwrap(), vec![instr::LINK.0, 0xf3, 0xf4]); assert_eq!(two_oper.1[0], Operand(Address::Oper1, 0)); assert_eq!(two_oper.1[1], Operand(Address::Oper2, 0)); - } - - #[test] - fn test_instruction_tofrom_string() { - let happy_cases = vec![ - "NOP", - " NOP", - "NOP ", - "nop", - "PUSH $expr", - "CONST $expr 4", - "jmp @3", - ]; - - let sad_cases = vec![ - "NOP 1", - "push", - "const 4", - ]; - - for i in happy_cases.iter() { - assert!(i.parse::().is_ok()); - } - - for i in sad_cases.iter() { - assert!(i.parse::().is_err()); - } - } - - // TODO: test program from and to string + } #[test] fn test_program_parse() { diff --git a/hyphae/src/vm.rs b/hyphae/src/vm.rs index 1af7788..77b85ad 100644 --- a/hyphae/src/vm.rs +++ b/hyphae/src/vm.rs @@ -145,14 +145,6 @@ impl VM { self.running = false; } - pub fn run_step(&mut self) { - if self.ictr < self.prog.len() && - !self.err_state && self.running { - self.execute_instruction(); - self.ictr += 1; - } - } - #[inline(always)] fn execute_instruction(&mut self) { macro_rules! e { diff --git a/snippets/in_progress_numbers.rs b/snippets/in_progress_numbers.rs new file mode 100644 index 0000000..39333dc --- /dev/null +++ b/snippets/in_progress_numbers.rs @@ -0,0 +1,642 @@ +use alloc::boxed::Box; +use alloc::{vec, vec::Vec}; +use alloc::fmt::Debug; +use lexer::{E_TOO_MANY_DECIMALS, E_TOO_MANY_SLASH}; + +use core::cmp::Ordering; +use core::{fmt, u8}; +use core::ops::{Add, Div, Mul, Sub}; + + +pub const E_INCOMPREHENSIBLE: &str = "could not parse number literal"; +pub const E_POUND_TRUNCATED: &str = "pound sign implies additional input"; +pub const E_BASE_PARSE_FAIL: &str = "failed to parse explicit base literal"; +pub const E_UNKNOWN_CONTROL: &str = "unknown character in number literal"; +pub const E_EMPTY_INPUT: &str = "empty string cannot be a number"; + +const NUM_INF: &str = "+inf.0"; +const NUM_NEG_INF: &str = "-inf.0"; +const NUM_NAN: &str = "+nan.0"; +const NUM_NEG_NAN: &str = "-nan.0"; + +pub const NegativeFlag: u8 = 0b10000000; // positive value if off +pub const DecimalFlag: u8 = 0b01000000; // single integer if off +pub const FractionFlag: u8 = 0b00100000; // decimal if off +pub const ScientificFlag: u8 = 0b00010000; // requires a second flags byte +pub const InfiniteFlag: u8 = 0b00001000; // can be positive or negative +pub const NotANumberFlag: u8 = 0b00000100; // can be positive or negative because r7rs +pub const OverflownFlag: u8 = 0b00000010; // poisons exactness + + +/* NUMBER BYTES FORMAT + * Generally the format within the byte array operates like this + * (guaranteed header) 1. NumberFlags (u8) + * (for each integer) 2. Byte Length (u8) + * (for each integer) 3. N proceeding bytes of data + * + * If Scientific Notation is used the leading number may be a decimal. + * In this case, there will be three total numbers + * + * All numbers are big endian + */ +#[repr(transparent)] +#[derive(Clone, Debug, PartialEq)] +pub struct Number<'src> (pub &'src [u8]); + + +/* WARNING + * member functions tend to assume that number encoding is consistent + * use Number::is_valid() to double check numbers from unknown sources + * + * TODO: maybe mark raw-indexing member functions as unsafe + */ +impl Number<'_> { + #[inline(always)] + pub fn byte_length(&self) -> u8 { + if self.0[0] & (InfiniteFlag | NotANumberFlag) != 0 { + return 1; + } + + let mut len = self.0[1] + 2; + if self.0[0] & (DecimalFlag | FractionFlag | ScientificFlag) != 0 { + len += self.0[len as usize] + 1; + } + + if self.0[0] & ScientificFlag != 0 && + self.0[0] & DecimalFlag != 0 { + len += self.0[len as usize]; + } + + len + } + + pub fn is_valid(&self) -> bool { + let len = self.0.len(); + if len < 1 { + return false; + } + + let decimal = self.0[0] & DecimalFlag != 0; + let fraction = self.0[0] & FractionFlag != 0; + let scientific = self.0[0] & ScientificFlag != 0; + let overflown = self.0[0] & OverflownFlag != 0; + let infinite = self.0[0] & InfiniteFlag != 0; + let notanumber = self.0[0] & NotANumberFlag != 0; + + // check flags + if overflown { + return false + } + + if (decimal && fraction) || (scientific && fraction) { + return false + } + + if (infinite || notanumber) && + (decimal || fraction || scientific || len != 1) { + return false + } + + // at least 3 bytes for a single u8 + if len < 3 { + return false + } + + let mut cur = self.0[1] + 2; + if len < cur as usize { + return false + } + + if decimal || fraction || scientific { + if len < (cur + 1) as usize { + return false; + } + + cur += self.0[cur as usize]; + if len < (cur + 1) as usize { + return false; + } + } + + if scientific && decimal { + cur += 1; + if len < (cur + 1) as usize { + return false + } + + cur += self.0[cur as usize]; + if len < (cur + 1) as usize { + return false + } + } + + true + } + + #[inline(always)] + pub fn is_exact(&self) -> bool { + self.0[0] & ScientificFlag == 0 + } + + #[inline(always)] + pub fn make_exact_into(&self, dst:&mut Vec) { + // expand scientific notation else just direct copy + if self.0[0] & ScientificFlag != 0 { + self.normalize_scientific_into(dst); + return + } + + self.copy_into(dst); + } + + #[inline(always)] + pub fn make_inexact_into(&self, dst: &mut Vec) { + // basically just convert a fraction into an actual division + todo!() + } + + // use this so you dont have to worry about clone while casting + #[inline(always)] + pub fn copy_into(&self, dst: &mut Vec) { + for i in self.0 { + dst.push(*i) + } + } + + #[inline(always)] + pub fn normalize_scientific_into(&self, dst: &mut Vec) { + todo!() + } + + #[inline(always)] + pub fn simplify_fraction_in_place(&mut self) { + if self.0[0] & FractionFlag == 0 { + return + } + + // can technically do this in place + // each element of the fraction will only shrink + todo!() + } + + #[inline(always)] + pub fn from_str_into(src: &str, dst: &mut Vec) -> Result<(), &'static str> { + // handle symbolic values + match src { + NUM_INF => { + dst.push(0 as u8 | InfiniteFlag); + return Ok(()); + }, + + NUM_NEG_INF => { + dst.push(0 as u8 | NegativeFlag | InfiniteFlag); + return Ok(()); + }, + + NUM_NAN => { + dst.push(0 as u8 | NotANumberFlag); + return Ok(()); + }, + + NUM_NEG_NAN => { + dst.push(0 as u8 | NegativeFlag | NotANumberFlag); + return Ok(()); + }, + + _ => (), + } + + let mut ctrl_flags = 0 as u8; + let mut operands = vec![]; + let mut digits_per_byte = 3; // default to decimal encoding + let mut base = 0; + let mut iter = src.chars().peekable(); + + match iter.next() { + Some('+') => (), + Some('-') => { + ctrl_flags |= NegativeFlag; + }, + Some('#') => { + match iter.next() { + None => return Err(E_POUND_TRUNCATED), + Some('i') => /* force_inexact = true */ (), + Some('e') => /* force_exact = true */ (), + Some('x') => { digits_per_byte = 2; base = 16 }, + Some('d') => { digits_per_byte = 3; base = 10 }, + Some('o') => { digits_per_byte = 4; base = 8 }, + Some('b') => { digits_per_byte = 8; base = 2 }, + _ => return Err(E_UNKNOWN_CONTROL), + } + }, + Some(a) if a.is_digit(10) => (), + Some(_) => return Err(E_INCOMPREHENSIBLE), + None => return Err(E_EMPTY_INPUT), + } + + let mut ops_needed = 1; + if base != 10 { + // cant mix non-decimal base and other number representations + let mut len = 0 as u8; + while let Some(chunk) = { + let mut chk = vec![]; + for _ in 0..digits_per_byte { + if let Some(c) = iter.next() { + chk.push(c as u8) + } + } + if chk.len() < 1 { None } else { Some(chk) } + } { + let Ok(val) = u8::from_str_radix( + unsafe {str::from_utf8_unchecked(chunk.as_slice())}, base) else { + return Err(E_BASE_PARSE_FAIL) + }; + operands.push(val); + len += 1; + } + // integer numbers prepended with their length + operands.insert(0, len); + ops_needed -= 1; + + } else { + // just a decimal number, but could have a weird format + loop { + macro_rules! pack_operand { + () => { + let s = unsafe { str::from_utf8_unchecked(operands.as_slice()) }; + let f = usize::from_str_radix(&s, 10).expect("str cast"); + let f = f.to_be_bytes(); + operands.clear(); + + dst.push(f.len() as u8); + dst.append(&mut f.to_vec()); + ops_needed -= 1; + } + } + + match iter.next() { + Some(c) if c.is_digit(10) => { + operands.push(c as u8); + }, + + Some('.') => { + ops_needed += 1; + if ctrl_flags & (FractionFlag | ScientificFlag) != 0 { + return Err(E_INCOMPREHENSIBLE) + } + + if ctrl_flags & DecimalFlag != 0 { + return Err(E_TOO_MANY_DECIMALS) + } + + ctrl_flags |= DecimalFlag; + pack_operand!(); + }, + + Some('/') => { + ops_needed += 1; + if ctrl_flags & (DecimalFlag | ScientificFlag) != 0 { + return Err(E_INCOMPREHENSIBLE) + } + + if ctrl_flags & FractionFlag != 0 { + return Err(E_TOO_MANY_SLASH) + } + + ctrl_flags |= DecimalFlag; + pack_operand!(); + }, + + Some('e') => { + ops_needed += 1; + if ctrl_flags & FractionFlag != 0 { + return Err(E_INCOMPREHENSIBLE) + } + + ctrl_flags |= ScientificFlag; + let mut newctrl = 0 as u8; + + if let Some('-') = iter.peek() { + newctrl |= NegativeFlag; + } + + pack_operand!(); + dst.push(newctrl); + }, + + Some(_) => return Err(E_INCOMPREHENSIBLE), + + None => { + pack_operand!(); + break; + } + } + } + } + + if ops_needed != 0 { + return Err(E_INCOMPREHENSIBLE); + } + + dst.insert(0, ctrl_flags); + Number(dst.as_slice()).simplify_fraction_in_place(); + Ok(()) + } + + pub fn from_u8_into(src: u8, dst: &mut Vec) -> Number { + dst.push(0 as u8); + dst.push(src); + Number(dst.as_slice()) + } +} + +impl fmt::Display for Number<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // can implement after I finish division + todo!() + } +} + +impl<'a> From<&'a Box<[u8]>> for Number<'a> { + fn from(value: &'a Box<[u8]>) -> Self { + Number(value.as_ref()) + } +} + +impl<'a> From<&'a Vec> for Number<'a> { + fn from(value: &'a Vec) -> Self { + Number(value.as_slice()) + } +} + +impl<'a> From<&'a [u8]> for Number<'a> { + fn from(value: &'a [u8]) -> Self { + Number(value) + } +} + +impl<'a> Into<&'a [u8]> for Number<'a> { + fn into(self) -> &'a [u8] { + self.0 + } +} + +impl Add for Number<'_> { + type Output = Box<[u8]>; + fn add(self, rhs: Self) -> Self::Output { + todo!() + } +} + +impl Sub for Number<'_> { + type Output = Box<[u8]>; + fn sub(self, rhs: Self) -> Self::Output { + todo!() + } +} + +impl Mul for Number<'_> { + type Output = Box<[u8]>; + fn mul(self, rhs: Self) -> Self::Output { + todo!() + } +} + +impl Div for Number<'_> { + type Output = Box<[u8]>; + fn div(self, rhs: Self) -> Self::Output { + // divide unsigned integer by unsigned integer + // the inputs (lh and rh) start with length byte + // returns a decimal index + fn div_ints(lh: &[u8], rh: &[u8], dest: &mut Vec) -> u8 { + todo!() + } + + /* Options + * divide a single int by a single int + * - (make fraction) + * divide a fraction by a single int + * - (multiply denominator) + * divide a decimal by a single int + * - (divide straight through) + * divide a scientific note by a single int + * - divide the first num + * - multiply by however much is needed for ones place (like 3.5) + * - add or subtract from the second number accordingly + * + * divide a single int by a fraction + * - output denom * lh / numer + * divide a single int by a decimal + */ + todo!() + } +} + +impl PartialEq for Number<'_> { + fn eq(&self, other: &Number) -> bool { + todo!() + } +} + +impl PartialOrd for Number<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + todo!() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_number_tests() { + assert_eq!("1.3".parse::(), + Ok(Number::Flt(Float(1.3)))); + + assert_eq!("1".parse::(), + Ok(Number::Flt(Float(1 as f64)))); + + assert_eq!("1.3e3".parse::(), + Ok(Number::Sci(ScientificNotation(1.3, 3)))); + + assert_eq!("+1.3".parse::(), + Ok(Number::Flt(Float(1.3)))); + + assert_eq!("-1.3".parse::(), + Ok(Number::Flt(Float(-1.3)))); + + assert_eq!("#d234".parse::(), + Ok(Number::Flt(Float(234.0)))); + + assert_eq!("#o17".parse::(), + Ok(Number::Fra(Fraction(15, 1)))); + + assert_eq!("#xAA".parse::(), + Ok(Number::Fra(Fraction(170, 1)))); + + assert_eq!("#b101".parse::(), + Ok(Number::Flt(Float(5.0)))); + + assert_eq!("2/4".parse::(), + Ok(Number::Fra(Fraction(2, 4)))); + + assert_eq!("#e1/5".parse::(), + Ok(Number::Fra(Fraction(1, 5)))); + + assert_eq!("#i1/5".parse::(), + Ok(Number::Flt(Float(0.2)))); + + assert_eq!("#e1e1".parse::(), + Ok(Number::Sci(ScientificNotation(1.0, 1)))); + + assert_eq!("+inf.0".parse::(), + Ok(Number::Sym(SymbolicNumber::Inf))); + + assert_eq!("2e3".parse::(), + Ok(ScientificNotation(2.0, 3))); + + assert_eq!("0e1".parse::(), + Ok(ScientificNotation(0.0, 1))); + + assert_eq!("-1e34".parse::(), + Ok(ScientificNotation(-1.0, 34))); + + assert_eq!("3.3e3".parse::(), + Ok(ScientificNotation(3.3, 3))); + + assert_eq!("2".parse::(), + Err(E_SCIENTIFIC_E)); + + assert_eq!("2e2e2".parse::(), + Err(E_SCIENTIFIC_MULTI_E)); + + assert_eq!("2/3".parse::(), + Ok(Fraction(2, 3))); + + assert_eq!("0/1".parse::(), + Ok(Fraction(0, 1))); + + assert_eq!("-1/34".parse::(), + Ok(Fraction(-1, 34))); + + assert_eq!("2".parse::(), + Err(E_NO_DENOMINATOR)); + + assert_eq!("2/2/2".parse::(), + Err(E_MULTI_DENOMINATOR)); + + assert_eq!("2/0".parse::(), + Err(E_ZERO_DENOMINATOR)); + + assert_eq!("3.3/3".parse::(), + Err(E_NUMERATOR_PARSE_FAIL)); + } + + #[test] + fn test_number_addition_subtraction_cases() { + let cases = vec![ + vec!["1/5", "4/5", "1/1"], + vec!["1/5", "0.8", "1/1"], + vec!["1e1", "2.0", "12/1"], + vec!["1e1", "2/1", "12/1"], + vec!["1e1", "1/2", "10.5"], + ]; + + cases.iter().for_each(|case| { + println!("+ {:#?}", case); + let x = case[0].parse::().unwrap(); + let y = case[1].parse::().unwrap(); + let z = case[2].parse::().unwrap(); + + // test some mathematical properties + assert_eq!(x + y, z); + assert_eq!(x + y, y + x); + assert_eq!(z - x, y); + assert_eq!(x + y - x, y); + }); + + // theres no reason this should adhere to all the other rules + let x = "+inf.0".parse::().unwrap(); + let y = "1e1".parse::().unwrap(); + let z = "+inf.0".parse::().unwrap(); + assert_eq!(x + y, z); + } + + #[test] + fn test_number_multiplication_division_cases() { + let cases = vec![ + vec!["1/5", "5e0", "1/1"], + vec!["1/5", "5", "1/1"], + vec!["1/5", "2/1", "2/5"], + vec!["4.4", "1/2", "2.2"], + vec!["12.0", "1/2", "6/1"], + vec!["1e1", "2.0", "20/1"], + vec!["1e1", "2/1", "20/1"], + vec!["1e1", "1/2", "5/1"], + ]; + + cases.iter().for_each(|case| { + println!("+ {:#?}", case); + let x = case[0].parse::().unwrap(); + let y = case[1].parse::().unwrap(); + let z = case[2].parse::().unwrap(); + + // test some mathematical properties + assert_eq!(x * y, z); + assert_eq!(x * y, y * x); + assert_eq!(z / x, y); + assert_eq!(x * y / x, y); + }); + } + + #[test] + fn test_number_pow_cases() { + // TODO: add scientific notation cases + let cases = vec![ + vec!["2", "2", "4"], + vec!["2/1", "2/1", "4/1"], + vec!["2/1", "2/-1", "1/4"], + vec!["2/1", "2/2", "2/1"], + vec!["2/1", "2.0", "4/1"], + vec!["27/8", "2/-3", "4/9"] + ]; + + cases.iter().for_each(|case| { + println!("+ {:#?}", case); + let x = case[0].parse::().unwrap(); + let y = case[1].parse::().unwrap(); + let z = case[2].parse::().unwrap(); + assert_eq!(x.pow(y), z); + }); + } + + #[test] + fn test_number_ord_cases() { + // TODO: add more cases + let cases = vec![ + vec!["1/2", "1.0", "1e1"], + ]; + + cases.iter().for_each(|case| { + println!("+ {:#?}", case); + let x = case[0].parse::().unwrap(); + let y = case[1].parse::().unwrap(); + let z = case[2].parse::().unwrap(); + assert!(x < y); + assert!(y < z); + assert!(x < z); + }); + } + + #[test] + fn float_negative_exponent_case() { + if let Float(0.1) = "1e-1" + .parse::() + .unwrap() + .make_inexact() { + return + } + + assert!(false) + } +}