This commit is contained in:
parent
668e0712c8
commit
b1fccce8e3
24 changed files with 276 additions and 165 deletions
19
Cargo.toml
19
Cargo.toml
|
@ -2,6 +2,24 @@
|
||||||
members = ["talc-lang", "talc-bin", "talc-std", "talc-macros"]
|
members = ["talc-lang", "talc-bin", "talc-std", "talc-macros"]
|
||||||
resolver = "2"
|
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]
|
[profile.release-opt]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
|
@ -10,3 +28,4 @@ codegen-units = 1
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,9 @@ version = "0.2.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.81.0"
|
rust-version = "1.81.0"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "talc"
|
name = "talc"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
|
@ -86,7 +86,7 @@ impl Highlighter for TalcHelper {
|
||||||
buf += format;
|
buf += format;
|
||||||
buf += token.content;
|
buf += token.content;
|
||||||
if !format.is_empty() {
|
if !format.is_empty() {
|
||||||
buf += "\x1b[0m"
|
buf += "\x1b[0m";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf += &line[(last.idx as usize)..];
|
buf += &line[(last.idx as usize)..];
|
||||||
|
@ -126,7 +126,7 @@ impl Validator for TalcHelper {
|
||||||
match k {
|
match k {
|
||||||
K::Eof => break,
|
K::Eof => break,
|
||||||
K::LParen | K::LBrack | K::LBrace | K::If | K::While | K::For | K::Try => {
|
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() {
|
K::RParen => match delims.pop() {
|
||||||
Some(K::LParen) => (),
|
Some(K::LParen) => (),
|
||||||
|
|
|
@ -68,7 +68,7 @@ fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.show_ast {
|
if args.show_ast {
|
||||||
eprintln!("{}", ex);
|
eprintln!("{ex}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let func = match compile(&ex, Some(name)) {
|
let func = match compile(&ex, Some(name)) {
|
||||||
|
|
|
@ -99,7 +99,7 @@ fn read_init_file(args: &Args) -> Result<Option<String>, std::io::Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_line(
|
fn exec_line(
|
||||||
vm: Rc<RefCell<Vm>>,
|
vm: &mut Vm,
|
||||||
line: &str,
|
line: &str,
|
||||||
args: &Args,
|
args: &Args,
|
||||||
c: &ReplColors,
|
c: &ReplColors,
|
||||||
|
@ -118,7 +118,7 @@ fn exec_line(
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.show_ast {
|
if args.show_ast {
|
||||||
eprintln!("{}", ex);
|
eprintln!("{ex}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let func = match compile_repl(&ex, globals) {
|
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()]) {
|
match vm.run_function(func.clone(), vec![func.into()]) {
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
let prev2 = vm.get_global(*SYM_PREV2).unwrap().clone();
|
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())));
|
rl.set_helper(Some(TalcHelper::new(vm.clone())));
|
||||||
|
|
||||||
if let Some(src) = init_src {
|
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 {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ version = "0.2.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.81.0"
|
rust-version = "1.81.0"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num = { version = "0.4", features = [] }
|
num = { version = "0.4", features = [] }
|
||||||
lazy_static = "1.5"
|
lazy_static = "1.5"
|
||||||
|
|
|
@ -100,7 +100,7 @@ pub fn compile_repl(expr: &Expr, globals: &mut Vec<Symbol>) -> Result<Function>
|
||||||
Ok(comp.finish_repl(globals))
|
Ok(comp.finish_repl(globals))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Default for Compiler<'a> {
|
impl Default for Compiler<'_> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let mut scope = HashMap::new();
|
let mut scope = HashMap::new();
|
||||||
scope.insert(*SYM_SELF, VarKind::Local(0));
|
scope.insert(*SYM_SELF, VarKind::Local(0));
|
||||||
|
@ -355,15 +355,13 @@ impl<'a> Compiler<'a> {
|
||||||
self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
|
self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
|
||||||
}
|
}
|
||||||
ResolveOutcome::InParent => {
|
ResolveOutcome::InParent => {
|
||||||
let n = match self.closes.iter().position(|(n, _)| *n == name) {
|
let pos = self.closes.iter().position(|(n, _)| *n == name);
|
||||||
Some(n) => n,
|
let idx = pos.unwrap_or_else(|| {
|
||||||
None => {
|
let idx = self.closes.len();
|
||||||
let n = self.closes.len();
|
self.closes.push((name, idx));
|
||||||
self.closes.push((name, n));
|
idx
|
||||||
n
|
});
|
||||||
}
|
self.emit(I::LoadUpvalue(Arg24::from_usize(idx)));
|
||||||
};
|
|
||||||
self.emit(I::LoadUpvalue(Arg24::from_usize(n)));
|
|
||||||
}
|
}
|
||||||
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
|
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
|
||||||
self.emit(I::LoadGlobal(Arg24::from_symbol(name)));
|
self.emit(I::LoadGlobal(Arg24::from_symbol(name)));
|
||||||
|
@ -412,15 +410,13 @@ impl<'a> Compiler<'a> {
|
||||||
self.emit(I::StoreClosedLocal(Arg24::from_usize(n)));
|
self.emit(I::StoreClosedLocal(Arg24::from_usize(n)));
|
||||||
}
|
}
|
||||||
ResolveOutcome::InParent => {
|
ResolveOutcome::InParent => {
|
||||||
let n = match self.closes.iter().position(|(n, _)| *n == name) {
|
let pos = self.closes.iter().position(|(n, _)| *n == name);
|
||||||
Some(n) => n,
|
let idx = pos.unwrap_or_else(|| {
|
||||||
None => {
|
let idx = self.closes.len();
|
||||||
let n = self.closes.len();
|
self.closes.push((name, idx));
|
||||||
self.closes.push((name, n));
|
idx
|
||||||
n
|
});
|
||||||
}
|
self.emit(I::StoreUpvalue(Arg24::from_usize(idx)));
|
||||||
};
|
|
||||||
self.emit(I::StoreUpvalue(Arg24::from_usize(n)));
|
|
||||||
}
|
}
|
||||||
ResolveOutcome::Var(VarKind::Global) => {
|
ResolveOutcome::Var(VarKind::Global) => {
|
||||||
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
|
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
|
||||||
|
@ -878,7 +874,7 @@ impl<'a> Compiler<'a> {
|
||||||
|
|
||||||
self.emit(I::ListDestructure(
|
self.emit(I::ListDestructure(
|
||||||
i.unwrap_or(len),
|
i.unwrap_or(len),
|
||||||
i.map(|n| len - n - 1).unwrap_or(0),
|
i.map_or(0, |n| len - n - 1),
|
||||||
i.is_some(),
|
i.is_some(),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
lstring::LStr,
|
lstring::LStr,
|
||||||
symbol::{Symbol, SYM_MSG, SYM_TYPE},
|
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>;
|
pub type Result<T> = std::result::Result<T, Exception>;
|
||||||
|
|
||||||
|
@ -18,7 +18,10 @@ impl Exception {
|
||||||
Self { ty, msg }
|
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 table = table.borrow();
|
||||||
let ty = table.get(&(*SYM_TYPE).into())?;
|
let ty = table.get(&(*SYM_TYPE).into())?;
|
||||||
let msg = table.get(&(*SYM_MSG).into())?;
|
let msg = table.get(&(*SYM_MSG).into())?;
|
||||||
|
@ -32,11 +35,11 @@ impl Exception {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_table(self) -> Rc<RefCell<HashMap<HashValue, Value>>> {
|
pub fn to_value(self) -> Value {
|
||||||
let mut table = HashMap::new();
|
Value::new_table(|table| {
|
||||||
table.insert((*SYM_TYPE).into(), self.ty.into());
|
table.insert((*SYM_TYPE).into(), self.ty.into());
|
||||||
table.insert((*SYM_MSG).into(), Value::String(self.msg));
|
table.insert((*SYM_MSG).into(), Value::String(self.msg));
|
||||||
Rc::new(RefCell::new(table))
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 chunk;
|
||||||
pub mod compiler;
|
pub mod compiler;
|
||||||
pub mod exception;
|
pub mod exception;
|
||||||
|
|
|
@ -621,7 +621,7 @@ impl LString {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Bytes<'a>(Copied<slice::Iter<'a, u8>>);
|
pub struct Bytes<'a>(Copied<slice::Iter<'a, u8>>);
|
||||||
|
|
||||||
impl<'a> Iterator for Bytes<'a> {
|
impl Iterator for Bytes<'_> {
|
||||||
type Item = u8;
|
type Item = u8;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -662,19 +662,19 @@ impl<'a> Iterator for Bytes<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ExactSizeIterator for Bytes<'a> {
|
impl ExactSizeIterator for Bytes<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FusedIterator for Bytes<'a> {}
|
impl FusedIterator for Bytes<'_> {}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LosslessChars<'a>(&'a [u8]);
|
pub struct LosslessChars<'a>(&'a [u8]);
|
||||||
|
|
||||||
impl<'a> Iterator for LosslessChars<'a> {
|
impl Iterator for LosslessChars<'_> {
|
||||||
type Item = Result<char, u8>;
|
type Item = Result<char, u8>;
|
||||||
|
|
||||||
#[inline]
|
#[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> {
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
let (new_bytes, res) = next_codepoint_back(self.0)?;
|
let (new_bytes, res) = next_codepoint_back(self.0)?;
|
||||||
self.0 = new_bytes;
|
self.0 = new_bytes;
|
||||||
|
@ -702,7 +702,7 @@ impl<'a> DoubleEndedIterator for LosslessChars<'a> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LosslessCharsIndices<'a>(&'a [u8], usize);
|
pub struct LosslessCharsIndices<'a>(&'a [u8], usize);
|
||||||
|
|
||||||
impl<'a> Iterator for LosslessCharsIndices<'a> {
|
impl Iterator for LosslessCharsIndices<'_> {
|
||||||
type Item = (usize, Result<char, u8>);
|
type Item = (usize, Result<char, u8>);
|
||||||
|
|
||||||
#[inline]
|
#[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> {
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
let (new_bytes, res) = next_codepoint_back(self.0)?;
|
let (new_bytes, res) = next_codepoint_back(self.0)?;
|
||||||
self.0 = new_bytes;
|
self.0 = new_bytes;
|
||||||
|
@ -739,7 +739,7 @@ impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Chars<'a>(LosslessChars<'a>);
|
pub struct Chars<'a>(LosslessChars<'a>);
|
||||||
|
|
||||||
impl<'a> Iterator for Chars<'a> {
|
impl Iterator for Chars<'_> {
|
||||||
type Item = char;
|
type Item = char;
|
||||||
|
|
||||||
#[inline]
|
#[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> {
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
loop {
|
loop {
|
||||||
if let Ok(c) = self.0.next_back()? {
|
if let Ok(c) = self.0.next_back()? {
|
||||||
|
@ -770,7 +770,7 @@ impl<'a> DoubleEndedIterator for Chars<'a> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CharsIndices<'a>(LosslessCharsIndices<'a>);
|
pub struct CharsIndices<'a>(LosslessCharsIndices<'a>);
|
||||||
|
|
||||||
impl<'a> Iterator for CharsIndices<'a> {
|
impl Iterator for CharsIndices<'_> {
|
||||||
type Item = (usize, char);
|
type Item = (usize, char);
|
||||||
|
|
||||||
#[inline]
|
#[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> {
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
loop {
|
loop {
|
||||||
if let (index, Ok(c)) = self.0.next_back()? {
|
if let (index, Ok(c)) = self.0.next_back()? {
|
||||||
|
|
|
@ -376,9 +376,7 @@ impl ops::Shl for &Int {
|
||||||
type Output = Int;
|
type Output = Int;
|
||||||
|
|
||||||
fn shl(self, rhs: Self) -> Self::Output {
|
fn shl(self, rhs: Self) -> Self::Output {
|
||||||
if rhs.is_negative() {
|
assert!(!rhs.is_negative(), "attempt to shift left with negative");
|
||||||
panic!("attempt to shift left with negative")
|
|
||||||
}
|
|
||||||
let Some(rhs) = rhs.to_u32() else {
|
let Some(rhs) = rhs.to_u32() else {
|
||||||
panic!("shift left overflow")
|
panic!("shift left overflow")
|
||||||
};
|
};
|
||||||
|
@ -417,9 +415,7 @@ impl ops::ShlAssign<i64> for Int {
|
||||||
impl ops::Shr for &Int {
|
impl ops::Shr for &Int {
|
||||||
type Output = Int;
|
type Output = Int;
|
||||||
fn shr(self, rhs: Self) -> Self::Output {
|
fn shr(self, rhs: Self) -> Self::Output {
|
||||||
if rhs.is_negative() {
|
assert!(!rhs.is_negative(), "attempt to shift right with negative");
|
||||||
panic!("attempt to shift right with negative")
|
|
||||||
}
|
|
||||||
let Some(rhs) = rhs.to_u32() else {
|
let Some(rhs) = rhs.to_u32() else {
|
||||||
return Int::ZERO
|
return Int::ZERO
|
||||||
};
|
};
|
||||||
|
@ -899,7 +895,7 @@ impl Int {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modinv(&self, modulus: &Int) -> Option<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 {
|
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]
|
#[inline]
|
||||||
pub fn eq_i64(&self, n: i64) -> bool {
|
pub fn eq_i64(&self, n: i64) -> bool {
|
||||||
self.to_i64().is_some_and(|v| v == n)
|
self.to_i64().is_some_and(|v| v == n)
|
||||||
|
|
|
@ -119,7 +119,7 @@ impl Range {
|
||||||
let Some(s) = &self.start else { return None };
|
let Some(s) = &self.start else { return None };
|
||||||
Some(RangeIterator {
|
Some(RangeIterator {
|
||||||
cur: s.into(),
|
cur: s.into(),
|
||||||
end: self.end.as_ref().map(|e| e.into()),
|
end: self.end.as_ref().map(Into::into),
|
||||||
inclusive: self.inclusive,
|
inclusive: self.inclusive,
|
||||||
done: false,
|
done: false,
|
||||||
})
|
})
|
||||||
|
@ -127,16 +127,8 @@ impl Range {
|
||||||
|
|
||||||
pub fn index_slice<'a, T>(&self, l: &'a [T]) -> &'a [T] {
|
pub fn index_slice<'a, T>(&self, l: &'a [T]) -> &'a [T] {
|
||||||
let len = l.len();
|
let len = l.len();
|
||||||
let s = self
|
let s = self.start.as_ref().map_or(0, |s| to_slice_index(s, len));
|
||||||
.start
|
let e = self.end.as_ref().map_or(len, |e| to_slice_index(e, len));
|
||||||
.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 {
|
let e = if self.inclusive {
|
||||||
e.saturating_add(1).min(len)
|
e.saturating_add(1).min(len)
|
||||||
} else {
|
} else {
|
||||||
|
@ -150,16 +142,8 @@ impl Range {
|
||||||
|
|
||||||
pub fn replace_range<T: Clone>(&self, l: &mut Vec<T>, rep: &[T]) {
|
pub fn replace_range<T: Clone>(&self, l: &mut Vec<T>, rep: &[T]) {
|
||||||
let len = l.len();
|
let len = l.len();
|
||||||
let s = self
|
let s = self.start.as_ref().map_or(0, |s| to_slice_index(s, len));
|
||||||
.start
|
let e = self.end.as_ref().map_or(len, |e| to_slice_index(e, len));
|
||||||
.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 {
|
let e = if self.inclusive {
|
||||||
e.saturating_add(1).min(len)
|
e.saturating_add(1).min(len)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,9 +18,12 @@ impl From<&Int> for Ratio {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RatioExt: Sized {
|
pub trait RatioExt: Sized {
|
||||||
|
#[must_use]
|
||||||
fn div_euclid(&self, n: &Self) -> Self;
|
fn div_euclid(&self, n: &Self) -> Self;
|
||||||
|
#[must_use]
|
||||||
fn rem_euclid(&self, n: &Self) -> Self;
|
fn rem_euclid(&self, n: &Self) -> Self;
|
||||||
fn to_f64_uc(&self) -> f64;
|
fn to_f64_uc(&self) -> f64;
|
||||||
|
#[must_use]
|
||||||
fn approximate_float(x: f64) -> Option<Self>;
|
fn approximate_float(x: f64) -> Option<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,23 +53,20 @@ impl RatioExt for Ratio {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn approximate_float(x: f64) -> Option<Self> {
|
fn approximate_float(x: f64) -> Option<Self> {
|
||||||
match Rational64::approximate_float(x) {
|
if let Some(r) = Rational64::approximate_float(x) {
|
||||||
Some(r) => {
|
let r = Ratio::new((*r.numer()).into(), (*r.denom()).into());
|
||||||
let r = Ratio::new((*r.numer()).into(), (*r.denom()).into());
|
Some(r)
|
||||||
Some(r)
|
} else {
|
||||||
|
let mut y = x;
|
||||||
|
if y.abs() < 1.0 {
|
||||||
|
y = 1.0 / y;
|
||||||
}
|
}
|
||||||
None => {
|
let i = Int::from_f64(y)?;
|
||||||
let mut y = x;
|
let r = Ratio::from_integer(i.into());
|
||||||
if y.abs() < 1.0 {
|
if x.abs() < 1.0 {
|
||||||
y = 1.0 / y;
|
Some(r.recip())
|
||||||
}
|
} else {
|
||||||
let i = Int::from_f64(y)?;
|
Some(r)
|
||||||
let r = Ratio::from_integer(i.into());
|
|
||||||
if x.abs() < 1.0 {
|
|
||||||
Some(r.recip())
|
|
||||||
} else {
|
|
||||||
Some(r)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,10 +50,10 @@ fn optimize_ex(expr: &mut Expr) {
|
||||||
fn optimize_ex_with(expr: &mut Expr, state: OptState) {
|
fn optimize_ex_with(expr: &mut Expr, state: OptState) {
|
||||||
let span = expr.span;
|
let span = expr.span;
|
||||||
match &mut expr.kind {
|
match &mut expr.kind {
|
||||||
ExprKind::Literal(_) => (),
|
ExprKind::Literal(_)
|
||||||
ExprKind::Ident(_) => (),
|
| ExprKind::Ident(_)
|
||||||
ExprKind::Var(_) => (),
|
| ExprKind::Var(_)
|
||||||
ExprKind::Global(_) => (),
|
| ExprKind::Global(_) => (),
|
||||||
ExprKind::UnaryOp(o, e) => {
|
ExprKind::UnaryOp(o, e) => {
|
||||||
optimize_ex(e);
|
optimize_ex(e);
|
||||||
if let Some(a) = e.value() {
|
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) {
|
fn optimize_lv(e: &mut LValue) {
|
||||||
match &mut e.kind {
|
match &mut e.kind {
|
||||||
LValueKind::Ident(_) => (),
|
LValueKind::Ident(_) | LValueKind::Var(_) | LValueKind::Global(_) => (),
|
||||||
LValueKind::Var(_) => (),
|
|
||||||
LValueKind::Global(_) => (),
|
|
||||||
LValueKind::Index(l, r) => {
|
LValueKind::Index(l, r) => {
|
||||||
optimize_ex(l);
|
optimize_ex(l);
|
||||||
optimize_ex(r);
|
optimize_ex(r);
|
||||||
|
|
|
@ -77,18 +77,31 @@ impl Display for Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
|
#[must_use]
|
||||||
pub fn new_list(f: impl FnOnce(&mut Vec<Value>)) -> Self {
|
pub fn new_list(f: impl FnOnce(&mut Vec<Value>)) -> Self {
|
||||||
let mut list = Vec::new();
|
let mut list = Vec::new();
|
||||||
f(&mut list);
|
f(&mut list);
|
||||||
list.into()
|
list.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
#[expect(clippy::mutable_key_type)]
|
||||||
pub fn new_table(f: impl FnOnce(&mut HashMap<HashValue, Value>)) -> Self {
|
pub fn new_table(f: impl FnOnce(&mut HashMap<HashValue, Value>)) -> Self {
|
||||||
let mut table = HashMap::new();
|
let mut table = HashMap::new();
|
||||||
f(&mut table);
|
f(&mut table);
|
||||||
table.into()
|
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(
|
pub fn write_to_lstring(
|
||||||
&self,
|
&self,
|
||||||
w: &mut LString,
|
w: &mut LString,
|
||||||
|
|
|
@ -438,6 +438,7 @@ impl Value {
|
||||||
Ok(V::String(s.into()))
|
Ok(V::String(s.into()))
|
||||||
}
|
}
|
||||||
(V::Table(t1), V::Table(t2)) => {
|
(V::Table(t1), V::Table(t2)) => {
|
||||||
|
#[expect(clippy::mutable_key_type)]
|
||||||
let mut t = t1.borrow().clone();
|
let mut t = t1.borrow().clone();
|
||||||
t.extend(t2.borrow().iter().map(|(k, v)| (k.clone(), v.clone())));
|
t.extend(t2.borrow().iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||||
Ok(t.into())
|
Ok(t.into())
|
||||||
|
@ -508,13 +509,15 @@ impl Value {
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static RANGE_ALL: Rc<Range> = Rc::new(Range::range_all());
|
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 {
|
pub fn to_cell(self) -> Self {
|
||||||
Value::Cell(Rc::new(RefCell::new(self)))
|
Value::Cell(Rc::new(RefCell::new(self)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn iter_unpack(self) -> Option<Self> {
|
pub fn iter_unpack(self) -> Option<Self> {
|
||||||
match self {
|
match self {
|
||||||
Self::Symbol(s) if s == *SYM_END_ITERATION => None,
|
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 {
|
pub fn iter_pack(v: Option<Self>) -> Self {
|
||||||
match v {
|
match v {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
|
|
|
@ -219,7 +219,7 @@ impl Vm {
|
||||||
frame.ip = catch.addr;
|
frame.ip = catch.addr;
|
||||||
frame.locals.truncate(table.local_count);
|
frame.locals.truncate(table.local_count);
|
||||||
self.stack.truncate(try_frame.stack_len);
|
self.stack.truncate(try_frame.stack_len);
|
||||||
self.stack.push(Value::Table(exc.to_table()));
|
self.stack.push(exc.to_value());
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -411,26 +411,24 @@ impl Vm {
|
||||||
let Value::List(list) = list else {
|
let Value::List(list) = list else {
|
||||||
panic!("not a list")
|
panic!("not a list")
|
||||||
};
|
};
|
||||||
match ext {
|
if let Value::List(ext) = ext {
|
||||||
Value::List(ext) => {
|
list.borrow_mut().extend_from_slice(ext.borrow().as_slice());
|
||||||
list.borrow_mut().extend_from_slice(ext.borrow().as_slice());
|
} else {
|
||||||
}
|
let mut list = list.borrow_mut();
|
||||||
_ => {
|
let f = ext.to_iter_function()?;
|
||||||
let mut list = list.borrow_mut();
|
loop {
|
||||||
let f = ext.to_iter_function()?;
|
self.push(f.clone());
|
||||||
loop {
|
self.instr_call(0, frame)?;
|
||||||
self.push(f.clone());
|
match self.pop().iter_unpack() {
|
||||||
self.instr_call(0, frame)?;
|
None => break,
|
||||||
match self.pop().iter_unpack() {
|
Some(v) => list.push(v),
|
||||||
None => break,
|
|
||||||
Some(v) => list.push(v),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.push(Value::List(list));
|
self.push(Value::List(list));
|
||||||
}
|
}
|
||||||
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
|
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
|
||||||
|
#[expect(clippy::mutable_key_type)]
|
||||||
I::NewTable(n) => {
|
I::NewTable(n) => {
|
||||||
let n = n as usize;
|
let n = n as usize;
|
||||||
let mut table = HashMap::new();
|
let mut table = HashMap::new();
|
||||||
|
|
|
@ -4,6 +4,9 @@ version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.68.2"
|
rust-version = "1.68.2"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,9 @@ version = "0.2.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.81.0"
|
rust-version = "1.81.0"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
talc-lang = { path = "../talc-lang" }
|
talc-lang = { path = "../talc-lang" }
|
||||||
talc-macros = { path = "../talc-macros" }
|
talc-macros = { path = "../talc-macros" }
|
||||||
|
|
|
@ -19,14 +19,11 @@ pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn rethrow(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn rethrow(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, table] = unpack_args!(args);
|
let [_, exc] = unpack_args!(args);
|
||||||
if let Value::Table(table) = table {
|
let Some(e) = Exception::from_value(&exc) else {
|
||||||
if let Some(e) = Exception::from_table(&table) {
|
|
||||||
return Err(e)
|
|
||||||
}
|
|
||||||
throw!(*SYM_VALUE_ERROR, "rethrow: argument not a valid exception")
|
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) {
|
pub fn load(vm: &mut Vm) {
|
||||||
|
|
|
@ -210,14 +210,11 @@ fn get_arg<'a>(
|
||||||
n: Option<usize>,
|
n: Option<usize>,
|
||||||
faidx: &mut usize,
|
faidx: &mut usize,
|
||||||
) -> Result<&'a Value> {
|
) -> Result<&'a Value> {
|
||||||
let i = match n {
|
let i = n.unwrap_or_else(|| {
|
||||||
Some(n) => n,
|
let i = *faidx;
|
||||||
None => {
|
*faidx += 1;
|
||||||
let i = *faidx;
|
i
|
||||||
*faidx += 1;
|
});
|
||||||
i
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if i >= args.len() {
|
if i >= args.len() {
|
||||||
throw!(
|
throw!(
|
||||||
*SYM_FORMAT_ERROR,
|
*SYM_FORMAT_ERROR,
|
||||||
|
@ -259,13 +256,11 @@ fn format_float(
|
||||||
let res = match (prec, ty) {
|
let res = match (prec, ty) {
|
||||||
(None, FmtType::Str) => write!(buf, "{}", Value::Float(f)),
|
(None, FmtType::Str) => write!(buf, "{}", Value::Float(f)),
|
||||||
(None, FmtType::Repr) => write!(buf, "{:#}", Value::Float(f)),
|
(None, FmtType::Repr) => write!(buf, "{:#}", Value::Float(f)),
|
||||||
(None, FmtType::Exp(true)) => write!(buf, "{:E}", f),
|
(None, FmtType::Exp(true)) => write!(buf, "{f:E}"),
|
||||||
(None, FmtType::Exp(false)) => write!(buf, "{:e}", f),
|
(None, FmtType::Exp(false)) => write!(buf, "{f:e}"),
|
||||||
(Some(p), FmtType::Str) | (Some(p), FmtType::Repr) => {
|
(Some(p), FmtType::Str | FmtType::Repr) => write!(buf, "{f:.p$}"),
|
||||||
write!(buf, "{0:.1$}", f, p)
|
(Some(p), FmtType::Exp(true)) => write!(buf, "{f:.p$E}"),
|
||||||
}
|
(Some(p), FmtType::Exp(false)) => write!(buf, "{f:.p$e}"),
|
||||||
(Some(p), FmtType::Exp(true)) => write!(buf, "{0:.1$E}", f, p),
|
|
||||||
(Some(p), FmtType::Exp(false)) => write!(buf, "{0:.1$e}", f, p),
|
|
||||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
|
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
|
||||||
};
|
};
|
||||||
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||||
|
@ -279,9 +274,9 @@ fn format_complex(
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
format_float(cx.re, prec, ty, buf, "complex")?;
|
format_float(cx.re, prec, ty, buf, "complex")?;
|
||||||
if cx.im < 0.0 {
|
if cx.im < 0.0 {
|
||||||
buf.push_char('-')
|
buf.push_char('-');
|
||||||
} else {
|
} else {
|
||||||
buf.push_char('+')
|
buf.push_char('+');
|
||||||
}
|
}
|
||||||
format_float(cx.im.abs(), prec, ty, buf, "complex")?;
|
format_float(cx.im.abs(), prec, ty, buf, "complex")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -298,7 +293,7 @@ fn format_int(
|
||||||
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
||||||
}
|
}
|
||||||
let res = match ty {
|
let res = match ty {
|
||||||
FmtType::Str | 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::Hex(caps) => write!(buf, "{}", n.to_str_radix_case(16, caps)),
|
||||||
FmtType::Oct => write!(buf, "{}", n.to_str_radix(8)),
|
FmtType::Oct => write!(buf, "{}", n.to_str_radix(8)),
|
||||||
FmtType::Sex => write!(buf, "{}", n.to_str_radix(6)),
|
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")
|
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value")
|
||||||
}
|
}
|
||||||
let res = match ty {
|
let res = match ty {
|
||||||
FmtType::Str => write!(buf, "{}", v),
|
FmtType::Str => write!(buf, "{v}"),
|
||||||
FmtType::Repr => write!(buf, "{:#}", v),
|
FmtType::Repr => write!(buf, "{v:#}"),
|
||||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value"),
|
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value"),
|
||||||
};
|
};
|
||||||
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||||
|
@ -344,7 +339,7 @@ fn format_string(
|
||||||
buf: &mut LString,
|
buf: &mut LString,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if let Some(prec) = prec {
|
if let Some(prec) = prec {
|
||||||
s = &s[..prec]
|
s = &s[..prec];
|
||||||
}
|
}
|
||||||
let res = match ty {
|
let res = match ty {
|
||||||
FmtType::Str => {
|
FmtType::Str => {
|
||||||
|
@ -359,7 +354,7 @@ fn format_string(
|
||||||
|
|
||||||
fn pad_str(n: usize, c: char, buf: &mut LString) {
|
fn pad_str(n: usize, c: char, buf: &mut LString) {
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
buf.push_char(c)
|
buf.push_char(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use talc_lang::{
|
use talc_lang::{
|
||||||
exception::Result,
|
exception::Result,
|
||||||
number::Int,
|
number::{Int, IntType},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
throw,
|
throw,
|
||||||
|
@ -94,7 +94,7 @@ fn is_prime_inner(n: &Int) -> bool {
|
||||||
|
|
||||||
fn pollard_rho(n: &Int, a: Int, b: &Int) -> Option<Int> {
|
fn pollard_rho(n: &Int, a: Int, b: &Int) -> Option<Int> {
|
||||||
let mut x = a.clone();
|
let mut x = a.clone();
|
||||||
let mut y = a.clone();
|
let mut y = a;
|
||||||
let mut d = Int::ONE;
|
let mut d = Int::ONE;
|
||||||
while d.eq_i64(1) {
|
while d.eq_i64(1) {
|
||||||
x = &(&(&x * &x) + b) % n;
|
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("iroot", iroot().into());
|
||||||
|
|
||||||
vm.set_global_name("jacobi", jacobi().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)]
|
#[native_func(1)]
|
||||||
|
@ -239,9 +244,9 @@ pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
for factor in factorize(&n) {
|
for factor in factorize(&n) {
|
||||||
if last_factor != factor {
|
if last_factor != factor {
|
||||||
res *= &factor - 1;
|
res *= &factor - 1;
|
||||||
last_factor = factor
|
last_factor = factor;
|
||||||
} else {
|
} else {
|
||||||
res *= factor
|
res *= factor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(res.into())
|
Ok(res.into())
|
||||||
|
@ -391,9 +396,8 @@ pub fn jacobi(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
if n.is_zero() {
|
if n.is_zero() {
|
||||||
if a.to_i32().is_some_and(|v| v == -1 || v == 1) {
|
if a.to_i32().is_some_and(|v| v == -1 || v == 1) {
|
||||||
return Ok(Value::from(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
|
// (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) {
|
if n.to_u32().is_some_and(|v| v == 1) {
|
||||||
return Ok(Value::from(res))
|
return Ok(Value::from(res))
|
||||||
|
}
|
||||||
|
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 {
|
} else {
|
||||||
return Ok(Value::from(0))
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -877,7 +877,7 @@ pub fn mean(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
sum / count
|
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 m = Value::Float(0.0);
|
||||||
let mut s = Value::Float(0.0);
|
let mut s = Value::Float(0.0);
|
||||||
let mut k = 1;
|
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] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
variance_inner(vm, iter, false)
|
variance_inner(vm, &iter, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[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] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let v = variance_inner(vm, iter, false)?;
|
let v = variance_inner(vm, &iter, false)?;
|
||||||
Ok(match v {
|
Ok(match v {
|
||||||
Value::Int(n) => Value::Float(n.to_f64_uc().sqrt()),
|
Value::Int(n) => Value::Float(n.to_f64_uc().sqrt()),
|
||||||
Value::Float(f) => Value::Float(f.sqrt()),
|
Value::Float(f) => Value::Float(f.sqrt()),
|
||||||
|
@ -918,7 +918,7 @@ pub fn pvariance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, iter] = unpack_args!(args);
|
let [_, iter] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
variance_inner(vm, iter, true)
|
variance_inner(vm, &iter, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[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] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let v = variance_inner(vm, iter, true)?;
|
let v = variance_inner(vm, &iter, true)?;
|
||||||
Ok(match v {
|
Ok(match v {
|
||||||
Value::Int(n) => Value::Float(n.to_f64_uc().sqrt()),
|
Value::Int(n) => Value::Float(n.to_f64_uc().sqrt()),
|
||||||
Value::Float(f) => Value::Float(f.sqrt()),
|
Value::Float(f) => Value::Float(f.sqrt()),
|
||||||
|
@ -940,7 +940,7 @@ pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
struct OrdValue(Value);
|
struct OrdValue(Value);
|
||||||
impl std::cmp::Eq for OrdValue {}
|
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 {
|
impl std::cmp::Ord for OrdValue {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Less)
|
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Less)
|
||||||
|
|
|
@ -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()
|
cs.iter()
|
||||||
.map(|c| c.map_or(Value::Nil, match_to_value))
|
.map(|c| c.map_or(Value::Nil, match_to_value))
|
||||||
.collect::<Vec<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")
|
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
|
||||||
};
|
};
|
||||||
let re = regex_from(&re, "captures_once")?;
|
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)]
|
#[native_func(2)]
|
||||||
|
@ -193,7 +196,7 @@ pub fn captures(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let re = regex_from(&re, "captures")?;
|
let re = regex_from(&re, "captures")?;
|
||||||
Ok(re
|
Ok(re
|
||||||
.captures_iter(s)
|
.captures_iter(s)
|
||||||
.map(captures_to_value)
|
.map(|cap| captures_to_value(&cap))
|
||||||
.collect::<Vec<Value>>()
|
.collect::<Vec<Value>>()
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue