embiggened integers
This commit is contained in:
parent
6748444c7f
commit
a1d39eaf58
32 changed files with 2443 additions and 997 deletions
52
Cargo.lock
generated
52
Cargo.lock
generated
|
@ -162,11 +162,11 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -177,9 +177,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.168"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
|
@ -232,6 +232,31 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.6"
|
||||
|
@ -250,12 +275,24 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
@ -434,9 +471,7 @@ name = "talc-lang"
|
|||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"num-complex",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"num",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
|
@ -453,6 +488,7 @@ name = "talc-std"
|
|||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"num-bigint",
|
||||
"rand",
|
||||
"regex",
|
||||
"talc-lang",
|
||||
|
|
|
@ -4,7 +4,9 @@ use talc_lang::{
|
|||
compiler::compile,
|
||||
lstring::LString,
|
||||
optimize::optimize,
|
||||
parser, serial,
|
||||
parser,
|
||||
prelude::*,
|
||||
serial,
|
||||
symbol::Symbol,
|
||||
value::{function::disasm_recursive, Value},
|
||||
vm::Vm,
|
||||
|
@ -112,7 +114,7 @@ fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
|
|||
ExitCode::FAILURE
|
||||
}
|
||||
Ok(Value::Bool(false)) => ExitCode::FAILURE,
|
||||
Ok(Value::Int(n)) => ExitCode::from(n as u8),
|
||||
Ok(Value::Int(n)) => ExitCode::from(n.to_u64().unwrap_or(1) as u8),
|
||||
_ => ExitCode::SUCCESS,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@ version = "0.2.1"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
num-complex = "0.4"
|
||||
num-rational = { version = "0.4", default-features = false, features = [] }
|
||||
num-traits = "0.2"
|
||||
num = { version = "0.4", features = [] }
|
||||
lazy_static = "1.5"
|
||||
unicode-ident = "1.0"
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::rc::Rc;
|
|||
use crate::chunk::{Arg24, Catch, Chunk, Instruction as I};
|
||||
use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
|
||||
use crate::parser::Pos;
|
||||
use crate::prelude::*;
|
||||
use crate::symbol::{Symbol, SYM_REPL, SYM_SELF};
|
||||
use crate::throw;
|
||||
use crate::value::function::{FuncAttrs, Function};
|
||||
|
@ -27,9 +28,9 @@ impl fmt::Display for CompileError {
|
|||
type Result<T> = std::result::Result<T, CompileError>;
|
||||
|
||||
macro_rules! throw {
|
||||
($pos:expr, $($t:tt)*) => {
|
||||
return Err(CompileError { pos: $pos, msg: format!($($t)*) })
|
||||
};
|
||||
($pos:expr, $($t:tt)*) => {
|
||||
return Err(CompileError { pos: $pos, msg: format!($($t)*) })
|
||||
};
|
||||
}
|
||||
|
||||
enum ResolveOutcome {
|
||||
|
@ -766,21 +767,28 @@ impl<'a> Compiler<'a> {
|
|||
match val {
|
||||
Value::Nil => {
|
||||
self.emit(I::Nil);
|
||||
return
|
||||
}
|
||||
Value::Bool(b) => {
|
||||
self.emit(I::Bool(*b));
|
||||
}
|
||||
Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i) => {
|
||||
self.emit(I::Int(Arg24::from_i64(*i)));
|
||||
return
|
||||
}
|
||||
Value::Symbol(s) => {
|
||||
self.emit(I::Symbol(Arg24::from_symbol(*s)));
|
||||
return
|
||||
}
|
||||
_ => {
|
||||
let n = self.add_const(val.clone());
|
||||
self.emit(I::Const(Arg24::from_usize(n)));
|
||||
Value::Int(i) => {
|
||||
if let Some(v) = i.to_i32() {
|
||||
if (-0x80_0000..=0x7f_ffff).contains(&v) {
|
||||
self.emit(I::Int(Arg24::from_i32(v)));
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
let n = self.add_const(val.clone());
|
||||
self.emit(I::Const(Arg24::from_usize(n)));
|
||||
}
|
||||
|
||||
fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) -> Result<()> {
|
||||
|
|
|
@ -8,9 +8,19 @@ pub mod chunk;
|
|||
pub mod compiler;
|
||||
pub mod exception;
|
||||
pub mod lstring;
|
||||
pub mod number;
|
||||
pub mod optimize;
|
||||
pub mod parser;
|
||||
pub mod serial;
|
||||
pub mod symbol;
|
||||
pub mod value;
|
||||
pub mod vm;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::number::RatioExt;
|
||||
pub use num::complex::ComplexFloat;
|
||||
pub use num::integer::{Integer, Roots};
|
||||
pub use num::traits::{
|
||||
ConstOne, ConstZero, Euclid, FromPrimitive, Num, One, Signed, ToPrimitive, Zero,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -546,13 +546,6 @@ impl io::Write for LString {
|
|||
}
|
||||
}
|
||||
|
||||
//impl fmt::Write for LString {
|
||||
// fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
// self.extend(s.as_bytes());
|
||||
// Ok(())
|
||||
// }
|
||||
//}
|
||||
|
||||
//
|
||||
// methods
|
||||
//
|
||||
|
|
986
talc-lang/src/number/int.rs
Normal file
986
talc-lang/src/number/int.rs
Normal 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()
|
||||
}
|
||||
}
|
10
talc-lang/src/number/mod.rs
Normal file
10
talc-lang/src/number/mod.rs
Normal 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;
|
217
talc-lang/src/number/range.rs
Normal file
217
talc-lang/src/number/range.rs
Normal 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, "*"),
|
||||
}
|
||||
}
|
||||
}
|
73
talc-lang/src/number/ratio.rs
Normal file
73
talc-lang/src/number/ratio.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,7 +35,9 @@ pub enum UnaryOp {
|
|||
Neg,
|
||||
Not,
|
||||
BitNot,
|
||||
RangeEndless,
|
||||
RangeFrom,
|
||||
RangeTo,
|
||||
RangeToIncl,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -26,6 +26,9 @@ pub enum TokenKind {
|
|||
AmperEqual,
|
||||
Star,
|
||||
StarEqual,
|
||||
StarDotDot,
|
||||
StarDotDotStar,
|
||||
StarDotDotEqual,
|
||||
Plus,
|
||||
PlusPlus,
|
||||
PlusPlusEqual,
|
||||
|
@ -115,6 +118,9 @@ impl TokenKind {
|
|||
K::AmperEqual => "'&='",
|
||||
K::Star => "'*'",
|
||||
K::StarEqual => "'*='",
|
||||
K::StarDotDot => "'*..'",
|
||||
K::StarDotDotStar => "'*..*'",
|
||||
K::StarDotDotEqual => "'*..='",
|
||||
K::Plus => "'+'",
|
||||
K::PlusPlus => "'++'",
|
||||
K::PlusPlusEqual => "'++='",
|
||||
|
@ -489,6 +495,14 @@ impl<'s> Lexer<'s> {
|
|||
},
|
||||
'*' => match self.and_peek()? {
|
||||
'=' => self.and_emit(K::StarEqual),
|
||||
'.' => match self.and_peek()? {
|
||||
'.' => match self.and_peek()? {
|
||||
'*' => self.and_emit(K::StarDotDotStar),
|
||||
'=' => self.and_emit(K::StarDotDotEqual),
|
||||
_ => self.emit(K::StarDotDot),
|
||||
},
|
||||
_ => self.unexpected(),
|
||||
},
|
||||
_ => self.emit(K::Star),
|
||||
},
|
||||
'/' => match self.and_peek()? {
|
||||
|
|
|
@ -10,31 +10,31 @@ use super::{
|
|||
parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span,
|
||||
SpanParserError, Token, TokenKind,
|
||||
};
|
||||
use num_complex::Complex64;
|
||||
use num::complex::Complex64;
|
||||
use ExprKind as E;
|
||||
use TokenKind as T;
|
||||
|
||||
type Result<T> = std::result::Result<T, ParserError>;
|
||||
|
||||
macro_rules! expect {
|
||||
($self:expr, $($t:tt)*) => {{
|
||||
let t = $self.next()?;
|
||||
match t.kind {
|
||||
$($t)* => t,
|
||||
e => return Err(ParserError { span: t.span, msg: expect_inner!(e, $($t)*) })
|
||||
}
|
||||
}};
|
||||
($self:expr, $($t:tt)*) => {{
|
||||
let t = $self.next()?;
|
||||
match t.kind {
|
||||
$($t)* => t,
|
||||
e => return Err(ParserError { span: t.span, msg: expect_inner!(e, $($t)*) })
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! expect_inner {
|
||||
($e:expr, $($tok:path)|*) => {
|
||||
{
|
||||
let mut s = format!("unexpected token {}, expected ", $e.name())
|
||||
+ $($tok.name() + ", " +)* "";
|
||||
s.truncate(s.len() - 2);
|
||||
s
|
||||
}
|
||||
};
|
||||
($e:expr, $($tok:path)|*) => {
|
||||
{
|
||||
let mut s = format!("unexpected token {}, expected ", $e.name())
|
||||
+ $($tok.name() + ", " +)* "";
|
||||
s.truncate(s.len() - 2);
|
||||
s
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! try_next {
|
||||
|
@ -48,9 +48,9 @@ macro_rules! try_next {
|
|||
}
|
||||
|
||||
macro_rules! throw {
|
||||
($span:expr, $($t:tt)*) => {
|
||||
return Err(ParserError { span: $span, msg: format!($($t)*) })
|
||||
};
|
||||
($span:expr, $($t:tt)*) => {
|
||||
return Err(ParserError { span: $span, msg: format!($($t)*) })
|
||||
};
|
||||
}
|
||||
|
||||
impl TokenKind {
|
||||
|
@ -108,13 +108,15 @@ impl TokenKind {
|
|||
T::Minus => Some(UnaryOp::Neg),
|
||||
T::Not => Some(UnaryOp::Not),
|
||||
T::Tilde => Some(UnaryOp::BitNot),
|
||||
T::StarDotDot => Some(UnaryOp::RangeTo),
|
||||
T::StarDotDotEqual => Some(UnaryOp::RangeToIncl),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn postfix_unary_op(self) -> Option<UnaryOp> {
|
||||
match self {
|
||||
T::DotDotStar => Some(UnaryOp::RangeEndless),
|
||||
T::DotDotStar => Some(UnaryOp::RangeFrom),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +126,7 @@ impl UnaryOp {
|
|||
fn precedence(self) -> u8 {
|
||||
match self {
|
||||
UnaryOp::Not => 0,
|
||||
UnaryOp::RangeEndless => 40,
|
||||
UnaryOp::RangeFrom | UnaryOp::RangeTo | UnaryOp::RangeToIncl => 40,
|
||||
UnaryOp::Neg | UnaryOp::BitNot => 110,
|
||||
}
|
||||
}
|
||||
|
@ -159,25 +161,26 @@ fn b<T>(t: T) -> Box<T> {
|
|||
}
|
||||
|
||||
impl TokenKind {
|
||||
#[rustfmt::skip]
|
||||
fn expr_first(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
T::Return
|
||||
| T::Continue
|
||||
| T::Break | T::Var
|
||||
| T::Global | T::Fn
|
||||
| T::Not | T::Backslash
|
||||
| T::Amper | T::Minus
|
||||
| T::Tilde | T::Identifier
|
||||
| T::LParen | T::LBrack
|
||||
| T::LBrace | T::Dollar
|
||||
| T::Do | T::If
|
||||
| T::While | T::For
|
||||
| T::Try | T::Integer
|
||||
| T::Float | T::Imaginary
|
||||
| T::String | T::Symbol
|
||||
| T::True | T::False
|
||||
| T::Nil
|
||||
matches!(self,
|
||||
| T::Return | T::Continue
|
||||
| T::Break | T::Var
|
||||
| T::Global | T::Fn
|
||||
| T::Not | T::StarDotDot
|
||||
| T::StarDotDotStar
|
||||
| T::StarDotDotEqual
|
||||
| T::Backslash | T::Amper
|
||||
| T::Minus | T::Tilde
|
||||
| T::Identifier
|
||||
| T::LParen | T::LBrack
|
||||
| T::LBrace | T::Dollar
|
||||
| T::Do | T::If | T::While
|
||||
| T::For | T::Try
|
||||
| T::Integer | T::Float
|
||||
| T::Imaginary | T::String
|
||||
| T::Symbol | T::True
|
||||
| T::False | T::Nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -398,6 +401,7 @@ impl<'s> Parser<'s> {
|
|||
T::True => Ok(E::Literal(Value::Bool(true)).span(tok.span)),
|
||||
T::False => Ok(E::Literal(Value::Bool(false)).span(tok.span)),
|
||||
T::Nil => Ok(E::Literal(Value::Nil).span(tok.span)),
|
||||
T::StarDotDotStar => Ok(E::Literal(Value::range_all()).span(tok.span)),
|
||||
t => throw!(
|
||||
tok.span,
|
||||
"unexpected token {}, expected expression",
|
||||
|
@ -503,7 +507,7 @@ impl<'s> Parser<'s> {
|
|||
..
|
||||
}) => {
|
||||
let args = self.parse_ident_list()?;
|
||||
expect!(self, T::Dot);
|
||||
expect!(self, T::Arrow);
|
||||
let body = self.parse_lambda()?;
|
||||
let body_span = body.span;
|
||||
Ok(E::Lambda(args, b(body)).span(span + body_span))
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
use core::fmt;
|
||||
use std::io::{self, Write};
|
||||
|
||||
use num::{bigint::Sign, BigInt};
|
||||
|
||||
use crate::{
|
||||
chunk::{Chunk, Instruction, TryTable},
|
||||
lstring::LStr,
|
||||
number::Int,
|
||||
prelude::*,
|
||||
symbol::Symbol,
|
||||
value::{function::Function, range::RangeType, Value},
|
||||
value::{function::Function, Value},
|
||||
};
|
||||
|
||||
const MAGIC: [u8; 8] = *b"\x7fTALC\0\0\0";
|
||||
|
@ -109,12 +113,12 @@ impl<'w, W: Write> ProgramWriter<'w, W> {
|
|||
}
|
||||
Value::Int(n) => {
|
||||
self.write_u8(3)?;
|
||||
self.write_i64(*n)
|
||||
self.write_int(n.clone())
|
||||
}
|
||||
Value::Ratio(r) => {
|
||||
self.write_u8(4)?;
|
||||
self.write_i64(*r.numer())?;
|
||||
self.write_i64(*r.denom())
|
||||
self.write_int(Int::from(r.numer().clone()))?;
|
||||
self.write_int(Int::from(r.denom().clone()))
|
||||
}
|
||||
Value::Float(f) => {
|
||||
self.write_u8(5)?;
|
||||
|
@ -127,13 +131,19 @@ impl<'w, W: Write> ProgramWriter<'w, W> {
|
|||
}
|
||||
Value::Range(r) => {
|
||||
self.write_u8(7)?;
|
||||
match r.ty {
|
||||
RangeType::Open => self.write_u8(0)?,
|
||||
RangeType::Closed => self.write_u8(1)?,
|
||||
RangeType::Endless => self.write_u8(2)?,
|
||||
if let Some(s) = &r.start {
|
||||
self.write_u8(1)?;
|
||||
self.write_int(s.into())?;
|
||||
} else {
|
||||
self.write_u8(0)?;
|
||||
}
|
||||
self.write_i64(r.start)?;
|
||||
self.write_i64(r.stop)
|
||||
if let Some(e) = &r.end {
|
||||
self.write_u8(1)?;
|
||||
self.write_int(e.into())?;
|
||||
} else {
|
||||
self.write_u8(0)?;
|
||||
}
|
||||
self.write_bool(r.inclusive)
|
||||
}
|
||||
Value::String(s) => {
|
||||
self.write_u8(8)?;
|
||||
|
@ -194,11 +204,36 @@ impl<'w, W: Write> ProgramWriter<'w, W> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_int(&mut self, n: Int) -> Result<()> {
|
||||
if let Some(i) = n.to_i64() {
|
||||
self.write_u8(0x80)?;
|
||||
self.write_i64(i)
|
||||
} else {
|
||||
let big = BigInt::from(n);
|
||||
let (sign, digits) = big.to_u64_digits();
|
||||
match sign {
|
||||
Sign::Minus => self.write_u8(0xff)?,
|
||||
Sign::NoSign => self.write_u8(0x00)?,
|
||||
Sign::Plus => self.write_u8(0x01)?,
|
||||
}
|
||||
self.write_u32(digits.len() as u32)?;
|
||||
for digit in digits {
|
||||
self.write_u64(digit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_i64(&mut self, n: i64) -> Result<()> {
|
||||
self.w.write_all(&n.to_le_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_u64(&mut self, n: u64) -> Result<()> {
|
||||
self.w.write_all(&n.to_le_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_u32(&mut self, n: u32) -> Result<()> {
|
||||
self.w.write_all(&n.to_le_bytes())?;
|
||||
Ok(())
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
use crate::{
|
||||
exception::{throw, Result},
|
||||
symbol::{symbol, SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR},
|
||||
lstring::LStr,
|
||||
number::Int,
|
||||
prelude::*,
|
||||
symbol::{symbol, SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||
value::function::NativeFunc,
|
||||
vm::Vm,
|
||||
vmcalliter,
|
||||
};
|
||||
|
||||
use super::{range::RangeType, Value};
|
||||
use super::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn index(&self, idx: Self) -> Result<Self> {
|
||||
|
@ -14,8 +17,15 @@ impl Value {
|
|||
match (self, idx) {
|
||||
(V::List(l), V::Int(i)) => {
|
||||
let l = l.borrow();
|
||||
if i >= 0 && (i as usize) < l.len() {
|
||||
Ok(l[i as usize].clone())
|
||||
let Some(i) = i.to_usize() else {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
"index {i} out of bounds for list of length {}",
|
||||
l.len()
|
||||
)
|
||||
};
|
||||
if i < l.len() {
|
||||
Ok(l[i].clone())
|
||||
} else {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
|
@ -25,14 +35,20 @@ impl Value {
|
|||
}
|
||||
}
|
||||
(V::Range(r), V::Int(i)) => {
|
||||
if i >= 0
|
||||
&& (r.ty == RangeType::Endless
|
||||
|| i < r.stop || (r.ty == RangeType::Closed && i == r.stop))
|
||||
{
|
||||
Ok((r.start + i).into())
|
||||
} else {
|
||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
|
||||
if !i.is_negative() {
|
||||
if let Some(s) = &r.start {
|
||||
let n = &Int::from(s) + &i;
|
||||
if let Some(e) = &r.end {
|
||||
let e = Int::from(e);
|
||||
if n < e || (n == e && r.inclusive) {
|
||||
return Ok(n.into())
|
||||
}
|
||||
} else {
|
||||
return Ok(n.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
|
||||
}
|
||||
(V::Table(t), i) if i.hashable() => {
|
||||
let t = t.borrow();
|
||||
|
@ -40,58 +56,13 @@ impl Value {
|
|||
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
|
||||
}
|
||||
(V::String(s), V::Range(r)) => {
|
||||
let slen = s.len();
|
||||
match r.ty {
|
||||
RangeType::Open => {
|
||||
if r.start < 0 || r.start > slen as i64 {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for string of length {}",
|
||||
r.stop,
|
||||
slen
|
||||
)
|
||||
}
|
||||
if r.stop < 0 || r.stop > slen as i64 {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for string of length {}",
|
||||
r.stop,
|
||||
slen
|
||||
)
|
||||
}
|
||||
Ok(s[r.start as usize..r.stop as usize].into())
|
||||
}
|
||||
RangeType::Closed => {
|
||||
if r.start < 0 || r.start > slen as i64 {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for string of length {}",
|
||||
r.stop,
|
||||
slen
|
||||
)
|
||||
}
|
||||
if r.stop < 0 || r.stop >= slen as i64 {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for string of length {}",
|
||||
r.stop,
|
||||
slen
|
||||
)
|
||||
}
|
||||
Ok(s[r.start as usize..=r.stop as usize].into())
|
||||
}
|
||||
RangeType::Endless => {
|
||||
if r.start < 0 || r.start > slen as i64 {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for string of length {}",
|
||||
r.stop,
|
||||
slen
|
||||
)
|
||||
}
|
||||
Ok(s[r.start as usize..].into())
|
||||
}
|
||||
}
|
||||
let ns = r.index_slice(s.as_bytes());
|
||||
Ok(LStr::from_bytes(ns).into())
|
||||
}
|
||||
(V::List(l), V::Range(r)) => {
|
||||
let l = l.borrow();
|
||||
let nl = r.index_slice(l.as_slice());
|
||||
Ok(nl.to_vec().into())
|
||||
}
|
||||
(col, idx) => {
|
||||
if let Ok(ii) = idx.clone().to_iter_function() {
|
||||
|
@ -113,8 +84,15 @@ impl Value {
|
|||
match (self, idx) {
|
||||
(V::List(l), V::Int(i)) => {
|
||||
let mut l = l.borrow_mut();
|
||||
if i >= 0 && (i as usize) < l.len() {
|
||||
l[i as usize] = val;
|
||||
let Some(i) = i.to_usize() else {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
"index {i} out of bounds for list of length {}",
|
||||
l.len()
|
||||
)
|
||||
};
|
||||
if i < l.len() {
|
||||
l[i] = val;
|
||||
Ok(())
|
||||
} else {
|
||||
throw!(
|
||||
|
@ -135,69 +113,27 @@ impl Value {
|
|||
Ok(())
|
||||
}
|
||||
(V::List(t), V::Range(r)) => {
|
||||
let iter = val.to_iter_function()?;
|
||||
let mut vals = Vec::new();
|
||||
while let Some(v) = vmcalliter!(vm; iter.clone())? {
|
||||
vals.push(v);
|
||||
if let Value::List(l) = val {
|
||||
let mut tm = t.borrow_mut();
|
||||
if let Ok(l) = l.try_borrow() {
|
||||
r.replace_range(&mut tm, l.as_slice());
|
||||
Ok(())
|
||||
} else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"cannot replace part of a list with itself"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
let iter = val.to_iter_function()?;
|
||||
let mut vals = Vec::new();
|
||||
while let Some(v) = vmcalliter!(vm; iter.clone())? {
|
||||
vals.push(v);
|
||||
}
|
||||
let mut tm = t.borrow_mut();
|
||||
r.replace_range(&mut tm, &vals);
|
||||
Ok(())
|
||||
}
|
||||
let mut tm = t.borrow_mut();
|
||||
match r.ty {
|
||||
RangeType::Open => {
|
||||
if r.start < 0 || r.start > tm.len() as i64 {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for list of length {}",
|
||||
r.stop,
|
||||
tm.len()
|
||||
)
|
||||
}
|
||||
if r.stop < 0 || r.stop > tm.len() as i64 {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for list of length {}",
|
||||
r.stop,
|
||||
tm.len()
|
||||
)
|
||||
}
|
||||
let end = tm.split_off(r.stop as usize);
|
||||
tm.truncate(r.start as usize);
|
||||
tm.extend(vals.into_iter().chain(end));
|
||||
}
|
||||
RangeType::Closed => {
|
||||
if r.start < 0 || r.start > tm.len() as i64 {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for list of length {}",
|
||||
r.stop,
|
||||
tm.len()
|
||||
)
|
||||
}
|
||||
if r.stop < 0 || r.stop >= tm.len() as i64 {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for list of length {}",
|
||||
r.stop,
|
||||
tm.len()
|
||||
)
|
||||
}
|
||||
let end = tm.split_off(r.stop as usize + 1);
|
||||
tm.truncate(r.start as usize);
|
||||
tm.extend(vals.into_iter().chain(end));
|
||||
}
|
||||
RangeType::Endless => {
|
||||
if r.start < 0 || r.start > tm.len() as i64 {
|
||||
throw!(
|
||||
*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for list of length {}",
|
||||
r.stop,
|
||||
tm.len()
|
||||
)
|
||||
}
|
||||
tm.truncate(r.start as usize);
|
||||
tm.extend(vals);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
(col, idx) => {
|
||||
if let Ok(ii) = idx.clone().to_iter_function() {
|
||||
|
|
|
@ -3,23 +3,19 @@ use std::borrow::Cow;
|
|||
use std::io;
|
||||
use std::{cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, rc::Rc};
|
||||
|
||||
pub use num_complex::Complex64;
|
||||
use num_complex::ComplexFloat;
|
||||
pub use num_rational::Rational64;
|
||||
use num::{complex::Complex64, BigInt};
|
||||
|
||||
use crate::exception::{throw, Exception};
|
||||
use crate::lstring::{LStr, LString};
|
||||
use crate::number::{Int, Range, Ratio};
|
||||
use crate::prelude::*;
|
||||
use crate::symbol::{Symbol, SYM_HASH_ERROR};
|
||||
|
||||
use self::{
|
||||
function::{Function, NativeFunc},
|
||||
range::{Range, RangeType},
|
||||
};
|
||||
use self::function::{Function, NativeFunc};
|
||||
|
||||
pub mod function;
|
||||
pub mod index;
|
||||
pub mod ops;
|
||||
pub mod range;
|
||||
|
||||
type RcList = Rc<RefCell<Vec<Value>>>;
|
||||
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
|
||||
|
@ -31,11 +27,11 @@ pub enum Value {
|
|||
Nil,
|
||||
Bool(bool),
|
||||
Symbol(Symbol),
|
||||
Range(Range),
|
||||
Range(Rc<Range>),
|
||||
|
||||
Int(i64),
|
||||
Int(Int),
|
||||
Float(f64),
|
||||
Ratio(Rational64),
|
||||
Ratio(Rc<Ratio>),
|
||||
Complex(Complex64),
|
||||
|
||||
Cell(Rc<RefCell<Value>>),
|
||||
|
@ -113,11 +109,7 @@ impl Value {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
Self::Range(r) => match r.ty {
|
||||
RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
|
||||
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
|
||||
RangeType::Endless => write!(w, "{}..*", r.start),
|
||||
},
|
||||
Self::Range(r) => write!(w, "{r}"),
|
||||
Self::Int(i) => write!(w, "{i}"),
|
||||
Self::Float(x) => write!(w, "{x:?}"),
|
||||
Self::Ratio(r) => write!(w, "{}/{}", r.numer(), r.denom()),
|
||||
|
@ -401,6 +393,23 @@ macro_rules! impl_from {
|
|||
}
|
||||
}
|
||||
};
|
||||
($ty:ty, $var:ident, rchash) => {
|
||||
impl From<$ty> for Value {
|
||||
fn from(value: $ty) -> Self {
|
||||
Self::$var(Rc::new(value))
|
||||
}
|
||||
}
|
||||
impl From<Rc<$ty>> for Value {
|
||||
fn from(value: Rc<$ty>) -> Self {
|
||||
Self::$var(value)
|
||||
}
|
||||
}
|
||||
impl From<$ty> for HashValue {
|
||||
fn from(value: $ty) -> Self {
|
||||
Self(Value::$var(Rc::new(value)))
|
||||
}
|
||||
}
|
||||
};
|
||||
($ty:ty, $var:ident, into) => {
|
||||
impl From<$ty> for Value {
|
||||
fn from(value: $ty) -> Self {
|
||||
|
@ -412,10 +421,12 @@ macro_rules! impl_from {
|
|||
|
||||
impl_from!(bool, Bool, hash);
|
||||
impl_from!(Symbol, Symbol, hash);
|
||||
impl_from!(Range, Range);
|
||||
impl_from!(i64, Int, hash);
|
||||
impl_from!(Range, Range, rc);
|
||||
impl_from!(Int, Int, hash);
|
||||
impl_from!(i64, Int, into);
|
||||
impl_from!(BigInt, Int, into);
|
||||
impl_from!(f64, Float);
|
||||
impl_from!(Rational64, Ratio, hash);
|
||||
impl_from!(Ratio, Ratio, rchash);
|
||||
impl_from!(Complex64, Complex);
|
||||
impl_from!(HashMap<HashValue, Value>, Table, rcref);
|
||||
impl_from!(Vec<Value>, List, rcref);
|
||||
|
@ -439,3 +450,15 @@ impl<T: NativeValue + 'static> From<T> for Value {
|
|||
Self::Native(Rc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Value {
|
||||
fn from(value: String) -> Self {
|
||||
Self::String(LStr::from_str(&value).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Value {
|
||||
fn from(value: &str) -> Self {
|
||||
Self::String(LStr::from_str(value).into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,43 +5,31 @@ use std::{
|
|||
rc::Rc,
|
||||
};
|
||||
|
||||
use num_complex::{Complex64, ComplexFloat};
|
||||
use num_rational::Rational64;
|
||||
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
|
||||
use num::complex::Complex64;
|
||||
|
||||
use crate::{
|
||||
exception::{throw, Result},
|
||||
lstring::LString,
|
||||
number::{Int, Range, Ratio},
|
||||
prelude::*,
|
||||
symbol::{symbol, SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||
value::range::RangeType,
|
||||
vm::Vm,
|
||||
};
|
||||
|
||||
use super::{
|
||||
function::{FuncAttrs, NativeFunc},
|
||||
range::Range,
|
||||
HashValue, Value,
|
||||
};
|
||||
|
||||
pub trait RatioExt {
|
||||
fn to_f64(&self) -> f64;
|
||||
}
|
||||
|
||||
impl RatioExt for Rational64 {
|
||||
fn to_f64(&self) -> f64 {
|
||||
num_traits::ToPrimitive::to_f64(self).unwrap_or(f64::NAN)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn truthy(&self) -> bool {
|
||||
match self {
|
||||
Value::Nil => false,
|
||||
Value::Bool(b) => *b,
|
||||
Value::Range(r) => matches!(r.len(), None | Some(1..)),
|
||||
Value::Int(n) => *n != 0,
|
||||
Value::Range(r) => !r.is_empty(),
|
||||
Value::Int(n) => !n.is_zero(),
|
||||
Value::Float(x) => *x != 0.0 && !x.is_nan(),
|
||||
Value::Ratio(r) => !(*r.numer() == 0 && *r.denom() != 0),
|
||||
Value::Ratio(r) => !r.is_zero(),
|
||||
Value::Complex(c) => !(c.re == 0.0 && c.im == 0.0 || c.is_nan()),
|
||||
Value::String(s) => !s.is_empty(),
|
||||
Value::List(l) => l.borrow().len() > 0,
|
||||
|
@ -59,17 +47,17 @@ impl Value {
|
|||
pub fn promote(a: Value, b: Value) -> (Value, Value) {
|
||||
use Value as V;
|
||||
match (&a, &b) {
|
||||
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
|
||||
(V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
|
||||
(V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
|
||||
(V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64()), b),
|
||||
(V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64().into()), b),
|
||||
(V::Int(x), V::Ratio(..)) => (V::Ratio(Ratio::from(x).into()), b),
|
||||
(V::Int(x), V::Float(..)) => (V::Float(x.to_f64_uc()), b),
|
||||
(V::Int(x), V::Complex(..)) => (V::Complex(x.to_f64_uc().into()), b),
|
||||
(V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64_uc()), b),
|
||||
(V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64_uc().into()), b),
|
||||
(V::Float(x), V::Complex(..)) => (V::Complex(x.into()), b),
|
||||
(V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
|
||||
(V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
|
||||
(V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
|
||||
(V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64())),
|
||||
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64().into())),
|
||||
(V::Ratio(..), V::Int(y)) => (a, V::Ratio(Ratio::from(y).into())),
|
||||
(V::Float(..), V::Int(y)) => (a, V::Float(y.to_f64_uc())),
|
||||
(V::Complex(..), V::Int(y)) => (a, V::Complex(y.to_f64_uc().into())),
|
||||
(V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64_uc())),
|
||||
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64_uc().into())),
|
||||
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
|
||||
_ => (a, b),
|
||||
}
|
||||
|
@ -84,20 +72,8 @@ impl Neg for Value {
|
|||
fn neg(self) -> Self::Output {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Int(x) => {
|
||||
if let Some(x) = x.checked_neg() {
|
||||
Ok(V::Int(x))
|
||||
} else {
|
||||
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
|
||||
}
|
||||
}
|
||||
V::Ratio(x) => {
|
||||
if let Some(x) = Rational64::ZERO.checked_sub(&x) {
|
||||
Ok(V::Ratio(x))
|
||||
} else {
|
||||
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
|
||||
}
|
||||
}
|
||||
V::Int(x) => Ok(V::Int(-x)),
|
||||
V::Ratio(x) => Ok(V::Ratio((-x.as_ref()).into())),
|
||||
V::Float(x) => Ok(V::Float(-x)),
|
||||
V::Complex(x) => Ok(V::Complex(-x)),
|
||||
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
|
||||
|
@ -109,26 +85,8 @@ impl Value {
|
|||
pub fn abs(self) -> Result<Self> {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Int(x) => {
|
||||
if let Some(x) = x.checked_abs() {
|
||||
Ok(V::Int(x))
|
||||
} else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"overflow when finding absolute value of {self}"
|
||||
)
|
||||
}
|
||||
}
|
||||
V::Ratio(x) => {
|
||||
if let Some((x, _)) = ratio_checked_absign(&x) {
|
||||
Ok(V::Ratio(x))
|
||||
} else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"overflow when finding absolute value of {self}"
|
||||
)
|
||||
}
|
||||
}
|
||||
V::Int(x) => Ok(V::Int(x.abs())),
|
||||
V::Ratio(x) => Ok(V::Ratio(x.abs().into())),
|
||||
V::Float(x) => Ok(V::Float(x.abs())),
|
||||
V::Complex(x) => Ok(V::Float(x.norm())),
|
||||
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
|
||||
|
@ -141,37 +99,29 @@ impl Value {
|
|||
/////////////////////////
|
||||
|
||||
macro_rules! impl_value_arith {
|
||||
($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => {
|
||||
($trait:ident, $name:ident, $op:tt, $verb:literal) => {
|
||||
|
||||
impl $trait<Value> for Value {
|
||||
type Output = Result<Self>;
|
||||
fn $name(self, rhs: Value) -> Self::Output {
|
||||
use Value as V;
|
||||
let (a, b) = promote(self, rhs);
|
||||
match (&a, &b) {
|
||||
(V::Int(x), V::Int(y)) => if let Some(v) = x.$checked(y) {
|
||||
Ok(V::Int(v))
|
||||
} else {
|
||||
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
|
||||
},
|
||||
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.$checked(y) {
|
||||
Ok(V::Ratio(v))
|
||||
} else {
|
||||
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
|
||||
},
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x $op y)),
|
||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x $op y)),
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, concat!("cannot ", $verb, " {:#} and {:#}"), l, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl $trait<Value> for Value {
|
||||
type Output = Result<Self>;
|
||||
fn $name(self, rhs: Value) -> Self::Output {
|
||||
use Value as V;
|
||||
let (a, b) = promote(self, rhs);
|
||||
match (&a, &b) {
|
||||
(V::Int(x), V::Int(y)) => Ok(V::Int(x $op y)),
|
||||
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio((x.as_ref() $op y.as_ref()).into())),
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x $op y)),
|
||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x $op y)),
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, concat!("cannot ", $verb, " {:#} and {:#}"), l, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
impl_value_arith!(Add, add, checked_add, +, "add");
|
||||
impl_value_arith!(Sub, sub, checked_sub, -, "subtract");
|
||||
impl_value_arith!(Mul, mul, checked_mul, *, "multiply");
|
||||
impl_value_arith!(Add, add, +, "add");
|
||||
impl_value_arith!(Sub, sub, -, "subtract");
|
||||
impl_value_arith!(Mul, mul, *, "multiply");
|
||||
|
||||
impl Div<Value> for Value {
|
||||
type Output = Result<Self>;
|
||||
|
@ -179,18 +129,16 @@ impl Div<Value> for Value {
|
|||
use Value as V;
|
||||
let (a, b) = promote(self, rhs);
|
||||
match (&a, &b) {
|
||||
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer division by 0"),
|
||||
(V::Int(x), V::Int(y)) => Ok(Value::Ratio((*x, *y).into())),
|
||||
(V::Int(_), V::Int(y)) if y.is_zero() => {
|
||||
throw!(*SYM_VALUE_ERROR, "integer division by 0")
|
||||
}
|
||||
(V::Int(x), V::Int(y)) => {
|
||||
Ok(Value::from(Ratio::new(x.clone().into(), y.clone().into())))
|
||||
}
|
||||
(V::Ratio(_), V::Ratio(r)) if r.is_zero() => {
|
||||
throw!(*SYM_VALUE_ERROR, "rational division by 0")
|
||||
}
|
||||
(V::Ratio(x), V::Ratio(y)) => {
|
||||
if let Some(v) = x.checked_div(y) {
|
||||
Ok(V::Ratio(v))
|
||||
} else {
|
||||
throw!(*SYM_VALUE_ERROR, "overflow when dividing {a} and {b}")
|
||||
}
|
||||
}
|
||||
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio((x.as_ref() / y.as_ref()).into())),
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
|
||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}"),
|
||||
|
@ -202,54 +150,20 @@ impl Div<Value> for Value {
|
|||
// modulo and integer division //
|
||||
///////////////////////////////////
|
||||
|
||||
#[inline]
|
||||
fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> {
|
||||
let a = if r.is_negative() {
|
||||
Rational64::ZERO.checked_sub(r)?
|
||||
} else {
|
||||
*r
|
||||
};
|
||||
Some((a, r.signum()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
|
||||
let (r2_abs, r2_sgn) = ratio_checked_absign(r2)?;
|
||||
Some(r1.checked_div(&r2_abs)?.floor() * r2_sgn)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
|
||||
let q = ratio_checked_div_euclid(r1, r2)?;
|
||||
r1.checked_sub(&r2.checked_mul(&q)?)
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn modulo(self, rhs: Value) -> Result<Self> {
|
||||
use Value as V;
|
||||
let (a, b) = promote(self, rhs);
|
||||
match (&a, &b) {
|
||||
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer modulo by 0"),
|
||||
(V::Int(x), V::Int(y)) => {
|
||||
if let Some(v) = x.checked_rem_euclid(*y) {
|
||||
Ok(V::Int(v))
|
||||
} else {
|
||||
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
|
||||
}
|
||||
(V::Int(_), V::Int(y)) if y.is_zero() => {
|
||||
throw!(*SYM_VALUE_ERROR, "integer modulo by 0")
|
||||
}
|
||||
|
||||
(V::Int(x), V::Int(y)) => Ok(V::Int(x.rem_euclid(y))),
|
||||
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
|
||||
throw!(*SYM_VALUE_ERROR, "rational modulo by 0")
|
||||
}
|
||||
(V::Ratio(x), V::Ratio(y)) => {
|
||||
if let Some(v) = ratio_checked_rem_euclid(x, y) {
|
||||
Ok(V::Ratio(v))
|
||||
} else {
|
||||
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
|
||||
}
|
||||
}
|
||||
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(*y))),
|
||||
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x.rem_euclid(y).into())),
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(y))),
|
||||
(V::Complex(x), V::Complex(y)) => {
|
||||
let n = x / y;
|
||||
let n = Complex64::new(n.re().floor(), n.im().floor());
|
||||
|
@ -263,33 +177,15 @@ impl Value {
|
|||
use Value as V;
|
||||
let (a, b) = promote(self, rhs);
|
||||
match (&a, &b) {
|
||||
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer divsion by 0"),
|
||||
(V::Int(x), V::Int(y)) => {
|
||||
if let Some(v) = x.checked_div_euclid(*y) {
|
||||
Ok(V::Int(v))
|
||||
} else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"overflow when integer dividing {a} and {b}"
|
||||
)
|
||||
}
|
||||
(V::Int(_), V::Int(y)) if y.is_zero() => {
|
||||
throw!(*SYM_VALUE_ERROR, "integer divsion by 0")
|
||||
}
|
||||
|
||||
(V::Int(x), V::Int(y)) => Ok(Value::from(x.div_euclid(y))),
|
||||
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
|
||||
throw!(*SYM_VALUE_ERROR, "integer division by 0")
|
||||
}
|
||||
(V::Ratio(x), V::Ratio(y)) => {
|
||||
if let Some(v) = ratio_checked_div_euclid(x, y) {
|
||||
Ok(V::Ratio(v))
|
||||
} else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"overflow when integer dividing {a} and {b}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(*y))),
|
||||
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x.rem_euclid(y).into())),
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(y))),
|
||||
(V::Complex(x), V::Complex(y)) => {
|
||||
let n = x / y;
|
||||
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
|
||||
|
@ -304,29 +200,11 @@ impl Value {
|
|||
//////////////////////
|
||||
|
||||
#[inline]
|
||||
fn ipow(n: i64, p: u64) -> Option<i64> {
|
||||
match (n, p) {
|
||||
(0, 0) => None,
|
||||
(0, _) => Some(0),
|
||||
(_, 0) => Some(1),
|
||||
(1, _) => Some(1),
|
||||
(-1, p) => (-1_i64).checked_pow((p % 2) as u32),
|
||||
(_, p) if p > u32::MAX as u64 => None,
|
||||
(n, p) => n.checked_pow(p as u32),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
|
||||
match p {
|
||||
i64::MIN => match (n, d) {
|
||||
(0, _) => Some((0, 1)),
|
||||
(1, 1) => Some((1, 1)),
|
||||
(-1, 1) => Some((-1, 1)),
|
||||
_ => None,
|
||||
},
|
||||
0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)),
|
||||
_ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)),
|
||||
fn rpow(n: &Int, d: &Int, p: &Int) -> Option<Ratio> {
|
||||
let (sign, p) = (p.isignum(), p.abs());
|
||||
match sign {
|
||||
0.. => Some(Ratio::new(n.ipow(&p)?.into(), d.ipow(&p)?.into())),
|
||||
_ => Some(Ratio::new(d.ipow(&p)?.into(), n.ipow(&p)?.into())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -334,10 +212,10 @@ impl Value {
|
|||
pub fn pow(self, rhs: Value) -> Result<Self> {
|
||||
use Value as V;
|
||||
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
|
||||
if x.is_zero() && *y == 0 {
|
||||
if x.is_zero() && y.is_zero() {
|
||||
throw!(*SYM_VALUE_ERROR, "rational zero to integer zero power")
|
||||
}
|
||||
let Some(v) = rpow(*(*x).numer(), *(*x).denom(), *y) else {
|
||||
let Some(v) = rpow(&x.numer().into(), &x.denom().into(), y) else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"overflow when raising {self} to the power {rhs}"
|
||||
|
@ -347,11 +225,11 @@ impl Value {
|
|||
}
|
||||
let (a, b) = promote(self, rhs);
|
||||
match (&a, &b) {
|
||||
(V::Int(0), V::Int(0)) => {
|
||||
(V::Int(x), V::Int(y)) if x.is_zero() && y.is_zero() => {
|
||||
throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power")
|
||||
}
|
||||
(V::Int(x), V::Int(y @ 0..)) => {
|
||||
if let Some(v) = ipow(*x, *y as u64) {
|
||||
(V::Int(x), V::Int(y)) if !y.is_negative() => {
|
||||
if let Some(v) = x.ipow(y) {
|
||||
Ok(V::Int(v))
|
||||
} else {
|
||||
throw!(
|
||||
|
@ -361,7 +239,7 @@ impl Value {
|
|||
}
|
||||
}
|
||||
(V::Int(x), V::Int(y)) => {
|
||||
if let Some(v) = rpow(*x, 1, *y) {
|
||||
if let Some(v) = rpow(x, &Int::ONE, y) {
|
||||
Ok(V::Ratio(v.into()))
|
||||
} else {
|
||||
throw!(
|
||||
|
@ -370,7 +248,7 @@ impl Value {
|
|||
)
|
||||
}
|
||||
}
|
||||
(V::Ratio(x), V::Ratio(y)) => Ok(V::Float(x.to_f64().powf(y.to_f64()))),
|
||||
(V::Ratio(x), V::Ratio(y)) => Ok(V::Float(x.to_f64_uc().powf(y.to_f64_uc()))),
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x.powf(*y))),
|
||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x.powc(*y))),
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}"),
|
||||
|
@ -389,7 +267,16 @@ impl Shl<Value> for Value {
|
|||
use Value as V;
|
||||
match (self, rhs) {
|
||||
(V::Int(a), V::Int(b)) => {
|
||||
Ok(Value::Int((a as u64).wrapping_shl(b as u32) as i64))
|
||||
if b.is_negative() {
|
||||
throw!(
|
||||
*SYM_TYPE_ERROR,
|
||||
"cannot shift {a} left by negative amount {b}"
|
||||
)
|
||||
}
|
||||
if b.gt_i64(u32::MAX as i64) {
|
||||
throw!(*SYM_TYPE_ERROR, "cannot shift {a} left by {b}: too large")
|
||||
}
|
||||
Ok(Value::Int(a << b))
|
||||
}
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} left by {r:#}"),
|
||||
}
|
||||
|
@ -403,7 +290,13 @@ impl Shr<Value> for Value {
|
|||
use Value as V;
|
||||
match (self, rhs) {
|
||||
(V::Int(a), V::Int(b)) => {
|
||||
Ok(Value::Int((a as u64).wrapping_shr(b as u32) as i64))
|
||||
if b.is_negative() {
|
||||
throw!(
|
||||
*SYM_TYPE_ERROR,
|
||||
"cannot shift {a} right by negative amount {b}"
|
||||
)
|
||||
}
|
||||
Ok(Value::Int(a >> b))
|
||||
}
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
|
||||
}
|
||||
|
@ -464,7 +357,6 @@ impl Not for Value {
|
|||
|
||||
impl PartialEq for Value {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
use super::range::RangeType as Rty;
|
||||
use Value as V;
|
||||
match (self, other) {
|
||||
(V::Nil, V::Nil) => true,
|
||||
|
@ -473,31 +365,23 @@ impl PartialEq for Value {
|
|||
(V::Ratio(a), V::Ratio(b)) => *a == *b,
|
||||
(V::Float(a), V::Float(b)) => *a == *b,
|
||||
(V::Complex(a), V::Complex(b)) => *a == *b,
|
||||
(V::Int(a), V::Ratio(b)) => Rational64::from(*a) == *b,
|
||||
(V::Ratio(a), V::Int(b)) => *a == Rational64::from(*b),
|
||||
(V::Int(a), V::Float(b)) => *a as f64 == *b,
|
||||
(V::Float(a), V::Int(b)) => *a == *b as f64,
|
||||
(V::Int(a), V::Complex(b)) => Complex64::from(*a as f64) == *b,
|
||||
(V::Complex(a), V::Int(b)) => *a == Complex64::from(*b as f64),
|
||||
(V::Ratio(a), V::Float(b)) => a.to_f64() == *b,
|
||||
(V::Float(a), V::Ratio(b)) => *a == b.to_f64(),
|
||||
(V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64()) == *b,
|
||||
(V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64()),
|
||||
(V::Int(a), V::Ratio(b)) => b.is_integer() && a.to_big().as_ref() == b.numer(),
|
||||
(V::Ratio(a), V::Int(b)) => a.is_integer() && a.numer() == b.to_big().as_ref(),
|
||||
(V::Int(a), V::Float(b)) => a.to_f64_uc() == *b,
|
||||
(V::Float(a), V::Int(b)) => *a == b.to_f64_uc(),
|
||||
(V::Int(a), V::Complex(b)) => Complex64::from(a.to_f64_uc()) == *b,
|
||||
(V::Complex(a), V::Int(b)) => *a == Complex64::from(b.to_f64_uc()),
|
||||
(V::Ratio(a), V::Float(b)) => a.to_f64_uc() == *b,
|
||||
(V::Float(a), V::Ratio(b)) => *a == b.to_f64_uc(),
|
||||
(V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64_uc()) == *b,
|
||||
(V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64_uc()),
|
||||
(V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b,
|
||||
(V::Complex(a), V::Float(b)) => *a == Complex64::from(*b),
|
||||
(V::String(a), V::String(b)) => *a == *b,
|
||||
(V::List(a), V::List(b)) => *a.borrow() == *b.borrow(),
|
||||
(V::Symbol(a), V::Symbol(b)) => a == b,
|
||||
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
|
||||
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
|
||||
(Rty::Open, Rty::Open) | (Rty::Closed, Rty::Closed) => {
|
||||
a.start == b.start && a.stop == b.stop
|
||||
}
|
||||
(Rty::Open, Rty::Closed) => a.start == b.start && a.stop == b.stop - 1,
|
||||
(Rty::Closed, Rty::Open) => a.start == b.start && a.stop - 1 == b.stop,
|
||||
(Rty::Endless, Rty::Endless) => a.start == b.start,
|
||||
_ => false,
|
||||
},
|
||||
(V::Range(a), V::Range(b)) => a.as_ref() == b.as_ref(),
|
||||
(V::Native(a), b) => a.partial_eq(b),
|
||||
(a, V::Native(b)) => b.partial_eq(a),
|
||||
_ => false,
|
||||
|
@ -514,12 +398,12 @@ impl PartialOrd for Value {
|
|||
(V::Int(a), V::Int(b)) => a.partial_cmp(b),
|
||||
(V::Ratio(a), V::Ratio(b)) => a.partial_cmp(b),
|
||||
(V::Float(a), V::Float(b)) => a.partial_cmp(b),
|
||||
(V::Int(a), V::Ratio(b)) => Rational64::from(*a).partial_cmp(b),
|
||||
(V::Ratio(a), V::Int(b)) => a.partial_cmp(&Rational64::from(*b)),
|
||||
(V::Int(a), V::Float(b)) => (*a as f64).partial_cmp(b),
|
||||
(V::Float(a), V::Int(b)) => a.partial_cmp(&(*b as f64)),
|
||||
(V::Ratio(a), V::Float(b)) => a.to_f64().partial_cmp(b),
|
||||
(V::Float(a), V::Ratio(b)) => a.partial_cmp(&b.to_f64()),
|
||||
(V::Int(a), V::Ratio(b)) => Ratio::from(a).partial_cmp(b),
|
||||
(V::Ratio(a), V::Int(b)) => a.as_ref().partial_cmp(&Ratio::from(b)),
|
||||
(V::Int(a), V::Float(b)) => a.to_f64_uc().partial_cmp(b),
|
||||
(V::Float(a), V::Int(b)) => a.partial_cmp(&b.to_f64_uc()),
|
||||
(V::Ratio(a), V::Float(b)) => a.to_f64_uc().partial_cmp(b),
|
||||
(V::Float(a), V::Ratio(b)) => a.partial_cmp(&b.to_f64_uc()),
|
||||
(V::String(a), V::String(b)) => a.partial_cmp(b),
|
||||
(V::List(a), V::List(b)) => a.borrow().partial_cmp(&*b.borrow()),
|
||||
(V::Symbol(a), V::Symbol(b)) => a.partial_cmp(b),
|
||||
|
@ -574,19 +458,9 @@ impl Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn range(&self, other: &Self, closed: bool) -> Result<Self> {
|
||||
if let (Value::Int(start), Value::Int(stop)) = (self, other) {
|
||||
let ty = if closed {
|
||||
RangeType::Closed
|
||||
} else {
|
||||
RangeType::Open
|
||||
};
|
||||
Ok(Range {
|
||||
start: *start,
|
||||
stop: *stop,
|
||||
ty,
|
||||
}
|
||||
.into())
|
||||
pub fn range(&self, other: &Self) -> Result<Self> {
|
||||
if let (Value::Int(start), Value::Int(end)) = (self, other) {
|
||||
Ok(Range::range(start, end).into())
|
||||
} else {
|
||||
throw!(
|
||||
*SYM_TYPE_ERROR,
|
||||
|
@ -595,19 +469,48 @@ impl Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn range_endless(&self) -> Result<Self> {
|
||||
if let Value::Int(start) = self {
|
||||
Ok(Range {
|
||||
start: *start,
|
||||
stop: 0,
|
||||
ty: RangeType::Endless,
|
||||
}
|
||||
.into())
|
||||
pub fn range_incl(&self, other: &Self) -> Result<Self> {
|
||||
if let (Value::Int(start), Value::Int(end)) = (self, other) {
|
||||
Ok(Range::range_incl(start, end).into())
|
||||
} else {
|
||||
throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}")
|
||||
throw!(
|
||||
*SYM_TYPE_ERROR,
|
||||
"cannot create range between {self:#} and {other:#}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn range_from(&self) -> Result<Self> {
|
||||
if let Value::Int(start) = self {
|
||||
Ok(Range::range_from(start).into())
|
||||
} else {
|
||||
throw!(*SYM_TYPE_ERROR, "cannot create range from {self:#}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn range_to(&self) -> Result<Self> {
|
||||
if let Value::Int(end) = self {
|
||||
Ok(Range::range_to(end).into())
|
||||
} else {
|
||||
throw!(*SYM_TYPE_ERROR, "cannot create range to {self:#}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn range_to_incl(&self) -> Result<Self> {
|
||||
if let Value::Int(end) = self {
|
||||
Ok(Range::range_to_incl(end).into())
|
||||
} else {
|
||||
throw!(*SYM_TYPE_ERROR, "cannot create range to {self:#}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn range_all() -> Self {
|
||||
thread_local! {
|
||||
static RANGE_ALL: Rc<Range> = Rc::new(Range::range_all());
|
||||
}
|
||||
Self::Range(RANGE_ALL.with(|x| x.clone()))
|
||||
}
|
||||
|
||||
pub fn to_cell(self) -> Self {
|
||||
Value::Cell(Rc::new(RefCell::new(self)))
|
||||
}
|
||||
|
@ -630,7 +533,10 @@ impl Value {
|
|||
match self {
|
||||
Self::Function(_) | Self::NativeFunc(_) => Ok(self),
|
||||
Self::Range(range) => {
|
||||
let range_iter = RefCell::new(range.into_iter());
|
||||
let Some(iter) = range.try_iterate() else {
|
||||
throw!(*SYM_VALUE_ERROR, "cannot iterate range {range}")
|
||||
};
|
||||
let range_iter = RefCell::new(iter);
|
||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||
Ok(Value::iter_pack(
|
||||
range_iter.borrow_mut().next().map(Value::from),
|
||||
|
|
|
@ -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..),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -74,8 +74,8 @@ pub fn binary_op(o: BinaryOp, a: Value, b: Value) -> Result<Value> {
|
|||
BinaryOp::Ge => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Less)),
|
||||
BinaryOp::Lt => a.val_cmp(&b).map(|o| Value::Bool(o == Ordering::Less)),
|
||||
BinaryOp::Le => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Greater)),
|
||||
BinaryOp::Range => a.range(&b, false),
|
||||
BinaryOp::RangeIncl => a.range(&b, true),
|
||||
BinaryOp::Range => a.range(&b),
|
||||
BinaryOp::RangeIncl => a.range_incl(&b),
|
||||
BinaryOp::Concat => a.concat(&b),
|
||||
BinaryOp::Append => a.append(b),
|
||||
}
|
||||
|
@ -86,7 +86,9 @@ pub fn unary_op(o: UnaryOp, a: Value) -> Result<Value> {
|
|||
UnaryOp::Neg => -a,
|
||||
UnaryOp::Not => Ok(Value::Bool(!a.truthy())),
|
||||
UnaryOp::BitNot => a.not(),
|
||||
UnaryOp::RangeEndless => a.range_endless(),
|
||||
UnaryOp::RangeFrom => a.range_from(),
|
||||
UnaryOp::RangeTo => a.range_to(),
|
||||
UnaryOp::RangeToIncl => a.range_to_incl(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,7 +354,7 @@ impl Vm {
|
|||
self.push(Value::Symbol(sym));
|
||||
}
|
||||
// [] -> [n]
|
||||
I::Int(n) => self.push(Value::Int(i64::from(n))),
|
||||
I::Int(n) => self.push(Value::from(i64::from(n))),
|
||||
// [x] -> [x,x]
|
||||
I::Dup => self.push(self.stack[self.stack.len() - 1].clone()),
|
||||
// [x,y] -> [x,y,x,y]
|
||||
|
|
|
@ -24,35 +24,35 @@ fn assert_eval(src: &str, value: Value) {
|
|||
fn scope() {
|
||||
assert_eval(
|
||||
"
|
||||
var x = 7
|
||||
var y = 10
|
||||
do
|
||||
var x = 200
|
||||
y = 100
|
||||
end
|
||||
x + y
|
||||
",
|
||||
Value::Int(7 + 100),
|
||||
var x = 7
|
||||
var y = 10
|
||||
do
|
||||
var x = 200
|
||||
y = 100
|
||||
end
|
||||
x + y
|
||||
",
|
||||
Value::from(7 + 100),
|
||||
);
|
||||
assert_eval(
|
||||
"
|
||||
var cond = true
|
||||
var z = 2
|
||||
if cond then var z = 5 else var z = 6 end
|
||||
z
|
||||
",
|
||||
Value::Int(2),
|
||||
var cond = true
|
||||
var z = 2
|
||||
if cond then var z = 5 else var z = 6 end
|
||||
z
|
||||
",
|
||||
Value::from(2),
|
||||
);
|
||||
assert_eval(
|
||||
"
|
||||
var i = 55
|
||||
var j = 66
|
||||
for i in 0..10 do
|
||||
j = i
|
||||
end
|
||||
i + j
|
||||
",
|
||||
Value::Int(55 + 9),
|
||||
var i = 55
|
||||
var j = 66
|
||||
for i in 0..10 do
|
||||
j = i
|
||||
end
|
||||
i + j
|
||||
",
|
||||
Value::from(55 + 9),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -60,26 +60,26 @@ fn scope() {
|
|||
fn forloop() {
|
||||
assert_eval(
|
||||
"
|
||||
sum = 0
|
||||
for i in 0..5 do
|
||||
for j in 0..10 do
|
||||
sum += i*j
|
||||
end
|
||||
end
|
||||
sum
|
||||
",
|
||||
Value::Int(45 * 10),
|
||||
sum = 0
|
||||
for i in 0..5 do
|
||||
for j in 0..10 do
|
||||
sum += i*j
|
||||
end
|
||||
end
|
||||
sum
|
||||
",
|
||||
Value::from(45 * 10),
|
||||
);
|
||||
assert_eval(
|
||||
"
|
||||
map = {a=3, b=2, c=4, d=7}
|
||||
prod = 1
|
||||
for k in map do
|
||||
prod *= map[k]
|
||||
end
|
||||
prod
|
||||
",
|
||||
Value::Int(3 * 2 * 4 * 7),
|
||||
map = {a=3, b=2, c=4, d=7}
|
||||
prod = 1
|
||||
for k in map do
|
||||
prod *= map[k]
|
||||
end
|
||||
prod
|
||||
",
|
||||
Value::from(3 * 2 * 4 * 7),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -87,28 +87,28 @@ fn forloop() {
|
|||
fn closures() {
|
||||
assert_eval(
|
||||
"
|
||||
var x = 2
|
||||
next = \\. do x = x * 2 + 1 end
|
||||
next() + next() + next()
|
||||
",
|
||||
Value::Int(5 + 11 + 23),
|
||||
var x = 2
|
||||
next = \\->do x = x * 2 + 1 end
|
||||
next() + next() + next()
|
||||
",
|
||||
Value::from(5 + 11 + 23),
|
||||
);
|
||||
assert_eval(
|
||||
"
|
||||
var x = 0
|
||||
fn outer(n) do
|
||||
fn inner() do
|
||||
x += n
|
||||
n += 1
|
||||
x
|
||||
end
|
||||
end
|
||||
var x = 0
|
||||
fn outer(n) do
|
||||
fn inner() do
|
||||
x += n
|
||||
n += 1
|
||||
x
|
||||
end
|
||||
end
|
||||
|
||||
var f = outer(2)
|
||||
var g = outer(6)
|
||||
f() + f() + g() + g() + f()
|
||||
",
|
||||
Value::Int(2 + 5 + 11 + 18 + 22),
|
||||
var f = outer(2)
|
||||
var g = outer(6)
|
||||
f() + f() + g() + g() + f()
|
||||
",
|
||||
Value::from(2 + 5 + 11 + 18 + 22),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -116,15 +116,15 @@ fn closures() {
|
|||
fn tailcall() {
|
||||
assert_eval(
|
||||
"
|
||||
fn test(n, a) do
|
||||
if n <= 0 then
|
||||
a
|
||||
else
|
||||
self(n-1, n+a)
|
||||
end
|
||||
end
|
||||
test(24, 0)
|
||||
",
|
||||
Value::Int(300),
|
||||
fn test(n, a) do
|
||||
if n <= 0 then
|
||||
a
|
||||
else
|
||||
self(n-1, n+a)
|
||||
end
|
||||
end
|
||||
test(24, 0)
|
||||
",
|
||||
Value::from(300),
|
||||
) // = 24 + 23 + ... + 1
|
||||
}
|
||||
|
|
|
@ -7,10 +7,11 @@ edition = "2021"
|
|||
talc-lang = { path = "../talc-lang" }
|
||||
talc-macros = { path = "../talc-macros" }
|
||||
lazy_static = "1.5"
|
||||
num-bigint = { version = "0.4", features = ["rand"], optional = true }
|
||||
regex = { version = "1.11", optional = true }
|
||||
rand = { version = "0.8", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["rand", "regex"]
|
||||
rand = ["dep:rand"]
|
||||
rand = ["dep:rand", "dep:num-bigint"]
|
||||
regex = ["dep:regex"]
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::cmp::Ordering;
|
|||
|
||||
use talc_lang::{
|
||||
exception::{exception, Result},
|
||||
number::Int,
|
||||
prelude::*,
|
||||
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||
throw,
|
||||
value::{function::NativeFunc, Value},
|
||||
|
@ -69,7 +71,7 @@ pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
Ok(col)
|
||||
}
|
||||
|
||||
fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<i64> {
|
||||
fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<Int> {
|
||||
let ord = vmcall!(vm; by, l, r)?;
|
||||
let Value::Int(ord) = ord else {
|
||||
throw!(
|
||||
|
@ -117,17 +119,17 @@ fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, u
|
|||
|
||||
while eq <= gt {
|
||||
let ord = call_comparison(vm, by.clone(), vals[eq].clone(), pivot.clone())?;
|
||||
match ord {
|
||||
..=-1 => {
|
||||
match ord.cmp(&Int::ZERO) {
|
||||
Ordering::Less => {
|
||||
vals.swap(eq, lt);
|
||||
lt += 1;
|
||||
eq += 1;
|
||||
}
|
||||
1.. => {
|
||||
Ordering::Greater => {
|
||||
vals.swap(eq, gt);
|
||||
gt -= 1;
|
||||
}
|
||||
0 => {
|
||||
Ordering::Equal => {
|
||||
eq += 1;
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +154,7 @@ fn insertion_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<()> {
|
|||
while j > 0 {
|
||||
let ord =
|
||||
call_comparison(vm, by.clone(), vals[j - 1].clone(), vals[j].clone())?;
|
||||
if ord <= 0 {
|
||||
if !ord.is_positive() {
|
||||
break
|
||||
}
|
||||
vals.swap(j, j - 1);
|
||||
|
@ -212,9 +214,9 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let a = vmcall!(vm; key.clone(), a)?;
|
||||
let b = vmcall!(vm; key.clone(), b)?;
|
||||
match a.partial_cmp(&b) {
|
||||
Some(Ordering::Greater) => Ok(Value::Int(1)),
|
||||
Some(Ordering::Equal) => Ok(Value::Int(0)),
|
||||
Some(Ordering::Less) => Ok(Value::Int(-1)),
|
||||
Some(Ordering::Greater) => Ok(Value::from(1)),
|
||||
Some(Ordering::Equal) => Ok(Value::from(0)),
|
||||
Some(Ordering::Less) => Ok(Value::from(-1)),
|
||||
None => throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"values returned from sort key were incomparable"
|
||||
|
|
|
@ -13,6 +13,7 @@ use lazy_static::lazy_static;
|
|||
use talc_lang::{
|
||||
exception::Result,
|
||||
lstring::LString,
|
||||
prelude::*,
|
||||
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||
throw,
|
||||
value::{function::NativeFunc, HashValue, NativeValue, Value},
|
||||
|
@ -294,7 +295,7 @@ pub fn read(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let Value::Int(nbytes) = nbytes else {
|
||||
throw!(*SYM_TYPE_ERROR, "read expected integer, got {nbytes:#}")
|
||||
};
|
||||
let Ok(nbytes) = usize::try_from(nbytes) else {
|
||||
let Some(nbytes) = nbytes.to_usize() else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"number of bytes to read must be nonnegative"
|
||||
|
@ -464,11 +465,14 @@ pub fn tcp_connect_timeout(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
|
||||
};
|
||||
let timeout = match timeout {
|
||||
Value::Int(n) if n >= 0 => Duration::from_secs(n as u64),
|
||||
Value::Int(n) => match n.to_usize() {
|
||||
Some(n) => Duration::from_secs(n as u64),
|
||||
None => throw!(*SYM_VALUE_ERROR, "tcp_connect_timeout: invalid timeout",),
|
||||
},
|
||||
Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => {
|
||||
Duration::from_secs_f64(n)
|
||||
}
|
||||
Value::Int(_) | Value::Float(_) => {
|
||||
Value::Float(_) => {
|
||||
throw!(*SYM_VALUE_ERROR, "tcp_connect_timeout: invalid timeout")
|
||||
}
|
||||
_ => throw!(
|
||||
|
@ -755,7 +759,7 @@ pub fn exit_code(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
match proc.wait() {
|
||||
Ok(code) => Ok(code
|
||||
.code()
|
||||
.map(|c| Value::Int(c as i64))
|
||||
.map(|c| Value::from(c as i64))
|
||||
.unwrap_or_default()),
|
||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||
}
|
||||
|
|
|
@ -3,10 +3,12 @@ use std::io::Write;
|
|||
use talc_lang::{
|
||||
exception::{exception, Result},
|
||||
lstring::{LStr, LString},
|
||||
parser::{parse_int, to_lstring_radix},
|
||||
number::{Complex, Int, Ratio},
|
||||
parser::parse_int,
|
||||
prelude::*,
|
||||
symbol::SYM_TYPE_ERROR,
|
||||
throw,
|
||||
value::{Complex64, Rational64, Value},
|
||||
value::Value,
|
||||
vm::Vm,
|
||||
};
|
||||
use talc_macros::native_func;
|
||||
|
@ -235,16 +237,16 @@ fn get_val(args: &[Value], i: FmtIndex, faidx: &mut usize) -> Result<usize> {
|
|||
let Value::Int(v) = v else {
|
||||
throw!(
|
||||
*SYM_FORMAT_ERROR,
|
||||
"expected positive integer argument, found {v:#}"
|
||||
"expected small positive integer argument, found {v:#}"
|
||||
)
|
||||
};
|
||||
if *v < 0 {
|
||||
throw!(
|
||||
match v.to_usize() {
|
||||
Some(v) => Ok(v),
|
||||
None => throw!(
|
||||
*SYM_FORMAT_ERROR,
|
||||
"expected positive integer argument, found {v}"
|
||||
)
|
||||
"expected small positive integer argument, found {v}"
|
||||
),
|
||||
}
|
||||
Ok(*v as usize)
|
||||
}
|
||||
|
||||
fn format_float(
|
||||
|
@ -270,7 +272,7 @@ fn format_float(
|
|||
}
|
||||
|
||||
fn format_complex(
|
||||
cx: Complex64,
|
||||
cx: Complex,
|
||||
prec: Option<usize>,
|
||||
ty: FmtType,
|
||||
buf: &mut LString,
|
||||
|
@ -286,7 +288,7 @@ fn format_complex(
|
|||
}
|
||||
|
||||
fn format_int(
|
||||
n: i64,
|
||||
n: &Int,
|
||||
prec: Option<usize>,
|
||||
ty: FmtType,
|
||||
buf: &mut LString,
|
||||
|
@ -296,26 +298,25 @@ fn format_int(
|
|||
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
||||
}
|
||||
let res = match ty {
|
||||
FmtType::Str => write!(buf, "{}", Value::Int(n)),
|
||||
FmtType::Repr => write!(buf, "{:#}", Value::Int(n)),
|
||||
FmtType::Hex(caps) => write!(buf, "{}", to_lstring_radix(n, 16, caps)),
|
||||
FmtType::Oct => write!(buf, "{}", to_lstring_radix(n, 8, false)),
|
||||
FmtType::Sex => write!(buf, "{}", to_lstring_radix(n, 6, false)),
|
||||
FmtType::Bin => write!(buf, "{}", to_lstring_radix(n, 2, false)),
|
||||
FmtType::Str | FmtType::Repr => write!(buf, "{}", n),
|
||||
FmtType::Hex(caps) => write!(buf, "{}", n.to_str_radix_case(16, caps)),
|
||||
FmtType::Oct => write!(buf, "{}", n.to_str_radix(8)),
|
||||
FmtType::Sex => write!(buf, "{}", n.to_str_radix(6)),
|
||||
FmtType::Bin => write!(buf, "{}", n.to_str_radix(2)),
|
||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
|
||||
};
|
||||
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||
}
|
||||
|
||||
fn format_ratio(
|
||||
n: Rational64,
|
||||
n: &Ratio,
|
||||
prec: Option<usize>,
|
||||
ty: FmtType,
|
||||
buf: &mut LString,
|
||||
) -> Result<()> {
|
||||
format_int(*n.numer(), prec, ty, buf, "ratio")?;
|
||||
format_int(&Int::from(n.numer()), prec, ty, buf, "ratio")?;
|
||||
buf.push_char('/');
|
||||
format_int(*n.denom(), prec, ty, buf, "ratio")?;
|
||||
format_int(&Int::from(n.denom()), prec, ty, buf, "ratio")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -383,8 +384,8 @@ fn format_arg(
|
|||
let width_val = fmtcode.width.map(|i| get_val(args, i, faidx)).transpose()?;
|
||||
|
||||
match fmt_arg {
|
||||
Value::Int(n) => format_int(*n, prec_val, fmtcode.ty, &mut buf, "integer")?,
|
||||
Value::Ratio(n) => format_ratio(*n, prec_val, fmtcode.ty, &mut buf)?,
|
||||
Value::Int(n) => format_int(n, prec_val, fmtcode.ty, &mut buf, "integer")?,
|
||||
Value::Ratio(n) => format_ratio(n, prec_val, fmtcode.ty, &mut buf)?,
|
||||
Value::Float(f) => format_float(*f, prec_val, fmtcode.ty, &mut buf, "float")?,
|
||||
Value::Complex(n) => format_complex(*n, prec_val, fmtcode.ty, &mut buf)?,
|
||||
Value::String(s) => format_string(s, prec_val, fmtcode.ty, &mut buf)?,
|
||||
|
|
452
talc-std/src/ints.rs
Normal file
452
talc-std/src/ints.rs
Normal 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))
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ use std::{
|
|||
use talc_lang::{
|
||||
exception::{throw, Result},
|
||||
lstring::LString,
|
||||
prelude::*,
|
||||
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||
value::Value,
|
||||
vm::Vm,
|
||||
|
@ -189,7 +190,14 @@ pub fn arg(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
throw!(*SYM_TYPE_ERROR, "arg expected integer, got {n:#}")
|
||||
};
|
||||
let cmd_args = vm.args();
|
||||
if n < 0 || (n as usize) >= cmd_args.len() {
|
||||
let Some(n) = n.to_usize() else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"arg number must be small positive integer, got {}",
|
||||
n,
|
||||
)
|
||||
};
|
||||
if n >= cmd_args.len() {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"arg number {} out of range for {} arguments",
|
||||
|
@ -197,7 +205,7 @@ pub fn arg(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
cmd_args.len()
|
||||
)
|
||||
}
|
||||
Ok(Value::from(cmd_args[n as usize].clone()))
|
||||
Ok(Value::from(cmd_args[n].clone()))
|
||||
}
|
||||
|
||||
#[native_func(0)]
|
||||
|
|
|
@ -2,9 +2,10 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
|||
|
||||
use talc_lang::{
|
||||
exception::Result,
|
||||
prelude::*,
|
||||
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||
throw,
|
||||
value::{function::NativeFunc, ops::RatioExt, range::RangeType, HashValue, Value},
|
||||
value::{function::NativeFunc, HashValue, Value},
|
||||
vm::Vm,
|
||||
vmcall, vmcalliter,
|
||||
};
|
||||
|
@ -186,7 +187,7 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let Value::Int(count) = count else {
|
||||
throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}")
|
||||
};
|
||||
let Ok(count) = count.try_into() else {
|
||||
let Some(count) = count.to_usize() else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"take expected nonnegative integer, got {count:#}"
|
||||
|
@ -215,10 +216,10 @@ pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let Value::Int(count) = count else {
|
||||
throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}")
|
||||
};
|
||||
let Ok(count) = count.try_into() else {
|
||||
let Some(count) = count.to_usize() else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"count expected nonnegative integer, got {count:#}"
|
||||
"count expected small nonnegative integer, got {count:#}"
|
||||
)
|
||||
};
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
@ -305,12 +306,12 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let Value::Int(by) = by else {
|
||||
throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}")
|
||||
};
|
||||
if by <= 0 {
|
||||
let Some(by) = by.to_usize() else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"step expected positive integer, got {by:#}"
|
||||
"step expected small positive integer, got {by:#}"
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let state = RefCell::new(Step::First);
|
||||
let f = move |vm: &mut Vm, _| match state.take() {
|
||||
|
@ -322,7 +323,7 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
Ok(Value::iter_pack(res))
|
||||
}
|
||||
Step::Going => {
|
||||
for _ in 0..(by - 1) {
|
||||
for _ in 1..by {
|
||||
if vmcall!(vm; iter.clone())? == Value::Nil {
|
||||
return Ok(Value::iter_pack(None))
|
||||
}
|
||||
|
@ -648,8 +649,10 @@ pub fn len(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
Value::String(s) => return Ok((s.chars().count() as i64).into()),
|
||||
Value::List(l) => return Ok((l.borrow().len() as i64).into()),
|
||||
Value::Table(t) => return Ok((t.borrow().len() as i64).into()),
|
||||
Value::Range(r) if r.ty != RangeType::Endless => {
|
||||
return Ok((r.len().unwrap() as i64).into())
|
||||
Value::Range(ref r) => {
|
||||
if let Some(len) = r.len() {
|
||||
return Ok(len.into())
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
@ -692,7 +695,7 @@ pub fn sum(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let mut result = Value::Int(0);
|
||||
let mut result = Value::from(0);
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
result = (result + value)?;
|
||||
}
|
||||
|
@ -704,7 +707,7 @@ pub fn prod(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let mut result = Value::Int(1);
|
||||
let mut result = Value::from(1);
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
result = (result * value)?;
|
||||
}
|
||||
|
@ -757,6 +760,10 @@ pub fn nth(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
throw!(*SYM_TYPE_ERROR, "nth expected integer")
|
||||
};
|
||||
|
||||
let Some(n) = n.to_usize() else {
|
||||
throw!(*SYM_VALUE_ERROR, "nth expected small positive integer")
|
||||
};
|
||||
|
||||
for _ in 0..n {
|
||||
if vmcalliter!(vm; iter.clone())?.is_none() {
|
||||
return Ok(Value::Nil)
|
||||
|
@ -849,8 +856,8 @@ pub fn count(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
while let Some(v) = vmcalliter!(vm; iter.clone())? {
|
||||
let hv = v.try_into()?;
|
||||
map.entry(hv)
|
||||
.and_modify(|v: &mut Value| *v = (v.clone() + Value::Int(1)).unwrap())
|
||||
.or_insert(Value::Int(1));
|
||||
.and_modify(|v: &mut Value| *v = (v.clone() + Value::from(1)).unwrap())
|
||||
.or_insert(Value::from(1));
|
||||
}
|
||||
|
||||
Ok(map.into())
|
||||
|
@ -862,7 +869,7 @@ pub fn mean(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let mut sum = Value::Float(0.0);
|
||||
let mut count = Value::Int(0);
|
||||
let mut count = Value::from(0);
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
sum = (sum + value)?;
|
||||
count = (count + Value::from(1))?;
|
||||
|
@ -876,11 +883,11 @@ fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> {
|
|||
let mut k = 1;
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
let old_m = m.clone();
|
||||
m = (m.clone() + ((value.clone() - m.clone())? / Value::Int(k))?)?;
|
||||
m = (m.clone() + ((value.clone() - m.clone())? / Value::from(k))?)?;
|
||||
s = (s + ((value.clone() - m.clone())? * (value - old_m)?)?)?;
|
||||
k += 1;
|
||||
}
|
||||
s / Value::Int(k - if pop { 1 } else { 2 })
|
||||
s / Value::from(k - if pop { 1 } else { 2 })
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
|
@ -898,9 +905,9 @@ pub fn stdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
|
||||
let v = variance_inner(vm, iter, false)?;
|
||||
Ok(match v {
|
||||
Value::Int(n) => Value::Float((n as f64).sqrt()),
|
||||
Value::Int(n) => Value::Float(n.to_f64_uc().sqrt()),
|
||||
Value::Float(f) => Value::Float(f.sqrt()),
|
||||
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
|
||||
Value::Ratio(r) => Value::Float(r.to_f64_uc().sqrt()),
|
||||
Value::Complex(c) => Value::Complex(c.sqrt()),
|
||||
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
|
||||
})
|
||||
|
@ -921,9 +928,9 @@ pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
|
||||
let v = variance_inner(vm, iter, true)?;
|
||||
Ok(match v {
|
||||
Value::Int(n) => Value::Float((n as f64).sqrt()),
|
||||
Value::Int(n) => Value::Float(n.to_f64_uc().sqrt()),
|
||||
Value::Float(f) => Value::Float(f.sqrt()),
|
||||
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
|
||||
Value::Ratio(r) => Value::Float(r.to_f64_uc().sqrt()),
|
||||
Value::Complex(c) => Value::Complex(c.sqrt()),
|
||||
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
|
||||
})
|
||||
|
@ -957,10 +964,10 @@ pub fn median(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let (_, _, _) = hi.select_nth_unstable(0);
|
||||
let m2 = vals.swap_remove(count / 2);
|
||||
let m1 = vals.swap_remove(count / 2 - 1);
|
||||
(m1.0 + m2.0)? / Value::Int(2)
|
||||
(m1.0 + m2.0)? / Value::from(2)
|
||||
} else {
|
||||
let (_, _, _) = vals.select_nth_unstable(count / 2);
|
||||
let m = vals.swap_remove(count / 2);
|
||||
m.0 / Value::Int(1)
|
||||
m.0 / Value::from(1)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,10 @@ pub mod collection;
|
|||
pub mod exception;
|
||||
pub mod file;
|
||||
pub mod format;
|
||||
pub mod ints;
|
||||
pub mod io;
|
||||
pub mod iter;
|
||||
pub mod num;
|
||||
pub mod math;
|
||||
pub mod string;
|
||||
pub mod value;
|
||||
|
||||
|
@ -27,7 +28,8 @@ pub fn load_all(vm: &mut Vm) {
|
|||
format::load(vm);
|
||||
io::load(vm);
|
||||
iter::load(vm);
|
||||
num::load(vm);
|
||||
math::load(vm);
|
||||
ints::load(vm);
|
||||
string::load(vm);
|
||||
value::load(vm);
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use talc_lang::{
|
||||
exception::Result,
|
||||
parser::{parse_int, to_lstring_radix},
|
||||
number::{Complex, Int, Ratio},
|
||||
prelude::*,
|
||||
symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||
throw,
|
||||
value::{ops::RatioExt, Complex64, Value},
|
||||
value::Value,
|
||||
vm::Vm,
|
||||
vmcalliter,
|
||||
};
|
||||
use talc_macros::native_func;
|
||||
|
||||
|
@ -25,8 +23,8 @@ lazy_static! {
|
|||
#[inline]
|
||||
fn to_floaty(v: Value) -> Value {
|
||||
match v {
|
||||
Value::Int(v) => Value::Float(v as f64),
|
||||
Value::Ratio(v) => Value::Float(v.to_f64()),
|
||||
Value::Int(v) => Value::Float(v.to_f64_uc()),
|
||||
Value::Ratio(v) => Value::Float(v.to_f64_uc()),
|
||||
Value::Float(v) => Value::Float(v),
|
||||
Value::Complex(v) => Value::Complex(v),
|
||||
v => v,
|
||||
|
@ -36,8 +34,8 @@ fn to_floaty(v: Value) -> Value {
|
|||
#[inline]
|
||||
fn to_complex(v: Value) -> Value {
|
||||
match v {
|
||||
Value::Int(v) => Value::Complex((v as f64).into()),
|
||||
Value::Ratio(v) => Value::Complex(v.to_f64().into()),
|
||||
Value::Int(v) => Value::Complex(v.to_f64_uc().into()),
|
||||
Value::Ratio(v) => Value::Complex(v.to_f64_uc().into()),
|
||||
Value::Float(v) => Value::Complex(v.into()),
|
||||
Value::Complex(v) => Value::Complex(v),
|
||||
v => v,
|
||||
|
@ -57,8 +55,8 @@ pub fn load(vm: &mut Vm) {
|
|||
|
||||
vm.set_global_name("inf", (f64::INFINITY).into());
|
||||
vm.set_global_name("NaN", (f64::NAN).into());
|
||||
vm.set_global_name("infi", Complex64::new(0.0, f64::INFINITY).into());
|
||||
vm.set_global_name("NaNi", Complex64::new(0.0, f64::NAN).into());
|
||||
vm.set_global_name("infi", Complex::new(0.0, f64::INFINITY).into());
|
||||
vm.set_global_name("NaNi", Complex::new(0.0, f64::NAN).into());
|
||||
|
||||
vm.set_global_name("bin", bin().into());
|
||||
vm.set_global_name("sex", sex().into());
|
||||
|
@ -68,15 +66,6 @@ pub fn load(vm: &mut Vm) {
|
|||
vm.set_global_name("to_radix_upper", to_radix_upper().into());
|
||||
vm.set_global_name("from_radix", from_radix().into());
|
||||
|
||||
vm.set_global_name("gcd", gcd().into());
|
||||
vm.set_global_name("gcdn", gcdn().into());
|
||||
vm.set_global_name("lcm", lcm().into());
|
||||
vm.set_global_name("lcmn", lcmn().into());
|
||||
vm.set_global_name("isqrt", isqrt().into());
|
||||
vm.set_global_name("isprime", isprime().into());
|
||||
vm.set_global_name("factors", factors().into());
|
||||
vm.set_global_name("totient", totient().into());
|
||||
|
||||
vm.set_global_name("min", min().into());
|
||||
vm.set_global_name("max", max().into());
|
||||
vm.set_global_name("floor", floor().into());
|
||||
|
@ -137,7 +126,7 @@ pub fn bin(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}")
|
||||
};
|
||||
Ok(to_lstring_radix(x, 2, false).into())
|
||||
Ok(Value::from(x.to_str_radix_case(2, false)))
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
|
@ -146,7 +135,7 @@ pub fn sex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "sex expected integer argument, got {x:#}")
|
||||
};
|
||||
Ok(to_lstring_radix(x, 6, false).into())
|
||||
Ok(Value::from(x.to_str_radix_case(6, false)))
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
|
@ -155,7 +144,7 @@ pub fn oct(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "oct expected integer argument, got {x:#}")
|
||||
};
|
||||
Ok(to_lstring_radix(x, 8, false).into())
|
||||
Ok(Value::from(x.to_str_radix_case(8, false)))
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
|
@ -164,7 +153,7 @@ pub fn hex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "hex expected integer argument, got {x:#}")
|
||||
};
|
||||
Ok(to_lstring_radix(x, 16, false).into())
|
||||
Ok(Value::from(x.to_str_radix_case(16, false)))
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
|
@ -176,10 +165,13 @@ pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
"to_radix expected integer arguments, got {x:#} and {radix:#}"
|
||||
)
|
||||
};
|
||||
if *radix < 2 || *radix > 36 {
|
||||
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 0..=36")
|
||||
let Some(radix) = radix.to_u32() else {
|
||||
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 2..=36")
|
||||
};
|
||||
if !(2..=36).contains(&radix) {
|
||||
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 2..=36")
|
||||
}
|
||||
Ok(to_lstring_radix(*x, *radix as u32, false).into())
|
||||
Ok(Value::from(x.to_str_radix_case(radix, false)))
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
|
@ -191,13 +183,19 @@ pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
"to_radix_upper expected integer arguments, got {x:#} and {radix:#}"
|
||||
)
|
||||
};
|
||||
if *radix < 2 || *radix > 36 {
|
||||
let Some(radix) = radix.to_u32() else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"to_radix_upper expected radix in range 0..=36"
|
||||
"to_radix_upper expected radix in range 2..=36"
|
||||
)
|
||||
};
|
||||
if !(2..=36).contains(&radix) {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"to_radix_upper expected radix in range 2..=36"
|
||||
)
|
||||
}
|
||||
Ok(to_lstring_radix(*x, *radix as u32, true).into())
|
||||
Ok(Value::from(x.to_str_radix_case(radix, true)))
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
|
@ -209,285 +207,32 @@ pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
"from_radix expected string and integer arguments, got {s:#} and {radix:#}"
|
||||
)
|
||||
};
|
||||
if *radix < 2 || *radix > 36 {
|
||||
let Some(radix) = radix.to_u32() else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"from_radix expected radix in range 0..=36"
|
||||
"from_radix expected radix in range 2..=36"
|
||||
)
|
||||
};
|
||||
if !(2..=36).contains(&radix) {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"from_radix expected radix in range 2..=36"
|
||||
)
|
||||
}
|
||||
match parse_int(s.as_ref(), *radix as u32) {
|
||||
let Ok(s) = s.to_str() else {
|
||||
throw!(*SYM_VALUE_ERROR, "from_radix: string has invalid UTF-8",)
|
||||
};
|
||||
match Int::from_str_radix(s, radix) {
|
||||
Ok(v) => Ok(v.into()),
|
||||
Err(_) => throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"string was not a valid integer in given radix"
|
||||
"from_radix: string {:#} is not a valid integer in radix {}",
|
||||
s,
|
||||
radix
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// integer operations
|
||||
//
|
||||
|
||||
fn isqrt_inner(mut n: i64) -> i64 {
|
||||
assert!(n >= 0, "isqrt input should be nonnegative");
|
||||
if n < 2 {
|
||||
return n
|
||||
}
|
||||
|
||||
let mut c = 0;
|
||||
let mut d = 1 << 62;
|
||||
|
||||
while d > n {
|
||||
d >>= 2;
|
||||
}
|
||||
|
||||
while d != 0 {
|
||||
if n >= c + d {
|
||||
n -= c + d;
|
||||
c = (c >> 1) + d;
|
||||
} else {
|
||||
c >>= 1;
|
||||
}
|
||||
d >>= 2;
|
||||
}
|
||||
c
|
||||
}
|
||||
|
||||
pub fn gcd_inner(a: i64, b: i64) -> i64 {
|
||||
let (mut a, mut b) = (a.abs(), b.abs());
|
||||
if a == 0 {
|
||||
return b
|
||||
}
|
||||
if b == 0 {
|
||||
return a
|
||||
}
|
||||
|
||||
let az = a.trailing_zeros();
|
||||
a >>= az;
|
||||
let bz = b.trailing_zeros();
|
||||
b >>= bz;
|
||||
let z = az.min(bz);
|
||||
|
||||
loop {
|
||||
if a > b {
|
||||
std::mem::swap(&mut a, &mut b);
|
||||
}
|
||||
b -= a;
|
||||
if b == 0 {
|
||||
return a << z
|
||||
}
|
||||
b >>= b.trailing_zeros();
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(
|
||||
*SYM_TYPE_ERROR,
|
||||
"isqrt expected integer argument, got {x:#}"
|
||||
)
|
||||
};
|
||||
if x < 0 {
|
||||
throw!(*SYM_VALUE_ERROR, "isqrt: argument must be positive")
|
||||
}
|
||||
Ok(isqrt_inner(x).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(
|
||||
*SYM_TYPE_ERROR,
|
||||
"isprime expected integer argument, got {x:#}"
|
||||
)
|
||||
};
|
||||
if x < 2 {
|
||||
return Ok(false.into())
|
||||
}
|
||||
for p in [2, 3, 5, 7] {
|
||||
if x % p == 0 {
|
||||
return Ok((x == p).into())
|
||||
}
|
||||
}
|
||||
if x < 11 {
|
||||
return Ok(false.into())
|
||||
}
|
||||
let lim = isqrt_inner(x);
|
||||
let mut i = 12;
|
||||
while i <= lim + 1 {
|
||||
if x % (i - 1) == 0 {
|
||||
return Ok(false.into())
|
||||
}
|
||||
if x % (i + 1) == 0 {
|
||||
return Ok(false.into())
|
||||
}
|
||||
i += 6;
|
||||
}
|
||||
Ok(true.into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x, y] = unpack_args!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
|
||||
};
|
||||
let Value::Int(y) = y else {
|
||||
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {y:#}")
|
||||
};
|
||||
Ok(gcd_inner(x, y).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, args] = unpack_args!(args);
|
||||
let args = args.to_iter_function()?;
|
||||
|
||||
let mut g = 0;
|
||||
|
||||
while let Some(a) = vmcalliter!(vm; args.clone())? {
|
||||
let Value::Int(a) = a else {
|
||||
throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}")
|
||||
};
|
||||
g = gcd_inner(g, a);
|
||||
}
|
||||
|
||||
Ok(g.into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x, y] = unpack_args!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
|
||||
};
|
||||
let Value::Int(y) = y else {
|
||||
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}")
|
||||
};
|
||||
let g = gcd_inner(x, y);
|
||||
if g == 0 {
|
||||
Ok(Value::from(0))
|
||||
} else {
|
||||
(Value::from(x) / Value::from(g))? * Value::from(y)
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, args] = unpack_args!(args);
|
||||
let args = args.to_iter_function()?;
|
||||
|
||||
let mut l = 1;
|
||||
|
||||
while let Some(a) = vmcalliter!(vm; args.clone())? {
|
||||
let Value::Int(a) = a else {
|
||||
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
|
||||
};
|
||||
let g = gcd_inner(l, a);
|
||||
if g == 0 {
|
||||
return Ok(Value::from(0))
|
||||
};
|
||||
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?;
|
||||
let Value::Int(new_l) = new_l else {
|
||||
unreachable!("int//int * int != int")
|
||||
};
|
||||
l = new_l;
|
||||
}
|
||||
|
||||
Ok(Value::from(l))
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
let Value::Int(mut x) = x else {
|
||||
throw!(
|
||||
*SYM_TYPE_ERROR,
|
||||
"factors expected integer argument, got {x:#}"
|
||||
)
|
||||
};
|
||||
let mut factors = Vec::new();
|
||||
if x <= 1 {
|
||||
return Ok(factors.into())
|
||||
}
|
||||
while x & 1 == 0 {
|
||||
x >>= 1;
|
||||
factors.push(Value::Int(2));
|
||||
}
|
||||
while x % 3 == 0 {
|
||||
x /= 3;
|
||||
factors.push(Value::Int(3));
|
||||
}
|
||||
let mut i = 5;
|
||||
while x >= i * i {
|
||||
while x % i == 0 {
|
||||
x /= i;
|
||||
factors.push(Value::Int(i));
|
||||
}
|
||||
i += 2;
|
||||
while x % i == 0 {
|
||||
x /= i;
|
||||
factors.push(Value::Int(i));
|
||||
}
|
||||
i += 4;
|
||||
}
|
||||
if x > 1 {
|
||||
factors.push(Value::Int(x));
|
||||
}
|
||||
Ok(factors.into())
|
||||
}
|
||||
|
||||
fn totient_prime(n: &mut u64, p: u64) -> u64 {
|
||||
if *n % p != 0 {
|
||||
return 1
|
||||
}
|
||||
*n /= p;
|
||||
let mut v = p - 1;
|
||||
while *n % p == 0 {
|
||||
*n /= p;
|
||||
v *= p;
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(
|
||||
*SYM_TYPE_ERROR,
|
||||
"totient expected integer argument, got {x:#}"
|
||||
)
|
||||
};
|
||||
if x <= 1 {
|
||||
return Ok(1.into())
|
||||
}
|
||||
let mut x = x as u64;
|
||||
let mut totient = 1;
|
||||
if x & 1 == 0 {
|
||||
x >>= 1;
|
||||
}
|
||||
while x & 1 == 0 {
|
||||
x >>= 1;
|
||||
totient <<= 1;
|
||||
}
|
||||
totient *= totient_prime(&mut x, 3);
|
||||
let mut i = 5;
|
||||
while x >= i * i {
|
||||
totient *= totient_prime(&mut x, i);
|
||||
i += 2;
|
||||
totient *= totient_prime(&mut x, i);
|
||||
i += 4;
|
||||
}
|
||||
if x > 1 {
|
||||
totient *= x - 1;
|
||||
}
|
||||
Ok((totient as i64).into())
|
||||
}
|
||||
|
||||
//
|
||||
// numeric operations
|
||||
//
|
||||
|
@ -518,7 +263,7 @@ pub fn floor(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
match x {
|
||||
Value::Int(x) => Ok(Value::Int(x)),
|
||||
Value::Float(x) => Ok(Value::Float(x.floor())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.floor())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.floor().into())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "floor expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
@ -529,7 +274,7 @@ pub fn ceil(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
match x {
|
||||
Value::Int(x) => Ok(Value::Int(x)),
|
||||
Value::Float(x) => Ok(Value::Float(x.ceil())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.ceil())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.ceil().into())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "ceil expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
@ -540,7 +285,7 @@ pub fn round(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
match x {
|
||||
Value::Int(x) => Ok(Value::Int(x)),
|
||||
Value::Float(x) => Ok(Value::Float(x.round())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.round())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.round().into())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "round expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
@ -551,7 +296,7 @@ pub fn trunc(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
match x {
|
||||
Value::Int(x) => Ok(Value::Int(x)),
|
||||
Value::Float(x) => Ok(Value::Float(x.trunc())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.trunc())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.trunc().into())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "trunc expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
@ -560,9 +305,9 @@ pub fn trunc(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
pub fn fract(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
match x {
|
||||
Value::Int(_) => Ok(Value::Int(0)),
|
||||
Value::Int(_) => Ok(Value::from(0)),
|
||||
Value::Float(x) => Ok(Value::Float(x.fract())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.fract())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.fract().into())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "fract expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
@ -572,12 +317,8 @@ pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let [_, x] = unpack_args!(args);
|
||||
match x {
|
||||
Value::Int(x) => Ok(Value::Int(x.signum())),
|
||||
Value::Float(x) => Ok(Value::Float(if x == 0.0 { 0.0 } else { x.signum() })),
|
||||
Value::Ratio(x) => match x.cmp(&0.into()) {
|
||||
Ordering::Greater => Ok(Value::Ratio(1.into())),
|
||||
Ordering::Less => Ok(Value::Ratio((-1).into())),
|
||||
Ordering::Equal => Ok(Value::Ratio(0.into())),
|
||||
},
|
||||
Value::Float(x) => Ok(Value::Float(if x == 0.0 { x } else { x.signum() })),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.signum().into())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
@ -666,7 +407,7 @@ pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
"float_to_bits expected float argument, got {val:#}"
|
||||
)
|
||||
};
|
||||
Ok(Value::Int(f.to_bits() as i64))
|
||||
Ok(Value::Int(Int::from_u64(f.to_bits()).unwrap()))
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
|
@ -678,7 +419,13 @@ pub fn float_of_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
"float_of_bits expected integer argument, got {val:#}"
|
||||
)
|
||||
};
|
||||
Ok(Value::Float(f64::from_bits(i as u64)))
|
||||
let Some(v) = i.to_u64() else {
|
||||
throw!(
|
||||
*SYM_TYPE_ERROR,
|
||||
"float_of_bits expected integer in 0..2^64, got {i}"
|
||||
)
|
||||
};
|
||||
Ok(Value::Float(f64::from_bits(v)))
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -690,7 +437,7 @@ pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let [_, v] = unpack_args!(args);
|
||||
match v {
|
||||
Value::Int(x) => Ok(Value::Int(x)),
|
||||
Value::Ratio(x) => Ok(Value::Int(*x.numer())),
|
||||
Value::Ratio(x) => Ok(Value::from(Int::from(x.numer()))),
|
||||
v => throw!(
|
||||
*SYM_TYPE_ERROR,
|
||||
"numer expected rational argument, got {v:#}"
|
||||
|
@ -702,8 +449,8 @@ pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, v] = unpack_args!(args);
|
||||
match v {
|
||||
Value::Int(_) => Ok(Value::Int(1)),
|
||||
Value::Ratio(x) => Ok(Value::Int(*x.denom())),
|
||||
Value::Int(_) => Ok(Value::from(1)),
|
||||
Value::Ratio(x) => Ok(Value::from(Int::from(x.denom()))),
|
||||
v => throw!(
|
||||
*SYM_TYPE_ERROR,
|
||||
"denom expected rational argument, got {v:#}"
|
||||
|
@ -731,8 +478,8 @@ pub fn re(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
pub fn im(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, v] = unpack_args!(args);
|
||||
match to_complex(v) {
|
||||
Value::Int(_) => Ok(Value::Int(0)),
|
||||
Value::Ratio(_) => Ok(Value::Ratio(0.into())),
|
||||
Value::Int(_) => Ok(Value::from(0)),
|
||||
Value::Ratio(_) => Ok(Value::Ratio(Ratio::zero().into())),
|
||||
Value::Float(_) => Ok(Value::Float(0.0)),
|
||||
Value::Complex(z) => Ok(Value::Float(z.im)),
|
||||
v => throw!(*SYM_TYPE_ERROR, "im expected numeric argument, got {v:#}"),
|
|
@ -1,9 +1,11 @@
|
|||
use num_bigint::RandBigInt;
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
use talc_lang::{
|
||||
exception::Result,
|
||||
number::Int,
|
||||
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||
throw,
|
||||
value::{range::RangeType, Value},
|
||||
value::Value,
|
||||
vm::Vm,
|
||||
vmcalliter,
|
||||
};
|
||||
|
@ -46,14 +48,13 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
if r.is_empty() {
|
||||
throw!(*SYM_VALUE_ERROR, "rand_in: empty range")
|
||||
}
|
||||
match r.ty {
|
||||
RangeType::Open => {
|
||||
Ok(Value::Int(rand::thread_rng().gen_range(r.start..r.stop)))
|
||||
}
|
||||
RangeType::Closed => {
|
||||
Ok(Value::Int(rand::thread_rng().gen_range(r.start..=r.stop)))
|
||||
}
|
||||
RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"),
|
||||
let (Some(s), Some(e)) = (&r.start, &r.end) else {
|
||||
throw!(*SYM_VALUE_ERROR, "rand_in: infinite range")
|
||||
};
|
||||
if r.inclusive {
|
||||
Ok(Int::from(rand::thread_rng().gen_bigint_range(s, &(e + 1))).into())
|
||||
} else {
|
||||
Ok(Int::from(rand::thread_rng().gen_bigint_range(s, e)).into())
|
||||
}
|
||||
}
|
||||
col => {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use talc_lang::{
|
||||
exception::Result,
|
||||
lstring::LString,
|
||||
prelude::*,
|
||||
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||
throw,
|
||||
value::Value,
|
||||
|
@ -40,7 +41,7 @@ pub fn ord(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
if chars.next().is_some() {
|
||||
throw!(*SYM_VALUE_ERROR, "argument to ord must have length 1")
|
||||
};
|
||||
Ok(Value::Int(c as u32 as i64))
|
||||
Ok(Value::from(c as u32 as i64))
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
|
@ -49,7 +50,7 @@ pub fn chr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let Value::Int(i) = i else {
|
||||
throw!(*SYM_TYPE_ERROR, "chr expected integer argument, got {i:#}")
|
||||
};
|
||||
let Ok(i) = u32::try_from(i) else {
|
||||
let Some(i) = i.to_u32() else {
|
||||
throw!(*SYM_VALUE_ERROR, "argument to chr is not a valid codepoint")
|
||||
};
|
||||
let Some(c) = char::from_u32(i) else {
|
||||
|
@ -67,7 +68,7 @@ pub fn len_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
"len_bytes expected string argument, got {s:#}"
|
||||
)
|
||||
};
|
||||
Ok(Value::Int(s.len() as i64))
|
||||
Ok(Value::from(s.len() as i64))
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
|
@ -205,12 +206,20 @@ pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
let bytes: Vec<u8> = b
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|v| match v {
|
||||
Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8),
|
||||
_ => throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"str_of_bytes expected list of integers in 0..=255"
|
||||
),
|
||||
.map(|v| {
|
||||
let Value::Int(i) = v else {
|
||||
throw!(
|
||||
*SYM_TYPE_ERROR,
|
||||
"str_of_bytes expected list of integers in 0..=255"
|
||||
)
|
||||
};
|
||||
let Some(n) = i.to_u8() else {
|
||||
throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"str_of_bytes expected list of integers in 0..=255"
|
||||
)
|
||||
};
|
||||
Ok(n)
|
||||
})
|
||||
.collect::<Result<Vec<u8>>>()?;
|
||||
Ok(LString::from(bytes).into())
|
||||
|
|
|
@ -3,10 +3,12 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
|||
use talc_lang::{
|
||||
exception::{exception, Result},
|
||||
lformat,
|
||||
number::{Int, Ratio},
|
||||
parser::{parse_float, parse_int},
|
||||
prelude::*,
|
||||
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||
throw,
|
||||
value::{ops::RatioExt, HashValue, Rational64, Value},
|
||||
value::{HashValue, Value},
|
||||
vm::Vm,
|
||||
};
|
||||
use talc_macros::native_func;
|
||||
|
@ -71,24 +73,22 @@ pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
(v, b"string") => Ok(Value::String(lformat!("{v}").into())),
|
||||
(v, b"bool") => Ok(Value::Bool(v.truthy())),
|
||||
|
||||
(Value::Symbol(s), b"int") => Ok(Value::Int(s.id() as i64)),
|
||||
(Value::Symbol(s), b"int") => Ok(Value::from(s.id() as i64)),
|
||||
|
||||
(Value::Int(x), b"ratio") => Ok(Value::Ratio(x.into())),
|
||||
(Value::Int(x), b"float") => Ok(Value::Float(x as f64)),
|
||||
(Value::Int(x), b"complex") => Ok(Value::Complex((x as f64).into())),
|
||||
(Value::Ratio(x), b"int") => Ok(Value::Int(x.to_integer())),
|
||||
(Value::Ratio(x), b"float") => Ok(Value::Float(x.to_f64())),
|
||||
(Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64().into())),
|
||||
(Value::Float(x), b"int") => Ok(Value::Int(x as i64)),
|
||||
(Value::Float(x), b"ratio") => {
|
||||
let r = Rational64::approximate_float(x).ok_or_else(|| {
|
||||
exception!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"float {x:?} could not be converted to ratio"
|
||||
)
|
||||
})?;
|
||||
Ok(Value::Ratio(r))
|
||||
}
|
||||
(Value::Int(x), b"ratio") => Ok(Value::from(Ratio::from_integer(x.into()))),
|
||||
(Value::Int(x), b"float") => Ok(Value::Float(x.to_f64_uc())),
|
||||
(Value::Int(x), b"complex") => Ok(Value::Complex(x.to_f64_uc().into())),
|
||||
(Value::Ratio(x), b"int") => Ok(Value::from(x.to_integer())),
|
||||
(Value::Ratio(x), b"float") => Ok(Value::Float(x.to_f64_uc())),
|
||||
(Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64_uc().into())),
|
||||
(Value::Float(x), b"int") => Ok(Value::from(Int::from_f64(x).unwrap_or_default())),
|
||||
(Value::Float(x), b"ratio") => match Ratio::approximate_float(x) {
|
||||
Some(v) => Ok(Value::from(v)),
|
||||
None => throw!(
|
||||
*SYM_VALUE_ERROR,
|
||||
"float {x} could not be converted to ratio"
|
||||
),
|
||||
},
|
||||
(Value::Float(x), b"complex") => Ok(Value::Complex(x.into())),
|
||||
|
||||
(Value::String(s), b"int") => parse_int(s.as_ref(), 10)
|
||||
|
|
Loading…
Reference in a new issue