Compare commits
8 commits
d759937b11
...
467e1e7188
| Author | SHA1 | Date | |
|---|---|---|---|
| 467e1e7188 | |||
| a99175669f | |||
| 6f16c2e084 | |||
| 333beba0bb | |||
| d008ce8060 | |||
| 54a0e32fe1 | |||
| 18770575cb | |||
| 5582da5b41 |
8 changed files with 356 additions and 661 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
|
@ -118,6 +118,14 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fairy-ring"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"hyphae",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.4"
|
version = "0.15.4"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ cargo-features = ["profile-rustflags"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = ["mycelium", "decomposer", "hyphae", "organelle"]
|
members = ["mycelium", "decomposer", "hyphae", "organelle", "fairy-ring"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
|
||||||
|
|
@ -105,13 +105,21 @@ fn main() {
|
||||||
let mut isa_fromstr = "impl FromStr for Operation {\n".to_owned();
|
let mut isa_fromstr = "impl FromStr for Operation {\n".to_owned();
|
||||||
isa_fromstr += " type Err = &'static str;\n";
|
isa_fromstr += " type Err = &'static str;\n";
|
||||||
isa_fromstr += " fn from_str(v: &str) -> Result<Self, Self::Err> {\n";
|
isa_fromstr += " fn from_str(v: &str) -> Result<Self, Self::Err> {\n";
|
||||||
|
isa_fromstr += " let a = v.to_ascii_uppercase();\n";
|
||||||
|
isa_fromstr += " let v = a.as_str();\n";
|
||||||
isa_fromstr += " match v {\n";
|
isa_fromstr += " match v {\n";
|
||||||
|
|
||||||
let mut isa_from_str = "impl TryFrom<&str> for Operation {\n".to_owned();
|
let mut isa_from_str = "impl TryFrom<&str> for Operation {\n".to_owned();
|
||||||
isa_from_str += " type Error = &'static str;\n";
|
isa_from_str += " type Error = &'static str;\n";
|
||||||
isa_from_str += " fn try_from(v: &str) -> Result<Self, Self::Error> {\n";
|
isa_from_str += " fn try_from(v: &str) -> Result<Self, Self::Error> {\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";
|
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();
|
let mut isa_num_args = "impl Operation {\n".to_owned();
|
||||||
isa_num_args += " pub fn num_args(&self) -> Result<u8, &'static str> {\n";
|
isa_num_args += " pub fn num_args(&self) -> Result<u8, &'static str> {\n";
|
||||||
isa_num_args += " match self.0 {\n";
|
isa_num_args += " match self.0 {\n";
|
||||||
|
|
@ -133,6 +141,9 @@ fn main() {
|
||||||
isa_fromstr += format!(" \"{}\" => Ok({}),\n",
|
isa_fromstr += format!(" \"{}\" => Ok({}),\n",
|
||||||
const_name, const_name).as_str();
|
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())
|
isa_num_args += format!(" {} => Ok({}),\n", idx, instr.args.len())
|
||||||
.as_str();
|
.as_str();
|
||||||
|
|
||||||
|
|
@ -154,6 +165,11 @@ fn main() {
|
||||||
isa_fromstr += " }\n";
|
isa_fromstr += " }\n";
|
||||||
isa_fromstr += "}\n\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 += " _ => Err(\"illegal instruction\"),\n";
|
||||||
isa_num_args += " }\n";
|
isa_num_args += " }\n";
|
||||||
isa_num_args += " }\n";
|
isa_num_args += " }\n";
|
||||||
|
|
@ -163,9 +179,11 @@ fn main() {
|
||||||
isa += isa_from_byte.as_str();
|
isa += isa_from_byte.as_str();
|
||||||
isa += isa_from_str.as_str();
|
isa += isa_from_str.as_str();
|
||||||
isa += isa_fromstr.as_str();
|
isa += isa_fromstr.as_str();
|
||||||
|
isa += isa_into_str.as_str();
|
||||||
isa += isa_num_args.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, "{}", isa).unwrap();
|
||||||
write!(&mut output_file, "\n\npub const TOTAL_INSTRUCTIONS: usize = {};", peak)
|
write!(&mut output_file, "\n\npub const TOTAL_INSTRUCTIONS: usize = {};", peak)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@ use core::ptr::NonNull;
|
||||||
use alloc::{vec, vec::Vec};
|
use alloc::{vec, vec::Vec};
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
use alloc::boxed::Box;
|
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};
|
use organelle::{Number, Fraction, SymbolicNumber, Float, ScientificNotation};
|
||||||
|
|
||||||
|
|
@ -97,6 +98,12 @@ impl<T> DerefMut for Gc<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Display> Display for Gc<T> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> {
|
||||||
|
write!(f, "{}", **self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// takes a pointer to target Rc
|
// takes a pointer to target Rc
|
||||||
macro_rules! shallow_copy_rc {
|
macro_rules! shallow_copy_rc {
|
||||||
( $src:expr ) => {
|
( $src:expr ) => {
|
||||||
|
|
@ -264,6 +271,18 @@ impl Index<usize> 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)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum Datum {
|
pub enum Datum {
|
||||||
Number(Number),
|
Number(Number),
|
||||||
|
|
@ -276,6 +295,43 @@ pub enum Datum {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Datum {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> {
|
||||||
|
match self {
|
||||||
|
Datum::Number(n) => write!(f, "{}", Into::<String>::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
|
// implemented by hand to force deep copy on Cons datum
|
||||||
impl Clone for Datum {
|
impl Clone for Datum {
|
||||||
fn clone(&self) -> Datum {
|
fn clone(&self) -> Datum {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ use crate::heap::Datum;
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
|
use alloc::str::FromStr;
|
||||||
|
use alloc::fmt::{Display, Formatter, Error as E};
|
||||||
|
|
||||||
use core::ops::Index;
|
use core::ops::Index;
|
||||||
use core::mem::transmute;
|
use core::mem::transmute;
|
||||||
|
|
@ -64,12 +66,6 @@ pub enum Address {
|
||||||
Char = 0xfa, // immutable access only
|
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)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Operand(pub Address, pub usize);
|
pub struct Operand(pub Address, pub usize);
|
||||||
|
|
||||||
|
|
@ -146,6 +142,56 @@ impl Into<Vec<u8>> for Operand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for Operand {
|
||||||
|
type Err = &'static str;
|
||||||
|
fn from_str(v: &str) -> Result<Self, Self::Err> {
|
||||||
|
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::<usize>().is_ok() =>
|
||||||
|
Ok(Operand(Address::Stack, a[1..].parse::<usize>().unwrap())),
|
||||||
|
a if a.chars().nth(0).unwrap() == '@' &&
|
||||||
|
a.len() > 1 &&
|
||||||
|
a[1..].parse::<usize>().is_ok() =>
|
||||||
|
Ok(Operand(Address::Instr, a[1..].parse::<usize>().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::<usize>().is_ok() =>
|
||||||
|
Ok(Operand(Address::Numer, a.parse::<usize>().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 {
|
impl Operand {
|
||||||
fn byte_length(&self) -> u8 {
|
fn byte_length(&self) -> u8 {
|
||||||
1 + self.0.operand_size()
|
1 + self.0.operand_size()
|
||||||
|
|
@ -183,6 +229,46 @@ impl Into<Vec<u8>> for Instruction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for Instruction {
|
||||||
|
type Err = &'static str;
|
||||||
|
fn from_str(v: &str) -> Result<Self, Self::Err> {
|
||||||
|
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 {
|
impl Instruction {
|
||||||
fn byte_length(&self) -> u8 {
|
fn byte_length(&self) -> u8 {
|
||||||
self.1.iter()
|
self.1.iter()
|
||||||
|
|
@ -247,6 +333,52 @@ impl<'a> Index<usize> 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<Self, Self::Err> {
|
||||||
|
//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::<Instruction>()?);
|
||||||
|
|
||||||
|
// 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<u8> for DeserializerControlCode {
|
impl TryFrom<u8> for DeserializerControlCode {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
|
@ -280,6 +412,31 @@ mod tests {
|
||||||
use crate::instr;
|
use crate::instr;
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn test_operand_parse() {
|
fn test_operand_parse() {
|
||||||
let bad_addressing =
|
let bad_addressing =
|
||||||
|
|
@ -357,7 +514,36 @@ mod tests {
|
||||||
assert_eq!(two_bytes.unwrap(), vec![instr::LINK.0, 0xf3, 0xf4]);
|
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[0], Operand(Address::Oper1, 0));
|
||||||
assert_eq!(two_oper.1[1], Operand(Address::Oper2, 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::<Instruction>().is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in sad_cases.iter() {
|
||||||
|
assert!(i.parse::<Instruction>().is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test program from and to string
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_program_parse() {
|
fn test_program_parse() {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
use organelle::{Fraction, Number, Numeric};
|
use organelle::{Float, Fraction, Number, Numeric};
|
||||||
|
|
||||||
use crate::hmap::QuickMap;
|
use crate::hmap::QuickMap;
|
||||||
use crate::stackstack::StackStack;
|
use crate::stackstack::StackStack;
|
||||||
|
|
@ -145,6 +145,14 @@ impl VM {
|
||||||
self.running = false;
|
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)]
|
#[inline(always)]
|
||||||
fn execute_instruction(&mut self) {
|
fn execute_instruction(&mut self) {
|
||||||
macro_rules! e {
|
macro_rules! e {
|
||||||
|
|
@ -391,6 +399,28 @@ impl VM {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
i::NTOBV => access!(&instr.1[0], {
|
||||||
|
if let Datum::Number(snum) = **access!(&instr.1[0]) {
|
||||||
|
let n = snum.make_inexact();
|
||||||
|
if !snum.is_exact() || n.0.fract() != 0.0 ||
|
||||||
|
n.0 > u8::MAX.into() || n.0 < 0.0 {
|
||||||
|
e!("input to NTOBV cannot cleanly convert");
|
||||||
|
}
|
||||||
|
Datum::ByteVector(n.0.to_ne_bytes().to_vec()).into()
|
||||||
|
} else {
|
||||||
|
e!("illegal argument to NTOBV instruction");
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
i::BVTON => access!(&instr.1[0], {
|
||||||
|
if let Datum::ByteVector(sbv) = &**access!(&instr.1[0]) {
|
||||||
|
let sf64 = f64::from_ne_bytes(sbv.clone().try_into().unwrap());
|
||||||
|
Datum::Number(Number::Flt(Float(sf64))).into()
|
||||||
|
} else {
|
||||||
|
e!("illegal argument to BVTON instruction");
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
i::NTOC => access!(&instr.1[0], {
|
i::NTOC => access!(&instr.1[0], {
|
||||||
if let Datum::Number(snum) = **access!(&instr.1[0]) {
|
if let Datum::Number(snum) = **access!(&instr.1[0]) {
|
||||||
let n = snum.make_inexact();
|
let n = snum.make_inexact();
|
||||||
|
|
@ -1090,16 +1120,16 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn isa_conversions() {
|
fn isa_conversions() {
|
||||||
let mut vm = VM::from(Program(vec![], vec![
|
let mut vm = VM::from(Program(vec![], vec![
|
||||||
|
|
||||||
// load from stack into expr
|
// load from stack into expr
|
||||||
Instruction(i::DUPL, vec![Operand(Address::Stack, 0),
|
Instruction(i::DUPL, vec![Operand(Address::Stack, 0),
|
||||||
Operand(Address::Expr, 0)]),
|
Operand(Address::Expr, 0)]),
|
||||||
|
|
||||||
// create a strunct and push to stack
|
// create a struct and push to stack
|
||||||
Instruction(i::CTOS, vec![Operand(Address::Expr, 0)]),
|
Instruction(i::CTOS, vec![Operand(Address::Expr, 0)]),
|
||||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||||
|
|
||||||
// reset expr to original char
|
// reset expr to original char
|
||||||
|
|
||||||
Instruction(i::DUPL, vec![Operand(Address::Stack, 1),
|
Instruction(i::DUPL, vec![Operand(Address::Stack, 1),
|
||||||
Operand(Address::Expr, 0)]),
|
Operand(Address::Expr, 0)]),
|
||||||
|
|
||||||
|
|
@ -1115,9 +1145,18 @@ mod tests {
|
||||||
Instruction(i::NTOE, vec![Operand(Address::Expr, 0)]),
|
Instruction(i::NTOE, vec![Operand(Address::Expr, 0)]),
|
||||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||||
|
|
||||||
|
// convert to ByteVector and push to stack
|
||||||
|
Instruction(i::NTOBV, vec![Operand(Address::Expr, 0)]),
|
||||||
|
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||||
|
|
||||||
|
// convert back to inexact and push to stack
|
||||||
|
Instruction(i::BVTON, vec![Operand(Address::Expr, 0)]),
|
||||||
|
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||||
|
|
||||||
// create a char and push to stack
|
// create a char and push to stack
|
||||||
Instruction(i::NTOC, vec![Operand(Address::Expr, 0)]),
|
Instruction(i::NTOC, vec![Operand(Address::Expr, 0)]),
|
||||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||||
|
|
||||||
])).with_stack({
|
])).with_stack({
|
||||||
let mut i = StackStack::new();
|
let mut i = StackStack::new();
|
||||||
i.push_current_stack(Datum::Char(b'a').into());
|
i.push_current_stack(Datum::Char(b'a').into());
|
||||||
|
|
@ -1127,12 +1166,14 @@ mod tests {
|
||||||
let case = TestResult{
|
let case = TestResult{
|
||||||
expr: None,
|
expr: None,
|
||||||
stack: vec![
|
stack: vec![
|
||||||
(0, Datum::Char(b'a').into()),
|
(0, Datum::Char(b'a').into()), // NTOC
|
||||||
(1, Datum::Number(Number::Fra(Fraction(97, 1))).into()),
|
(1, Datum::Number(Number::Flt(Float(97.0))).into()), // BVTON
|
||||||
(2, Datum::Number(Number::Flt(Float(97.0))).into()),
|
(2, Datum::ByteVector(vec![0, 0, 0, 0, 0, 64, 88, 64]).into()), // NTOBV
|
||||||
(3, Datum::Number(Number::Fra(Fraction(97, 1))).into()),
|
(3, Datum::Number(Number::Fra(Fraction(97, 1))).into()), // NTOE
|
||||||
(4, Datum::String(vec![b'a']).into()),
|
(4, Datum::Number(Number::Flt(Float(97.0))).into()), // NTOI
|
||||||
(5, Datum::Char(b'a').into()),
|
(5, Datum::Number(Number::Fra(Fraction(97, 1))).into()), // CTON
|
||||||
|
(6, Datum::String(vec![b'a']).into()), // CTOS
|
||||||
|
(7, Datum::Char(b'a').into()), // DUPL
|
||||||
],
|
],
|
||||||
syms: vec![],
|
syms: vec![],
|
||||||
errr: None,
|
errr: None,
|
||||||
|
|
|
||||||
|
|
@ -641,6 +641,34 @@ character byte.
|
||||||
Requires mutable access to input address.
|
Requires mutable access to input address.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
[[instructions]]
|
||||||
|
name = "ntobv"
|
||||||
|
args = ["src"]
|
||||||
|
output = ""
|
||||||
|
description = """
|
||||||
|
The ntobv instruction accepts a single number input. This operand is overwritten
|
||||||
|
by a new number datum that represents the inexact form of the source number.
|
||||||
|
|
||||||
|
The inexact form is a normalization of fraction or scientific notation datum to
|
||||||
|
float datum casted to ByteVector datum.
|
||||||
|
|
||||||
|
Requires mutable access to input address.
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[instructions]]
|
||||||
|
name = "bvton"
|
||||||
|
args = ["src"]
|
||||||
|
output = ""
|
||||||
|
description = """
|
||||||
|
The bvton instruction accepts a byte vector input. This operand is overwritten
|
||||||
|
by a new number datum that represents the inexact form of the source byte vector.
|
||||||
|
|
||||||
|
The normalized fraction or scientific notation data represented in a
|
||||||
|
ByteVector datum is casted to an inexact floating point number.
|
||||||
|
|
||||||
|
Requires mutable access to input address.
|
||||||
|
"""
|
||||||
|
|
||||||
[[instructions]]
|
[[instructions]]
|
||||||
name = "ntoc"
|
name = "ntoc"
|
||||||
args = ["src"]
|
args = ["src"]
|
||||||
|
|
|
||||||
|
|
@ -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<u8>) {
|
|
||||||
// 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<u8>) {
|
|
||||||
// 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<u8>) {
|
|
||||||
for i in self.0 {
|
|
||||||
dst.push(*i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn normalize_scientific_into(&self, dst: &mut Vec<u8>) {
|
|
||||||
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<u8>) -> 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<u8>) -> 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<u8>> for Number<'a> {
|
|
||||||
fn from(value: &'a Vec<u8>) -> 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>) -> 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<Ordering> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_number_tests() {
|
|
||||||
assert_eq!("1.3".parse::<Number>(),
|
|
||||||
Ok(Number::Flt(Float(1.3))));
|
|
||||||
|
|
||||||
assert_eq!("1".parse::<Number>(),
|
|
||||||
Ok(Number::Flt(Float(1 as f64))));
|
|
||||||
|
|
||||||
assert_eq!("1.3e3".parse::<Number>(),
|
|
||||||
Ok(Number::Sci(ScientificNotation(1.3, 3))));
|
|
||||||
|
|
||||||
assert_eq!("+1.3".parse::<Number>(),
|
|
||||||
Ok(Number::Flt(Float(1.3))));
|
|
||||||
|
|
||||||
assert_eq!("-1.3".parse::<Number>(),
|
|
||||||
Ok(Number::Flt(Float(-1.3))));
|
|
||||||
|
|
||||||
assert_eq!("#d234".parse::<Number>(),
|
|
||||||
Ok(Number::Flt(Float(234.0))));
|
|
||||||
|
|
||||||
assert_eq!("#o17".parse::<Number>(),
|
|
||||||
Ok(Number::Fra(Fraction(15, 1))));
|
|
||||||
|
|
||||||
assert_eq!("#xAA".parse::<Number>(),
|
|
||||||
Ok(Number::Fra(Fraction(170, 1))));
|
|
||||||
|
|
||||||
assert_eq!("#b101".parse::<Number>(),
|
|
||||||
Ok(Number::Flt(Float(5.0))));
|
|
||||||
|
|
||||||
assert_eq!("2/4".parse::<Number>(),
|
|
||||||
Ok(Number::Fra(Fraction(2, 4))));
|
|
||||||
|
|
||||||
assert_eq!("#e1/5".parse::<Number>(),
|
|
||||||
Ok(Number::Fra(Fraction(1, 5))));
|
|
||||||
|
|
||||||
assert_eq!("#i1/5".parse::<Number>(),
|
|
||||||
Ok(Number::Flt(Float(0.2))));
|
|
||||||
|
|
||||||
assert_eq!("#e1e1".parse::<Number>(),
|
|
||||||
Ok(Number::Sci(ScientificNotation(1.0, 1))));
|
|
||||||
|
|
||||||
assert_eq!("+inf.0".parse::<Number>(),
|
|
||||||
Ok(Number::Sym(SymbolicNumber::Inf)));
|
|
||||||
|
|
||||||
assert_eq!("2e3".parse::<Number>(),
|
|
||||||
Ok(ScientificNotation(2.0, 3)));
|
|
||||||
|
|
||||||
assert_eq!("0e1".parse::<Number>(),
|
|
||||||
Ok(ScientificNotation(0.0, 1)));
|
|
||||||
|
|
||||||
assert_eq!("-1e34".parse::<Number>(),
|
|
||||||
Ok(ScientificNotation(-1.0, 34)));
|
|
||||||
|
|
||||||
assert_eq!("3.3e3".parse::<Number>(),
|
|
||||||
Ok(ScientificNotation(3.3, 3)));
|
|
||||||
|
|
||||||
assert_eq!("2".parse::<Number>(),
|
|
||||||
Err(E_SCIENTIFIC_E));
|
|
||||||
|
|
||||||
assert_eq!("2e2e2".parse::<Number>(),
|
|
||||||
Err(E_SCIENTIFIC_MULTI_E));
|
|
||||||
|
|
||||||
assert_eq!("2/3".parse::<Number>(),
|
|
||||||
Ok(Fraction(2, 3)));
|
|
||||||
|
|
||||||
assert_eq!("0/1".parse::<Number>(),
|
|
||||||
Ok(Fraction(0, 1)));
|
|
||||||
|
|
||||||
assert_eq!("-1/34".parse::<Number>(),
|
|
||||||
Ok(Fraction(-1, 34)));
|
|
||||||
|
|
||||||
assert_eq!("2".parse::<Number>(),
|
|
||||||
Err(E_NO_DENOMINATOR));
|
|
||||||
|
|
||||||
assert_eq!("2/2/2".parse::<Number>(),
|
|
||||||
Err(E_MULTI_DENOMINATOR));
|
|
||||||
|
|
||||||
assert_eq!("2/0".parse::<Number>(),
|
|
||||||
Err(E_ZERO_DENOMINATOR));
|
|
||||||
|
|
||||||
assert_eq!("3.3/3".parse::<Number>(),
|
|
||||||
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::<Number>().unwrap();
|
|
||||||
let y = case[1].parse::<Number>().unwrap();
|
|
||||||
let z = case[2].parse::<Number>().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::<Number>().unwrap();
|
|
||||||
let y = "1e1".parse::<Number>().unwrap();
|
|
||||||
let z = "+inf.0".parse::<Number>().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::<Number>().unwrap();
|
|
||||||
let y = case[1].parse::<Number>().unwrap();
|
|
||||||
let z = case[2].parse::<Number>().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::<Number>().unwrap();
|
|
||||||
let y = case[1].parse::<Number>().unwrap();
|
|
||||||
let z = case[2].parse::<Number>().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::<Number>().unwrap();
|
|
||||||
let y = case[1].parse::<Number>().unwrap();
|
|
||||||
let z = case[2].parse::<Number>().unwrap();
|
|
||||||
assert!(x < y);
|
|
||||||
assert!(y < z);
|
|
||||||
assert!(x < z);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn float_negative_exponent_case() {
|
|
||||||
if let Float(0.1) = "1e-1"
|
|
||||||
.parse::<Number>()
|
|
||||||
.unwrap()
|
|
||||||
.make_inexact() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue