embiggened integers

This commit is contained in:
trimill 2024-12-21 00:55:45 -05:00
parent 6748444c7f
commit a1d39eaf58
32 changed files with 2443 additions and 997 deletions

52
Cargo.lock generated
View file

@ -162,11 +162,11 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "home"
version = "0.5.9"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
dependencies = [
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -177,9 +177,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.168"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "linux-raw-sys"
@ -232,6 +232,31 @@ dependencies = [
"libc",
]
[[package]]
name = "num"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
"rand",
]
[[package]]
name = "num-complex"
version = "0.4.6"
@ -250,12 +275,24 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
@ -434,9 +471,7 @@ name = "talc-lang"
version = "0.2.1"
dependencies = [
"lazy_static",
"num-complex",
"num-rational",
"num-traits",
"num",
"unicode-ident",
]
@ -453,6 +488,7 @@ name = "talc-std"
version = "0.2.1"
dependencies = [
"lazy_static",
"num-bigint",
"rand",
"regex",
"talc-lang",

View file

@ -4,7 +4,9 @@ use talc_lang::{
compiler::compile,
lstring::LString,
optimize::optimize,
parser, serial,
parser,
prelude::*,
serial,
symbol::Symbol,
value::{function::disasm_recursive, Value},
vm::Vm,
@ -112,7 +114,7 @@ fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
ExitCode::FAILURE
}
Ok(Value::Bool(false)) => ExitCode::FAILURE,
Ok(Value::Int(n)) => ExitCode::from(n as u8),
Ok(Value::Int(n)) => ExitCode::from(n.to_u64().unwrap_or(1) as u8),
_ => ExitCode::SUCCESS,
}
}

View file

@ -4,8 +4,6 @@ version = "0.2.1"
edition = "2021"
[dependencies]
num-complex = "0.4"
num-rational = { version = "0.4", default-features = false, features = [] }
num-traits = "0.2"
num = { version = "0.4", features = [] }
lazy_static = "1.5"
unicode-ident = "1.0"

View file

@ -5,6 +5,7 @@ use std::rc::Rc;
use crate::chunk::{Arg24, Catch, Chunk, Instruction as I};
use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
use crate::parser::Pos;
use crate::prelude::*;
use crate::symbol::{Symbol, SYM_REPL, SYM_SELF};
use crate::throw;
use crate::value::function::{FuncAttrs, Function};
@ -766,22 +767,29 @@ impl<'a> Compiler<'a> {
match val {
Value::Nil => {
self.emit(I::Nil);
return
}
Value::Bool(b) => {
self.emit(I::Bool(*b));
}
Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i) => {
self.emit(I::Int(Arg24::from_i64(*i)));
return
}
Value::Symbol(s) => {
self.emit(I::Symbol(Arg24::from_symbol(*s)));
return
}
Value::Int(i) => {
if let Some(v) = i.to_i32() {
if (-0x80_0000..=0x7f_ffff).contains(&v) {
self.emit(I::Int(Arg24::from_i32(v)));
return
}
}
}
_ => (),
}
_ => {
let n = self.add_const(val.clone());
self.emit(I::Const(Arg24::from_usize(n)));
}
}
}
fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) -> Result<()> {
match (&lv.kind, o) {

View file

@ -8,9 +8,19 @@ pub mod chunk;
pub mod compiler;
pub mod exception;
pub mod lstring;
pub mod number;
pub mod optimize;
pub mod parser;
pub mod serial;
pub mod symbol;
pub mod value;
pub mod vm;
pub mod prelude {
pub use crate::number::RatioExt;
pub use num::complex::ComplexFloat;
pub use num::integer::{Integer, Roots};
pub use num::traits::{
ConstOne, ConstZero, Euclid, FromPrimitive, Num, One, Signed, ToPrimitive, Zero,
};
}

View file

@ -546,13 +546,6 @@ impl io::Write for LString {
}
}
//impl fmt::Write for LString {
// fn write_str(&mut self, s: &str) -> fmt::Result {
// self.extend(s.as_bytes());
// Ok(())
// }
//}
//
// methods
//

986
talc-lang/src/number/int.rs Normal file
View file

@ -0,0 +1,986 @@
use core::{f64, panic};
use std::{borrow::Cow, cmp::Ordering, fmt, hash::Hash, mem::ManuallyDrop, ops, rc::Rc};
use num::{
bigint::{ParseBigIntError, Sign},
integer::Roots,
traits::CheckedEuclid,
BigInt, Integer, Num,
};
use crate::prelude::*;
pub enum IntType<'a> {
Int(i64),
Big(&'a BigInt),
}
#[inline]
const fn is_int_small(n: i64) -> bool {
let b = n >> 62;
b == 0 || b == -1
}
union IntInner {
int: i64,
big: ManuallyDrop<Rc<BigInt>>,
}
impl IntInner {
#[inline]
fn from_int(n: i64) -> Self {
if is_int_small(n) {
unsafe { Self::from_int_unchecked(n) }
} else {
Self::from_big_direct(BigInt::from(n))
}
}
#[inline]
const unsafe fn from_int_unchecked(n: i64) -> Self {
Self { int: (n << 1) | 1 }
}
#[inline]
fn from_big(n: BigInt) -> Self {
if let Some(n) = n.to_i64() {
if is_int_small(n) {
return unsafe { Self::from_int_unchecked(n) }
}
}
Self::from_big_direct(n)
}
#[inline]
fn from_big_ref(n: &BigInt) -> Self {
if let Some(n) = n.to_i64() {
if is_int_small(n) {
return unsafe { Self::from_int_unchecked(n) }
}
}
Self::from_big_direct(n.clone())
}
#[inline]
fn from_big_direct(n: BigInt) -> Self {
Self {
big: ManuallyDrop::new(Rc::new(n)),
}
}
#[inline]
fn from_big_rc(n: Rc<BigInt>) -> Self {
Self {
big: ManuallyDrop::new(n),
}
}
#[inline]
const fn is_big(&self) -> bool {
unsafe { self.int & 1 == 0 }
}
#[inline]
fn expand(&self) -> IntType {
if self.is_big() {
IntType::Big(unsafe { self.big.as_ref() })
} else {
IntType::Int(unsafe { self.int >> 1 })
}
}
#[inline]
fn as_int(&self) -> Option<i64> {
if let IntType::Int(n) = self.expand() {
Some(n)
} else {
None
}
}
#[inline]
fn to_big(&self) -> Cow<BigInt> {
match self.expand() {
IntType::Big(n) => Cow::Borrowed(n),
IntType::Int(n) => Cow::Owned(BigInt::from(n)),
}
}
}
impl Clone for IntInner {
fn clone(&self) -> Self {
if let Some(n) = self.as_int() {
unsafe { Self::from_int_unchecked(n) }
} else {
Self::from_big_rc(ManuallyDrop::into_inner(unsafe { self.big.clone() }))
}
}
}
impl Drop for IntInner {
fn drop(&mut self) {
if self.is_big() {
unsafe { ManuallyDrop::drop(&mut self.big) }
}
}
}
//
// Int implementation
//
#[derive(Clone)]
pub struct Int(IntInner);
impl Int {
/// panics if n is not small enough
pub const fn from_small(n: i64) -> Self {
assert!(is_int_small(n), "argument to from_small was too large");
Self(unsafe { IntInner::from_int_unchecked(n) })
}
}
impl Default for Int {
#[inline]
fn default() -> Self {
Self(unsafe { IntInner::from_int_unchecked(0) })
}
}
impl From<i8> for Int {
#[inline]
fn from(value: i8) -> Self {
Self::from_small(value as i64)
}
}
impl From<u8> for Int {
#[inline]
fn from(value: u8) -> Self {
Self::from_small(value as i64)
}
}
impl From<i16> for Int {
#[inline]
fn from(value: i16) -> Self {
Self::from_small(value as i64)
}
}
impl From<u16> for Int {
#[inline]
fn from(value: u16) -> Self {
Self::from_small(value as i64)
}
}
impl From<i32> for Int {
#[inline]
fn from(value: i32) -> Self {
Self::from_small(value as i64)
}
}
impl From<u32> for Int {
#[inline]
fn from(value: u32) -> Self {
Self::from_small(value as i64)
}
}
impl From<i64> for Int {
#[inline]
fn from(value: i64) -> Self {
Self(IntInner::from_int(value))
}
}
impl From<u64> for Int {
#[inline]
fn from(value: u64) -> Self {
if let Ok(v) = value.try_into() {
Self(IntInner::from_int(v))
} else {
Self(IntInner::from_big_direct(BigInt::from(value)))
}
}
}
impl From<i128> for Int {
#[inline]
fn from(value: i128) -> Self {
if let Ok(v) = value.try_into() {
Self(IntInner::from_int(v))
} else {
Self(IntInner::from_big_direct(BigInt::from(value)))
}
}
}
impl From<u128> for Int {
#[inline]
fn from(value: u128) -> Self {
if let Ok(v) = value.try_into() {
Self(IntInner::from_int(v))
} else {
Self(IntInner::from_big_direct(BigInt::from(value)))
}
}
}
impl From<isize> for Int {
#[inline]
fn from(value: isize) -> Self {
if let Ok(v) = value.try_into() {
Self(IntInner::from_int(v))
} else {
Self(IntInner::from_big_direct(BigInt::from(value)))
}
}
}
impl From<usize> for Int {
#[inline]
fn from(value: usize) -> Self {
if let Ok(v) = value.try_into() {
Self(IntInner::from_int(v))
} else {
Self(IntInner::from_big_direct(BigInt::from(value)))
}
}
}
impl From<BigInt> for Int {
fn from(value: BigInt) -> Self {
Self(IntInner::from_big(value))
}
}
impl From<&BigInt> for Int {
fn from(value: &BigInt) -> Self {
Self(IntInner::from_big_ref(value))
}
}
macro_rules! impl_binop {
($($trait:ident)::+,$($atrait:ident)::+,$fname:ident,$afname:ident,$op:tt,$checked:ident) => {
impl $($trait)::+ for &Int {
type Output = Int;
fn $fname(self, rhs: Self) -> Self::Output {
match (self.expand(), rhs.expand()) {
(IntType::Int(a), IntType::Int(b)) => match a.$checked(b) {
Some(n) => Int::from(n),
None => Int::from(BigInt::from(a) $op b),
}
(IntType::Int(a), IntType::Big(b)) => Int::from(a $op b),
(IntType::Big(a), IntType::Int(b)) => Int::from(a $op b),
(IntType::Big(a), IntType::Big(b)) => Int::from(a $op b),
}
}
}
impl $($trait)::+ for Int {
type Output = Int;
fn $fname(self, rhs: Int) -> Self::Output {
&self $op &rhs
}
}
impl $($trait)::+<i64> for &Int {
type Output = Int;
fn $fname(self, rhs: i64) -> Self::Output {
self $op &Int::from(rhs)
}
}
impl $($atrait)::+ for Int {
fn $afname(&mut self, rhs: Int) {
*self = &*self $op &rhs;
}
}
impl $($atrait)::+<&Int> for Int {
fn $afname(&mut self, rhs: &Int) {
*self = &*self $op rhs;
}
}
impl $($atrait)::+<i64> for Int {
fn $afname(&mut self, rhs: i64) {
*self = &*self $op rhs;
}
}
};
}
impl_binop!(ops::Add, ops::AddAssign, add, add_assign, +, checked_add);
impl_binop!(ops::Sub, ops::SubAssign, sub, sub_assign, -, checked_sub);
impl_binop!(ops::Mul, ops::MulAssign, mul, mul_assign, *, checked_mul);
impl_binop!(ops::Div, ops::DivAssign, div, div_assign, /, checked_div);
impl_binop!(ops::Rem, ops::RemAssign, rem, rem_assign, %, checked_rem);
macro_rules! impl_logicop {
($($trait:ident)::+,$($atrait:ident)::+,$fname:ident,$afname:ident,$op:tt) => {
impl $($trait)::+ for &Int {
type Output = Int;
fn $fname(self, rhs: Self) -> Self::Output {
match (self.expand(), rhs.expand()) {
(IntType::Int(a), IntType::Int(b)) => Int::from(a $op b),
(IntType::Int(a), IntType::Big(b)) => Int::from(BigInt::from(a) $op b),
(IntType::Big(a), IntType::Int(b)) => Int::from(a $op BigInt::from(b)),
(IntType::Big(a), IntType::Big(b)) => Int::from(a $op b),
}
}
}
impl $($trait)::+ for Int {
type Output = Int;
fn $fname(self, rhs: Int) -> Self::Output {
&self $op &rhs
}
}
impl $($trait)::+<i64> for &Int {
type Output = Int;
fn $fname(self, rhs: i64) -> Self::Output {
self $op &Int::from(rhs)
}
}
impl $($atrait)::+<&Int> for Int {
fn $afname(&mut self, rhs: &Int) {
*self = &*self $op rhs;
}
}
impl $($atrait)::+<i64> for Int {
fn $afname(&mut self, rhs: i64) {
*self = &*self $op rhs;
}
}
};
}
impl_logicop!(ops::BitAnd, ops::BitAndAssign, bitand, bitand_assign, &);
impl_logicop!(ops::BitOr, ops::BitOrAssign, bitor, bitor_assign, |);
impl_logicop!(ops::BitXor, ops::BitXorAssign, bitxor, bitxor_assign, ^);
impl ops::Shl for &Int {
type Output = Int;
fn shl(self, rhs: Self) -> Self::Output {
if rhs.is_negative() {
panic!("attempt to shift left with negative")
}
let Some(rhs) = rhs.to_u32() else {
panic!("shift left overflow")
};
Int::from(self.to_big().as_ref() << rhs)
}
}
impl ops::Shl for Int {
type Output = Int;
fn shl(self, rhs: Int) -> Self::Output {
&self << &rhs
}
}
impl ops::Shl<i64> for &Int {
type Output = Int;
fn shl(self, rhs: i64) -> Self::Output {
self << &Int::from(rhs)
}
}
impl ops::ShlAssign<&Int> for Int {
fn shl_assign(&mut self, rhs: &Int) {
*self = &*self << rhs;
}
}
impl ops::ShlAssign<i64> for Int {
fn shl_assign(&mut self, rhs: i64) {
*self = &*self << &Int::from(rhs);
}
}
impl ops::Shr for &Int {
type Output = Int;
fn shr(self, rhs: Self) -> Self::Output {
if rhs.is_negative() {
panic!("attempt to shift right with negative")
}
let Some(rhs) = rhs.to_u32() else {
return Int::ZERO
};
Int::from(self.to_big().as_ref() >> rhs)
}
}
impl ops::Shr for Int {
type Output = Int;
fn shr(self, rhs: Int) -> Self::Output {
&self >> &rhs
}
}
impl ops::Shr<i64> for &Int {
type Output = Int;
fn shr(self, rhs: i64) -> Self::Output {
self >> &Int::from(rhs)
}
}
impl ops::ShrAssign<&Int> for Int {
fn shr_assign(&mut self, rhs: &Int) {
*self = &*self >> rhs;
}
}
impl ops::ShrAssign<i64> for Int {
fn shr_assign(&mut self, rhs: i64) {
*self = &*self >> &Int::from(rhs);
}
}
impl ops::Neg for &Int {
type Output = Int;
fn neg(self) -> Int {
match self.expand() {
IntType::Int(n) => Int::from(-n),
IntType::Big(n) => Int::from(-n),
}
}
}
impl ops::Neg for Int {
type Output = Int;
fn neg(self) -> Int {
-&self
}
}
impl ops::Not for &Int {
type Output = Int;
fn not(self) -> Int {
match self.expand() {
IntType::Int(n) => Int::from(!n),
IntType::Big(n) => Int::from(!n),
}
}
}
impl ops::Not for Int {
type Output = Int;
fn not(self) -> Int {
!&self
}
}
impl PartialEq for Int {
fn eq(&self, other: &Self) -> bool {
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
a.eq(&b)
} else {
let a = self.0.to_big();
let b = other.0.to_big();
a.as_ref().eq(b.as_ref())
}
}
}
impl Eq for Int {}
impl PartialOrd for Int {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Int {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
a.cmp(&b)
} else {
let a = self.0.to_big();
let b = other.0.to_big();
a.as_ref().cmp(b.as_ref())
}
}
}
impl ConstZero for Int {
const ZERO: Self = Self::from_small(0);
}
impl ConstOne for Int {
const ONE: Self = Self::from_small(1);
}
impl Zero for Int {
#[inline]
fn zero() -> Self {
Self::ZERO
}
fn is_zero(&self) -> bool {
match self.expand() {
IntType::Int(n) => n == 0,
IntType::Big(n) => n.is_zero(),
}
}
}
impl One for Int {
#[inline]
fn one() -> Self {
Self::ONE
}
}
impl Num for Int {
type FromStrRadixErr = ParseBigIntError;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
match i64::from_str_radix(str, radix) {
Ok(v) => Ok(Self::from(v)),
Err(_) => Ok(Self::from(BigInt::from_str_radix(str, radix)?)),
}
}
}
impl Integer for Int {
fn div_floor(&self, other: &Self) -> Self {
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
return Integer::div_floor(&a, &b).into()
}
let (a, b) = (self.0.to_big(), other.0.to_big());
a.div_floor(&b).into()
}
fn mod_floor(&self, other: &Self) -> Self {
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
return a.mod_floor(&b).into()
}
let (a, b) = (self.0.to_big(), other.0.to_big());
a.mod_floor(&b).into()
}
fn gcd(&self, other: &Self) -> Self {
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
return a.gcd(&b).into()
}
let (a, b) = (self.0.to_big(), other.0.to_big());
a.gcd(&b).into()
}
fn lcm(&self, other: &Self) -> Self {
let (a, b) = (self.0.to_big(), other.0.to_big());
a.lcm(&b).into()
}
fn is_multiple_of(&self, other: &Self) -> bool {
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
return a.is_multiple_of(&b)
}
let (a, b) = (self.0.to_big(), other.0.to_big());
a.is_multiple_of(&b)
}
fn is_even(&self) -> bool {
match self.expand() {
IntType::Int(n) => n.is_even(),
IntType::Big(n) => n.is_even(),
}
}
fn is_odd(&self) -> bool {
match self.expand() {
IntType::Int(n) => n.is_odd(),
IntType::Big(n) => n.is_odd(),
}
}
fn div_rem(&self, other: &Self) -> (Self, Self) {
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
let (q, r) = a.div_rem(&b);
return (q.into(), r.into())
}
let (a, b) = (self.0.to_big(), other.0.to_big());
let (q, r) = a.div_rem(&b);
(q.into(), r.into())
}
}
impl Signed for Int {
fn abs(&self) -> Self {
match self.expand() {
IntType::Int(n) => Int::from(n.abs()),
IntType::Big(n) => Int::from(n.abs()),
}
}
fn abs_sub(&self, other: &Self) -> Self {
(self - other).abs()
}
fn signum(&self) -> Self {
match self.expand() {
IntType::Int(n) => Int::from(n.signum()),
IntType::Big(n) => Int::from(n.signum()),
}
}
fn is_positive(&self) -> bool {
match self.expand() {
IntType::Int(n) => n > 0,
IntType::Big(n) => n.is_positive(),
}
}
fn is_negative(&self) -> bool {
match self.expand() {
IntType::Int(n) => n < 0,
IntType::Big(n) => n.is_negative(),
}
}
}
impl Roots for Int {
fn nth_root(&self, n: u32) -> Self {
match self.expand() {
IntType::Int(i) => Int::from(i.nth_root(n)),
IntType::Big(b) => Int::from(b.nth_root(n)),
}
}
}
impl Euclid for Int {
fn div_euclid(&self, v: &Self) -> Self {
if let (Some(a), Some(b)) = (self.0.as_int(), v.0.as_int()) {
if let Some(v) = CheckedEuclid::checked_div_euclid(&a, &b) {
return Int::from(v)
}
}
let (a, b) = (self.0.to_big(), v.0.to_big());
a.div_euclid(&b).into()
}
fn rem_euclid(&self, v: &Self) -> Self {
if let (Some(a), Some(b)) = (self.0.as_int(), v.0.as_int()) {
if let Some(v) = CheckedEuclid::checked_rem_euclid(&a, &b) {
return Int::from(v)
}
}
let (a, b) = (self.0.to_big(), v.0.to_big());
a.rem_euclid(&b).into()
}
}
impl Int {
pub fn to_str_radix(&self, radix: u32) -> String {
self.to_big().to_str_radix(radix)
}
pub fn to_str_radix_case(&self, radix: u32, upper: bool) -> String {
let mut s = self.to_big().to_str_radix(radix);
if upper {
s.make_ascii_uppercase();
}
s
}
}
impl fmt::Debug for Int {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt::Display::fmt(&self, f)
}
}
impl fmt::Display for Int {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.to_big().as_ref(), f)
}
}
impl fmt::Binary for Int {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Binary::fmt(self.to_big().as_ref(), f)
}
}
impl fmt::Octal for Int {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Octal::fmt(self.to_big().as_ref(), f)
}
}
impl fmt::LowerHex for Int {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(self.to_big().as_ref(), f)
}
}
impl fmt::UpperHex for Int {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::UpperHex::fmt(self.to_big().as_ref(), f)
}
}
impl Hash for Int {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self.expand() {
IntType::Int(n) => n.hash(state),
IntType::Big(b) => match b.to_i64() {
Some(n) => n.hash(state),
None => b.hash(state),
},
}
}
}
impl FromPrimitive for Int {
fn from_i64(n: i64) -> Option<Self> {
Some(Self::from(n))
}
fn from_u64(n: u64) -> Option<Self> {
Some(Self::from(n))
}
fn from_isize(n: isize) -> Option<Self> {
Some(Self::from(n))
}
fn from_i128(n: i128) -> Option<Self> {
Some(Self::from(n))
}
fn from_usize(n: usize) -> Option<Self> {
Some(Self::from(n))
}
fn from_u128(n: u128) -> Option<Self> {
Some(Self::from(n))
}
fn from_f32(n: f32) -> Option<Self> {
Some(Int::from(BigInt::from_f32(n)?))
}
fn from_f64(n: f64) -> Option<Self> {
Some(Int::from(BigInt::from_f64(n)?))
}
}
impl ToPrimitive for Int {
fn to_i64(&self) -> Option<i64> {
match self.expand() {
IntType::Int(n) => Some(n),
IntType::Big(b) => b.to_i64(),
}
}
fn to_u64(&self) -> Option<u64> {
match self.expand() {
IntType::Int(n) => n.to_u64(),
IntType::Big(b) => b.to_u64(),
}
}
fn to_isize(&self) -> Option<isize> {
match self.expand() {
IntType::Int(n) => n.to_isize(),
IntType::Big(b) => b.to_isize(),
}
}
fn to_i128(&self) -> Option<i128> {
match self.expand() {
IntType::Int(n) => Some(n as i128),
IntType::Big(b) => b.to_i128(),
}
}
fn to_usize(&self) -> Option<usize> {
match self.expand() {
IntType::Int(n) => n.to_usize(),
IntType::Big(b) => b.to_usize(),
}
}
fn to_u128(&self) -> Option<u128> {
match self.expand() {
IntType::Int(n) => n.to_u128(),
IntType::Big(b) => b.to_u128(),
}
}
fn to_f32(&self) -> Option<f32> {
match self.expand() {
IntType::Int(n) => n.to_f32(),
IntType::Big(b) => b.to_f32(),
}
}
fn to_f64(&self) -> Option<f64> {
match self.expand() {
IntType::Int(n) => n.to_f64(),
IntType::Big(b) => b.to_f64(),
}
}
}
impl Int {
#[inline]
pub fn to_f64_uc(&self) -> f64 {
self.to_f64().unwrap_or(f64::NAN)
}
#[inline]
pub fn expand(&self) -> IntType {
self.0.expand()
}
/// exp must be nonnegative, both cannot be zero.
pub fn ipow(&self, exp: &Int) -> Option<Int> {
if self.is_zero() && exp.is_zero() || exp.is_negative() {
return None
}
if exp.is_zero() {
return Some(Int::ONE)
}
if exp.is_one() {
return Some(self.clone())
}
if self.is_zero() || self.is_one() {
return Some(self.clone())
}
if self == &Int::from_small(-1) {
return if exp.is_even() {
Some(Int::ONE)
} else {
Some(self.clone())
}
}
if let Some(e) = exp.to_u32() {
if let Some(i) = self.0.as_int() {
if let Some(v) = i.checked_pow(e) {
return Some(Int::from(v))
}
}
Some(Self::from(self.0.to_big().pow(e)))
} else {
None
}
}
pub fn modpow(&self, exp: &Int, modulus: &Int) -> Int {
self.to_big()
.modpow(&exp.to_big(), &modulus.to_big())
.into()
}
pub fn modinv(&self, modulus: &Int) -> Option<Int> {
self.to_big().modinv(&modulus.to_big()).map(|v| v.into())
}
pub fn isignum(&self) -> i64 {
match self.expand() {
IntType::Int(i) => i.signum(),
IntType::Big(b) => match b.sign() {
Sign::Minus => -1,
Sign::NoSign => 0,
Sign::Plus => 1,
},
}
}
#[inline]
pub fn to_big(&self) -> Cow<BigInt> {
self.0.to_big()
}
pub fn trailing_zeros(&self) -> Option<u64> {
match self.expand() {
IntType::Int(0) => None,
IntType::Int(n) => Some(n.trailing_zeros() as u64),
IntType::Big(n) => n.trailing_zeros(),
}
}
#[inline]
pub fn eq_i64(&self, n: i64) -> bool {
self.to_i64().is_some_and(|v| v == n)
}
#[inline]
pub fn ne_i64(&self, n: i64) -> bool {
!self.eq_i64(n)
}
#[inline]
pub fn cmp_i64(&self, n: i64) -> Ordering {
match self.to_i64() {
Some(v) => v.cmp(&n),
None => {
if self.is_negative() {
Ordering::Less
} else {
Ordering::Greater
}
}
}
}
#[inline]
pub fn lt_i64(&self, n: i64) -> bool {
self.cmp_i64(n).is_lt()
}
#[inline]
pub fn gt_i64(&self, n: i64) -> bool {
self.cmp_i64(n).is_gt()
}
#[inline]
pub fn le_i64(&self, n: i64) -> bool {
self.cmp_i64(n).is_le()
}
#[inline]
pub fn ge_i64(&self, n: i64) -> bool {
self.cmp_i64(n).is_ge()
}
}
impl From<Int> for BigInt {
#[inline]
fn from(value: Int) -> Self {
value.0.to_big().into_owned()
}
}
impl From<&Int> for BigInt {
#[inline]
fn from(value: &Int) -> Self {
value.0.to_big().into_owned()
}
}

