Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
/* Mycelium Scheme
|
|
|
|
|
* Copyright (C) 2025 Ava Affine
|
|
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
2025-07-24 19:44:43 +00:00
|
|
|
#![cfg_attr(not(test), no_std)]
|
|
|
|
|
extern crate alloc;
|
|
|
|
|
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
use alloc::string::String;
|
|
|
|
|
use alloc::format;
|
|
|
|
|
use alloc::fmt::Debug;
|
2025-08-26 17:11:37 +00:00
|
|
|
use alloc::vec;
|
|
|
|
|
use alloc::vec::Vec;
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
use core::{cmp::Ordering, f64, ops::{Add, Div, Mul, Sub}, str::FromStr};
|
2025-05-21 14:48:36 -07:00
|
|
|
use num::{integer::{gcd}, pow::Pow};
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
|
|
|
|
|
pub const E_INCOMPREHENSIBLE: &str = "could not comprehend number literal";
|
|
|
|
|
pub const E_BASE_PARSE_FAIL: &str = "failed to parse explicit base literal";
|
|
|
|
|
pub const E_POUND_TRUNCATED: &str = "pound sign implies additional input";
|
|
|
|
|
pub const E_UNKNOWN_CONTROL: &str = "unknown character in number literal";
|
|
|
|
|
pub const E_EMPTY_INPUT: &str = "empty string cannot be a number";
|
|
|
|
|
pub const E_UNKNOWN_SYMBOL: &str = "unknown symbolic number repr";
|
|
|
|
|
pub const E_NO_DENOMINATOR: &str = "fraction is missing a denominator";
|
|
|
|
|
pub const E_MULTI_DENOMINATOR: &str = "fraction has too many denominators";
|
|
|
|
|
pub const E_ZERO_DENOMINATOR: &str = "denominator cannot be zero";
|
|
|
|
|
pub const E_NUMERATOR_PARSE_FAIL: &str = "couldnt parse numerator";
|
|
|
|
|
pub const E_DENOMINATOR_PARSE_FAIL: &str = "couldnt parse denominator";
|
|
|
|
|
pub const E_FLOAT_PARSE_FAIL: &str = "couldnt parse float";
|
|
|
|
|
pub const E_SCIENTIFIC_E: &str = "scientific notation implies an 'e'";
|
|
|
|
|
pub const E_SCIENTIFIC_MULTI_E: &str = "scientific notation implies only a single 'e'";
|
|
|
|
|
pub const E_SCIENTIFIC_OPERAND: &str = "couldnt parse 32 bit float operand";
|
|
|
|
|
pub const E_SCIENTIFIC_POWER: &str = "couldnt parse integer power";
|
|
|
|
|
|
2025-05-19 14:38:11 -07:00
|
|
|
pub trait Numeric: Copy + Clone + Debug + FromStr + Into<String> {
|
2025-05-16 15:04:53 -07:00
|
|
|
fn is_exact(&self) -> bool;
|
|
|
|
|
fn make_inexact(&self) -> Float;
|
|
|
|
|
fn make_exact(&self) -> Fraction;
|
|
|
|
|
}
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2025-05-19 14:38:11 -07:00
|
|
|
pub struct ScientificNotation (pub f32, pub isize);
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
|
|
|
pub enum SymbolicNumber {
|
|
|
|
|
Inf,
|
|
|
|
|
NegInf,
|
|
|
|
|
NaN,
|
|
|
|
|
NegNan,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2025-05-19 14:38:11 -07:00
|
|
|
pub struct Fraction (pub isize, pub isize);
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2025-05-19 14:38:11 -07:00
|
|
|
pub struct Float (pub f64);
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
|
|
|
pub enum Number {
|
|
|
|
|
Sci(ScientificNotation),
|
|
|
|
|
Fra(Fraction),
|
|
|
|
|
Flt(Float),
|
|
|
|
|
Sym(SymbolicNumber)
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 01:13:13 +00:00
|
|
|
impl Default for Number {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Number::Fra(Fraction(0, 1))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
impl From<SymbolicNumber> for Number {
|
|
|
|
|
fn from(value: SymbolicNumber) -> Self {
|
|
|
|
|
Number::Sym(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<ScientificNotation> for Number {
|
|
|
|
|
fn from(value: ScientificNotation) -> Self {
|
|
|
|
|
Number::Sci(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Fraction> for Number {
|
|
|
|
|
fn from(value: Fraction) -> Self {
|
|
|
|
|
Number::Fra(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Float> for Number {
|
|
|
|
|
fn from(value: Float) -> Self {
|
|
|
|
|
Number::Flt(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: both the following impls should be done with a macro
|
|
|
|
|
impl Into<String> for Number {
|
|
|
|
|
fn into(self) -> String {
|
|
|
|
|
match self {
|
|
|
|
|
Number::Sci(x) => x.into(),
|
|
|
|
|
Number::Fra(x) => x.into(),
|
|
|
|
|
Number::Flt(x) => x.into(),
|
|
|
|
|
Number::Sym(x) => x.into(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Numeric for Number {
|
|
|
|
|
fn is_exact(&self) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
Number::Sci(x) => x.is_exact(),
|
|
|
|
|
Number::Fra(x) => x.is_exact(),
|
|
|
|
|
Number::Flt(x) => x.is_exact(),
|
|
|
|
|
Number::Sym(x) => x.is_exact(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn make_exact(&self) -> Fraction {
|
|
|
|
|
match self {
|
|
|
|
|
Number::Sci(x) => x.make_exact(),
|
|
|
|
|
Number::Fra(x) => x.make_exact(),
|
|
|
|
|
Number::Flt(x) => x.make_exact(),
|
|
|
|
|
Number::Sym(x) => x.make_exact(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn make_inexact(&self) -> Float {
|
|
|
|
|
match self {
|
|
|
|
|
Number::Sci(x) => x.make_inexact(),
|
|
|
|
|
Number::Fra(x) => x.make_inexact(),
|
|
|
|
|
Number::Flt(x) => x.make_inexact(),
|
|
|
|
|
Number::Sym(x) => x.make_inexact(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<f64> for Number {
|
|
|
|
|
fn from(value: f64) -> Self {
|
|
|
|
|
match value {
|
|
|
|
|
f64::INFINITY => Number::Sym(SymbolicNumber::Inf),
|
|
|
|
|
f64::NEG_INFINITY => Number::Sym(SymbolicNumber::NegInf),
|
|
|
|
|
_ if value.is_nan() => Number::Sym(SymbolicNumber::NaN),
|
|
|
|
|
_ if value.fract() == 0.0 => Number::Fra(Fraction(value as isize, 1)),
|
|
|
|
|
_ => Number::Flt(Float(value))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FromStr for Number {
|
|
|
|
|
type Err = &'static str;
|
|
|
|
|
|
|
|
|
|
/* Number forms
|
|
|
|
|
* - 1.3
|
|
|
|
|
* - 1e100
|
|
|
|
|
* - 1.3e100
|
|
|
|
|
* - +1.3
|
|
|
|
|
* - -2.3
|
|
|
|
|
* - #d124
|
|
|
|
|
* - #o2535 // base 8
|
|
|
|
|
* - #x8A3D // base 16
|
|
|
|
|
* - #b1011 // base 2
|
|
|
|
|
* - 2/4 // inexact
|
|
|
|
|
* - #e1/5 // exact (fraction is as is)
|
|
|
|
|
* - #e1e1 // exact 1e1 (= 10)
|
|
|
|
|
* - #i1/5 // inexact (collapse fraction to decimal)
|
|
|
|
|
* - +inf.0, -inf.0, +nan.0, -nan.0
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
let maybe_sym = value.parse::<SymbolicNumber>();
|
|
|
|
|
if maybe_sym.is_ok() {
|
|
|
|
|
return Ok(Number::Sym(maybe_sym.unwrap()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Only two things that we need to handle here.
|
|
|
|
|
* 1. leading with a +/-
|
|
|
|
|
* 2. leading with a #i or a #e
|
|
|
|
|
* These are mutually exclusive options.
|
|
|
|
|
*
|
|
|
|
|
* Once they have been managed or ruled out we can
|
|
|
|
|
* just try each number type
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
let mut force_exact = false;
|
|
|
|
|
let mut force_inexact = false;
|
|
|
|
|
let mut base = 0;
|
|
|
|
|
let mut iter = value.chars();
|
|
|
|
|
let mut start_idx: usize = 0;
|
|
|
|
|
|
|
|
|
|
match iter.next() {
|
|
|
|
|
Some('+') => start_idx = 1,
|
|
|
|
|
Some('-') => start_idx = 0,
|
|
|
|
|
Some('#') => {
|
|
|
|
|
start_idx = 2;
|
|
|
|
|
match iter.next() {
|
|
|
|
|
None => return Err(E_POUND_TRUNCATED),
|
|
|
|
|
Some('i') => force_inexact = true,
|
|
|
|
|
Some('e') => force_exact = true,
|
|
|
|
|
Some('x') => base = 16,
|
|
|
|
|
Some('d') => base = 10,
|
|
|
|
|
Some('o') => base = 8,
|
|
|
|
|
Some('b') => base = 2,
|
|
|
|
|
_ => return Err(E_UNKNOWN_CONTROL),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => return Err(E_EMPTY_INPUT),
|
|
|
|
|
_ => ()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let substr = &value[start_idx..];
|
|
|
|
|
let res;
|
|
|
|
|
|
|
|
|
|
if base > 0 {
|
|
|
|
|
let num = isize::from_str_radix(substr, base)
|
|
|
|
|
.or(Err(E_BASE_PARSE_FAIL))?;
|
|
|
|
|
return Ok(Number::Fra(Fraction(num, 1)));
|
|
|
|
|
|
|
|
|
|
} else if let Ok(num) = substr.parse::<ScientificNotation>() {
|
|
|
|
|
res = Number::Sci(num);
|
|
|
|
|
|
|
|
|
|
} else if let Ok(num) = substr.parse::<Fraction>() {
|
|
|
|
|
res = Number::Fra(num);
|
|
|
|
|
|
|
|
|
|
} else if let Ok(num) = substr.parse::<Float>() {
|
|
|
|
|
res = Number::Flt(num);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
return Err(E_INCOMPREHENSIBLE)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if force_exact {
|
|
|
|
|
return Ok(Number::Fra(res.make_exact()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if force_inexact {
|
|
|
|
|
return Ok(Number::Flt(res.make_inexact()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(res)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-26 17:11:37 +00:00
|
|
|
// this looks rushed and it is
|
|
|
|
|
// would rather work on organelles replacement than improve this
|
|
|
|
|
impl Into<Vec<u8>> for Number {
|
|
|
|
|
fn into(self) -> Vec<u8> {
|
|
|
|
|
let mut out = vec![];
|
|
|
|
|
match self {
|
|
|
|
|
Number::Sci(num) => {
|
|
|
|
|
out.push(0x00);
|
|
|
|
|
for ele in num.0.to_be_bytes().iter() {
|
|
|
|
|
out.push(*ele);
|
|
|
|
|
}
|
|
|
|
|
for ele in num.1.to_be_bytes().iter() {
|
|
|
|
|
out.push(*ele);
|
|
|
|
|
}
|
|
|
|
|
out
|
|
|
|
|
},
|
|
|
|
|
Number::Flt(num) => {
|
|
|
|
|
out.push(0x01 as u8);
|
|
|
|
|
for ele in num.0.to_be_bytes().iter() {
|
|
|
|
|
out.push(*ele);
|
|
|
|
|
}
|
|
|
|
|
out
|
|
|
|
|
},
|
|
|
|
|
Number::Fra(num) => {
|
|
|
|
|
out.push(0x02);
|
|
|
|
|
for ele in num.0.to_be_bytes().iter() {
|
|
|
|
|
out.push(*ele);
|
|
|
|
|
}
|
|
|
|
|
for ele in num.1.to_be_bytes().iter() {
|
|
|
|
|
out.push(*ele);
|
|
|
|
|
}
|
|
|
|
|
out
|
|
|
|
|
},
|
|
|
|
|
Number::Sym(num) => {
|
|
|
|
|
match num {
|
|
|
|
|
SymbolicNumber::Inf => out.push(0x03),
|
|
|
|
|
SymbolicNumber::NaN => out.push(0x04),
|
|
|
|
|
SymbolicNumber::NegInf => out.push(0x05),
|
|
|
|
|
SymbolicNumber::NegNan => out.push(0x06),
|
|
|
|
|
}
|
|
|
|
|
out
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// same as the Into impl
|
|
|
|
|
impl TryFrom<&[u8]> for Number {
|
|
|
|
|
type Error = &'static str;
|
|
|
|
|
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
|
|
|
|
match value[0] {
|
|
|
|
|
0x03 => Ok(Number::Sym(SymbolicNumber::Inf)),
|
|
|
|
|
0x04 => Ok(Number::Sym(SymbolicNumber::NaN)),
|
|
|
|
|
0x05 => Ok(Number::Sym(SymbolicNumber::NegInf)),
|
|
|
|
|
0x06 => Ok(Number::Sym(SymbolicNumber::NegNan)),
|
|
|
|
|
0x00 if value.len() >= (1 + 4 + (isize::BITS / 8)) as usize => {
|
|
|
|
|
let mut i: [u8; 4] = [0, 0, 0, 0];
|
|
|
|
|
value[1..5].iter().zip(i.iter_mut())
|
|
|
|
|
.for_each(|(a, b)| { *b = *a });
|
|
|
|
|
let i = f32::from_be_bytes(i);
|
|
|
|
|
|
|
|
|
|
let mut j: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
|
|
|
|
|
value[5..13].iter().zip(j.iter_mut())
|
|
|
|
|
.for_each(|(a, b)| { *b = *a });
|
|
|
|
|
let j = isize::from_be_bytes(j);
|
|
|
|
|
Ok(Number::Sci(ScientificNotation(i, j)))
|
|
|
|
|
},
|
|
|
|
|
0x01 if value.len() >= 9 as usize => {
|
|
|
|
|
let mut i: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
|
|
|
|
|
value[1..9].iter().zip(i.iter_mut())
|
|
|
|
|
.for_each(|(a, b)| { *b = *a });
|
|
|
|
|
let i = f64::from_be_bytes(i);
|
|
|
|
|
Ok(Number::Flt(Float(i)))
|
|
|
|
|
},
|
|
|
|
|
0x02 if value.len() >= 1 + ((isize::BITS / 8) * 2) as usize => {
|
|
|
|
|
let mut i: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
|
|
|
|
|
value[1..9].iter().zip(i.iter_mut())
|
|
|
|
|
.for_each(|(a, b)| { *b = *a });
|
|
|
|
|
let i = isize::from_be_bytes(i);
|
|
|
|
|
let mut j: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
|
|
|
|
|
value[9..17].iter().zip(j.iter_mut())
|
|
|
|
|
.for_each(|(a, b)| { *b = *a });
|
|
|
|
|
let j = isize::from_be_bytes(j);
|
|
|
|
|
Ok(Number::Fra(Fraction(i, j)))
|
|
|
|
|
},
|
|
|
|
|
_ => Err("attempted to deserialize invalid number format")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
impl Add for Number {
|
|
|
|
|
type Output = Number;
|
|
|
|
|
fn add(self, rhs: Self) -> Self::Output {
|
|
|
|
|
if self.is_exact() && rhs.is_exact() {
|
|
|
|
|
let Fraction(lnum, lden) = self.make_exact();
|
|
|
|
|
let Fraction(rnum, rden) = rhs.make_exact();
|
|
|
|
|
let num = (lnum * rden) + (rnum * lden);
|
|
|
|
|
let den = lden * rden;
|
|
|
|
|
Number::Fra(Fraction(num, den).simplify())
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
let Float(l) = self.make_inexact();
|
|
|
|
|
let Float(r) = rhs.make_inexact();
|
|
|
|
|
let res = l + r;
|
|
|
|
|
res.into()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Sub for Number {
|
|
|
|
|
type Output = Number;
|
|
|
|
|
fn sub(self, rhs: Self) -> Self::Output {
|
|
|
|
|
if self.is_exact() && rhs.is_exact() {
|
|
|
|
|
let Fraction(lnum, lden) = self.make_exact();
|
|
|
|
|
let Fraction(rnum, rden) = rhs.make_exact();
|
|
|
|
|
let num = (lnum * rden) - (rnum * lden);
|
|
|
|
|
let den = lden * rden;
|
|
|
|
|
Number::Fra(Fraction(num, den).simplify())
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
let Float(l) = self.make_inexact();
|
|
|
|
|
let Float(r) = rhs.make_inexact();
|
|
|
|
|
let res = l - r;
|
|
|
|
|
res.into()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Mul for Number {
|
|
|
|
|
type Output = Number;
|
|
|
|
|
fn mul(self, rhs: Self) -> Self::Output {
|
|
|
|
|
if self.is_exact() && rhs.is_exact() {
|
|
|
|
|
let Fraction(lnum, lden) = self.make_exact();
|
|
|
|
|
let Fraction(rnum, rden) = rhs.make_exact();
|
|
|
|
|
let num = lnum * rnum;
|
|
|
|
|
let den = lden * rden;
|
|
|
|
|
Number::Fra(Fraction(num, den).simplify())
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
let Float(l) = self.make_inexact();
|
|
|
|
|
let Float(r) = rhs.make_inexact();
|
|
|
|
|
let res = l * r;
|
|
|
|
|
res.into()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Div for Number {
|
|
|
|
|
type Output = Number;
|
|
|
|
|
fn div(self, rhs: Self) -> Self::Output {
|
|
|
|
|
if self.is_exact() && rhs.is_exact() {
|
|
|
|
|
let Fraction(lnum, lden) = self.make_exact();
|
|
|
|
|
let Fraction(rnum, rden) = rhs.make_exact();
|
|
|
|
|
let num = lnum * rden;
|
|
|
|
|
let den = rnum * lden;
|
|
|
|
|
Number::Fra(Fraction(num, den).simplify())
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
let Float(l) = self.make_inexact();
|
|
|
|
|
let Float(r) = rhs.make_inexact();
|
|
|
|
|
let res = l / r;
|
|
|
|
|
res.into()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Pow<Number> for Number {
|
|
|
|
|
type Output = Number;
|
|
|
|
|
fn pow(self, rhs: Number) -> Self::Output {
|
|
|
|
|
if self.is_exact() && rhs.is_exact() {
|
2025-05-16 15:04:53 -07:00
|
|
|
let Fraction(mut lnum, mut lden) = self
|
|
|
|
|
.make_exact()
|
|
|
|
|
.simplify();
|
|
|
|
|
let Fraction(rnum, rden) = rhs
|
|
|
|
|
.make_exact()
|
|
|
|
|
.simplify();
|
|
|
|
|
|
|
|
|
|
// flip first frac if second one is negative
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
if rnum < 0 {
|
2025-05-16 15:04:53 -07:00
|
|
|
let tmp = lnum;
|
|
|
|
|
lnum = lden;
|
|
|
|
|
lden = tmp;
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
}
|
|
|
|
|
|
2025-05-16 15:04:53 -07:00
|
|
|
// apply fractional exponent (denominator)
|
|
|
|
|
let mut intermediate_numer = f64::powf(lnum as f64, 1.0 / rden as f64);
|
|
|
|
|
let mut intermediate_denom = f64::powf(lden as f64, 1.0 / rden as f64);
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
|
2025-05-16 15:04:53 -07:00
|
|
|
// apply whole exponent (numerator)
|
|
|
|
|
intermediate_numer =
|
|
|
|
|
f64::powf(intermediate_numer, rnum.abs() as f64);
|
|
|
|
|
intermediate_denom =
|
|
|
|
|
f64::powf(intermediate_denom, rnum.abs() as f64);
|
|
|
|
|
|
|
|
|
|
if intermediate_numer.fract() == 0.0 && intermediate_denom.fract() == 0.0 {
|
|
|
|
|
Number::Fra(Fraction(intermediate_numer as isize, intermediate_denom as isize)
|
|
|
|
|
.simplify())
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
} else {
|
2025-05-16 15:04:53 -07:00
|
|
|
(intermediate_numer / intermediate_denom).into()
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
}
|
|
|
|
|
|
2025-05-16 15:04:53 -07:00
|
|
|
// one or both are already inexact
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
} else {
|
|
|
|
|
let Float(l) = self.make_inexact();
|
|
|
|
|
let Float(r) = rhs.make_inexact();
|
|
|
|
|
let res = f64::powf(l, r);
|
|
|
|
|
res.into()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PartialEq for Number {
|
|
|
|
|
fn eq(&self, other: &Number) -> bool {
|
|
|
|
|
//if self.is_exact() && other.is_exact() {
|
|
|
|
|
// TODO: Figure out a way to comp two fractions without reducing
|
|
|
|
|
// to a float and losing the precision of exact numbers
|
|
|
|
|
//} else {
|
|
|
|
|
self.make_inexact() == other.make_inexact()
|
|
|
|
|
//}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PartialOrd for Number{
|
|
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
|
|
|
//if self.is_exact() && other.is_exact() {
|
|
|
|
|
// TODO: Figure out a way to comp two fractions without reducing
|
|
|
|
|
// to a float and losing the precision of exact numbers
|
|
|
|
|
//} else {
|
|
|
|
|
self.make_inexact().0.partial_cmp(&other.make_inexact().0)
|
|
|
|
|
//}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FromStr for SymbolicNumber {
|
|
|
|
|
type Err = &'static str;
|
|
|
|
|
|
|
|
|
|
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
match value {
|
|
|
|
|
"+inf.0" => Ok(SymbolicNumber::Inf),
|
|
|
|
|
"-inf.0" => Ok(SymbolicNumber::NegInf),
|
|
|
|
|
"+nan.0" => Ok(SymbolicNumber::NaN),
|
|
|
|
|
"-nan.0" => Ok(SymbolicNumber::NegNan),
|
|
|
|
|
_ => Err(E_UNKNOWN_SYMBOL)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Into<String> for SymbolicNumber {
|
|
|
|
|
fn into(self) -> String {
|
|
|
|
|
match self {
|
|
|
|
|
SymbolicNumber::Inf => format!("+inf.0"),
|
|
|
|
|
SymbolicNumber::NegInf => format!("-inf.0"),
|
|
|
|
|
SymbolicNumber::NaN => format!("+nan.0"),
|
|
|
|
|
SymbolicNumber::NegNan => format!("-nan.0"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Numeric for SymbolicNumber {
|
|
|
|
|
fn is_exact(&self) -> bool {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn make_inexact(&self) -> Float {
|
|
|
|
|
match self {
|
|
|
|
|
SymbolicNumber::Inf => Float(f64::INFINITY),
|
|
|
|
|
SymbolicNumber::NegInf => Float(f64::NEG_INFINITY),
|
|
|
|
|
SymbolicNumber::NaN | SymbolicNumber::NegNan => Float(f64::NAN),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn make_exact(&self) -> Fraction {
|
|
|
|
|
panic!("attempted to make inf or nan into fraction")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Fraction {
|
|
|
|
|
fn simplify(&self) -> Fraction {
|
2025-05-16 15:04:53 -07:00
|
|
|
let mut n = self.0;
|
|
|
|
|
let mut d = self.1;
|
|
|
|
|
/* normalize negative to numerator
|
|
|
|
|
* if numerator is also negative this becomes a positive frac
|
|
|
|
|
*/
|
|
|
|
|
if self.1 < 0 {
|
|
|
|
|
d = 0 - self.1;
|
|
|
|
|
n = 0 - self.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* divide both by their greatest common divisor
|
|
|
|
|
* leading to simplest possible form
|
|
|
|
|
*/
|
|
|
|
|
let g = gcd(n, d);
|
|
|
|
|
Fraction(n / g, d / g)
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FromStr for Fraction {
|
|
|
|
|
type Err = &'static str;
|
|
|
|
|
|
|
|
|
|
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
let part: usize;
|
|
|
|
|
if let Some(idx) = value.find('/') {
|
|
|
|
|
part = idx;
|
|
|
|
|
} else {
|
|
|
|
|
return Err(E_NO_DENOMINATOR)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// make sure there is ONLY ONE slash
|
|
|
|
|
if let Some(idx) = value.rfind('/') && idx != part {
|
|
|
|
|
return Err(E_MULTI_DENOMINATOR)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let numerator_text = &value[..part];
|
|
|
|
|
let denominator_text = &value[part+1..];
|
|
|
|
|
|
|
|
|
|
let numerator = numerator_text.parse::<isize>()
|
|
|
|
|
.or(Err(E_NUMERATOR_PARSE_FAIL))?;
|
|
|
|
|
let denominator = denominator_text.parse::<isize>()
|
|
|
|
|
.or(Err(E_DENOMINATOR_PARSE_FAIL))?;
|
|
|
|
|
|
|
|
|
|
if denominator == 0 {
|
|
|
|
|
return Err(E_ZERO_DENOMINATOR)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(Fraction(numerator, denominator))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Into<String> for Fraction {
|
|
|
|
|
fn into(self) -> String {
|
2025-05-19 14:38:11 -07:00
|
|
|
format!("{}/{}", self.0, self.1)
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Numeric for Fraction {
|
|
|
|
|
fn is_exact(&self) -> bool {
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn make_inexact(&self) -> Float {
|
|
|
|
|
Float(self.0 as f64 / self.1 as f64)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn make_exact(&self) -> Fraction {
|
|
|
|
|
*self
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FromStr for Float {
|
|
|
|
|
type Err = &'static str;
|
|
|
|
|
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
Ok(Float(value.parse::<f64>().or(Err(E_FLOAT_PARSE_FAIL))?))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Into<String> for Float {
|
|
|
|
|
fn into(self) -> String {
|
2025-05-19 14:38:11 -07:00
|
|
|
if self.is_exact() {
|
|
|
|
|
format!("{}", self.0)
|
|
|
|
|
} else {
|
|
|
|
|
format!("#i{}", self.0)
|
|
|
|
|
}
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Numeric for Float {
|
|
|
|
|
fn is_exact(&self) -> bool {
|
|
|
|
|
self.0.fract() == 0.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn make_inexact(&self) -> Float {
|
|
|
|
|
*self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn make_exact(&self) -> Fraction {
|
|
|
|
|
if self.0.fract() == 0.0 {
|
|
|
|
|
Fraction(self.0 as isize, 1)
|
|
|
|
|
} else {
|
2025-06-26 10:52:54 -07:00
|
|
|
todo!("rational approximation implementation")
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FromStr for ScientificNotation {
|
|
|
|
|
type Err = &'static str;
|
|
|
|
|
|
|
|
|
|
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
let part: usize;
|
|
|
|
|
if let Some(idx) = value.find('e') {
|
|
|
|
|
part = idx;
|
|
|
|
|
} else {
|
|
|
|
|
return Err(E_SCIENTIFIC_E)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// make sure there is ONLY ONE slash
|
|
|
|
|
if let Some(idx) = value.rfind('e') && idx != part {
|
|
|
|
|
return Err(E_SCIENTIFIC_MULTI_E)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let operand_text = &value[..part];
|
|
|
|
|
let power_text = &value[part+1..];
|
|
|
|
|
|
|
|
|
|
let operand = operand_text.parse::<f32>()
|
|
|
|
|
.or(Err(E_SCIENTIFIC_OPERAND))?;
|
|
|
|
|
let power = power_text.parse::<isize>()
|
|
|
|
|
.or(Err(E_SCIENTIFIC_POWER))?;
|
|
|
|
|
|
|
|
|
|
Ok(ScientificNotation(operand, power))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Into<String> for ScientificNotation {
|
|
|
|
|
fn into(self) -> String {
|
2025-05-19 14:38:11 -07:00
|
|
|
format!("{}e{}", self.0, self.1)
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Numeric for ScientificNotation {
|
|
|
|
|
fn is_exact(&self) -> bool {
|
|
|
|
|
self.0.fract() == 0.0 && self.1 >= 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn make_inexact(&self) -> Float {
|
2025-05-16 15:04:53 -07:00
|
|
|
Float(self.0 as f64 * f64::powi(10.0, self.1 as i32) as f64)
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn make_exact(&self) -> Fraction {
|
|
|
|
|
self.make_inexact().make_exact()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
2025-08-26 17:11:37 +00:00
|
|
|
#[test]
|
|
|
|
|
fn serialize_deserialize_tests() {
|
|
|
|
|
let cases = vec![
|
|
|
|
|
"2/3".parse::<Number>().unwrap(),
|
|
|
|
|
"-4/5".parse::<Number>().unwrap(),
|
|
|
|
|
"2e45".parse::<Number>().unwrap(),
|
|
|
|
|
"1.2432566".parse::<Number>().unwrap(),
|
|
|
|
|
"+inf.0".parse::<Number>().unwrap(),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
for i in cases.iter() {
|
|
|
|
|
let j = Into::<Vec<u8>>::into(*i);
|
|
|
|
|
assert_eq!(*i, Number::try_from(j.as_slice()).unwrap());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
#[test]
|
|
|
|
|
fn parse_fraction_tests() {
|
|
|
|
|
assert_eq!("2/3".parse::<Fraction>(),
|
|
|
|
|
Ok(Fraction(2, 3)));
|
|
|
|
|
|
|
|
|
|
assert_eq!("0/1".parse::<Fraction>(),
|
|
|
|
|
Ok(Fraction(0, 1)));
|
|
|
|
|
|
|
|
|
|
assert_eq!("-1/34".parse::<Fraction>(),
|
|
|
|
|
Ok(Fraction(-1, 34)));
|
|
|
|
|
|
|
|
|
|
assert_eq!("2".parse::<Fraction>(),
|
|
|
|
|
Err(E_NO_DENOMINATOR));
|
|
|
|
|
|
|
|
|
|
assert_eq!("2/2/2".parse::<Fraction>(),
|
|
|
|
|
Err(E_MULTI_DENOMINATOR));
|
|
|
|
|
|
|
|
|
|
assert_eq!("2/0".parse::<Fraction>(),
|
|
|
|
|
Err(E_ZERO_DENOMINATOR));
|
|
|
|
|
|
|
|
|
|
assert_eq!("3.3/3".parse::<Fraction>(),
|
|
|
|
|
Err(E_NUMERATOR_PARSE_FAIL));
|
|
|
|
|
|
|
|
|
|
assert_eq!("2/two".parse::<Fraction>(),
|
|
|
|
|
Err(E_DENOMINATOR_PARSE_FAIL));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parse_scientific_notation_tests() {
|
|
|
|
|
assert_eq!("2e3".parse::<ScientificNotation>(),
|
|
|
|
|
Ok(ScientificNotation(2.0, 3)));
|
|
|
|
|
|
|
|
|
|
assert_eq!("0e1".parse::<ScientificNotation>(),
|
|
|
|
|
Ok(ScientificNotation(0.0, 1)));
|
|
|
|
|
|
|
|
|
|
assert_eq!("-1e34".parse::<ScientificNotation>(),
|
|
|
|
|
Ok(ScientificNotation(-1.0, 34)));
|
|
|
|
|
|
|
|
|
|
assert_eq!("3.3e3".parse::<ScientificNotation>(),
|
|
|
|
|
Ok(ScientificNotation(3.3, 3)));
|
|
|
|
|
|
|
|
|
|
assert_eq!("2".parse::<ScientificNotation>(),
|
|
|
|
|
Err(E_SCIENTIFIC_E));
|
|
|
|
|
|
|
|
|
|
assert_eq!("2e2e2".parse::<ScientificNotation>(),
|
|
|
|
|
Err(E_SCIENTIFIC_MULTI_E));
|
|
|
|
|
|
|
|
|
|
assert_eq!("2etwo".parse::<ScientificNotation>(),
|
|
|
|
|
Err(E_SCIENTIFIC_POWER));
|
|
|
|
|
|
|
|
|
|
assert_eq!("twoe2".parse::<ScientificNotation>(),
|
|
|
|
|
Err(E_SCIENTIFIC_OPERAND));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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"],
|
2025-05-16 15:04:53 -07:00
|
|
|
vec!["2/1", "2.0", "4/1"],
|
|
|
|
|
vec!["27/8", "2/-3", "4/9"]
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-05-16 15:04:53 -07:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn float_negative_exponent_case() {
|
|
|
|
|
if let Float(0.1) = "1e-1"
|
|
|
|
|
.parse::<Number>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.make_inexact() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert!(false)
|
|
|
|
|
}
|
Number library and integrations
This commit adds a number library which handles fractions, floats,
whole numbers, scientific notation, and special symbolic numbers
all according to the R7RS small specification.
Numeric trait is used to abstract operations across all number types
and a Number enum is used to offer a non-opaque type that stores any
kind of number.
Upon the Number enum is implemented the following traits:
- Add, Div, Sub, Mul
- Pow
- PartialEq
- PartialOrd
Which then offer the following operators to use on the Number enum
instances themselves: + - / * == != < > <= >= and of course x.pow(y).
Additionally, the number package contains parsing logic for each type
of number. FromStr is implemented as part of the Numeric trait, and
then in turn implemented on Number. Additionally Into<String> is
implemented for the Numeric trait and then on the Number enum type
as well.
Test cases have been added for basic cases, but could be expanded.
Additional modifications:
- LexError has a custom display implementation that properly outputs
formatted errors.
- Sexpr package updated to use new number package
Signed-off-by: Ava Affine <ava@sunnypup.io>
2025-05-15 12:49:08 -07:00
|
|
|
}
|