refactoring
All checks were successful
docs / test (push) Successful in 10s

This commit is contained in:
trimill 2024-12-31 22:09:51 -05:00
parent 668e0712c8
commit b1fccce8e3
24 changed files with 276 additions and 165 deletions

View file

@ -2,6 +2,24 @@
members = ["talc-lang", "talc-bin", "talc-std", "talc-macros"]
resolver = "2"
[workspace.lints.clippy]
semicolon_if_nothing_returned = "warn"
allow_attributes = "warn"
inconsistent_struct_constructor = "warn"
uninlined_format_args = "warn"
single_match_else = "warn"
redundant_closure_for_method_calls = "warn"
redundant_closure = "warn"
redundant_closure_call = "warn"
map_unwrap_or = "warn"
map_clone = "warn"
manual_assert = "warn"
needless_pass_by_value = "warn"
unnested_or_patterns = "warn"
redundant_else = "warn"
[profile.release-opt]
inherits = "release"
lto = "fat"
@ -10,3 +28,4 @@ codegen-units = 1
panic = "abort"

View file

@ -4,6 +4,9 @@ version = "0.2.2"
edition = "2021"
rust-version = "1.81.0"
[lints]
workspace = true
[[bin]]
name = "talc"
path = "src/main.rs"

View file

@ -86,7 +86,7 @@ impl Highlighter for TalcHelper {
buf += format;
buf += token.content;
if !format.is_empty() {
buf += "\x1b[0m"
buf += "\x1b[0m";
}
}
buf += &line[(last.idx as usize)..];
@ -126,7 +126,7 @@ impl Validator for TalcHelper {
match k {
K::Eof => break,
K::LParen | K::LBrack | K::LBrace | K::If | K::While | K::For | K::Try => {
delims.push(token.kind)
delims.push(token.kind);
}
K::RParen => match delims.pop() {
Some(K::LParen) => (),

View file

@ -68,7 +68,7 @@ fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
}
if args.show_ast {
eprintln!("{}", ex);
eprintln!("{ex}");
}
let func = match compile(&ex, Some(name)) {

View file

@ -99,7 +99,7 @@ fn read_init_file(args: &Args) -> Result<Option<String>, std::io::Error> {
}
fn exec_line(
vm: Rc<RefCell<Vm>>,
vm: &mut Vm,
line: &str,
args: &Args,
c: &ReplColors,
@ -118,7 +118,7 @@ fn exec_line(
}
if args.show_ast {
eprintln!("{}", ex);
eprintln!("{ex}");
}
let func = match compile_repl(&ex, globals) {
@ -135,7 +135,7 @@ fn exec_line(
}
}
let mut vm = vm.borrow_mut();
//let mut vm = vm.borrow_mut();
match vm.run_function(func.clone(), vec![func.into()]) {
Ok(v) => {
let prev2 = vm.get_global(*SYM_PREV2).unwrap().clone();
@ -196,7 +196,7 @@ pub fn repl(args: &Args) -> ExitCode {
rl.set_helper(Some(TalcHelper::new(vm.clone())));
if let Some(src) = init_src {
exec_line(vm.clone(), &src, args, &c, &mut globals);
exec_line(&mut vm.borrow_mut(), &src, args, &c, &mut globals);
}
loop {
@ -214,6 +214,6 @@ pub fn repl(args: &Args) -> ExitCode {
}
};
exec_line(vm.clone(), &line, args, &c, &mut globals);
exec_line(&mut vm.borrow_mut(), &line, args, &c, &mut globals);
}
}

View file

@ -4,6 +4,9 @@ version = "0.2.2"
edition = "2021"
rust-version = "1.81.0"
[lints]
workspace = true
[dependencies]
num = { version = "0.4", features = [] }
lazy_static = "1.5"

View file

@ -100,7 +100,7 @@ pub fn compile_repl(expr: &Expr, globals: &mut Vec<Symbol>) -> Result<Function>
Ok(comp.finish_repl(globals))
}
impl<'a> Default for Compiler<'a> {
impl Default for Compiler<'_> {
fn default() -> Self {
let mut scope = HashMap::new();
scope.insert(*SYM_SELF, VarKind::Local(0));
@ -355,15 +355,13 @@ impl<'a> Compiler<'a> {
self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
}
ResolveOutcome::InParent => {
let n = match self.closes.iter().position(|(n, _)| *n == name) {
Some(n) => n,
None => {
let n = self.closes.len();
self.closes.push((name, n));
n
}
};
self.emit(I::LoadUpvalue(Arg24::from_usize(n)));
let pos = self.closes.iter().position(|(n, _)| *n == name);
let idx = pos.unwrap_or_else(|| {
let idx = self.closes.len();
self.closes.push((name, idx));
idx
});
self.emit(I::LoadUpvalue(Arg24::from_usize(idx)));
}
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
self.emit(I::LoadGlobal(Arg24::from_symbol(name)));
@ -412,15 +410,13 @@ impl<'a> Compiler<'a> {
self.emit(I::StoreClosedLocal(Arg24::from_usize(n)));
}
ResolveOutcome::InParent => {
let n = match self.closes.iter().position(|(n, _)| *n == name) {
Some(n) => n,
None => {
let n = self.closes.len();
self.closes.push((name, n));
n
}
};
self.emit(I::StoreUpvalue(Arg24::from_usize(n)));
let pos = self.closes.iter().position(|(n, _)| *n == name);
let idx = pos.unwrap_or_else(|| {
let idx = self.closes.len();
self.closes.push((name, idx));
idx
});
self.emit(I::StoreUpvalue(Arg24::from_usize(idx)));
}
ResolveOutcome::Var(VarKind::Global) => {
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
@ -878,7 +874,7 @@ impl<'a> Compiler<'a> {
self.emit(I::ListDestructure(
i.unwrap_or(len),
i.map(|n| len - n - 1).unwrap_or(0),
i.map_or(0, |n| len - n - 1),
i.is_some(),
));

View file

@ -1,9 +1,9 @@
use crate::{
lstring::LStr,
symbol::{Symbol, SYM_MSG, SYM_TYPE},
value::{HashValue, Value},
value::Value,
};
use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc};
use std::{fmt::Display, rc::Rc};
pub type Result<T> = std::result::Result<T, Exception>;
@ -18,7 +18,10 @@ impl Exception {
Self { ty, msg }
}
pub fn from_table(table: &Rc<RefCell<HashMap<HashValue, Value>>>) -> Option<Self> {
pub fn from_value(value: &Value) -> Option<Self> {
let Value::Table(table) = value else {
return None
};
let table = table.borrow();
let ty = table.get(&(*SYM_TYPE).into())?;
let msg = table.get(&(*SYM_MSG).into())?;
@ -32,11 +35,11 @@ impl Exception {
}
}
pub fn to_table(self) -> Rc<RefCell<HashMap<HashValue, Value>>> {
let mut table = HashMap::new();
pub fn to_value(self) -> Value {
Value::new_table(|table| {
table.insert((*SYM_TYPE).into(), self.ty.into());
table.insert((*SYM_MSG).into(), Value::String(self.msg));
Rc::new(RefCell::new(table))
})
}
}

View file

@ -1,9 +1,3 @@
#![allow(clippy::mutable_key_type)]
#![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::allow_attributes)]
#![warn(clippy::inconsistent_struct_constructor)]
#![warn(clippy::uninlined_format_args)]
pub mod chunk;
pub mod compiler;
pub mod exception;

View file

@ -621,7 +621,7 @@ impl LString {
#[derive(Clone)]
pub struct Bytes<'a>(Copied<slice::Iter<'a, u8>>);
impl<'a> Iterator for Bytes<'a> {
impl Iterator for Bytes<'_> {
type Item = u8;
#[inline]
@ -662,19 +662,19 @@ impl<'a> Iterator for Bytes<'a> {
}
}
impl<'a> ExactSizeIterator for Bytes<'a> {
impl ExactSizeIterator for Bytes<'_> {
#[inline]
fn len(&self) -> usize {
self.0.len()
}
}
impl<'a> FusedIterator for Bytes<'a> {}
impl FusedIterator for Bytes<'_> {}
#[derive(Clone)]
pub struct LosslessChars<'a>(&'a [u8]);
impl<'a> Iterator for LosslessChars<'a> {
impl Iterator for LosslessChars<'_> {
type Item = Result<char, u8>;
#[inline]
@ -691,7 +691,7 @@ impl<'a> Iterator for LosslessChars<'a> {
}
}
impl<'a> DoubleEndedIterator for LosslessChars<'a> {
impl DoubleEndedIterator for LosslessChars<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
let (new_bytes, res) = next_codepoint_back(self.0)?;
self.0 = new_bytes;
@ -702,7 +702,7 @@ impl<'a> DoubleEndedIterator for LosslessChars<'a> {
#[derive(Clone)]
pub struct LosslessCharsIndices<'a>(&'a [u8], usize);
impl<'a> Iterator for LosslessCharsIndices<'a> {
impl Iterator for LosslessCharsIndices<'_> {
type Item = (usize, Result<char, u8>);
#[inline]
@ -724,7 +724,7 @@ impl<'a> Iterator for LosslessCharsIndices<'a> {
}
}
impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> {
impl DoubleEndedIterator for LosslessCharsIndices<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
let (new_bytes, res) = next_codepoint_back(self.0)?;
self.0 = new_bytes;
@ -739,7 +739,7 @@ impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> {
#[derive(Clone)]
pub struct Chars<'a>(LosslessChars<'a>);
impl<'a> Iterator for Chars<'a> {
impl Iterator for Chars<'_> {
type Item = char;
#[inline]
@ -757,7 +757,7 @@ impl<'a> Iterator for Chars<'a> {
}
}
impl<'a> DoubleEndedIterator for Chars<'a> {
impl DoubleEndedIterator for Chars<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
if let Ok(c) = self.0.next_back()? {
@ -770,7 +770,7 @@ impl<'a> DoubleEndedIterator for Chars<'a> {
#[derive(Clone)]
pub struct CharsIndices<'a>(LosslessCharsIndices<'a>);
impl<'a> Iterator for CharsIndices<'a> {
impl Iterator for CharsIndices<'_> {
type Item = (usize, char);
#[inline]
@ -788,7 +788,7 @@ impl<'a> Iterator for CharsIndices<'a> {
}
}
impl<'a> DoubleEndedIterator for CharsIndices<'a> {
impl DoubleEndedIterator for CharsIndices<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
if let (index, Ok(c)) = self.0.next_back()? {

View file

@ -376,9 +376,7 @@ 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")
}
assert!(!rhs.is_negative(), "attempt to shift left with negative");
let Some(rhs) = rhs.to_u32() else {
panic!("shift left overflow")
};
@ -417,9 +415,7 @@ impl ops::ShlAssign<i64> for Int {
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")
}
assert!(!rhs.is_negative(), "attempt to shift right with negative");
let Some(rhs) = rhs.to_u32() else {
return Int::ZERO
};
@ -899,7 +895,7 @@ impl Int {
}
pub fn modinv(&self, modulus: &Int) -> Option<Int> {
self.to_big().modinv(&modulus.to_big()).map(|v| v.into())
self.to_big().modinv(&modulus.to_big()).map(Into::into)
}
pub fn isignum(&self) -> i64 {
@ -926,6 +922,46 @@ impl Int {
}
}
pub fn trailing_ones(&self) -> Option<u64> {
match self.expand() {
IntType::Int(-1) => None,
IntType::Int(n) => Some(n.trailing_ones() as u64),
IntType::Big(n) => (!n).trailing_zeros(),
}
}
pub fn count_zeros(&self) -> Option<u64> {
if !self.is_negative() {
return None
}
match self.expand() {
IntType::Int(n) => Some(n.count_zeros() as u64),
IntType::Big(n) => {
let mut count = 0;
for digit in n.iter_u64_digits() {
count += digit.count_zeros() as u64;
}
Some(count)
}
}
}
pub fn count_ones(&self) -> Option<u64> {
if self.is_negative() {
return None
}
match self.expand() {
IntType::Int(n) => Some(n.count_ones() as u64),
IntType::Big(n) => {
let mut count = 0;
for digit in n.iter_u64_digits() {
count += digit.count_ones() as u64;
}
Some(count)
}
}
}
#[inline]
pub fn eq_i64(&self, n: i64) -> bool {
self.to_i64().is_some_and(|v| v == n)

View file

@ -119,7 +119,7 @@ impl Range {
let Some(s) = &self.start else { return None };
Some(RangeIterator {
cur: s.into(),
end: self.end.as_ref().map(|e| e.into()),
end: self.end.as_ref().map(Into::into),
inclusive: self.inclusive,
done: false,
})
@ -127,16 +127,8 @@ impl Range {
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 s = self.start.as_ref().map_or(0, |s| to_slice_index(s, len));
let e = self.end.as_ref().map_or(len, |e| to_slice_index(e, len));
let e = if self.inclusive {
e.saturating_add(1).min(len)
} else {
@ -150,16 +142,8 @@ impl Range {
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 s = self.start.as_ref().map_or(0, |s| to_slice_index(s, len));
let e = self.end.as_ref().map_or(len, |e| to_slice_index(e, len));
let e = if self.inclusive {
e.saturating_add(1).min(len)
} else {

View file

@ -18,9 +18,12 @@ impl From<&Int> for Ratio {
}
pub trait RatioExt: Sized {
#[must_use]
fn div_euclid(&self, n: &Self) -> Self;
#[must_use]
fn rem_euclid(&self, n: &Self) -> Self;
fn to_f64_uc(&self) -> f64;
#[must_use]
fn approximate_float(x: f64) -> Option<Self>;
}
@ -50,12 +53,10 @@ impl RatioExt for Ratio {
}
fn approximate_float(x: f64) -> Option<Self> {
match Rational64::approximate_float(x) {
Some(r) => {
if let Some(r) = Rational64::approximate_float(x) {
let r = Ratio::new((*r.numer()).into(), (*r.denom()).into());
Some(r)
}
None => {
} else {
let mut y = x;
if y.abs() < 1.0 {
y = 1.0 / y;
@ -69,5 +70,4 @@ impl RatioExt for Ratio {
}
}
}
}
}

View file

@ -50,10 +50,10 @@ fn optimize_ex(expr: &mut Expr) {
fn optimize_ex_with(expr: &mut Expr, state: OptState) {
let span = expr.span;
match &mut expr.kind {
ExprKind::Literal(_) => (),
ExprKind::Ident(_) => (),
ExprKind::Var(_) => (),
ExprKind::Global(_) => (),
ExprKind::Literal(_)
| ExprKind::Ident(_)
| ExprKind::Var(_)
| ExprKind::Global(_) => (),
ExprKind::UnaryOp(o, e) => {
optimize_ex(e);
if let Some(a) = e.value() {
@ -218,9 +218,7 @@ fn optimize_ex_with(expr: &mut Expr, state: OptState) {
fn optimize_lv(e: &mut LValue) {
match &mut e.kind {
LValueKind::Ident(_) => (),
LValueKind::Var(_) => (),
LValueKind::Global(_) => (),
LValueKind::Ident(_) | LValueKind::Var(_) | LValueKind::Global(_) => (),
LValueKind::Index(l, r) => {
optimize_ex(l);
optimize_ex(r);

View file

@ -77,18 +77,31 @@ impl Display for Value {
}
impl Value {
#[must_use]
pub fn new_list(f: impl FnOnce(&mut Vec<Value>)) -> Self {
let mut list = Vec::new();
f(&mut list);
list.into()
}
#[must_use]
#[expect(clippy::mutable_key_type)]
pub fn new_table(f: impl FnOnce(&mut HashMap<HashValue, Value>)) -> Self {
let mut table = HashMap::new();
f(&mut table);
table.into()
}
#[must_use]
pub fn empty_list() -> Self {
Vec::new().into()
}
#[must_use]
pub fn empty_table() -> Self {
HashMap::new().into()
}
pub fn write_to_lstring(
&self,
w: &mut LString,

View file

@ -438,6 +438,7 @@ impl Value {
Ok(V::String(s.into()))
}
(V::Table(t1), V::Table(t2)) => {
#[expect(clippy::mutable_key_type)]
let mut t = t1.borrow().clone();
t.extend(t2.borrow().iter().map(|(k, v)| (k.clone(), v.clone())));
Ok(t.into())
@ -508,13 +509,15 @@ impl Value {
thread_local! {
static RANGE_ALL: Rc<Range> = Rc::new(Range::range_all());
}
Self::Range(RANGE_ALL.with(|x| x.clone()))
Self::Range(RANGE_ALL.with(Clone::clone))
}
#[must_use]
pub fn to_cell(self) -> Self {
Value::Cell(Rc::new(RefCell::new(self)))
}
#[must_use]
pub fn iter_unpack(self) -> Option<Self> {
match self {
Self::Symbol(s) if s == *SYM_END_ITERATION => None,
@ -522,6 +525,7 @@ impl Value {
}
}
#[must_use]
pub fn iter_pack(v: Option<Self>) -> Self {
match v {
Some(v) => v,

View file

@ -219,7 +219,7 @@ impl Vm {
frame.ip = catch.addr;
frame.locals.truncate(table.local_count);
self.stack.truncate(try_frame.stack_len);
self.stack.push(Value::Table(exc.to_table()));
self.stack.push(exc.to_value());
return Ok(())
}
}
@ -411,11 +411,9 @@ impl Vm {
let Value::List(list) = list else {
panic!("not a list")
};
match ext {
Value::List(ext) => {
if let Value::List(ext) = ext {
list.borrow_mut().extend_from_slice(ext.borrow().as_slice());
}
_ => {
} else {
let mut list = list.borrow_mut();
let f = ext.to_iter_function()?;
loop {
@ -427,10 +425,10 @@ impl Vm {
}
}
}
}
self.push(Value::List(list));
}
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
#[expect(clippy::mutable_key_type)]
I::NewTable(n) => {
let n = n as usize;
let mut table = HashMap::new();

View file

@ -4,6 +4,9 @@ version = "0.2.1"
edition = "2021"
rust-version = "1.68.2"
[lints]
workspace = true
[lib]
proc-macro = true

View file

@ -4,6 +4,9 @@ version = "0.2.2"
edition = "2021"
rust-version = "1.81.0"
[lints]
workspace = true
[dependencies]
talc-lang = { path = "../talc-lang" }
talc-macros = { path = "../talc-macros" }

View file

@ -19,14 +19,11 @@ pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)]
pub fn rethrow(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, table] = unpack_args!(args);
if let Value::Table(table) = table {
if let Some(e) = Exception::from_table(&table) {
return Err(e)
}
let [_, exc] = unpack_args!(args);
let Some(e) = Exception::from_value(&exc) else {
throw!(*SYM_VALUE_ERROR, "rethrow: argument not a valid exception")
}
throw!(*SYM_TYPE_ERROR, "rethrow expected table")
};
Err(e)
}
pub fn load(vm: &mut Vm) {

View file

@ -210,14 +210,11 @@ fn get_arg<'a>(
n: Option<usize>,
faidx: &mut usize,
) -> Result<&'a Value> {
let i = match n {
Some(n) => n,
None => {
let i = n.unwrap_or_else(|| {
let i = *faidx;
*faidx += 1;
i
}
};
});
if i >= args.len() {
throw!(
*SYM_FORMAT_ERROR,
@ -259,13 +256,11 @@ fn format_float(
let res = match (prec, ty) {
(None, FmtType::Str) => write!(buf, "{}", Value::Float(f)),
(None, FmtType::Repr) => write!(buf, "{:#}", Value::Float(f)),
(None, FmtType::Exp(true)) => write!(buf, "{:E}", f),
(None, FmtType::Exp(false)) => write!(buf, "{:e}", f),
(Some(p), FmtType::Str) | (Some(p), FmtType::Repr) => {
write!(buf, "{0:.1$}", f, p)
}
(Some(p), FmtType::Exp(true)) => write!(buf, "{0:.1$E}", f, p),
(Some(p), FmtType::Exp(false)) => write!(buf, "{0:.1$e}", f, p),
(None, FmtType::Exp(true)) => write!(buf, "{f:E}"),
(None, FmtType::Exp(false)) => write!(buf, "{f:e}"),
(Some(p), FmtType::Str | FmtType::Repr) => write!(buf, "{f:.p$}"),
(Some(p), FmtType::Exp(true)) => write!(buf, "{f:.p$E}"),
(Some(p), FmtType::Exp(false)) => write!(buf, "{f:.p$e}"),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
};
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
@ -279,9 +274,9 @@ fn format_complex(
) -> Result<()> {
format_float(cx.re, prec, ty, buf, "complex")?;
if cx.im < 0.0 {
buf.push_char('-')
buf.push_char('-');
} else {
buf.push_char('+')
buf.push_char('+');
}
format_float(cx.im.abs(), prec, ty, buf, "complex")?;
Ok(())
@ -298,7 +293,7 @@ fn format_int(
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
}
let res = match ty {
FmtType::Str | FmtType::Repr => write!(buf, "{}", n),
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)),
@ -330,8 +325,8 @@ fn format_value(
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value")
}
let res = match ty {
FmtType::Str => write!(buf, "{}", v),
FmtType::Repr => write!(buf, "{:#}", v),
FmtType::Str => write!(buf, "{v}"),
FmtType::Repr => write!(buf, "{v:#}"),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value"),
};
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
@ -344,7 +339,7 @@ fn format_string(
buf: &mut LString,
) -> Result<()> {
if let Some(prec) = prec {
s = &s[..prec]
s = &s[..prec];
}
let res = match ty {
FmtType::Str => {
@ -359,7 +354,7 @@ fn format_string(
fn pad_str(n: usize, c: char, buf: &mut LString) {
for _ in 0..n {
buf.push_char(c)
buf.push_char(c);
}
}

View file

@ -1,6 +1,6 @@
use talc_lang::{
exception::Result,
number::Int,
number::{Int, IntType},
prelude::*,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
@ -94,7 +94,7 @@ fn is_prime_inner(n: &Int) -> bool {
fn pollard_rho(n: &Int, a: Int, b: &Int) -> Option<Int> {
let mut x = a.clone();
let mut y = a.clone();
let mut y = a;
let mut d = Int::ONE;
while d.eq_i64(1) {
x = &(&(&x * &x) + b) % n;
@ -194,6 +194,11 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("iroot", iroot().into());
vm.set_global_name("jacobi", jacobi().into());
vm.set_global_name("ctz", ctz().into());
vm.set_global_name("cto", cto().into());
vm.set_global_name("bitcnt", bitcnt().into());
vm.set_global_name("popcnt", popcnt().into());
}
#[native_func(1)]
@ -239,9 +244,9 @@ pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
for factor in factorize(&n) {
if last_factor != factor {
res *= &factor - 1;
last_factor = factor
last_factor = factor;
} else {
res *= factor
res *= factor;
}
}
Ok(res.into())
@ -391,9 +396,8 @@ pub fn jacobi(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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))
}
return Ok(Value::from(0))
}
// (k|l) = 0 if gcd(k,l) != 1
@ -446,7 +450,62 @@ pub fn jacobi(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
if n.to_u32().is_some_and(|v| v == 1) {
return Ok(Value::from(res))
} else {
}
return Ok(Value::from(0))
}
#[native_func(1)]
pub fn ctz(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, n] = unpack_args!(args);
let Value::Int(n) = n else {
throw!(*SYM_TYPE_ERROR, "ctz expected integer, got {n:#}")
};
if let Some(n) = n.trailing_zeros() {
Ok(Int::from(n).into())
} else {
Ok(0.into())
}
}
#[native_func(1)]
pub fn cto(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, n] = unpack_args!(args);
let Value::Int(n) = n else {
throw!(*SYM_TYPE_ERROR, "cto expected integer, got {n:#}")
};
if let Some(n) = n.trailing_ones() {
Ok(Int::from(n).into())
} else {
Ok(0.into())
}
}
#[native_func(1)]
pub fn bitcnt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, n] = unpack_args!(args);
let Value::Int(n) = n else {
throw!(*SYM_TYPE_ERROR, "cto expected integer, got {n:#}")
};
let cnt = match n.expand() {
IntType::Int(n) => 64 - n.leading_ones().max(n.leading_zeros()) as u64,
IntType::Big(b) => b.bits(),
};
Ok(Int::from(cnt).into())
}
#[native_func(1)]
pub fn popcnt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, n] = unpack_args!(args);
let Value::Int(n) = n else {
throw!(*SYM_TYPE_ERROR, "cto expected integer, got {n:#}")
};
if n.is_negative() {
// count_zeros always succeeds on negative numbers
let z = n.count_zeros().unwrap();
Ok(Value::from(-(z as i64)))
} else {
// count_ones always succeeds on nonnegative numbers
let z = n.count_ones().unwrap();
Ok(Value::from(z as i64))
}
}

View file

@ -877,7 +877,7 @@ pub fn mean(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
sum / count
}
fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> {
fn variance_inner(vm: &mut Vm, iter: &Value, pop: bool) -> Result<Value> {
let mut m = Value::Float(0.0);
let mut s = Value::Float(0.0);
let mut k = 1;
@ -895,7 +895,7 @@ pub fn variance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
variance_inner(vm, iter, false)
variance_inner(vm, &iter, false)
}
#[native_func(1)]
@ -903,7 +903,7 @@ pub fn stdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let v = variance_inner(vm, iter, false)?;
let v = variance_inner(vm, &iter, false)?;
Ok(match v {
Value::Int(n) => Value::Float(n.to_f64_uc().sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
@ -918,7 +918,7 @@ pub fn pvariance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
variance_inner(vm, iter, true)
variance_inner(vm, &iter, true)
}
#[native_func(1)]
@ -926,7 +926,7 @@ pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let v = variance_inner(vm, iter, true)?;
let v = variance_inner(vm, &iter, true)?;
Ok(match v {
Value::Int(n) => Value::Float(n.to_f64_uc().sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
@ -940,7 +940,7 @@ pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
struct OrdValue(Value);
impl std::cmp::Eq for OrdValue {}
#[allow(clippy::derive_ord_xor_partial_ord)]
#[expect(clippy::derive_ord_xor_partial_ord)]
impl std::cmp::Ord for OrdValue {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Less)

View file

@ -85,7 +85,7 @@ fn match_to_value(m: Match) -> Value {
})
}
fn captures_to_value(cs: Captures) -> Value {
fn captures_to_value(cs: &Captures) -> Value {
cs.iter()
.map(|c| c.map_or(Value::Nil, match_to_value))
.collect::<Vec<Value>>()
@ -178,7 +178,10 @@ pub fn captures_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
};
let re = regex_from(&re, "captures_once")?;
Ok(re.captures(s).map_or(Value::Nil, captures_to_value))
Ok(re
.captures(s)
.as_ref()
.map_or(Value::Nil, captures_to_value))
}
#[native_func(2)]
@ -193,7 +196,7 @@ pub fn captures(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let re = regex_from(&re, "captures")?;
Ok(re
.captures_iter(s)
.map(captures_to_value)
.map(|cap| captures_to_value(&cap))
.collect::<Vec<Value>>()
.into())
}