View file

@ -0,0 +1,10 @@
mod int;
pub use int::{Int, IntType};
mod range;
pub use range::Range;
mod ratio;
pub use ratio::{Ratio, RatioExt};
pub use num::complex::Complex64 as Complex;

View file

@ -0,0 +1,217 @@
use std::fmt;
use num::BigInt;
use crate::prelude::*;
use super::Int;
fn to_slice_index(n: &BigInt, len: usize) -> usize {
if n.is_negative() {
let n = n + len;
if n.is_negative() {
return 0
}
n.to_usize().unwrap_or(usize::MAX).min(len)
} else {
n.to_usize().unwrap_or(usize::MAX).min(len)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Range {
pub start: Option<BigInt>,
pub end: Option<BigInt>,
pub inclusive: bool,
}
impl Range {
#[expect(clippy::self_named_constructors)]
pub fn range<I, J>(start: I, end: J) -> Self
where
I: Into<BigInt>,
J: Into<BigInt>,
{
Self {
start: Some(start.into()),
end: Some(end.into()),
inclusive: false,
}
}
pub fn range_incl<I, J>(start: I, end: J) -> Self
where
I: Into<BigInt>,
J: Into<BigInt>,
{
Self {
start: Some(start.into()),
end: Some(end.into()),
inclusive: true,
}
}
pub fn range_from<I>(start: I) -> Self
where
I: Into<BigInt>,
{
Self {
start: Some(start.into()),
end: None,
inclusive: false,
}
}
pub fn range_to<I>(end: I) -> Self
where
I: Into<BigInt>,
{
Self {
start: None,
end: Some(end.into()),
inclusive: false,
}
}
pub fn range_to_incl<I>(end: I) -> Self
where
I: Into<BigInt>,
{
Self {
start: None,
end: Some(end.into()),
inclusive: true,
}
}
pub const fn range_all() -> Self {
Self {
start: None,
end: None,
inclusive: false,
}
}
pub fn len(&self) -> Option<Int> {
let (Some(s), Some(e)) = (&self.start, &self.end) else {
return None
};
if e < s {
return Some(Int::ZERO)
}
Some((e - s + if self.inclusive { 1i64 } else { 0i64 }).into())
}
pub fn is_empty(&self) -> bool {
let (Some(s), Some(e)) = (&self.start, &self.end) else {
return false
};
if e < s {
return true
}
if e == s && !self.inclusive {
return true
}
false
}
pub fn try_iterate(&self) -> Option<impl Iterator<Item = Int>> {
let Some(s) = &self.start else { return None };
Some(RangeIterator {
cur: s.into(),
end: self.end.as_ref().map(|e| e.into()),
inclusive: self.inclusive,
done: false,
})
}
pub fn index_slice<'a, T>(&self, l: &'a [T]) -> &'a [T] {
let len = l.len();
let s = self
.start
.as_ref()
.map(|s| to_slice_index(s, len))
.unwrap_or(0);
let e = self
.end
.as_ref()
.map(|e| to_slice_index(e, len))
.unwrap_or(len);
let e = if self.inclusive {
e.saturating_add(1).min(len)
} else {
e
};
if e <= s {
return &[]
}
&l[s..e]
}
pub fn replace_range<T: Clone>(&self, l: &mut Vec<T>, rep: &[T]) {
let len = l.len();
let s = self
.start
.as_ref()
.map(|s| to_slice_index(s, len))
.unwrap_or(0);
let e = self
.end
.as_ref()
.map(|e| to_slice_index(e, len))
.unwrap_or(len);
let e = if self.inclusive {
e.saturating_add(1).min(len)
} else {
e
};
if e < s {
l.splice(e..s, rep.iter().cloned());
} else {
l.splice(s..e, rep.iter().cloned());
}
}
}
struct RangeIterator {
cur: Int,
end: Option<Int>,
inclusive: bool,
done: bool,
}
impl Iterator for RangeIterator {
type Item = Int;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None
}
if let Some(end) = &self.end {
if &self.cur > end || (&self.cur == end && !self.inclusive) {
self.done = true;
return None
}
}
let mut val = &self.cur + 1;
std::mem::swap(&mut val, &mut self.cur);
Some(val)
}
}
impl fmt::Display for Range {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.start {
Some(s) => write!(f, "{s}..")?,
None => write!(f, "*..")?,
}
if self.inclusive {
write!(f, "=")?;
}
match &self.end {
Some(e) => write!(f, "{e}"),
None => write!(f, "*"),
}
}
}

