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

View file

@ -4,7 +4,9 @@ use talc_lang::{
compiler::compile, compiler::compile,
lstring::LString, lstring::LString,
optimize::optimize, optimize::optimize,
parser, serial, parser,
prelude::*,
serial,
symbol::Symbol, symbol::Symbol,
value::{function::disasm_recursive, Value}, value::{function::disasm_recursive, Value},
vm::Vm, vm::Vm,
@ -112,7 +114,7 @@ fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
ExitCode::FAILURE ExitCode::FAILURE
} }
Ok(Value::Bool(false)) => 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, _ => ExitCode::SUCCESS,
} }
} }

View file

@ -4,8 +4,6 @@ version = "0.2.1"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
num-complex = "0.4" num = { version = "0.4", features = [] }
num-rational = { version = "0.4", default-features = false, features = [] }
num-traits = "0.2"
lazy_static = "1.5" lazy_static = "1.5"
unicode-ident = "1.0" 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::chunk::{Arg24, Catch, Chunk, Instruction as I};
use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind}; use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
use crate::parser::Pos; use crate::parser::Pos;
use crate::prelude::*;
use crate::symbol::{Symbol, SYM_REPL, SYM_SELF}; use crate::symbol::{Symbol, SYM_REPL, SYM_SELF};
use crate::throw; use crate::throw;
use crate::value::function::{FuncAttrs, Function}; use crate::value::function::{FuncAttrs, Function};
@ -766,22 +767,29 @@ impl<'a> Compiler<'a> {
match val { match val {
Value::Nil => { Value::Nil => {
self.emit(I::Nil); self.emit(I::Nil);
return
} }
Value::Bool(b) => { Value::Bool(b) => {
self.emit(I::Bool(*b)); self.emit(I::Bool(*b));
} return
Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i) => {
self.emit(I::Int(Arg24::from_i64(*i)));
} }
Value::Symbol(s) => { Value::Symbol(s) => {
self.emit(I::Symbol(Arg24::from_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()); let n = self.add_const(val.clone());
self.emit(I::Const(Arg24::from_usize(n))); self.emit(I::Const(Arg24::from_usize(n)));
} }
}
}
fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) -> Result<()> { fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) -> Result<()> {
match (&lv.kind, o) { match (&lv.kind, o) {

View file

@ -8,9 +8,19 @@ pub mod chunk;
pub mod compiler; pub mod compiler;
pub mod exception; pub mod exception;
pub mod lstring; pub mod lstring;
pub mod number;
pub mod optimize; pub mod optimize;
pub mod parser; pub mod parser;
pub mod serial; pub mod serial;
pub mod symbol; pub mod symbol;
pub mod value; pub mod value;
pub mod vm; 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 // 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, Neg,
Not, Not,
BitNot, BitNot,
RangeEndless, RangeFrom,
RangeTo,
RangeToIncl,
} }
#[derive(Debug)] #[derive(Debug)]

View file

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

View file

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

View file

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

View file

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

View file

@ -3,23 +3,19 @@ use std::borrow::Cow;
use std::io; use std::io;
use std::{cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, rc::Rc}; use std::{cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, rc::Rc};
pub use num_complex::Complex64; use num::{complex::Complex64, BigInt};
use num_complex::ComplexFloat;
pub use num_rational::Rational64;
use crate::exception::{throw, Exception}; use crate::exception::{throw, Exception};
use crate::lstring::{LStr, LString}; use crate::lstring::{LStr, LString};
use crate::number::{Int, Range, Ratio};
use crate::prelude::*;
use crate::symbol::{Symbol, SYM_HASH_ERROR}; use crate::symbol::{Symbol, SYM_HASH_ERROR};
use self::{ use self::function::{Function, NativeFunc};
function::{Function, NativeFunc},
range::{Range, RangeType},
};
pub mod function; pub mod function;
pub mod index; pub mod index;
pub mod ops; pub mod ops;
pub mod range;
type RcList = Rc<RefCell<Vec<Value>>>; type RcList = Rc<RefCell<Vec<Value>>>;
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>; type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
@ -31,11 +27,11 @@ pub enum Value {
Nil, Nil,
Bool(bool), Bool(bool),
Symbol(Symbol), Symbol(Symbol),
Range(Range), Range(Rc<Range>),
Int(i64), Int(Int),
Float(f64), Float(f64),
Ratio(Rational64), Ratio(Rc<Ratio>),
Complex(Complex64), Complex(Complex64),
Cell(Rc<RefCell<Value>>), Cell(Rc<RefCell<Value>>),
@ -113,11 +109,7 @@ impl Value {
} }
Ok(()) Ok(())
} }
Self::Range(r) => match r.ty { Self::Range(r) => write!(w, "{r}"),
RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
RangeType::Endless => write!(w, "{}..*", r.start),
},
Self::Int(i) => write!(w, "{i}"), Self::Int(i) => write!(w, "{i}"),
Self::Float(x) => write!(w, "{x:?}"), Self::Float(x) => write!(w, "{x:?}"),
Self::Ratio(r) => write!(w, "{}/{}", r.numer(), r.denom()), 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) => { ($ty:ty, $var:ident, into) => {
impl From<$ty> for Value { impl From<$ty> for Value {
fn from(value: $ty) -> Self { fn from(value: $ty) -> Self {
@ -412,10 +421,12 @@ macro_rules! impl_from {
impl_from!(bool, Bool, hash); impl_from!(bool, Bool, hash);
impl_from!(Symbol, Symbol, hash); impl_from!(Symbol, Symbol, hash);
impl_from!(Range, Range); impl_from!(Range, Range, rc);
impl_from!(i64, Int, hash); impl_from!(Int, Int, hash);
impl_from!(i64, Int, into);
impl_from!(BigInt, Int, into);
impl_from!(f64, Float); impl_from!(f64, Float);
impl_from!(Rational64, Ratio, hash); impl_from!(Ratio, Ratio, rchash);
impl_from!(Complex64, Complex); impl_from!(Complex64, Complex);
impl_from!(HashMap<HashValue, Value>, Table, rcref); impl_from!(HashMap<HashValue, Value>, Table, rcref);
impl_from!(Vec<Value>, List, rcref); impl_from!(Vec<Value>, List, rcref);
@ -439,3 +450,15 @@ impl<T: NativeValue + 'static> From<T> for Value {
Self::Native(Rc::new(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, rc::Rc,
}; };
use num_complex::{Complex64, ComplexFloat}; use num::complex::Complex64;
use num_rational::Rational64;
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
use crate::{ use crate::{
exception::{throw, Result}, exception::{throw, Result},
lstring::LString, lstring::LString,
number::{Int, Range, Ratio},
prelude::*,
symbol::{symbol, SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, symbol::{symbol, SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
value::range::RangeType,
vm::Vm, vm::Vm,
}; };
use super::{ use super::{
function::{FuncAttrs, NativeFunc}, function::{FuncAttrs, NativeFunc},
range::Range,
HashValue, Value, 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 { impl Value {
pub fn truthy(&self) -> bool { pub fn truthy(&self) -> bool {
match self { match self {
Value::Nil => false, Value::Nil => false,
Value::Bool(b) => *b, Value::Bool(b) => *b,
Value::Range(r) => matches!(r.len(), None | Some(1..)), Value::Range(r) => !r.is_empty(),
Value::Int(n) => *n != 0, Value::Int(n) => !n.is_zero(),
Value::Float(x) => *x != 0.0 && !x.is_nan(), 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::Complex(c) => !(c.re == 0.0 && c.im == 0.0 || c.is_nan()),
Value::String(s) => !s.is_empty(), Value::String(s) => !s.is_empty(),
Value::List(l) => l.borrow().len() > 0, Value::List(l) => l.borrow().len() > 0,
@ -59,17 +47,17 @@ impl Value {
pub fn promote(a: Value, b: Value) -> (Value, Value) { pub fn promote(a: Value, b: Value) -> (Value, Value) {
use Value as V; use Value as V;
match (&a, &b) { match (&a, &b) {
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b), (V::Int(x), V::Ratio(..)) => (V::Ratio(Ratio::from(x).into()), b),
(V::Int(x), V::Float(..)) => (V::Float(*x as f64), b), (V::Int(x), V::Float(..)) => (V::Float(x.to_f64_uc()), b),
(V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b), (V::Int(x), V::Complex(..)) => (V::Complex(x.to_f64_uc().into()), b),
(V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64()), b), (V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64_uc()), b),
(V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64().into()), 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::Float(x), V::Complex(..)) => (V::Complex(x.into()), b),
(V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())), (V::Ratio(..), V::Int(y)) => (a, V::Ratio(Ratio::from(y).into())),
(V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)), (V::Float(..), V::Int(y)) => (a, V::Float(y.to_f64_uc())),
(V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())), (V::Complex(..), V::Int(y)) => (a, V::Complex(y.to_f64_uc().into())),
(V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64())), (V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64_uc())),
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64().into())), (V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64_uc().into())),
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())), (V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
_ => (a, b), _ => (a, b),
} }
@ -84,20 +72,8 @@ impl Neg for Value {
fn neg(self) -> Self::Output { fn neg(self) -> Self::Output {
use Value as V; use Value as V;
match self { match self {
V::Int(x) => { V::Int(x) => Ok(V::Int(-x)),
if let Some(x) = x.checked_neg() { V::Ratio(x) => Ok(V::Ratio((-x.as_ref()).into())),
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::Float(x) => Ok(V::Float(-x)), V::Float(x) => Ok(V::Float(-x)),
V::Complex(x) => Ok(V::Complex(-x)), V::Complex(x) => Ok(V::Complex(-x)),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"), a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
@ -109,26 +85,8 @@ impl Value {
pub fn abs(self) -> Result<Self> { pub fn abs(self) -> Result<Self> {
use Value as V; use Value as V;
match self { match self {
V::Int(x) => { V::Int(x) => Ok(V::Int(x.abs())),
if let Some(x) = x.checked_abs() { V::Ratio(x) => Ok(V::Ratio(x.abs().into())),
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::Float(x) => Ok(V::Float(x.abs())), V::Float(x) => Ok(V::Float(x.abs())),
V::Complex(x) => Ok(V::Float(x.norm())), V::Complex(x) => Ok(V::Float(x.norm())),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"), a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
@ -141,7 +99,7 @@ impl Value {
///////////////////////// /////////////////////////
macro_rules! impl_value_arith { 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 { impl $trait<Value> for Value {
type Output = Result<Self>; type Output = Result<Self>;
@ -149,16 +107,8 @@ macro_rules! impl_value_arith {
use Value as V; use Value as V;
let (a, b) = promote(self, rhs); let (a, b) = promote(self, rhs);
match (&a, &b) { match (&a, &b) {
(V::Int(x), V::Int(y)) => if let Some(v) = x.$checked(y) { (V::Int(x), V::Int(y)) => Ok(V::Int(x $op y)),
Ok(V::Int(v)) (V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio((x.as_ref() $op y.as_ref()).into())),
} 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::Float(x), V::Float(y)) => Ok(V::Float(x $op y)), (V::Float(x), V::Float(y)) => Ok(V::Float(x $op y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(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) (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!(Add, add, +, "add");
impl_value_arith!(Sub, sub, checked_sub, -, "subtract"); impl_value_arith!(Sub, sub, -, "subtract");
impl_value_arith!(Mul, mul, checked_mul, *, "multiply"); impl_value_arith!(Mul, mul, *, "multiply");
impl Div<Value> for Value { impl Div<Value> for Value {
type Output = Result<Self>; type Output = Result<Self>;
@ -179,18 +129,16 @@ impl Div<Value> for Value {
use Value as V; use Value as V;
let (a, b) = promote(self, rhs); let (a, b) = promote(self, rhs);
match (&a, &b) { match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer division by 0"), (V::Int(_), V::Int(y)) if y.is_zero() => {
(V::Int(x), V::Int(y)) => Ok(Value::Ratio((*x, *y).into())), 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() => { (V::Ratio(_), V::Ratio(r)) if r.is_zero() => {
throw!(*SYM_VALUE_ERROR, "rational division by 0") throw!(*SYM_VALUE_ERROR, "rational division by 0")
} }
(V::Ratio(x), V::Ratio(y)) => { (V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio((x.as_ref() / y.as_ref()).into())),
if let Some(v) = x.checked_div(y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when dividing {a} and {b}")
}
}
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)), (V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)), (V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}"), (l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}"),
@ -202,54 +150,20 @@ impl Div<Value> for Value {
// modulo and integer division // // 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 { impl Value {
pub fn modulo(self, rhs: Value) -> Result<Self> { pub fn modulo(self, rhs: Value) -> Result<Self> {
use Value as V; use Value as V;
let (a, b) = promote(self, rhs); let (a, b) = promote(self, rhs);
match (&a, &b) { match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer modulo by 0"), (V::Int(_), V::Int(y)) if y.is_zero() => {
(V::Int(x), V::Int(y)) => { throw!(*SYM_VALUE_ERROR, "integer modulo by 0")
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(x), V::Int(y)) => Ok(V::Int(x.rem_euclid(y))),
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => { (V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "rational modulo by 0") throw!(*SYM_VALUE_ERROR, "rational modulo by 0")
} }
(V::Ratio(x), V::Ratio(y)) => { (V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x.rem_euclid(y).into())),
if let Some(v) = ratio_checked_rem_euclid(x, y) { (V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(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::Complex(x), V::Complex(y)) => { (V::Complex(x), V::Complex(y)) => {
let n = x / y; let n = x / y;
let n = Complex64::new(n.re().floor(), n.im().floor()); let n = Complex64::new(n.re().floor(), n.im().floor());
@ -263,33 +177,15 @@ impl Value {
use Value as V; use Value as V;
let (a, b) = promote(self, rhs); let (a, b) = promote(self, rhs);
match (&a, &b) { match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer divsion by 0"), (V::Int(_), V::Int(y)) if y.is_zero() => {
(V::Int(x), V::Int(y)) => { throw!(*SYM_VALUE_ERROR, "integer divsion by 0")
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(x), V::Int(y)) => Ok(Value::from(x.div_euclid(y))),
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => { (V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "integer division by 0") throw!(*SYM_VALUE_ERROR, "integer division by 0")
} }
(V::Ratio(x), V::Ratio(y)) => { (V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x.rem_euclid(y).into())),
if let Some(v) = ratio_checked_div_euclid(x, y) { (V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(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::Complex(x), V::Complex(y)) => { (V::Complex(x), V::Complex(y)) => {
let n = x / y; let n = x / y;
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor()))) Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
@ -304,29 +200,11 @@ impl Value {
////////////////////// //////////////////////
#[inline] #[inline]
fn ipow(n: i64, p: u64) -> Option<i64> { fn rpow(n: &Int, d: &Int, p: &Int) -> Option<Ratio> {
match (n, p) { let (sign, p) = (p.isignum(), p.abs());
(0, 0) => None, match sign {
(0, _) => Some(0), 0.. => Some(Ratio::new(n.ipow(&p)?.into(), d.ipow(&p)?.into())),
(_, 0) => Some(1), _ => Some(Ratio::new(d.ipow(&p)?.into(), n.ipow(&p)?.into())),
(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)?)),
} }
} }
@ -334,10 +212,10 @@ impl Value {
pub fn pow(self, rhs: Value) -> Result<Self> { pub fn pow(self, rhs: Value) -> Result<Self> {
use Value as V; use Value as V;
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) { 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") 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!( throw!(
*SYM_VALUE_ERROR, *SYM_VALUE_ERROR,
"overflow when raising {self} to the power {rhs}" "overflow when raising {self} to the power {rhs}"
@ -347,11 +225,11 @@ impl Value {
} }
let (a, b) = promote(self, rhs); let (a, b) = promote(self, rhs);
match (&a, &b) { 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") throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power")
} }
(V::Int(x), V::Int(y @ 0..)) => { (V::Int(x), V::Int(y)) if !y.is_negative() => {
if let Some(v) = ipow(*x, *y as u64) { if let Some(v) = x.ipow(y) {
Ok(V::Int(v)) Ok(V::Int(v))
} else { } else {
throw!( throw!(
@ -361,7 +239,7 @@ impl Value {
} }
} }
(V::Int(x), V::Int(y)) => { (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())) Ok(V::Ratio(v.into()))
} else { } else {
throw!( 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::Float(x), V::Float(y)) => Ok(V::Float(x.powf(*y))),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x.powc(*y))), (V::Complex(x), V::Complex(y)) => Ok(V::Complex(x.powc(*y))),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}"), (l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}"),
@ -389,7 +267,16 @@ impl Shl<Value> for Value {
use Value as V; use Value as V;
match (self, rhs) { match (self, rhs) {
(V::Int(a), V::Int(b)) => { (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:#}"), (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; use Value as V;
match (self, rhs) { match (self, rhs) {
(V::Int(a), V::Int(b)) => { (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:#}"), (l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
} }
@ -464,7 +357,6 @@ impl Not for Value {
impl PartialEq for Value { impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
use super::range::RangeType as Rty;
use Value as V; use Value as V;
match (self, other) { match (self, other) {
(V::Nil, V::Nil) => true, (V::Nil, V::Nil) => true,
@ -473,31 +365,23 @@ impl PartialEq for Value {
(V::Ratio(a), V::Ratio(b)) => *a == *b, (V::Ratio(a), V::Ratio(b)) => *a == *b,
(V::Float(a), V::Float(b)) => *a == *b, (V::Float(a), V::Float(b)) => *a == *b,
(V::Complex(a), V::Complex(b)) => *a == *b, (V::Complex(a), V::Complex(b)) => *a == *b,
(V::Int(a), V::Ratio(b)) => Rational64::from(*a) == *b, (V::Int(a), V::Ratio(b)) => b.is_integer() && a.to_big().as_ref() == b.numer(),
(V::Ratio(a), V::Int(b)) => *a == Rational64::from(*b), (V::Ratio(a), V::Int(b)) => a.is_integer() && a.numer() == b.to_big().as_ref(),
(V::Int(a), V::Float(b)) => *a as f64 == *b, (V::Int(a), V::Float(b)) => a.to_f64_uc() == *b,
(V::Float(a), V::Int(b)) => *a == *b as f64, (V::Float(a), V::Int(b)) => *a == b.to_f64_uc(),
(V::Int(a), V::Complex(b)) => Complex64::from(*a as f64) == *b, (V::Int(a), V::Complex(b)) => Complex64::from(a.to_f64_uc()) == *b,
(V::Complex(a), V::Int(b)) => *a == Complex64::from(*b as f64), (V::Complex(a), V::Int(b)) => *a == Complex64::from(b.to_f64_uc()),
(V::Ratio(a), V::Float(b)) => a.to_f64() == *b, (V::Ratio(a), V::Float(b)) => a.to_f64_uc() == *b,
(V::Float(a), V::Ratio(b)) => *a == b.to_f64(), (V::Float(a), V::Ratio(b)) => *a == b.to_f64_uc(),
(V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64()) == *b, (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()), (V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64_uc()),
(V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b, (V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b,
(V::Complex(a), V::Float(b)) => *a == Complex64::from(*b), (V::Complex(a), V::Float(b)) => *a == Complex64::from(*b),
(V::String(a), V::String(b)) => *a == *b, (V::String(a), V::String(b)) => *a == *b,
(V::List(a), V::List(b)) => *a.borrow() == *b.borrow(), (V::List(a), V::List(b)) => *a.borrow() == *b.borrow(),
(V::Symbol(a), V::Symbol(b)) => a == b, (V::Symbol(a), V::Symbol(b)) => a == b,
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(), (V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) { (V::Range(a), V::Range(b)) => a.as_ref() == b.as_ref(),
(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::Native(a), b) => a.partial_eq(b), (V::Native(a), b) => a.partial_eq(b),
(a, V::Native(b)) => b.partial_eq(a), (a, V::Native(b)) => b.partial_eq(a),
_ => false, _ => false,
@ -514,12 +398,12 @@ impl PartialOrd for Value {
(V::Int(a), V::Int(b)) => a.partial_cmp(b), (V::Int(a), V::Int(b)) => a.partial_cmp(b),
(V::Ratio(a), V::Ratio(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::Float(a), V::Float(b)) => a.partial_cmp(b),
(V::Int(a), V::Ratio(b)) => Rational64::from(*a).partial_cmp(b), (V::Int(a), V::Ratio(b)) => Ratio::from(a).partial_cmp(b),
(V::Ratio(a), V::Int(b)) => a.partial_cmp(&Rational64::from(*b)), (V::Ratio(a), V::Int(b)) => a.as_ref().partial_cmp(&Ratio::from(b)),
(V::Int(a), V::Float(b)) => (*a as f64).partial_cmp(b), (V::Int(a), V::Float(b)) => a.to_f64_uc().partial_cmp(b),
(V::Float(a), V::Int(b)) => a.partial_cmp(&(*b as f64)), (V::Float(a), V::Int(b)) => a.partial_cmp(&b.to_f64_uc()),
(V::Ratio(a), V::Float(b)) => a.to_f64().partial_cmp(b), (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()), (V::Float(a), V::Ratio(b)) => a.partial_cmp(&b.to_f64_uc()),
(V::String(a), V::String(b)) => a.partial_cmp(b), (V::String(a), V::String(b)) => a.partial_cmp(b),
(V::List(a), V::List(b)) => a.borrow().partial_cmp(&*b.borrow()), (V::List(a), V::List(b)) => a.borrow().partial_cmp(&*b.borrow()),
(V::Symbol(a), V::Symbol(b)) => a.partial_cmp(b), (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> { pub fn range(&self, other: &Self) -> Result<Self> {
if let (Value::Int(start), Value::Int(stop)) = (self, other) { if let (Value::Int(start), Value::Int(end)) = (self, other) {
let ty = if closed { Ok(Range::range(start, end).into())
RangeType::Closed
} else {
RangeType::Open
};
Ok(Range {
start: *start,
stop: *stop,
ty,
}
.into())
} else { } else {
throw!( throw!(
*SYM_TYPE_ERROR, *SYM_TYPE_ERROR,
@ -595,19 +469,48 @@ impl Value {
} }
} }
pub fn range_endless(&self) -> Result<Self> { pub fn range_incl(&self, other: &Self) -> Result<Self> {
if let Value::Int(start) = self { if let (Value::Int(start), Value::Int(end)) = (self, other) {
Ok(Range { Ok(Range::range_incl(start, end).into())
start: *start,
stop: 0,
ty: RangeType::Endless,
}
.into())
} else { } 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 { pub fn to_cell(self) -> Self {
Value::Cell(Rc::new(RefCell::new(self))) Value::Cell(Rc::new(RefCell::new(self)))
} }
@ -630,7 +533,10 @@ impl Value {
match self { match self {
Self::Function(_) | Self::NativeFunc(_) => Ok(self), Self::Function(_) | Self::NativeFunc(_) => Ok(self),
Self::Range(range) => { 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> { let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
Ok(Value::iter_pack( Ok(Value::iter_pack(
range_iter.borrow_mut().next().map(Value::from), 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::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::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::Le => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Greater)),
BinaryOp::Range => a.range(&b, false), BinaryOp::Range => a.range(&b),
BinaryOp::RangeIncl => a.range(&b, true), BinaryOp::RangeIncl => a.range_incl(&b),
BinaryOp::Concat => a.concat(&b), BinaryOp::Concat => a.concat(&b),
BinaryOp::Append => a.append(b), BinaryOp::Append => a.append(b),
} }
@ -86,7 +86,9 @@ pub fn unary_op(o: UnaryOp, a: Value) -> Result<Value> {
UnaryOp::Neg => -a, UnaryOp::Neg => -a,
UnaryOp::Not => Ok(Value::Bool(!a.truthy())), UnaryOp::Not => Ok(Value::Bool(!a.truthy())),
UnaryOp::BitNot => a.not(), 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)); self.push(Value::Symbol(sym));
} }
// [] -> [n] // [] -> [n]
I::Int(n) => self.push(Value::Int(i64::from(n))), I::Int(n) => self.push(Value::from(i64::from(n))),
// [x] -> [x,x] // [x] -> [x,x]
I::Dup => self.push(self.stack[self.stack.len() - 1].clone()), I::Dup => self.push(self.stack[self.stack.len() - 1].clone()),
// [x,y] -> [x,y,x,y] // [x,y] -> [x,y,x,y]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,9 +2,10 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{ use talc_lang::{
exception::Result, exception::Result,
prelude::*,
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw, throw,
value::{function::NativeFunc, ops::RatioExt, range::RangeType, HashValue, Value}, value::{function::NativeFunc, HashValue, Value},
vm::Vm, vm::Vm,
vmcall, vmcalliter, vmcall, vmcalliter,
}; };
@ -186,7 +187,7 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(count) = count else { let Value::Int(count) = count else {
throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}") throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}")
}; };
let Ok(count) = count.try_into() else { let Some(count) = count.to_usize() else {
throw!( throw!(
*SYM_VALUE_ERROR, *SYM_VALUE_ERROR,
"take expected nonnegative integer, got {count:#}" "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 { let Value::Int(count) = count else {
throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}") throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}")
}; };
let Ok(count) = count.try_into() else { let Some(count) = count.to_usize() else {
throw!( throw!(
*SYM_VALUE_ERROR, *SYM_VALUE_ERROR,
"count expected nonnegative integer, got {count:#}" "count expected small nonnegative integer, got {count:#}"
) )
}; };
let iter = iter.to_iter_function()?; 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 { let Value::Int(by) = by else {
throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}") throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}")
}; };
if by <= 0 { let Some(by) = by.to_usize() else {
throw!( throw!(
*SYM_VALUE_ERROR, *SYM_VALUE_ERROR,
"step expected positive integer, got {by:#}" "step expected small positive integer, got {by:#}"
) )
} };
let state = RefCell::new(Step::First); let state = RefCell::new(Step::First);
let f = move |vm: &mut Vm, _| match state.take() { 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)) Ok(Value::iter_pack(res))
} }
Step::Going => { Step::Going => {
for _ in 0..(by - 1) { for _ in 1..by {
if vmcall!(vm; iter.clone())? == Value::Nil { if vmcall!(vm; iter.clone())? == Value::Nil {
return Ok(Value::iter_pack(None)) 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::String(s) => return Ok((s.chars().count() as i64).into()),
Value::List(l) => return Ok((l.borrow().len() 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::Table(t) => return Ok((t.borrow().len() as i64).into()),
Value::Range(r) if r.ty != RangeType::Endless => { Value::Range(ref r) => {
return Ok((r.len().unwrap() as i64).into()) 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] = unpack_args!(args);
let iter = iter.to_iter_function()?; 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())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
result = (result + value)?; 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] = unpack_args!(args);
let iter = iter.to_iter_function()?; 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())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
result = (result * value)?; 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") 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 { for _ in 0..n {
if vmcalliter!(vm; iter.clone())?.is_none() { if vmcalliter!(vm; iter.clone())?.is_none() {
return Ok(Value::Nil) 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())? { while let Some(v) = vmcalliter!(vm; iter.clone())? {
let hv = v.try_into()?; let hv = v.try_into()?;
map.entry(hv) map.entry(hv)
.and_modify(|v: &mut Value| *v = (v.clone() + Value::Int(1)).unwrap()) .and_modify(|v: &mut Value| *v = (v.clone() + Value::from(1)).unwrap())
.or_insert(Value::Int(1)); .or_insert(Value::from(1));
} }
Ok(map.into()) 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 iter = iter.to_iter_function()?;
let mut sum = Value::Float(0.0); 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())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
sum = (sum + value)?; sum = (sum + value)?;
count = (count + Value::from(1))?; 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; let mut k = 1;
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
let old_m = m.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)?)?)?; s = (s + ((value.clone() - m.clone())? * (value - old_m)?)?)?;
k += 1; k += 1;
} }
s / Value::Int(k - if pop { 1 } else { 2 }) s / Value::from(k - if pop { 1 } else { 2 })
} }
#[native_func(1)] #[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)?; let v = variance_inner(vm, iter, false)?;
Ok(match v { 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::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()), Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"), 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)?; let v = variance_inner(vm, iter, true)?;
Ok(match v { 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::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()), Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"), 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 (_, _, _) = hi.select_nth_unstable(0);
let m2 = vals.swap_remove(count / 2); let m2 = vals.swap_remove(count / 2);
let m1 = vals.swap_remove(count / 2 - 1); let m1 = vals.swap_remove(count / 2 - 1);
(m1.0 + m2.0)? / Value::Int(2) (m1.0 + m2.0)? / Value::from(2)
} else { } else {
let (_, _, _) = vals.select_nth_unstable(count / 2); let (_, _, _) = vals.select_nth_unstable(count / 2);
let m = vals.swap_remove(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 exception;
pub mod file; pub mod file;
pub mod format; pub mod format;
pub mod ints;
pub mod io; pub mod io;
pub mod iter; pub mod iter;
pub mod num; pub mod math;
pub mod string; pub mod string;
pub mod value; pub mod value;
@ -27,7 +28,8 @@ pub fn load_all(vm: &mut Vm) {
format::load(vm); format::load(vm);
io::load(vm); io::load(vm);
iter::load(vm); iter::load(vm);
num::load(vm); math::load(vm);
ints::load(vm);
string::load(vm); string::load(vm);
value::load(vm); value::load(vm);

View file

@ -1,14 +1,12 @@
use std::cmp::Ordering;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use talc_lang::{ use talc_lang::{
exception::Result, exception::Result,
parser::{parse_int, to_lstring_radix}, number::{Complex, Int, Ratio},
prelude::*,
symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw, throw,
value::{ops::RatioExt, Complex64, Value}, value::Value,
vm::Vm, vm::Vm,
vmcalliter,
}; };
use talc_macros::native_func; use talc_macros::native_func;
@ -25,8 +23,8 @@ lazy_static! {
#[inline] #[inline]
fn to_floaty(v: Value) -> Value { fn to_floaty(v: Value) -> Value {
match v { match v {
Value::Int(v) => Value::Float(v as f64), Value::Int(v) => Value::Float(v.to_f64_uc()),
Value::Ratio(v) => Value::Float(v.to_f64()), Value::Ratio(v) => Value::Float(v.to_f64_uc()),
Value::Float(v) => Value::Float(v), Value::Float(v) => Value::Float(v),
Value::Complex(v) => Value::Complex(v), Value::Complex(v) => Value::Complex(v),
v => v, v => v,
@ -36,8 +34,8 @@ fn to_floaty(v: Value) -> Value {
#[inline] #[inline]
fn to_complex(v: Value) -> Value { fn to_complex(v: Value) -> Value {
match v { match v {
Value::Int(v) => Value::Complex((v as f64).into()), Value::Int(v) => Value::Complex(v.to_f64_uc().into()),
Value::Ratio(v) => Value::Complex(v.to_f64().into()), Value::Ratio(v) => Value::Complex(v.to_f64_uc().into()),
Value::Float(v) => Value::Complex(v.into()), Value::Float(v) => Value::Complex(v.into()),
Value::Complex(v) => Value::Complex(v), Value::Complex(v) => Value::Complex(v),
v => 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("inf", (f64::INFINITY).into());
vm.set_global_name("NaN", (f64::NAN).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("infi", Complex::new(0.0, f64::INFINITY).into());
vm.set_global_name("NaNi", Complex64::new(0.0, f64::NAN).into()); vm.set_global_name("NaNi", Complex::new(0.0, f64::NAN).into());
vm.set_global_name("bin", bin().into()); vm.set_global_name("bin", bin().into());
vm.set_global_name("sex", sex().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("to_radix_upper", to_radix_upper().into());
vm.set_global_name("from_radix", from_radix().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("min", min().into());
vm.set_global_name("max", max().into()); vm.set_global_name("max", max().into());
vm.set_global_name("floor", floor().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 { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}") 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)] #[native_func(1)]
@ -146,7 +135,7 @@ pub fn sex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "sex expected integer argument, got {x:#}") 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)] #[native_func(1)]
@ -155,7 +144,7 @@ pub fn oct(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "oct expected integer argument, got {x:#}") 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)] #[native_func(1)]
@ -164,7 +153,7 @@ pub fn hex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "hex expected integer argument, got {x:#}") 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)] #[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:#}" "to_radix 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 expected radix in range 0..=36") 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)] #[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:#}" "to_radix_upper expected integer arguments, got {x:#} and {radix:#}"
) )
}; };
if *radix < 2 || *radix > 36 { let Some(radix) = radix.to_u32() else {
throw!( throw!(
*SYM_VALUE_ERROR, *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)] #[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:#}" "from_radix expected string and integer arguments, got {s:#} and {radix:#}"
) )
}; };
if *radix < 2 || *radix > 36 { let Some(radix) = radix.to_u32() else {
throw!( throw!(
*SYM_VALUE_ERROR, *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()), Ok(v) => Ok(v.into()),
Err(_) => throw!( Err(_) => throw!(
*SYM_VALUE_ERROR, *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 // numeric operations
// //
@ -518,7 +263,7 @@ pub fn floor(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match x { match x {
Value::Int(x) => Ok(Value::Int(x)), Value::Int(x) => Ok(Value::Int(x)),
Value::Float(x) => Ok(Value::Float(x.floor())), 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:#}"), 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 { match x {
Value::Int(x) => Ok(Value::Int(x)), Value::Int(x) => Ok(Value::Int(x)),
Value::Float(x) => Ok(Value::Float(x.ceil())), 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:#}"), 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 { match x {
Value::Int(x) => Ok(Value::Int(x)), Value::Int(x) => Ok(Value::Int(x)),
Value::Float(x) => Ok(Value::Float(x.round())), 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:#}"), 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 { match x {
Value::Int(x) => Ok(Value::Int(x)), Value::Int(x) => Ok(Value::Int(x)),
Value::Float(x) => Ok(Value::Float(x.trunc())), 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:#}"), 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> { pub fn fract(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
match x { match x {
Value::Int(_) => Ok(Value::Int(0)), Value::Int(_) => Ok(Value::from(0)),
Value::Float(x) => Ok(Value::Float(x.fract())), 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:#}"), 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); let [_, x] = unpack_args!(args);
match x { match x {
Value::Int(x) => Ok(Value::Int(x.signum())), Value::Int(x) => Ok(Value::Int(x.signum())),
Value::Float(x) => Ok(Value::Float(if x == 0.0 { 0.0 } else { x.signum() })), Value::Float(x) => Ok(Value::Float(if x == 0.0 { x } else { x.signum() })),
Value::Ratio(x) => match x.cmp(&0.into()) { Value::Ratio(x) => Ok(Value::Ratio(x.signum().into())),
Ordering::Greater => Ok(Value::Ratio(1.into())),
Ordering::Less => Ok(Value::Ratio((-1).into())),
Ordering::Equal => Ok(Value::Ratio(0.into())),
},
x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"), 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:#}" "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)] #[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:#}" "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); let [_, v] = unpack_args!(args);
match v { match v {
Value::Int(x) => Ok(Value::Int(x)), 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!( v => throw!(
*SYM_TYPE_ERROR, *SYM_TYPE_ERROR,
"numer expected rational argument, got {v:#}" "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> { pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args); let [_, v] = unpack_args!(args);
match v { match v {
Value::Int(_) => Ok(Value::Int(1)), Value::Int(_) => Ok(Value::from(1)),
Value::Ratio(x) => Ok(Value::Int(*x.denom())), Value::Ratio(x) => Ok(Value::from(Int::from(x.denom()))),
v => throw!( v => throw!(
*SYM_TYPE_ERROR, *SYM_TYPE_ERROR,
"denom expected rational argument, got {v:#}" "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> { pub fn im(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args); let [_, v] = unpack_args!(args);
match to_complex(v) { match to_complex(v) {
Value::Int(_) => Ok(Value::Int(0)), Value::Int(_) => Ok(Value::from(0)),
Value::Ratio(_) => Ok(Value::Ratio(0.into())), Value::Ratio(_) => Ok(Value::Ratio(Ratio::zero().into())),
Value::Float(_) => Ok(Value::Float(0.0)), Value::Float(_) => Ok(Value::Float(0.0)),
Value::Complex(z) => Ok(Value::Float(z.im)), Value::Complex(z) => Ok(Value::Float(z.im)),
v => throw!(*SYM_TYPE_ERROR, "im expected numeric argument, got {v:#}"), 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 rand::{seq::SliceRandom, Rng};
use talc_lang::{ use talc_lang::{
exception::Result, exception::Result,
number::Int,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw, throw,
value::{range::RangeType, Value}, value::Value,
vm::Vm, vm::Vm,
vmcalliter, vmcalliter,
}; };
@ -46,14 +48,13 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
if r.is_empty() { if r.is_empty() {
throw!(*SYM_VALUE_ERROR, "rand_in: empty range") throw!(*SYM_VALUE_ERROR, "rand_in: empty range")
} }
match r.ty { let (Some(s), Some(e)) = (&r.start, &r.end) else {
RangeType::Open => { throw!(*SYM_VALUE_ERROR, "rand_in: infinite range")
Ok(Value::Int(rand::thread_rng().gen_range(r.start..r.stop))) };
} if r.inclusive {
RangeType::Closed => { Ok(Int::from(rand::thread_rng().gen_bigint_range(s, &(e + 1))).into())
Ok(Value::Int(rand::thread_rng().gen_range(r.start..=r.stop))) } else {
} Ok(Int::from(rand::thread_rng().gen_bigint_range(s, e)).into())
RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"),
} }
} }
col => { col => {

View file

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

View file

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