diff --git a/Cargo.lock b/Cargo.lock index ecdcdf9..35bd457 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,6 +118,14 @@ 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 df6ee70..32c5fb0 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ cargo-features = ["profile-rustflags"] [workspace] resolver = "2" -members = ["mycelium", "decomposer", "hyphae", "organelle"] +members = ["mycelium", "decomposer", "hyphae", "organelle", "fairy-ring"] [profile.release] opt-level = 3 diff --git a/hyphae/build.rs b/hyphae/build.rs index 7984d48..2502a65 100644 --- a/hyphae/build.rs +++ b/hyphae/build.rs @@ -105,13 +105,21 @@ 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"; @@ -133,6 +141,9 @@ 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(); @@ -154,6 +165,11 @@ 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"; @@ -163,9 +179,11 @@ 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\n\n").unwrap(); + 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, "{}", 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 b3d1ce4..f02a693 100644 --- a/hyphae/src/heap.rs +++ b/hyphae/src/heap.rs @@ -23,7 +23,8 @@ use core::ptr::NonNull; use alloc::{vec, vec::Vec}; use alloc::rc::Rc; use alloc::boxed::Box; -use alloc::fmt::Debug; +use alloc::fmt::{Error as E, Debug, Formatter, Display}; +use alloc::string::String; use organelle::{Number, Fraction, SymbolicNumber, Float, ScientificNotation}; @@ -97,6 +98,12 @@ 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 ) => { @@ -264,6 +271,18 @@ 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), @@ -276,6 +295,43 @@ 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 c3cd48b..e0b990c 100644 --- a/hyphae/src/serializer.rs +++ b/hyphae/src/serializer.rs @@ -20,6 +20,8 @@ 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; @@ -64,12 +66,6 @@ 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); @@ -146,6 +142,56 @@ 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() @@ -183,6 +229,46 @@ 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() @@ -247,6 +333,52 @@ 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 { @@ -280,6 +412,31 @@ 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 = @@ -357,7 +514,36 @@ 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 9372d16..2ce21d8 100644 --- a/hyphae/src/vm.rs +++ b/hyphae/src/vm.rs @@ -145,6 +145,14 @@ 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 deleted file mode 100644 index 39333dc..0000000 --- a/snippets/in_progress_numbers.rs +++ /dev/null @@ -1,642 +0,0 @@ -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) - } -}