View file

@ -0,0 +1,73 @@
use num::Rational64;
use super::Int;
use crate::prelude::*;
pub type Ratio = num::BigRational;
impl From<Int> for Ratio {
fn from(value: Int) -> Self {
Ratio::from_integer(value.into())
}
}
impl From<&Int> for Ratio {
fn from(value: &Int) -> Self {
Ratio::from_integer(value.to_owned().into())
}
}
pub trait RatioExt: Sized {
fn div_euclid(&self, n: &Self) -> Self;
fn rem_euclid(&self, n: &Self) -> Self;
fn to_f64_uc(&self) -> f64;
fn approximate_float(x: f64) -> Option<Self>;
}
impl RatioExt for Ratio {
#[inline]
fn div_euclid(&self, n: &Self) -> Ratio {
let (n_abs, n_sgn) = (n.abs(), n.signum());
(self / n_abs).floor() * n_sgn
}
#[inline]
fn rem_euclid(&self, n: &Self) -> Ratio {
ToPrimitive::to_f64(self);
let n_abs = n.abs();
self - (self / &n_abs).floor() * &n_abs
}
#[inline]
fn to_f64_uc(&self) -> f64 {
if let Some(f) = self.to_f64() {
f
} else if self.is_negative() {
f64::NEG_INFINITY
} else {
f64::INFINITY
}
}
fn approximate_float(x: f64) -> Option<Self> {
match Rational64::approximate_float(x) {
Some(r) => {
let r = Ratio::new((*r.numer()).into(), (*r.denom()).into());
Some(r)
}
None => {
let mut y = x;
if y.abs() < 1.0 {
y = 1.0 / y;
}
let i = Int::from_f64(y)?;
let r = Ratio::from_integer(i.into());
if x.abs() < 1.0 {
Some(r.recip())
} else {
Some(r)
}
}
}
}
}

View file

@ -35,7 +35,9 @@ pub enum UnaryOp {
Neg,
Not,
BitNot,
RangeEndless,
RangeFrom,
RangeTo,
RangeToIncl,
}
#[derive(Debug)]

View file

@ -26,6 +26,9 @@ pub enum TokenKind {
AmperEqual,
Star,
StarEqual,
StarDotDot,
StarDotDotStar,
StarDotDotEqual,
Plus,
PlusPlus,
PlusPlusEqual,
@ -115,6 +118,9 @@ impl TokenKind {
K::AmperEqual => "'&='",
K::Star => "'*'",
K::StarEqual => "'*='",
K::StarDotDot => "'*..'",
K::StarDotDotStar => "'*..*'",
K::StarDotDotEqual => "'*..='",
K::Plus => "'+'",
K::PlusPlus => "'++'",
K::PlusPlusEqual => "'++='",
@ -489,6 +495,14 @@ impl<'s> Lexer<'s> {
},
'*' => match self.and_peek()? {
'=' => self.and_emit(K::StarEqual),
'.' => match self.and_peek()? {
'.' => match self.and_peek()? {
'*' => self.and_emit(K::StarDotDotStar),
'=' => self.and_emit(K::StarDotDotEqual),
_ => self.emit(K::StarDotDot),
},
_ => self.unexpected(),
},
_ => self.emit(K::Star),
},
'/' => match self.and_peek()? {

View file

@ -10,7 +10,7 @@ use super::{
parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span,
SpanParserError, Token, TokenKind,
};
use num_complex::Complex64;
use num::complex::Complex64;
use ExprKind as E;
use TokenKind as T;
@ -108,13 +108,15 @@ impl TokenKind {
T::Minus => Some(UnaryOp::Neg),
T::Not => Some(UnaryOp::Not),
T::Tilde => Some(UnaryOp::BitNot),
T::StarDotDot => Some(UnaryOp::RangeTo),
T::StarDotDotEqual => Some(UnaryOp::RangeToIncl),
_ => None,
}
}
fn postfix_unary_op(self) -> Option<UnaryOp> {
match self {
T::DotDotStar => Some(UnaryOp::RangeEndless),
T::DotDotStar => Some(UnaryOp::RangeFrom),
_ => None,
}
}
@ -124,7 +126,7 @@ impl UnaryOp {
fn precedence(self) -> u8 {
match self {
UnaryOp::Not => 0,
UnaryOp::RangeEndless => 40,
UnaryOp::RangeFrom | UnaryOp::RangeTo | UnaryOp::RangeToIncl => 40,
UnaryOp::Neg | UnaryOp::BitNot => 110,
}
}
@ -159,25 +161,26 @@ fn b<T>(t: T) -> Box<T> {
}
impl TokenKind {
#[rustfmt::skip]
fn expr_first(self) -> bool {
matches!(
self,
T::Return
| T::Continue
matches!(self,
| T::Return | T::Continue
| T::Break | T::Var
| T::Global | T::Fn
| T::Not | T::Backslash
| T::Amper | T::Minus
| T::Tilde | T::Identifier
| T::Not | T::StarDotDot
| T::StarDotDotStar
| T::StarDotDotEqual
| T::Backslash | T::Amper
| T::Minus | T::Tilde
| T::Identifier
| T::LParen | T::LBrack
| T::LBrace | T::Dollar
| T::Do | T::If
| T::While | T::For
| T::Try | T::Integer
| T::Float | T::Imaginary
| T::String | T::Symbol
| T::True | T::False
| T::Nil
| T::Do | T::If | T::While
| T::For | T::Try
| T::Integer | T::Float
| T::Imaginary | T::String
| T::Symbol | T::True
| T::False | T::Nil
)
}
}
@ -398,6 +401,7 @@ impl<'s> Parser<'s> {
T::True => Ok(E::Literal(Value::Bool(true)).span(tok.span)),
T::False => Ok(E::Literal(Value::Bool(false)).span(tok.span)),
T::Nil => Ok(E::Literal(Value::Nil).span(tok.span)),
T::StarDotDotStar => Ok(E::Literal(Value::range_all()).span(tok.span)),
t => throw!(
tok.span,
"unexpected token {}, expected expression",
@ -503,7 +507,7 @@ impl<'s> Parser<'s> {
..
}) => {
let args = self.parse_ident_list()?;
expect!(self, T::Dot);
expect!(self, T::Arrow);
let body = self.parse_lambda()?;
let body_span = body.span;
Ok(E::Lambda(args, b(body)).span(span + body_span))

View file

@ -1,11 +1,15 @@
use core::fmt;
use std::io::{self, Write};
use num::{bigint::Sign, BigInt};
use crate::{
chunk::{Chunk, Instruction, TryTable},
lstring::LStr,
number::Int,
prelude::*,
symbol::Symbol,
value::{function::Function, range::RangeType, Value},
value::{function::Function, Value},
};
const MAGIC: [u8; 8] = *b"\x7fTALC\0\0\0";
@ -109,12 +113,12 @@ impl<'w, W: Write> ProgramWriter<'w, W> {
}
Value::Int(n) => {
self.write_u8(3)?;
self.write_i64(*n)
self.write_int(n.clone())
}
Value::Ratio(r) => {
self.write_u8(4)?;
self.write_i64(*r.numer())?;
self.write_i64(*r.denom())
self.write_int(Int::from(r.numer().clone()))?;
self.write_int(Int::from(r.denom().clone()))
}
Value::Float(f) => {
self.write_u8(5)?;
@ -127,13 +131,19 @@ impl<'w, W: Write> ProgramWriter<'w, W> {
}
Value::Range(r) => {
self.write_u8(7)?;
match r.ty {
RangeType::Open => self.write_u8(0)?,
RangeType::Closed => self.write_u8(1)?,
RangeType::Endless => self.write_u8(2)?,
if let Some(s) = &r.start {
self.write_u8(1)?;
self.write_int(s.into())?;
} else {
self.write_u8(0)?;
}
self.write_i64(r.start)?;
self.write_i64(r.stop)
if let Some(e) = &r.end {
self.write_u8(1)?;
self.write_int(e.into())?;
} else {
self.write_u8(0)?;
}
self.write_bool(r.inclusive)
}
Value::String(s) => {
self.write_u8(8)?;
@ -194,11 +204,36 @@ impl<'w, W: Write> ProgramWriter<'w, W> {
Ok(())
}
fn write_int(&mut self, n: Int) -> Result<()> {
if let Some(i) = n.to_i64() {
self.write_u8(0x80)?;
self.write_i64(i)
} else {
let big = BigInt::from(n);
let (sign, digits) = big.to_u64_digits();
match sign {
Sign::Minus => self.write_u8(0xff)?,
Sign::NoSign => self.write_u8(0x00)?,
Sign::Plus => self.write_u8(0x01)?,
}
self.write_u32(digits.len() as u32)?;
for digit in digits {
self.write_u64(digit)?;
}
Ok(())
}
}
fn write_i64(&mut self, n: i64) -> Result<()> {
self.w.write_all(&n.to_le_bytes())?;
Ok(())
}
fn write_u64(&mut self, n: u64) -> Result<()> {
self.w.write_all(&n.to_le_bytes())?;
Ok(())
}
fn write_u32(&mut self, n: u32) -> Result<()> {
self.w.write_all(&n.to_le_bytes())?;
Ok(())

View file

@ -1,12 +1,15 @@
use crate::{
exception::{throw, Result},
symbol::{symbol, SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR},
lstring::LStr,
number::Int,
prelude::*,
symbol::{symbol, SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
value::function::NativeFunc,
vm::Vm,
vmcalliter,
};
use super::{range::RangeType, Value};
use super::Value;
impl Value {
pub fn index(&self, idx: Self) -> Result<Self> {
@ -14,8 +17,15 @@ impl Value {
match (self, idx) {
(V::List(l), V::Int(i)) => {
let l = l.borrow();
if i >= 0 && (i as usize) < l.len() {
Ok(l[i as usize].clone())
let Some(i) = i.to_usize() else {
throw!(
*SYM_INDEX_ERROR,
"index {i} out of bounds for list of length {}",
l.len()
)
};
if i < l.len() {
Ok(l[i].clone())
} else {
throw!(
*SYM_INDEX_ERROR,
@ -25,14 +35,20 @@ impl Value {
}
}
(V::Range(r), V::Int(i)) => {
if i >= 0
&& (r.ty == RangeType::Endless
|| i < r.stop || (r.ty == RangeType::Closed && i == r.stop))
{
Ok((r.start + i).into())
} else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
if !i.is_negative() {
if let Some(s) = &r.start {
let n = &Int::from(s) + &i;
if let Some(e) = &r.end {
let e = Int::from(e);
if n < e || (n == e && r.inclusive) {
return Ok(n.into())
}
} else {
return Ok(n.into())
}
}
}
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
}
(V::Table(t), i) if i.hashable() => {
let t = t.borrow();
@ -40,58 +56,13 @@ impl Value {
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
}
(V::String(s), V::Range(r)) => {
let slen = s.len();
match r.ty {
RangeType::Open => {
if r.start < 0 || r.start > slen as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
if r.stop < 0 || r.stop > slen as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
Ok(s[r.start as usize..r.stop as usize].into())
}
RangeType::Closed => {
if r.start < 0 || r.start > slen as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
if r.stop < 0 || r.stop >= slen as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
Ok(s[r.start as usize..=r.stop as usize].into())
}
RangeType::Endless => {
if r.start < 0 || r.start > slen as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
Ok(s[r.start as usize..].into())
}
let ns = r.index_slice(s.as_bytes());
Ok(LStr::from_bytes(ns).into())
}
(V::List(l), V::Range(r)) => {
let l = l.borrow();
let nl = r.index_slice(l.as_slice());
Ok(nl.to_vec().into())
}
(col, idx) => {
if let Ok(ii) = idx.clone().to_iter_function() {
@ -113,8 +84,15 @@ impl Value {
match (self, idx) {
(V::List(l), V::Int(i)) => {
let mut l = l.borrow_mut();
if i >= 0 && (i as usize) < l.len() {
l[i as usize] = val;
let Some(i) = i.to_usize() else {
throw!(
*SYM_INDEX_ERROR,
"index {i} out of bounds for list of length {}",
l.len()
)
};
if i < l.len() {
l[i] = val;
Ok(())
} else {
throw!(
@ -135,70 +113,28 @@ impl Value {
Ok(())
}
(V::List(t), V::Range(r)) => {
if let Value::List(l) = val {
let mut tm = t.borrow_mut();
if let Ok(l) = l.try_borrow() {
r.replace_range(&mut tm, l.as_slice());
Ok(())
} else {
throw!(
*SYM_VALUE_ERROR,
"cannot replace part of a list with itself"
)
}
} else {
let iter = val.to_iter_function()?;
let mut vals = Vec::new();
while let Some(v) = vmcalliter!(vm; iter.clone())? {
vals.push(v);
}
let mut tm = t.borrow_mut();
match r.ty {
RangeType::Open => {
if r.start < 0 || r.start > tm.len() as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
}
if r.stop < 0 || r.stop > tm.len() as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
}
let end = tm.split_off(r.stop as usize);
tm.truncate(r.start as usize);
tm.extend(vals.into_iter().chain(end));
}
RangeType::Closed => {
if r.start < 0 || r.start > tm.len() as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
}
if r.stop < 0 || r.stop >= tm.len() as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
}
let end = tm.split_off(r.stop as usize + 1);
tm.truncate(r.start as usize);
tm.extend(vals.into_iter().chain(end));
}
RangeType::Endless => {
if r.start < 0 || r.start > tm.len() as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
}
tm.truncate(r.start as usize);
tm.extend(vals);
}
}
r.replace_range(&mut tm, &vals);
Ok(())
}
}
(col, idx) => {
if let Ok(ii) = idx.clone().to_iter_function() {
let val = val.to_iter_function()?;

View file

@ -3,23 +3,19 @@ use std::borrow::Cow;
use std::io;
use std::{cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, rc::Rc};
pub use num_complex::Complex64;
use num_complex::ComplexFloat;
pub use num_rational::Rational64;
use num::{complex::Complex64, BigInt};
use crate::exception::{throw, Exception};
use crate::lstring::{LStr, LString};
use crate::number::{Int, Range, Ratio};
use crate::prelude::*;
use crate::symbol::{Symbol, SYM_HASH_ERROR};
use self::{
function::{Function, NativeFunc},
range::{Range, RangeType},
};
use self::function::{Function, NativeFunc};
pub mod function;
pub mod index;
pub mod ops;
pub mod range;
type RcList = Rc<RefCell<Vec<Value>>>;
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
@ -31,11 +27,11 @@ pub enum Value {
Nil,
Bool(bool),
Symbol(Symbol),
Range(Range),
Range(Rc<Range>),
Int(i64),
Int(Int),
Float(f64),
Ratio(Rational64),
Ratio(Rc<Ratio>),
Complex(Complex64),
Cell(Rc<RefCell<Value>>),
@ -113,11 +109,7 @@ impl Value {
}
Ok(())
}
Self::Range(r) => match r.ty {
RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
RangeType::Endless => write!(w, "{}..*", r.start),
},
Self::Range(r) => write!(w, "{r}"),
Self::Int(i) => write!(w, "{i}"),
Self::Float(x) => write!(w, "{x:?}"),
Self::Ratio(r) => write!(w, "{}/{}", r.numer(), r.denom()),
@ -401,6 +393,23 @@ macro_rules! impl_from {
}
}
};
($ty:ty, $var:ident, rchash) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self {
Self::$var(Rc::new(value))
}
}
impl From<Rc<$ty>> for Value {
fn from(value: Rc<$ty>) -> Self {
Self::$var(value)
}
}
impl From<$ty> for HashValue {
fn from(value: $ty) -> Self {
Self(Value::$var(Rc::new(value)))
}
}
};
($ty:ty, $var:ident, into) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self {
@ -412,10 +421,12 @@ macro_rules! impl_from {
impl_from!(bool, Bool, hash);
impl_from!(Symbol, Symbol, hash);
impl_from!(Range, Range);
impl_from!(i64, Int, hash);
impl_from!(Range, Range, rc);
impl_from!(Int, Int, hash);
impl_from!(i64, Int, into);
impl_from!(BigInt, Int, into);
impl_from!(f64, Float);
impl_from!(Rational64, Ratio, hash);
impl_from!(Ratio, Ratio, rchash);
impl_from!(Complex64, Complex);
impl_from!(HashMap<HashValue, Value>, Table, rcref);
impl_from!(Vec<Value>, List, rcref);
@ -439,3 +450,15 @@ impl<T: NativeValue + 'static> From<T> for Value {
Self::Native(Rc::new(value))
}
}
impl From<String> for Value {
fn from(value: String) -> Self {
Self::String(LStr::from_str(&value).into())
}
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Self::String(LStr::from_str(value).into())
}
}

View file

@ -5,43 +5,31 @@ use std::{
rc::Rc,
};
use num_complex::{Complex64, ComplexFloat};
use num_rational::Rational64;
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
use num::complex::Complex64;
use crate::{
exception::{throw, Result},
lstring::LString,
number::{Int, Range, Ratio},
prelude::*,
symbol::{symbol, SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
value::range::RangeType,
vm::Vm,
};
use super::{
function::{FuncAttrs, NativeFunc},
range::Range,
HashValue, Value,
};
pub trait RatioExt {
fn to_f64(&self) -> f64;
}
impl RatioExt for Rational64 {
fn to_f64(&self) -> f64 {
num_traits::ToPrimitive::to_f64(self).unwrap_or(f64::NAN)
}
}
impl Value {
pub fn truthy(&self) -> bool {
match self {
Value::Nil => false,
Value::Bool(b) => *b,
Value::Range(r) => matches!(r.len(), None | Some(1..)),
Value::Int(n) => *n != 0,
Value::Range(r) => !r.is_empty(),
Value::Int(n) => !n.is_zero(),
Value::Float(x) => *x != 0.0 && !x.is_nan(),
Value::Ratio(r) => !(*r.numer() == 0 && *r.denom() != 0),
Value::Ratio(r) => !r.is_zero(),
Value::Complex(c) => !(c.re == 0.0 && c.im == 0.0 || c.is_nan()),
Value::String(s) => !s.is_empty(),
Value::List(l) => l.borrow().len() > 0,
@ -59,17 +47,17 @@ impl Value {
pub fn promote(a: Value, b: Value) -> (Value, Value) {
use Value as V;
match (&a, &b) {
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
(V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
(V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
(V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64()), b),
(V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64().into()), b),
(V::Int(x), V::Ratio(..)) => (V::Ratio(Ratio::from(x).into()), b),
(V::Int(x), V::Float(..)) => (V::Float(x.to_f64_uc()), b),
(V::Int(x), V::Complex(..)) => (V::Complex(x.to_f64_uc().into()), b),
(V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64_uc()), b),
(V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64_uc().into()), b),
(V::Float(x), V::Complex(..)) => (V::Complex(x.into()), b),
(V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
(V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
(V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
(V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64())),
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64().into())),
(V::Ratio(..), V::Int(y)) => (a, V::Ratio(Ratio::from(y).into())),
(V::Float(..), V::Int(y)) => (a, V::Float(y.to_f64_uc())),
(V::Complex(..), V::Int(y)) => (a, V::Complex(y.to_f64_uc().into())),
(V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64_uc())),
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64_uc().into())),
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
_ => (a, b),
}
@ -84,20 +72,8 @@ impl Neg for Value {
fn neg(self) -> Self::Output {
use Value as V;
match self {
V::Int(x) => {
if let Some(x) = x.checked_neg() {
Ok(V::Int(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
}
}
V::Ratio(x) => {
if let Some(x) = Rational64::ZERO.checked_sub(&x) {
Ok(V::Ratio(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
}
}
V::Int(x) => Ok(V::Int(-x)),
V::Ratio(x) => Ok(V::Ratio((-x.as_ref()).into())),
V::Float(x) => Ok(V::Float(-x)),
V::Complex(x) => Ok(V::Complex(-x)),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
@ -109,26 +85,8 @@ impl Value {
pub fn abs(self) -> Result<Self> {
use Value as V;
match self {
V::Int(x) => {
if let Some(x) = x.checked_abs() {
Ok(V::Int(x))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when finding absolute value of {self}"
)
}
}
V::Ratio(x) => {
if let Some((x, _)) = ratio_checked_absign(&x) {
Ok(V::Ratio(x))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when finding absolute value of {self}"
)
}
}
V::Int(x) => Ok(V::Int(x.abs())),
V::Ratio(x) => Ok(V::Ratio(x.abs().into())),
V::Float(x) => Ok(V::Float(x.abs())),
V::Complex(x) => Ok(V::Float(x.norm())),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
@ -141,7 +99,7 @@ impl Value {
/////////////////////////
macro_rules! impl_value_arith {
($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => {
($trait:ident, $name:ident, $op:tt, $verb:literal) => {
impl $trait<Value> for Value {
type Output = Result<Self>;
@ -149,16 +107,8 @@ macro_rules! impl_value_arith {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(x), V::Int(y)) => if let Some(v) = x.$checked(y) {
Ok(V::Int(v))
} else {
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
},
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.$checked(y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
},
(V::Int(x), V::Int(y)) => Ok(V::Int(x $op y)),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio((x.as_ref() $op y.as_ref()).into())),
(V::Float(x), V::Float(y)) => Ok(V::Float(x $op y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x $op y)),
(l, r) => throw!(*SYM_TYPE_ERROR, concat!("cannot ", $verb, " {:#} and {:#}"), l, r)
@ -169,9 +119,9 @@ macro_rules! impl_value_arith {
};
}
impl_value_arith!(Add, add, checked_add, +, "add");
impl_value_arith!(Sub, sub, checked_sub, -, "subtract");
impl_value_arith!(Mul, mul, checked_mul, *, "multiply");
impl_value_arith!(Add, add, +, "add");
impl_value_arith!(Sub, sub, -, "subtract");
impl_value_arith!(Mul, mul, *, "multiply");
impl Div<Value> for Value {
type Output = Result<Self>;
@ -179,18 +129,16 @@ impl Div<Value> for Value {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer division by 0"),
(V::Int(x), V::Int(y)) => Ok(Value::Ratio((*x, *y).into())),
(V::Int(_), V::Int(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "integer division by 0")
}
(V::Int(x), V::Int(y)) => {
Ok(Value::from(Ratio::new(x.clone().into(), y.clone().into())))
}
(V::Ratio(_), V::Ratio(r)) if r.is_zero() => {
throw!(*SYM_VALUE_ERROR, "rational division by 0")
}
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = x.checked_div(y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when dividing {a} and {b}")
}
}
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio((x.as_ref() / y.as_ref()).into())),
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}"),
@ -202,54 +150,20 @@ impl Div<Value> for Value {
// modulo and integer division //
///////////////////////////////////
#[inline]
fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> {
let a = if r.is_negative() {
Rational64::ZERO.checked_sub(r)?
} else {
*r
};
Some((a, r.signum()))
}
#[inline]
fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
let (r2_abs, r2_sgn) = ratio_checked_absign(r2)?;
Some(r1.checked_div(&r2_abs)?.floor() * r2_sgn)
}
#[inline]
fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
let q = ratio_checked_div_euclid(r1, r2)?;
r1.checked_sub(&r2.checked_mul(&q)?)
}
impl Value {
pub fn modulo(self, rhs: Value) -> Result<Self> {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer modulo by 0"),
(V::Int(x), V::Int(y)) => {
if let Some(v) = x.checked_rem_euclid(*y) {
Ok(V::Int(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
(V::Int(_), V::Int(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "integer modulo by 0")
}
}
(V::Int(x), V::Int(y)) => Ok(V::Int(x.rem_euclid(y))),
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "rational modulo by 0")
}
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = ratio_checked_rem_euclid(x, y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
}
}
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(*y))),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x.rem_euclid(y).into())),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(y))),
(V::Complex(x), V::Complex(y)) => {
let n = x / y;
let n = Complex64::new(n.re().floor(), n.im().floor());
@ -263,33 +177,15 @@ impl Value {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer divsion by 0"),
(V::Int(x), V::Int(y)) => {
if let Some(v) = x.checked_div_euclid(*y) {
Ok(V::Int(v))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when integer dividing {a} and {b}"
)
(V::Int(_), V::Int(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "integer divsion by 0")
}
}
(V::Int(x), V::Int(y)) => Ok(Value::from(x.div_euclid(y))),
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "integer division by 0")
}
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = ratio_checked_div_euclid(x, y) {
Ok(V::Ratio(v))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when integer dividing {a} and {b}"
)
}
}
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(*y))),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x.rem_euclid(y).into())),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(y))),
(V::Complex(x), V::Complex(y)) => {
let n = x / y;
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
@ -304,29 +200,11 @@ impl Value {
//////////////////////
#[inline]
fn ipow(n: i64, p: u64) -> Option<i64> {
match (n, p) {
(0, 0) => None,
(0, _) => Some(0),
(_, 0) => Some(1),
(1, _) => Some(1),
(-1, p) => (-1_i64).checked_pow((p % 2) as u32),
(_, p) if p > u32::MAX as u64 => None,
(n, p) => n.checked_pow(p as u32),
}
}
#[inline]
fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
match p {
i64::MIN => match (n, d) {
(0, _) => Some((0, 1)),
(1, 1) => Some((1, 1)),
(-1, 1) => Some((-1, 1)),
_ => None,
},
0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)),
_ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)),
fn rpow(n: &Int, d: &Int, p: &Int) -> Option<Ratio> {
let (sign, p) = (p.isignum(), p.abs());
match sign {
0.. => Some(Ratio::new(n.ipow(&p)?.into(), d.ipow(&p)?.into())),
_ => Some(Ratio::new(d.ipow(&p)?.into(), n.ipow(&p)?.into())),
}
}
@ -334,10 +212,10 @@ impl Value {
pub fn pow(self, rhs: Value) -> Result<Self> {
use Value as V;
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
if x.is_zero() && *y == 0 {
if x.is_zero() && y.is_zero() {
throw!(*SYM_VALUE_ERROR, "rational zero to integer zero power")
}
let Some(v) = rpow(*(*x).numer(), *(*x).denom(), *y) else {
let Some(v) = rpow(&x.numer().into(), &x.denom().into(), y) else {
throw!(
*SYM_VALUE_ERROR,
"overflow when raising {self} to the power {rhs}"
@ -347,11 +225,11 @@ impl Value {
}
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(0), V::Int(0)) => {
(V::Int(x), V::Int(y)) if x.is_zero() && y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power")
}
(V::Int(x), V::Int(y @ 0..)) => {
if let Some(v) = ipow(*x, *y as u64) {
(V::Int(x), V::Int(y)) if !y.is_negative() => {
if let Some(v) = x.ipow(y) {
Ok(V::Int(v))
} else {
throw!(
@ -361,7 +239,7 @@ impl Value {
}
}
(V::Int(x), V::Int(y)) => {
if let Some(v) = rpow(*x, 1, *y) {
if let Some(v) = rpow(x, &Int::ONE, y) {
Ok(V::Ratio(v.into()))
} else {
throw!(
@ -370,7 +248,7 @@ impl Value {
)
}
}
(V::Ratio(x), V::Ratio(y)) => Ok(V::Float(x.to_f64().powf(y.to_f64()))),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Float(x.to_f64_uc().powf(y.to_f64_uc()))),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.powf(*y))),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x.powc(*y))),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}"),
@ -389,7 +267,16 @@ impl Shl<Value> for Value {
use Value as V;
match (self, rhs) {
(V::Int(a), V::Int(b)) => {
Ok(Value::Int((a as u64).wrapping_shl(b as u32) as i64))
if b.is_negative() {
throw!(
*SYM_TYPE_ERROR,
"cannot shift {a} left by negative amount {b}"
)
}
if b.gt_i64(u32::MAX as i64) {
throw!(*SYM_TYPE_ERROR, "cannot shift {a} left by {b}: too large")
}
Ok(Value::Int(a << b))
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} left by {r:#}"),
}
@ -403,7 +290,13 @@ impl Shr<Value> for Value {
use Value as V;
match (self, rhs) {
(V::Int(a), V::Int(b)) => {
Ok(Value::Int((a as u64).wrapping_shr(b as u32) as i64))
if b.is_negative() {
throw!(
*SYM_TYPE_ERROR,
"cannot shift {a} right by negative amount {b}"
)
}
Ok(Value::Int(a >> b))
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
}
@ -464,7 +357,6 @@ impl Not for Value {
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
use super::range::RangeType as Rty;
use Value as V;
match (self, other) {
(V::Nil, V::Nil) => true,
@ -473,31 +365,23 @@ impl PartialEq for Value {
(V::Ratio(a), V::Ratio(b)) => *a == *b,
(V::Float(a), V::Float(b)) => *a == *b,
(V::Complex(a), V::Complex(b)) => *a == *b,
(V::Int(a), V::Ratio(b)) => Rational64::from(*a) == *b,
(V::Ratio(a), V::Int(b)) => *a == Rational64::from(*b),
(V::Int(a), V::Float(b)) => *a as f64 == *b,
(V::Float(a), V::Int(b)) => *a == *b as f64,
(V::Int(a), V::Complex(b)) => Complex64::from(*a as f64) == *b,
(V::Complex(a), V::Int(b)) => *a == Complex64::from(*b as f64),
(V::Ratio(a), V::Float(b)) => a.to_f64() == *b,
(V::Float(a), V::Ratio(b)) => *a == b.to_f64(),
(V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64()) == *b,
(V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64()),
(V::Int(a), V::Ratio(b)) => b.is_integer() && a.to_big().as_ref() == b.numer(),
(V::Ratio(a), V::Int(b)) => a.is_integer() && a.numer() == b.to_big().as_ref(),
(V::Int(a), V::Float(b)) => a.to_f64_uc() == *b,
(V::Float(a), V::Int(b)) => *a == b.to_f64_uc(),
(V::Int(a), V::Complex(b)) => Complex64::from(a.to_f64_uc()) == *b,
(V::Complex(a), V::Int(b)) => *a == Complex64::from(b.to_f64_uc()),
(V::Ratio(a), V::Float(b)) => a.to_f64_uc() == *b,
(V::Float(a), V::Ratio(b)) => *a == b.to_f64_uc(),
(V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64_uc()) == *b,
(V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64_uc()),
(V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b,
(V::Complex(a), V::Float(b)) => *a == Complex64::from(*b),
(V::String(a), V::String(b)) => *a == *b,
(V::List(a), V::List(b)) => *a.borrow() == *b.borrow(),
(V::Symbol(a), V::Symbol(b)) => a == b,
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
(Rty::Open, Rty::Open) | (Rty::Closed, Rty::Closed) => {
a.start == b.start && a.stop == b.stop
}
(Rty::Open, Rty::Closed) => a.start == b.start && a.stop == b.stop - 1,
(Rty::Closed, Rty::Open) => a.start == b.start && a.stop - 1 == b.stop,
(Rty::Endless, Rty::Endless) => a.start == b.start,
_ => false,
},
(V::Range(a), V::Range(b)) => a.as_ref() == b.as_ref(),
(V::Native(a), b) => a.partial_eq(b),
(a, V::Native(b)) => b.partial_eq(a),
_ => false,
@ -514,12 +398,12 @@ impl PartialOrd for Value {
(V::Int(a), V::Int(b)) => a.partial_cmp(b),
(V::Ratio(a), V::Ratio(b)) => a.partial_cmp(b),
(V::Float(a), V::Float(b)) => a.partial_cmp(b),
(V::Int(a), V::Ratio(b)) => Rational64::from(*a).partial_cmp(b),
(V::Ratio(a), V::Int(b)) => a.partial_cmp(&Rational64::from(*b)),
(V::Int(a), V::Float(b)) => (*a as f64).partial_cmp(b),
(V::Float(a), V::Int(b)) => a.partial_cmp(&(*b as f64)),
(V::Ratio(a), V::Float(b)) => a.to_f64().partial_cmp(b),
(V::Float(a), V::Ratio(b)) => a.partial_cmp(&b.to_f64()),
(V::Int(a), V::Ratio(b)) => Ratio::from(a).partial_cmp(b),
(V::Ratio(a), V::Int(b)) => a.as_ref().partial_cmp(&Ratio::from(b)),
(V::Int(a), V::Float(b)) => a.to_f64_uc().partial_cmp(b),
(V::Float(a), V::Int(b)) => a.partial_cmp(&b.to_f64_uc()),
(V::Ratio(a), V::Float(b)) => a.to_f64_uc().partial_cmp(b),
(V::Float(a), V::Ratio(b)) => a.partial_cmp(&b.to_f64_uc()),
(V::String(a), V::String(b)) => a.partial_cmp(b),
(V::List(a), V::List(b)) => a.borrow().partial_cmp(&*b.borrow()),
(V::Symbol(a), V::Symbol(b)) => a.partial_cmp(b),
@ -574,19 +458,9 @@ impl Value {
}
}
pub fn range(&self, other: &Self, closed: bool) -> Result<Self> {
if let (Value::Int(start), Value::Int(stop)) = (self, other) {
let ty = if closed {
RangeType::Closed
} else {
RangeType::Open
};
Ok(Range {
start: *start,
stop: *stop,
ty,
}
.into())
pub fn range(&self, other: &Self) -> Result<Self> {
if let (Value::Int(start), Value::Int(end)) = (self, other) {
Ok(Range::range(start, end).into())
} else {
throw!(
*SYM_TYPE_ERROR,
@ -595,19 +469,48 @@ impl Value {
}
}
pub fn range_endless(&self) -> Result<Self> {
if let Value::Int(start) = self {
Ok(Range {
start: *start,
stop: 0,
ty: RangeType::Endless,
}
.into())
pub fn range_incl(&self, other: &Self) -> Result<Self> {
if let (Value::Int(start), Value::Int(end)) = (self, other) {
Ok(Range::range_incl(start, end).into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}")
throw!(
*SYM_TYPE_ERROR,
"cannot create range between {self:#} and {other:#}"
)
}
}
pub fn range_from(&self) -> Result<Self> {
if let Value::Int(start) = self {
Ok(Range::range_from(start).into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot create range from {self:#}")
}
}
pub fn range_to(&self) -> Result<Self> {
if let Value::Int(end) = self {
Ok(Range::range_to(end).into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot create range to {self:#}")
}
}
pub fn range_to_incl(&self) -> Result<Self> {
if let Value::Int(end) = self {
Ok(Range::range_to_incl(end).into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot create range to {self:#}")
}
}
pub fn range_all() -> Self {
thread_local! {
static RANGE_ALL: Rc<Range> = Rc::new(Range::range_all());
}
Self::Range(RANGE_ALL.with(|x| x.clone()))
}
pub fn to_cell(self) -> Self {
Value::Cell(Rc::new(RefCell::new(self)))
}
@ -630,7 +533,10 @@ impl Value {
match self {
Self::Function(_) | Self::NativeFunc(_) => Ok(self),
Self::Range(range) => {
let range_iter = RefCell::new(range.into_iter());
let Some(iter) = range.try_iterate() else {
throw!(*SYM_VALUE_ERROR, "cannot iterate range {range}")
};
let range_iter = RefCell::new(iter);
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
Ok(Value::iter_pack(
range_iter.borrow_mut().next().map(Value::from),

View file

@ -1,43 +0,0 @@
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RangeType {
Open,
Closed,
Endless,
}
#[derive(Clone, Copy, Debug)]
pub struct Range {
pub start: i64,
pub stop: i64,
pub ty: RangeType,
}
impl Range {
pub fn len(&self) -> Option<usize> {
match self.ty {
RangeType::Open => Some((self.stop - self.start).max(0) as usize),
RangeType::Closed => Some((self.stop - self.start + 1).max(0) as usize),
RangeType::Endless => None,
}
}
pub fn is_empty(&self) -> bool {
match self.ty {
RangeType::Open => self.stop - self.start <= 0,
RangeType::Closed => self.stop - self.start < 0,
RangeType::Endless => false,
}
}
}
impl IntoIterator for Range {
type Item = i64;
type IntoIter = Box<dyn Iterator<Item = i64>>;
fn into_iter(self) -> Self::IntoIter {
match self.ty {
RangeType::Open => Box::new(self.start..self.stop),
RangeType::Closed => Box::new(self.start..=self.stop),
RangeType::Endless => Box::new(self.start..),
}
}
}

View file

@ -74,8 +74,8 @@ pub fn binary_op(o: BinaryOp, a: Value, b: Value) -> Result<Value> {
BinaryOp::Ge => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Less)),
BinaryOp::Lt => a.val_cmp(&b).map(|o| Value::Bool(o == Ordering::Less)),
BinaryOp::Le => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Greater)),
BinaryOp::Range => a.range(&b, false),
BinaryOp::RangeIncl => a.range(&b, true),
BinaryOp::Range => a.range(&b),
BinaryOp::RangeIncl => a.range_incl(&b),
BinaryOp::Concat => a.concat(&b),
BinaryOp::Append => a.append(b),
}
@ -86,7 +86,9 @@ pub fn unary_op(o: UnaryOp, a: Value) -> Result<Value> {
UnaryOp::Neg => -a,
UnaryOp::Not => Ok(Value::Bool(!a.truthy())),
UnaryOp::BitNot => a.not(),
UnaryOp::RangeEndless => a.range_endless(),
UnaryOp::RangeFrom => a.range_from(),
UnaryOp::RangeTo => a.range_to(),
UnaryOp::RangeToIncl => a.range_to_incl(),
}
}
@ -352,7 +354,7 @@ impl Vm {
self.push(Value::Symbol(sym));
}
// [] -> [n]
I::Int(n) => self.push(Value::Int(i64::from(n))),
I::Int(n) => self.push(Value::from(i64::from(n))),
// [x] -> [x,x]
I::Dup => self.push(self.stack[self.stack.len() - 1].clone()),
// [x,y] -> [x,y,x,y]

View file

@ -32,7 +32,7 @@ fn scope() {
end
x + y
",
Value::Int(7 + 100),
Value::from(7 + 100),
);
assert_eval(
"
@ -41,7 +41,7 @@ fn scope() {
if cond then var z = 5 else var z = 6 end
z
",
Value::Int(2),
Value::from(2),
);
assert_eval(
"
@ -52,7 +52,7 @@ fn scope() {
end
i + j
",
Value::Int(55 + 9),
Value::from(55 + 9),
);
}
@ -68,7 +68,7 @@ fn forloop() {
end
sum
",
Value::Int(45 * 10),
Value::from(45 * 10),
);
assert_eval(
"
@ -79,7 +79,7 @@ fn forloop() {
end
prod
",
Value::Int(3 * 2 * 4 * 7),
Value::from(3 * 2 * 4 * 7),
);
}
@ -88,10 +88,10 @@ fn closures() {
assert_eval(
"
var x = 2
next = \\. do x = x * 2 + 1 end
next = \\->do x = x * 2 + 1 end
next() + next() + next()
",
Value::Int(5 + 11 + 23),
Value::from(5 + 11 + 23),
);
assert_eval(
"
@ -108,7 +108,7 @@ fn closures() {
var g = outer(6)
f() + f() + g() + g() + f()
",
Value::Int(2 + 5 + 11 + 18 + 22),
Value::from(2 + 5 + 11 + 18 + 22),
);
}
@ -125,6 +125,6 @@ fn tailcall() {
end
test(24, 0)
",
Value::Int(300),
Value::from(300),
) // = 24 + 23 + ... + 1
}

View file

@ -7,10 +7,11 @@ edition = "2021"
talc-lang = { path = "../talc-lang" }
talc-macros = { path = "../talc-macros" }
lazy_static = "1.5"
num-bigint = { version = "0.4", features = ["rand"], optional = true }
regex = { version = "1.11", optional = true }
rand = { version = "0.8", optional = true }
[features]
default = ["rand", "regex"]
rand = ["dep:rand"]
rand = ["dep:rand", "dep:num-bigint"]
regex = ["dep:regex"]

View file

@ -2,6 +2,8 @@ use std::cmp::Ordering;
use talc_lang::{
exception::{exception, Result},
number::Int,
prelude::*,
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{function::NativeFunc, Value},
@ -69,7 +71,7 @@ pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(col)
}
fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<i64> {
fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<Int> {
let ord = vmcall!(vm; by, l, r)?;
let Value::Int(ord) = ord else {
throw!(
@ -117,17 +119,17 @@ fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, u
while eq <= gt {
let ord = call_comparison(vm, by.clone(), vals[eq].clone(), pivot.clone())?;
match ord {
..=-1 => {
match ord.cmp(&Int::ZERO) {
Ordering::Less => {
vals.swap(eq, lt);
lt += 1;
eq += 1;
}
1.. => {
Ordering::Greater => {
vals.swap(eq, gt);
gt -= 1;
}
0 => {
Ordering::Equal => {
eq += 1;
}
}
@ -152,7 +154,7 @@ fn insertion_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<()> {
while j > 0 {
let ord =
call_comparison(vm, by.clone(), vals[j - 1].clone(), vals[j].clone())?;
if ord <= 0 {
if !ord.is_positive() {
break
}
vals.swap(j, j - 1);
@ -212,9 +214,9 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let a = vmcall!(vm; key.clone(), a)?;
let b = vmcall!(vm; key.clone(), b)?;
match a.partial_cmp(&b) {
Some(Ordering::Greater) => Ok(Value::Int(1)),
Some(Ordering::Equal) => Ok(Value::Int(0)),
Some(Ordering::Less) => Ok(Value::Int(-1)),
Some(Ordering::Greater) => Ok(Value::from(1)),
Some(Ordering::Equal) => Ok(Value::from(0)),
Some(Ordering::Less) => Ok(Value::from(-1)),
None => throw!(
*SYM_VALUE_ERROR,
"values returned from sort key were incomparable"

View file

@ -13,6 +13,7 @@ use lazy_static::lazy_static;
use talc_lang::{
exception::Result,
lstring::LString,
prelude::*,
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{function::NativeFunc, HashValue, NativeValue, Value},
@ -294,7 +295,7 @@ pub fn read(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(nbytes) = nbytes else {
throw!(*SYM_TYPE_ERROR, "read expected integer, got {nbytes:#}")
};
let Ok(nbytes) = usize::try_from(nbytes) else {
let Some(nbytes) = nbytes.to_usize() else {
throw!(
*SYM_VALUE_ERROR,
"number of bytes to read must be nonnegative"
@ -464,11 +465,14 @@ pub fn tcp_connect_timeout(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
};
let timeout = match timeout {
Value::Int(n) if n >= 0 => Duration::from_secs(n as u64),
Value::Int(n) => match n.to_usize() {
Some(n) => Duration::from_secs(n as u64),
None => throw!(*SYM_VALUE_ERROR, "tcp_connect_timeout: invalid timeout",),
},
Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => {
Duration::from_secs_f64(n)
}
Value::Int(_) | Value::Float(_) => {
Value::Float(_) => {
throw!(*SYM_VALUE_ERROR, "tcp_connect_timeout: invalid timeout")
}
_ => throw!(
@ -755,7 +759,7 @@ pub fn exit_code(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match proc.wait() {
Ok(code) => Ok(code
.code()
.map(|c| Value::Int(c as i64))
.map(|c| Value::from(c as i64))
.unwrap_or_default()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}

View file

@ -3,10 +3,12 @@ use std::io::Write;
use talc_lang::{
exception::{exception, Result},
lstring::{LStr, LString},
parser::{parse_int, to_lstring_radix},
number::{Complex, Int, Ratio},
parser::parse_int,
prelude::*,
symbol::SYM_TYPE_ERROR,
throw,
value::{Complex64, Rational64, Value},
value::Value,
vm::Vm,
};
use talc_macros::native_func;
@ -235,16 +237,16 @@ fn get_val(args: &[Value], i: FmtIndex, faidx: &mut usize) -> Result<usize> {
let Value::Int(v) = v else {
throw!(
*SYM_FORMAT_ERROR,
"expected positive integer argument, found {v:#}"
"expected small positive integer argument, found {v:#}"
)
};
if *v < 0 {
throw!(
match v.to_usize() {
Some(v) => Ok(v),
None => throw!(
*SYM_FORMAT_ERROR,
"expected positive integer argument, found {v}"
)
"expected small positive integer argument, found {v}"
),
}
Ok(*v as usize)
}
fn format_float(
@ -270,7 +272,7 @@ fn format_float(
}
fn format_complex(
cx: Complex64,
cx: Complex,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
@ -286,7 +288,7 @@ fn format_complex(
}
fn format_int(
n: i64,
n: &Int,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
@ -296,26 +298,25 @@ fn format_int(
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
}
let res = match ty {
FmtType::Str => write!(buf, "{}", Value::Int(n)),
FmtType::Repr => write!(buf, "{:#}", Value::Int(n)),
FmtType::Hex(caps) => write!(buf, "{}", to_lstring_radix(n, 16, caps)),
FmtType::Oct => write!(buf, "{}", to_lstring_radix(n, 8, false)),
FmtType::Sex => write!(buf, "{}", to_lstring_radix(n, 6, false)),
FmtType::Bin => write!(buf, "{}", to_lstring_radix(n, 2, false)),
FmtType::Str | FmtType::Repr => write!(buf, "{}", n),
FmtType::Hex(caps) => write!(buf, "{}", n.to_str_radix_case(16, caps)),
FmtType::Oct => write!(buf, "{}", n.to_str_radix(8)),
FmtType::Sex => write!(buf, "{}", n.to_str_radix(6)),
FmtType::Bin => write!(buf, "{}", n.to_str_radix(2)),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
};
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
}
fn format_ratio(
n: Rational64,
n: &Ratio,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
) -> Result<()> {
format_int(*n.numer(), prec, ty, buf, "ratio")?;
format_int(&Int::from(n.numer()), prec, ty, buf, "ratio")?;
buf.push_char('/');
format_int(*n.denom(), prec, ty, buf, "ratio")?;
format_int(&Int::from(n.denom()), prec, ty, buf, "ratio")?;
Ok(())
}
@ -383,8 +384,8 @@ fn format_arg(
let width_val = fmtcode.width.map(|i| get_val(args, i, faidx)).transpose()?;
match fmt_arg {
Value::Int(n) => format_int(*n, prec_val, fmtcode.ty, &mut buf, "integer")?,
Value::Ratio(n) => format_ratio(*n, prec_val, fmtcode.ty, &mut buf)?,
Value::Int(n) => format_int(n, prec_val, fmtcode.ty, &mut buf, "integer")?,
Value::Ratio(n) => format_ratio(n, prec_val, fmtcode.ty, &mut buf)?,
Value::Float(f) => format_float(*f, prec_val, fmtcode.ty, &mut buf, "float")?,
Value::Complex(n) => format_complex(*n, prec_val, fmtcode.ty, &mut buf)?,
Value::String(s) => format_string(s, prec_val, fmtcode.ty, &mut buf)?,

452
talc-std/src/ints.rs Normal file
View file

@ -0,0 +1,452 @@
use talc_lang::{
exception::Result,
number::Int,
prelude::*,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::Value,
vm::Vm,
vmcalliter,
};
use talc_macros::native_func;
use crate::unpack_args;
fn miller_rabin_iter(n: &Int, s: u64, d: &Int, a: &Int) -> bool {
if a > n {
return true
}
let mut x = a.modpow(d, n);
for _ in 0..s {
let y = &(&x * &x) % n;
if y.eq_i64(1) && x.ne_i64(1) && x != (n - 1) {
return false
}
x = y;
}
x.eq_i64(1)
}
fn miller_rabin(n: &Int) -> bool {
if n.lt_i64(2) {
return false
}
if n.is_even() {
return n.eq_i64(2)
}
for p in [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37] {
if (n % p).is_zero() {
return n.eq_i64(p)
}
}
if n.lt_i64(41) {
return false
}
let s = (n - 1).trailing_zeros().unwrap();
let d = n >> &Int::from(s);
for a in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37] {
if !miller_rabin_iter(n, s, &d, &Int::from_small(a)) {
return false
}
}
true
}
fn trial_div(n: u32) -> bool {
if n < 2 {
return false
}
if n % 2 == 0 {
return n == 2
}
for p in [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37] {
if n % p == 0 {
return n == p
}
}
if n < 41 {
return false
}
let lim = n.sqrt();
let mut i = 41;
while i <= lim + 1 {
if n % i == 0 {
return false
}
if n % (i + 2) == 0 {
return false
}
i += 6;
}
true
}
fn is_prime_inner(n: &Int) -> bool {
if n.lt_i64(2) {
return false
}
if let Some(m) = n.to_u32() {
trial_div(m)
} else {
miller_rabin(n)
}
}
fn pollard_rho(n: &Int, a: Int, b: &Int) -> Option<Int> {
let mut x = a.clone();
let mut y = a.clone();
let mut d = Int::ONE;
while d.eq_i64(1) {
x = &(&(&x * &x) + b) % n;
y = &(&(&y * &y) + b) % n;
y = &(&(&y * &y) + b) % n;
d = (&x - &y).gcd(n);
}
if &d == n {
None
} else {
Some(d)
}
}
fn pollard_rho_checks(n: &Int) -> Int {
let nminus2 = n - 2;
let mut a = Int::from_small(2);
while &a < n {
let mut b = Int::ONE;
while b < nminus2 {
if let Some(f) = pollard_rho(n, a.clone(), &b) {
return f
}
b += 1;
}
a += 1;
}
// we should never get here, hopefully
n.clone()
}
fn recurse_pollard_rho(n: &Int, factors: &mut Vec<Int>) {
if is_prime_inner(n) {
factors.push(n.clone());
return
}
let factor = pollard_rho_checks(n);
recurse_pollard_rho(&factor, factors);
recurse_pollard_rho(&(n / &factor), factors);
}
fn factorize(n: &Int) -> Vec<Int> {
let mut n = n.abs();
let mut factors = Vec::new();
if n.lt_i64(2) {
return factors
}
let tz = n.trailing_zeros().unwrap();
n = &n >> &Int::from(tz);
for _ in 0..tz {
factors.push(Int::from_small(2));
}
while (&n % 3).is_zero() {
n /= 3;
factors.push(Int::from_small(3));
}
let nsqrt = n.sqrt();
let limit = nsqrt.min(Int::from_small(1 << 15));
let mut i = Int::from_small(5);
while i < limit && n.gt_i64(1) {
while (&n % &i).is_zero() {
n /= &i;
factors.push(i.clone());
}
i += 2;
while (&n % &i).is_zero() {
n /= &i;
factors.push(i.clone());
}
i += 4;
}
if &i * &i <= n {
let len = factors.len();
recurse_pollard_rho(&n, &mut factors);
factors[len..].sort_unstable();
} else if n.gt_i64(1) {
factors.push(n);
}
factors
}
pub fn load(vm: &mut Vm) {
vm.set_global_name("isprime", isprime().into());
vm.set_global_name("factors", factors().into());
vm.set_global_name("totient", totient().into());
vm.set_global_name("powmod", powmod().into());
vm.set_global_name("invmod", invmod().into());
vm.set_global_name("gcd", gcd().into());
vm.set_global_name("gcdn", gcdn().into());
vm.set_global_name("lcm", lcm().into());
vm.set_global_name("lcmn", lcmn().into());
vm.set_global_name("isqrt", isqrt().into());
vm.set_global_name("iroot", iroot().into());
vm.set_global_name("jacobi", jacobi().into());
}
#[native_func(1)]
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, n] = unpack_args!(args);
let Value::Int(n) = n else {
throw!(
*SYM_TYPE_ERROR,
"isprime expected integer argument, got {n:#}"
)
};
Ok(Value::from(is_prime_inner(&n)))
}
#[native_func(1)]
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, n] = unpack_args!(args);
let Value::Int(n) = n else {
throw!(
*SYM_TYPE_ERROR,
"factors expected integer argument, got {n:#}"
)
};
let factors = factorize(&n);
let factors: Vec<Value> = factors.into_iter().map(Value::from).collect();
Ok(factors.into())
}
#[native_func(1)]
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, n] = unpack_args!(args);
let Value::Int(n) = n else {
throw!(
*SYM_TYPE_ERROR,
"totient expected integer argument, got {n:#}"
)
};
if n.is_zero() {
return Ok(Value::from(n))
}
let mut res = Int::ONE;
let mut last_factor = Int::ONE;
for factor in factorize(&n) {
if last_factor != factor {
res *= &factor - 1;
last_factor = factor
} else {
res *= factor
}
}
Ok(res.into())
}
#[native_func(3)]
pub fn powmod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, n, e, m] = unpack_args!(args);
let (Value::Int(n), Value::Int(e), Value::Int(m)) = (n, e, m) else {
throw!(*SYM_TYPE_ERROR, "powmod expected 3 integer arguments")
};
if m.is_zero() {
throw!(*SYM_VALUE_ERROR, "powmod: modulus was zero")
}
if e.is_negative() {
throw!(*SYM_VALUE_ERROR, "powmod: exponent was negative")
}
Ok(n.modpow(&e, &m).into())
}
#[native_func(2)]
pub fn invmod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, n, m] = unpack_args!(args);
let (Value::Int(n), Value::Int(m)) = (n, m) else {
throw!(*SYM_TYPE_ERROR, "invmod expected 2 integer arguments")
};
if m.is_zero() {
throw!(*SYM_VALUE_ERROR, "invmod: modulus was zero")
}
match n.modinv(&m) {
Some(v) => Ok(v.into()),
None => Ok(Value::Int(0.into())),
}
}
#[native_func(2)]
pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
};
let Value::Int(y) = y else {
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {y:#}")
};
Ok(x.gcd(&y).into())
}
#[native_func(1)]
pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?;
let mut g = Int::ZERO;
while let Some(a) = vmcalliter!(vm; args.clone())? {
let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}")
};
g = g.gcd(&a);
}
Ok(g.into())
}
#[native_func(2)]
pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
};
let Value::Int(y) = y else {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}")
};
Ok(x.lcm(&y).into())
}
#[native_func(1)]
pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?;
let mut l = Int::ONE;
while let Some(a) = vmcalliter!(vm; args.clone())? {
let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
};
l = l.lcm(&a);
}
Ok(Value::from(l))
}
#[native_func(1)]
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(
*SYM_TYPE_ERROR,
"isqrt expected integer argument, got {x:#}"
)
};
if x.is_negative() {
throw!(*SYM_VALUE_ERROR, "isqrt: radicand must be non-negative")
}
Ok(Value::Int(x.sqrt()))
}
#[native_func(2)]
pub fn iroot(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, n, x] = unpack_args!(args);
let (Value::Int(n), Value::Int(x)) = (&n, &x) else {
throw!(
*SYM_TYPE_ERROR,
"iroot expected integer arguments, got {n:#} and {x:#}"
)
};
if x.is_negative() {
throw!(
*SYM_VALUE_ERROR,
"isqrt: radicand must be non-negative, got {x}"
)
}
if !n.is_positive() {
throw!(*SYM_VALUE_ERROR, "isqrt: degree must be positive, got {n}")
}
let Some(n) = n.to_u32() else {
throw!(*SYM_VALUE_ERROR, "isqrt: degree {n} is too large")
};
Ok(Value::Int(x.nth_root(n)))
}
#[native_func(2)]
pub fn jacobi(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, a, n] = unpack_args!(args);
let (mut a, mut n) = match (a, n) {
(Value::Int(a), Value::Int(n)) => {
(a.to_big().into_owned(), n.to_big().into_owned())
}
(a, n) => throw!(
*SYM_TYPE_ERROR,
"jacobi expected integer arguments, got {a:#} and {n:#}",
),
};
// (a|0) = 1 if |a| = 1, 0 otherwise
if n.is_zero() {
if a.to_i32().is_some_and(|v| v == -1 || v == 1) {
return Ok(Value::from(1))
} else {
return Ok(Value::from(0))
}
}
// (k|l) = 0 if gcd(k,l) != 1
if a.is_even() && n.is_even() {
return Ok(Value::from(0))
}
let mut res = 1i64;
// (a|2n) = (a|2)(a|n),
// (a|2) = 0 if a is even, 1 if a = 1,7 mod 8, -1 if a = 3,5 mod 8
let am8 = ((&a % 8i32).to_i32().unwrap() + 8) % 8;
while n.is_even() {
n /= 2;
if am8 % 2 == 0 {
return Ok(Value::from(0))
}
if am8 == 3 || am8 == 5 {
res *= -1;
}
}
// (a|-1) = -1 if a < 0, 1 if a >= 0
if n.is_negative() {
n *= -1;
if a.is_negative() {
res *= -1;
}
}
a = ((a % &n) + &n) % &n;
while !a.is_zero() {
// (2a|n) = (2|n)(a|n)
// (2|n) = 1 if a = 1,7 mod 8, -1 if a = 3,5 mod 8
while a.is_even() {
a /= 2;
let nm8 = ((&n % 8i32).to_i32().unwrap() + 8) % 8;
if nm8 == 3 || nm8 == 5 {
res *= -1;
}
}
// (m|n)(n|m) = (-1)^((m-1)/2 (n-1)/2)
std::mem::swap(&mut a, &mut n);
let am4 = ((&a % 4i32).to_i32().unwrap() + 4) % 4;
let nm4 = ((&n % 4i32).to_i32().unwrap() + 4) % 4;
if am4 == 3 && nm4 == 3 {
res *= -1;
}
// (a|n) = (a mod n|n)
a = ((a % &n) + &n) % &n;
}
if n.to_u32().is_some_and(|v| v == 1) {
return Ok(Value::from(res))
} else {
return Ok(Value::from(0))
}
}

View file

@ -8,6 +8,7 @@ use std::{
use talc_lang::{
exception::{throw, Result},
lstring::LString,
prelude::*,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
value::Value,
vm::Vm,
@ -189,7 +190,14 @@ pub fn arg(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "arg expected integer, got {n:#}")
};
let cmd_args = vm.args();
if n < 0 || (n as usize) >= cmd_args.len() {
let Some(n) = n.to_usize() else {
throw!(
*SYM_VALUE_ERROR,
"arg number must be small positive integer, got {}",
n,
)
};
if n >= cmd_args.len() {
throw!(
*SYM_VALUE_ERROR,
"arg number {} out of range for {} arguments",
@ -197,7 +205,7 @@ pub fn arg(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
cmd_args.len()
)
}
Ok(Value::from(cmd_args[n as usize].clone()))
Ok(Value::from(cmd_args[n].clone()))
}
#[native_func(0)]

View file

@ -2,9 +2,10 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{
exception::Result,
prelude::*,
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{function::NativeFunc, ops::RatioExt, range::RangeType, HashValue, Value},
value::{function::NativeFunc, HashValue, Value},
vm::Vm,
vmcall, vmcalliter,
};
@ -186,7 +187,7 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(count) = count else {
throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}")
};
let Ok(count) = count.try_into() else {
let Some(count) = count.to_usize() else {
throw!(
*SYM_VALUE_ERROR,
"take expected nonnegative integer, got {count:#}"
@ -215,10 +216,10 @@ pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(count) = count else {
throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}")
};
let Ok(count) = count.try_into() else {
let Some(count) = count.to_usize() else {
throw!(
*SYM_VALUE_ERROR,
"count expected nonnegative integer, got {count:#}"
"count expected small nonnegative integer, got {count:#}"
)
};
let iter = iter.to_iter_function()?;
@ -305,12 +306,12 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(by) = by else {
throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}")
};
if by <= 0 {
let Some(by) = by.to_usize() else {
throw!(
*SYM_VALUE_ERROR,
"step expected positive integer, got {by:#}"
"step expected small positive integer, got {by:#}"
)
}
};
let state = RefCell::new(Step::First);
let f = move |vm: &mut Vm, _| match state.take() {
@ -322,7 +323,7 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::iter_pack(res))
}
Step::Going => {
for _ in 0..(by - 1) {
for _ in 1..by {
if vmcall!(vm; iter.clone())? == Value::Nil {
return Ok(Value::iter_pack(None))
}
@ -648,8 +649,10 @@ pub fn len(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::String(s) => return Ok((s.chars().count() as i64).into()),
Value::List(l) => return Ok((l.borrow().len() as i64).into()),
Value::Table(t) => return Ok((t.borrow().len() as i64).into()),
Value::Range(r) if r.ty != RangeType::Endless => {
return Ok((r.len().unwrap() as i64).into())
Value::Range(ref r) => {
if let Some(len) = r.len() {
return Ok(len.into())
}
}
_ => (),
}
@ -692,7 +695,7 @@ pub fn sum(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let mut result = Value::Int(0);
let mut result = Value::from(0);
while let Some(value) = vmcalliter!(vm; iter.clone())? {
result = (result + value)?;
}
@ -704,7 +707,7 @@ pub fn prod(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let mut result = Value::Int(1);
let mut result = Value::from(1);
while let Some(value) = vmcalliter!(vm; iter.clone())? {
result = (result * value)?;
}
@ -757,6 +760,10 @@ pub fn nth(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "nth expected integer")
};
let Some(n) = n.to_usize() else {
throw!(*SYM_VALUE_ERROR, "nth expected small positive integer")
};
for _ in 0..n {
if vmcalliter!(vm; iter.clone())?.is_none() {
return Ok(Value::Nil)
@ -849,8 +856,8 @@ pub fn count(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
while let Some(v) = vmcalliter!(vm; iter.clone())? {
let hv = v.try_into()?;
map.entry(hv)
.and_modify(|v: &mut Value| *v = (v.clone() + Value::Int(1)).unwrap())
.or_insert(Value::Int(1));
.and_modify(|v: &mut Value| *v = (v.clone() + Value::from(1)).unwrap())
.or_insert(Value::from(1));
}
Ok(map.into())
@ -862,7 +869,7 @@ pub fn mean(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter = iter.to_iter_function()?;
let mut sum = Value::Float(0.0);
let mut count = Value::Int(0);
let mut count = Value::from(0);
while let Some(value) = vmcalliter!(vm; iter.clone())? {
sum = (sum + value)?;
count = (count + Value::from(1))?;
@ -876,11 +883,11 @@ fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> {
let mut k = 1;
while let Some(value) = vmcalliter!(vm; iter.clone())? {
let old_m = m.clone();
m = (m.clone() + ((value.clone() - m.clone())? / Value::Int(k))?)?;
m = (m.clone() + ((value.clone() - m.clone())? / Value::from(k))?)?;
s = (s + ((value.clone() - m.clone())? * (value - old_m)?)?)?;
k += 1;
}
s / Value::Int(k - if pop { 1 } else { 2 })
s / Value::from(k - if pop { 1 } else { 2 })
}
#[native_func(1)]
@ -898,9 +905,9 @@ pub fn stdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let v = variance_inner(vm, iter, false)?;
Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Int(n) => Value::Float(n.to_f64_uc().sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64_uc().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
})
@ -921,9 +928,9 @@ pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let v = variance_inner(vm, iter, true)?;
Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Int(n) => Value::Float(n.to_f64_uc().sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64_uc().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
})
@ -957,10 +964,10 @@ pub fn median(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let (_, _, _) = hi.select_nth_unstable(0);
let m2 = vals.swap_remove(count / 2);
let m1 = vals.swap_remove(count / 2 - 1);
(m1.0 + m2.0)? / Value::Int(2)
(m1.0 + m2.0)? / Value::from(2)
} else {
let (_, _, _) = vals.select_nth_unstable(count / 2);
let m = vals.swap_remove(count / 2);
m.0 / Value::Int(1)
m.0 / Value::from(1)
}
}

View file

@ -9,9 +9,10 @@ pub mod collection;
pub mod exception;
pub mod file;
pub mod format;
pub mod ints;
pub mod io;
pub mod iter;
pub mod num;
pub mod math;
pub mod string;
pub mod value;
@ -27,7 +28,8 @@ pub fn load_all(vm: &mut Vm) {
format::load(vm);
io::load(vm);
iter::load(vm);
num::load(vm);
math::load(vm);
ints::load(vm);
string::load(vm);
value::load(vm);

View file

@ -1,14 +1,12 @@
use std::cmp::Ordering;
use lazy_static::lazy_static;
use talc_lang::{
exception::Result,
parser::{parse_int, to_lstring_radix},
number::{Complex, Int, Ratio},
prelude::*,
symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{ops::RatioExt, Complex64, Value},
value::Value,
vm::Vm,
vmcalliter,
};
use talc_macros::native_func;
@ -25,8 +23,8 @@ lazy_static! {
#[inline]
fn to_floaty(v: Value) -> Value {
match v {
Value::Int(v) => Value::Float(v as f64),
Value::Ratio(v) => Value::Float(v.to_f64()),
Value::Int(v) => Value::Float(v.to_f64_uc()),
Value::Ratio(v) => Value::Float(v.to_f64_uc()),
Value::Float(v) => Value::Float(v),
Value::Complex(v) => Value::Complex(v),
v => v,
@ -36,8 +34,8 @@ fn to_floaty(v: Value) -> Value {
#[inline]
fn to_complex(v: Value) -> Value {
match v {
Value::Int(v) => Value::Complex((v as f64).into()),
Value::Ratio(v) => Value::Complex(v.to_f64().into()),
Value::Int(v) => Value::Complex(v.to_f64_uc().into()),
Value::Ratio(v) => Value::Complex(v.to_f64_uc().into()),
Value::Float(v) => Value::Complex(v.into()),
Value::Complex(v) => Value::Complex(v),
v => v,
@ -57,8 +55,8 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("inf", (f64::INFINITY).into());
vm.set_global_name("NaN", (f64::NAN).into());
vm.set_global_name("infi", Complex64::new(0.0, f64::INFINITY).into());
vm.set_global_name("NaNi", Complex64::new(0.0, f64::NAN).into());
vm.set_global_name("infi", Complex::new(0.0, f64::INFINITY).into());
vm.set_global_name("NaNi", Complex::new(0.0, f64::NAN).into());
vm.set_global_name("bin", bin().into());
vm.set_global_name("sex", sex().into());
@ -68,15 +66,6 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("to_radix_upper", to_radix_upper().into());
vm.set_global_name("from_radix", from_radix().into());
vm.set_global_name("gcd", gcd().into());
vm.set_global_name("gcdn", gcdn().into());
vm.set_global_name("lcm", lcm().into());
vm.set_global_name("lcmn", lcmn().into());
vm.set_global_name("isqrt", isqrt().into());
vm.set_global_name("isprime", isprime().into());
vm.set_global_name("factors", factors().into());
vm.set_global_name("totient", totient().into());
vm.set_global_name("min", min().into());
vm.set_global_name("max", max().into());
vm.set_global_name("floor", floor().into());
@ -137,7 +126,7 @@ pub fn bin(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}")
};
Ok(to_lstring_radix(x, 2, false).into())
Ok(Value::from(x.to_str_radix_case(2, false)))
}
#[native_func(1)]
@ -146,7 +135,7 @@ pub fn sex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "sex expected integer argument, got {x:#}")
};
Ok(to_lstring_radix(x, 6, false).into())
Ok(Value::from(x.to_str_radix_case(6, false)))
}
#[native_func(1)]
@ -155,7 +144,7 @@ pub fn oct(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "oct expected integer argument, got {x:#}")
};
Ok(to_lstring_radix(x, 8, false).into())
Ok(Value::from(x.to_str_radix_case(8, false)))
}
#[native_func(1)]
@ -164,7 +153,7 @@ pub fn hex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "hex expected integer argument, got {x:#}")
};
Ok(to_lstring_radix(x, 16, false).into())
Ok(Value::from(x.to_str_radix_case(16, false)))
}
#[native_func(2)]
@ -176,10 +165,13 @@ pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
"to_radix expected integer arguments, got {x:#} and {radix:#}"
)
};
if *radix < 2 || *radix > 36 {
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 0..=36")
let Some(radix) = radix.to_u32() else {
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 2..=36")
};
if !(2..=36).contains(&radix) {
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 2..=36")
}
Ok(to_lstring_radix(*x, *radix as u32, false).into())
Ok(Value::from(x.to_str_radix_case(radix, false)))
}
#[native_func(2)]
@ -191,13 +183,19 @@ pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
"to_radix_upper expected integer arguments, got {x:#} and {radix:#}"
)
};
if *radix < 2 || *radix > 36 {
let Some(radix) = radix.to_u32() else {
throw!(
*SYM_VALUE_ERROR,
"to_radix_upper expected radix in range 0..=36"
"to_radix_upper expected radix in range 2..=36"
)
};
if !(2..=36).contains(&radix) {
throw!(
*SYM_VALUE_ERROR,
"to_radix_upper expected radix in range 2..=36"
)
}
Ok(to_lstring_radix(*x, *radix as u32, true).into())
Ok(Value::from(x.to_str_radix_case(radix, true)))
}
#[native_func(2)]
@ -209,285 +207,32 @@ pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
"from_radix expected string and integer arguments, got {s:#} and {radix:#}"
)
};
if *radix < 2 || *radix > 36 {
let Some(radix) = radix.to_u32() else {
throw!(
*SYM_VALUE_ERROR,
"from_radix expected radix in range 0..=36"
"from_radix expected radix in range 2..=36"
)
};
if !(2..=36).contains(&radix) {
throw!(
*SYM_VALUE_ERROR,
"from_radix expected radix in range 2..=36"
)
}
match parse_int(s.as_ref(), *radix as u32) {
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "from_radix: string has invalid UTF-8",)
};
match Int::from_str_radix(s, radix) {
Ok(v) => Ok(v.into()),
Err(_) => throw!(
*SYM_VALUE_ERROR,
"string was not a valid integer in given radix"
"from_radix: string {:#} is not a valid integer in radix {}",
s,
radix
),
}
}
//
// integer operations
//
fn isqrt_inner(mut n: i64) -> i64 {
assert!(n >= 0, "isqrt input should be nonnegative");
if n < 2 {
return n
}
let mut c = 0;
let mut d = 1 << 62;
while d > n {
d >>= 2;
}
while d != 0 {
if n >= c + d {
n -= c + d;
c = (c >> 1) + d;
} else {
c >>= 1;
}
d >>= 2;
}
c
}
pub fn gcd_inner(a: i64, b: i64) -> i64 {
let (mut a, mut b) = (a.abs(), b.abs());
if a == 0 {
return b
}
if b == 0 {
return a
}
let az = a.trailing_zeros();
a >>= az;
let bz = b.trailing_zeros();
b >>= bz;
let z = az.min(bz);
loop {
if a > b {
std::mem::swap(&mut a, &mut b);
}
b -= a;
if b == 0 {
return a << z
}
b >>= b.trailing_zeros();
}
}
#[native_func(1)]
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(
*SYM_TYPE_ERROR,
"isqrt expected integer argument, got {x:#}"
)
};
if x < 0 {
throw!(*SYM_VALUE_ERROR, "isqrt: argument must be positive")
}
Ok(isqrt_inner(x).into())
}
#[native_func(1)]
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(
*SYM_TYPE_ERROR,
"isprime expected integer argument, got {x:#}"
)
};
if x < 2 {
return Ok(false.into())
}
for p in [2, 3, 5, 7] {
if x % p == 0 {
return Ok((x == p).into())
}
}
if x < 11 {
return Ok(false.into())
}
let lim = isqrt_inner(x);
let mut i = 12;
while i <= lim + 1 {
if x % (i - 1) == 0 {
return Ok(false.into())
}
if x % (i + 1) == 0 {
return Ok(false.into())
}
i += 6;
}
Ok(true.into())
}
#[native_func(2)]
pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
};
let Value::Int(y) = y else {
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {y:#}")
};
Ok(gcd_inner(x, y).into())
}
#[native_func(1)]
pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?;
let mut g = 0;
while let Some(a) = vmcalliter!(vm; args.clone())? {
let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}")
};
g = gcd_inner(g, a);
}
Ok(g.into())
}
#[native_func(2)]
pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
};
let Value::Int(y) = y else {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}")
};
let g = gcd_inner(x, y);
if g == 0 {
Ok(Value::from(0))
} else {
(Value::from(x) / Value::from(g))? * Value::from(y)
}
}
#[native_func(1)]
pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?;
let mut l = 1;
while let Some(a) = vmcalliter!(vm; args.clone())? {
let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
};
let g = gcd_inner(l, a);
if g == 0 {
return Ok(Value::from(0))
};
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?;
let Value::Int(new_l) = new_l else {
unreachable!("int//int * int != int")
};
l = new_l;
}
Ok(Value::from(l))
}
#[native_func(1)]
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(mut x) = x else {
throw!(
*SYM_TYPE_ERROR,
"factors expected integer argument, got {x:#}"
)
};
let mut factors = Vec::new();
if x <= 1 {
return Ok(factors.into())
}
while x & 1 == 0 {
x >>= 1;
factors.push(Value::Int(2));
}
while x % 3 == 0 {
x /= 3;
factors.push(Value::Int(3));
}
let mut i = 5;
while x >= i * i {
while x % i == 0 {
x /= i;
factors.push(Value::Int(i));
}
i += 2;
while x % i == 0 {
x /= i;
factors.push(Value::Int(i));
}
i += 4;
}
if x > 1 {
factors.push(Value::Int(x));
}
Ok(factors.into())
}
fn totient_prime(n: &mut u64, p: u64) -> u64 {
if *n % p != 0 {
return 1
}
*n /= p;
let mut v = p - 1;
while *n % p == 0 {
*n /= p;
v *= p;
}
v
}
#[native_func(1)]
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(
*SYM_TYPE_ERROR,
"totient expected integer argument, got {x:#}"
)
};
if x <= 1 {
return Ok(1.into())
}
let mut x = x as u64;
let mut totient = 1;
if x & 1 == 0 {
x >>= 1;
}
while x & 1 == 0 {
x >>= 1;
totient <<= 1;
}
totient *= totient_prime(&mut x, 3);
let mut i = 5;
while x >= i * i {
totient *= totient_prime(&mut x, i);
i += 2;
totient *= totient_prime(&mut x, i);
i += 4;
}
if x > 1 {
totient *= x - 1;
}
Ok((totient as i64).into())
}
//
// numeric operations
//
@ -518,7 +263,7 @@ pub fn floor(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match x {
Value::Int(x) => Ok(Value::Int(x)),
Value::Float(x) => Ok(Value::Float(x.floor())),
Value::Ratio(x) => Ok(Value::Ratio(x.floor())),
Value::Ratio(x) => Ok(Value::Ratio(x.floor().into())),
x => throw!(*SYM_TYPE_ERROR, "floor expected real argument, got {x:#}"),
}
}
@ -529,7 +274,7 @@ pub fn ceil(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match x {
Value::Int(x) => Ok(Value::Int(x)),
Value::Float(x) => Ok(Value::Float(x.ceil())),
Value::Ratio(x) => Ok(Value::Ratio(x.ceil())),
Value::Ratio(x) => Ok(Value::Ratio(x.ceil().into())),
x => throw!(*SYM_TYPE_ERROR, "ceil expected real argument, got {x:#}"),
}
}
@ -540,7 +285,7 @@ pub fn round(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match x {
Value::Int(x) => Ok(Value::Int(x)),
Value::Float(x) => Ok(Value::Float(x.round())),
Value::Ratio(x) => Ok(Value::Ratio(x.round())),
Value::Ratio(x) => Ok(Value::Ratio(x.round().into())),
x => throw!(*SYM_TYPE_ERROR, "round expected real argument, got {x:#}"),
}
}
@ -551,7 +296,7 @@ pub fn trunc(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match x {
Value::Int(x) => Ok(Value::Int(x)),
Value::Float(x) => Ok(Value::Float(x.trunc())),
Value::Ratio(x) => Ok(Value::Ratio(x.trunc())),
Value::Ratio(x) => Ok(Value::Ratio(x.trunc().into())),
x => throw!(*SYM_TYPE_ERROR, "trunc expected real argument, got {x:#}"),
}
}
@ -560,9 +305,9 @@ pub fn trunc(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn fract(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
match x {
Value::Int(_) => Ok(Value::Int(0)),
Value::Int(_) => Ok(Value::from(0)),
Value::Float(x) => Ok(Value::Float(x.fract())),
Value::Ratio(x) => Ok(Value::Ratio(x.fract())),
Value::Ratio(x) => Ok(Value::Ratio(x.fract().into())),
x => throw!(*SYM_TYPE_ERROR, "fract expected real argument, got {x:#}"),
}
}
@ -572,12 +317,8 @@ pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
match x {
Value::Int(x) => Ok(Value::Int(x.signum())),
Value::Float(x) => Ok(Value::Float(if x == 0.0 { 0.0 } else { x.signum() })),
Value::Ratio(x) => match x.cmp(&0.into()) {
Ordering::Greater => Ok(Value::Ratio(1.into())),
Ordering::Less => Ok(Value::Ratio((-1).into())),
Ordering::Equal => Ok(Value::Ratio(0.into())),
},
Value::Float(x) => Ok(Value::Float(if x == 0.0 { x } else { x.signum() })),
Value::Ratio(x) => Ok(Value::Ratio(x.signum().into())),
x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"),
}
}
@ -666,7 +407,7 @@ pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
"float_to_bits expected float argument, got {val:#}"
)
};
Ok(Value::Int(f.to_bits() as i64))
Ok(Value::Int(Int::from_u64(f.to_bits()).unwrap()))
}
#[native_func(1)]
@ -678,7 +419,13 @@ pub fn float_of_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
"float_of_bits expected integer argument, got {val:#}"
)
};
Ok(Value::Float(f64::from_bits(i as u64)))
let Some(v) = i.to_u64() else {
throw!(
*SYM_TYPE_ERROR,
"float_of_bits expected integer in 0..2^64, got {i}"
)
};
Ok(Value::Float(f64::from_bits(v)))
}
//
@ -690,7 +437,7 @@ pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args);
match v {
Value::Int(x) => Ok(Value::Int(x)),
Value::Ratio(x) => Ok(Value::Int(*x.numer())),
Value::Ratio(x) => Ok(Value::from(Int::from(x.numer()))),
v => throw!(
*SYM_TYPE_ERROR,
"numer expected rational argument, got {v:#}"
@ -702,8 +449,8 @@ pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args);
match v {
Value::Int(_) => Ok(Value::Int(1)),
Value::Ratio(x) => Ok(Value::Int(*x.denom())),
Value::Int(_) => Ok(Value::from(1)),
Value::Ratio(x) => Ok(Value::from(Int::from(x.denom()))),
v => throw!(
*SYM_TYPE_ERROR,
"denom expected rational argument, got {v:#}"
@ -731,8 +478,8 @@ pub fn re(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn im(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args);
match to_complex(v) {
Value::Int(_) => Ok(Value::Int(0)),
Value::Ratio(_) => Ok(Value::Ratio(0.into())),
Value::Int(_) => Ok(Value::from(0)),
Value::Ratio(_) => Ok(Value::Ratio(Ratio::zero().into())),
Value::Float(_) => Ok(Value::Float(0.0)),
Value::Complex(z) => Ok(Value::Float(z.im)),
v => throw!(*SYM_TYPE_ERROR, "im expected numeric argument, got {v:#}"),

View file

@ -1,9 +1,11 @@
use num_bigint::RandBigInt;
use rand::{seq::SliceRandom, Rng};
use talc_lang::{
exception::Result,
number::Int,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{range::RangeType, Value},
value::Value,
vm::Vm,
vmcalliter,
};
@ -46,14 +48,13 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
if r.is_empty() {
throw!(*SYM_VALUE_ERROR, "rand_in: empty range")
}
match r.ty {
RangeType::Open => {
Ok(Value::Int(rand::thread_rng().gen_range(r.start..r.stop)))
}
RangeType::Closed => {
Ok(Value::Int(rand::thread_rng().gen_range(r.start..=r.stop)))
}
RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"),
let (Some(s), Some(e)) = (&r.start, &r.end) else {
throw!(*SYM_VALUE_ERROR, "rand_in: infinite range")
};
if r.inclusive {
Ok(Int::from(rand::thread_rng().gen_bigint_range(s, &(e + 1))).into())
} else {
Ok(Int::from(rand::thread_rng().gen_bigint_range(s, e)).into())
}
}
col => {

View file

@ -1,6 +1,7 @@
use talc_lang::{
exception::Result,
lstring::LString,
prelude::*,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::Value,
@ -40,7 +41,7 @@ pub fn ord(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
if chars.next().is_some() {
throw!(*SYM_VALUE_ERROR, "argument to ord must have length 1")
};
Ok(Value::Int(c as u32 as i64))
Ok(Value::from(c as u32 as i64))
}
#[native_func(1)]
@ -49,7 +50,7 @@ pub fn chr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(i) = i else {
throw!(*SYM_TYPE_ERROR, "chr expected integer argument, got {i:#}")
};
let Ok(i) = u32::try_from(i) else {
let Some(i) = i.to_u32() else {
throw!(*SYM_VALUE_ERROR, "argument to chr is not a valid codepoint")
};
let Some(c) = char::from_u32(i) else {
@ -67,7 +68,7 @@ pub fn len_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
"len_bytes expected string argument, got {s:#}"
)
};
Ok(Value::Int(s.len() as i64))
Ok(Value::from(s.len() as i64))
}
#[native_func(1)]
@ -205,12 +206,20 @@ pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let bytes: Vec<u8> = b
.borrow()
.iter()
.map(|v| match v {
Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8),
_ => throw!(
.map(|v| {
let Value::Int(i) = v else {
throw!(
*SYM_TYPE_ERROR,
"str_of_bytes expected list of integers in 0..=255"
)
};
let Some(n) = i.to_u8() else {
throw!(
*SYM_VALUE_ERROR,
"str_of_bytes expected list of integers in 0..=255"
),
)
};
Ok(n)
})
.collect::<Result<Vec<u8>>>()?;
Ok(LString::from(bytes).into())

View file

@ -3,10 +3,12 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{
exception::{exception, Result},
lformat,
number::{Int, Ratio},
parser::{parse_float, parse_int},
prelude::*,
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{ops::RatioExt, HashValue, Rational64, Value},
value::{HashValue, Value},
vm::Vm,
};
use talc_macros::native_func;
@ -71,24 +73,22 @@ pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
(v, b"string") => Ok(Value::String(lformat!("{v}").into())),
(v, b"bool") => Ok(Value::Bool(v.truthy())),
(Value::Symbol(s), b"int") => Ok(Value::Int(s.id() as i64)),
(Value::Symbol(s), b"int") => Ok(Value::from(s.id() as i64)),
(Value::Int(x), b"ratio") => Ok(Value::Ratio(x.into())),
(Value::Int(x), b"float") => Ok(Value::Float(x as f64)),
(Value::Int(x), b"complex") => Ok(Value::Complex((x as f64).into())),
(Value::Ratio(x), b"int") => Ok(Value::Int(x.to_integer())),
(Value::Ratio(x), b"float") => Ok(Value::Float(x.to_f64())),
(Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64().into())),
(Value::Float(x), b"int") => Ok(Value::Int(x as i64)),
(Value::Float(x), b"ratio") => {
let r = Rational64::approximate_float(x).ok_or_else(|| {
exception!(
(Value::Int(x), b"ratio") => Ok(Value::from(Ratio::from_integer(x.into()))),
(Value::Int(x), b"float") => Ok(Value::Float(x.to_f64_uc())),
(Value::Int(x), b"complex") => Ok(Value::Complex(x.to_f64_uc().into())),
(Value::Ratio(x), b"int") => Ok(Value::from(x.to_integer())),
(Value::Ratio(x), b"float") => Ok(Value::Float(x.to_f64_uc())),
(Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64_uc().into())),
(Value::Float(x), b"int") => Ok(Value::from(Int::from_f64(x).unwrap_or_default())),
(Value::Float(x), b"ratio") => match Ratio::approximate_float(x) {
Some(v) => Ok(Value::from(v)),
None => throw!(
*SYM_VALUE_ERROR,
"float {x:?} could not be converted to ratio"
)
})?;
Ok(Value::Ratio(r))
}
"float {x} could not be converted to ratio"
),
},
(Value::Float(x), b"complex") => Ok(Value::Complex(x.into())),
(Value::String(s), b"int") => parse_int(s.as_ref(), 10)