more functions

This commit is contained in:
TriMill 2022-09-20 21:30:13 -04:00
parent 749a404a90
commit 14b749d2ed
5 changed files with 216 additions and 4 deletions

View file

@ -7,6 +7,6 @@ edition = "2021"
num-complex = "0.4.2" num-complex = "0.4.2"
num-rational = "0.4.1" num-rational = "0.4.1"
num-traits = "0.2.15" num-traits = "0.2.15"
strum_macros = "0.24" strum_macros = "0.24.3"
strum = { version = "0.24", features = ["derive"] } strum = { version = "0.24.1", features = ["derive"] }
paste = "1.0.9" paste = "1.0.9"

View file

@ -7,6 +7,9 @@ pub fn load(env: &mut Environment) {
declare_fn!(env, skip, 2); declare_fn!(env, skip, 2);
declare_fn!(env, forall, 2); declare_fn!(env, forall, 2);
declare_fn!(env, exists, 2); declare_fn!(env, exists, 2);
declare_fn!(env, nth, 2);
declare_fn!(env, last, 1);
declare_fn!(env, void, 1);
} }
fn fn_take(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_take(args: Vec<Value>) -> Result<Value, RuntimeError> {
@ -76,3 +79,20 @@ fn fn_exists(args: Vec<Value>) -> Result<Value, RuntimeError> {
} }
Ok(Value::Bool(false)) Ok(Value::Bool(false))
} }
fn fn_nth(args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::Int(n) if *n < 0 => Err("First argument to nth must be positive".into()),
Value::Int(n) => args[1].iter()?.nth(*n as usize).unwrap_or(Ok(Value::Nil)),
_ => Err("First argument to nth must be an integer".into())
}
}
fn fn_last(args: Vec<Value>) -> Result<Value, RuntimeError> {
args[0].iter()?.last().unwrap_or(Ok(Value::Nil))
}
fn fn_void(args: Vec<Value>) -> Result<Value, RuntimeError> {
for _ in args[0].iter()? {}
Ok(Value::Nil)
}

View file

@ -1,6 +1,6 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use num_traits::{ToPrimitive, Pow}; use num_traits::{ToPrimitive, Pow, Signed};
use crate::{value::{Value, Complex, Rational}, RuntimeError, env::Environment, declare_fn}; use crate::{value::{Value, Complex, Rational}, RuntimeError, env::Environment, declare_fn};
@ -25,6 +25,7 @@ pub fn load(env: &mut Environment) {
declare_fn!(env, im, 1); declare_fn!(env, im, 1);
declare_fn!(env, min, 2); declare_fn!(env, min, 2);
declare_fn!(env, max, 2); declare_fn!(env, max, 2);
declare_fn!(env, abs, 1);
declare_fn!(env, floor, 1); declare_fn!(env, floor, 1);
declare_fn!(env, ceil, 1); declare_fn!(env, ceil, 1);
declare_fn!(env, round, 1); declare_fn!(env, round, 1);
@ -43,6 +44,8 @@ pub fn load(env: &mut Environment) {
declare_fn!(env, atanh, 1); declare_fn!(env, atanh, 1);
declare_fn!(env, exp, 1); declare_fn!(env, exp, 1);
declare_fn!(env, "log", fn_ln, 1); declare_fn!(env, "log", fn_ln, 1);
declare_fn!(env, is_prime, 1);
declare_fn!(env, factors, 1);
} }
// //
@ -98,6 +101,19 @@ fn fn_max(args: Vec<Value>) -> Result<Value, RuntimeError> {
} }
} }
fn fn_abs(args: Vec<Value>) -> Result<Value, RuntimeError> {
match args[0] {
Value::Int(n) => Ok(Value::Int(n.abs())),
Value::Float(f) => Ok(Value::Float(f.abs())),
Value::Rational(r) => Ok(Value::Rational(r.abs())),
_ => Err("Argument to floor must be real".into()),
}
}
//
// Rounding
//
fn fn_floor(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_floor(args: Vec<Value>) -> Result<Value, RuntimeError> {
match args[0] { match args[0] {
Value::Int(n) => Ok(Value::Int(n)), Value::Int(n) => Ok(Value::Int(n)),
@ -195,3 +211,67 @@ transcendental!{ acosh }
transcendental!{ atanh } transcendental!{ atanh }
transcendental!{ exp } transcendental!{ exp }
transcendental!{ ln } transcendental!{ ln }
//
// Factorization
//
fn fn_is_prime(args: Vec<Value>) -> Result<Value, RuntimeError> {
let n = match &args[0] {
Value::Int(n) => *n,
_ => return Err("Argument to is_prime must be an integer".into())
};
match n {
_ if n <= 1 => Ok(Value::Bool(false)),
2 | 3 => Ok(Value::Bool(true)),
_ if (n % 2 == 0) || (n % 3 == 0) => Ok(Value::Bool(false)),
_ => {
let mut i = 5;
while i*i <= n {
if n % i == 0 {
return Ok(Value::Bool(false));
}
i += 2;
if n % i == 0 {
return Ok(Value::Bool(false));
}
i += 4;
}
Ok(Value::Bool(true))
}
}
}
fn fn_factors(args: Vec<Value>) -> Result<Value, RuntimeError> {
let mut n = match &args[0] {
Value::Int(n) => n.abs(),
_ => return Err("Argument to is_prime must be an integer".into())
};
if n <= 1 {
return Ok(Value::from(vec![]));
}
let mut factors = vec![];
while n % 2 == 0 {
factors.push(Value::Int(2));
n >>= 1;
}
while n % 3 == 0 {
factors.push(Value::Int(3));
n /= 3;
}
let mut i = 5;
while n != 1 {
while n % i == 0 {
factors.push(Value::Int(i));
n /= i;
}
i += 2;
while n % i == 0 {
factors.push(Value::Int(i));
n /= i;
}
i += 4;
}
Ok(Value::from(factors))
}

View file

@ -21,15 +21,23 @@ macro_rules! declare_fn {
pub fn load(env: &mut Environment) { pub fn load(env: &mut Environment) {
declare_fn!(env, "type", fn_type, 1); declare_fn!(env, "type", fn_type, 1);
declare_fn!(env, type_eq, 1); declare_fn!(env, type_eq, 1);
declare_fn!(env, copy, 1);
declare_fn!(env, str, 1); declare_fn!(env, str, 1);
declare_fn!(env, repr, 1); declare_fn!(env, repr, 1);
declare_fn!(env, ord, 1); declare_fn!(env, ord, 1);
declare_fn!(env, chr, 1); declare_fn!(env, chr, 1);
declare_fn!(env, range, 2); declare_fn!(env, range, 2);
declare_fn!(env, count_by, 2);
declare_fn!(env, has, 2); declare_fn!(env, has, 2);
declare_fn!(env, len, 1); declare_fn!(env, len, 1);
declare_fn!(env, time, 0); declare_fn!(env, time, 0);
declare_fn!(env, list, 1); declare_fn!(env, list, 1);
declare_fn!(env, push, 2);
declare_fn!(env, pop, 1);
declare_fn!(env, append, 2);
declare_fn!(env, insert, 3);
declare_fn!(env, remove, 2);
} }
fn fn_type(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_type(args: Vec<Value>) -> Result<Value, RuntimeError> {
@ -44,6 +52,16 @@ fn fn_type_eq(args: Vec<Value>) -> Result<Value, RuntimeError> {
})) }))
} }
fn fn_copy(args: Vec<Value>) -> Result<Value, RuntimeError> {
Ok(match &args[0] {
Value::List(l) => Value::from(l.borrow().clone()),
Value::Map(m) => Value::from(m.borrow().clone()),
// Value::Func(f) => Value::Func(f.make_copy()) // TODO copy functions
Value::Data(_) => todo!(),
a => a.clone(),
})
}
fn fn_str(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_str(args: Vec<Value>) -> Result<Value, RuntimeError> {
Ok(Value::String(args[0].to_string())) Ok(Value::String(args[0].to_string()))
} }
@ -105,6 +123,26 @@ fn fn_range(args: Vec<Value>) -> Result<Value, RuntimeError> {
Ok(Value::Func(mk_range_inner(*start, *end, delta))) Ok(Value::Func(mk_range_inner(*start, *end, delta)))
} }
fn mk_countby_inner(start: i64, delta: i64) -> Func {
let counter = RefCell::new(start);
Func::BuiltinClosure {
arg_count: 0,
func: Rc::new(move |_| {
let res = *counter.borrow();
*counter.borrow_mut() += delta;
Ok(Value::Int(res))
})
}
}
fn fn_count_by(args: Vec<Value>) -> Result<Value, RuntimeError> {
let (start, delta) = match (&args[0], &args[1]) {
(Value::Int(a), Value::Int(b)) => (a, b),
_ => return Err("Both arguments to count_by must be integers".into())
};
Ok(Value::Func(mk_countby_inner(*start, *delta)))
}
fn fn_len(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_len(args: Vec<Value>) -> Result<Value, RuntimeError> {
Ok(Value::Int(args[0].len().map_err(RuntimeError::new_no_pos)? as i64)) Ok(Value::Int(args[0].len().map_err(RuntimeError::new_no_pos)? as i64))
} }
@ -127,3 +165,70 @@ fn fn_list(args: Vec<Value>) -> Result<Value, RuntimeError> {
for v in a { res.push(v?); } for v in a { res.push(v?); }
Ok(Value::from(res)) Ok(Value::from(res))
} }
fn fn_push(args: Vec<Value>) -> Result<Value, RuntimeError> {
if let Value::List(l) = &args[0] {
l.as_ref().borrow_mut().push(args[1].clone());
Ok(Value::Nil)
} else{
Err("First argument to push must be a list".into())
}
}
fn fn_pop(args: Vec<Value>) -> Result<Value, RuntimeError> {
if let Value::List(l) = &args[0] {
l.as_ref().borrow_mut().pop().ok_or("Pop on empty list".into())
} else{
Err("First argument to pop must be a list".into())
}
}
fn fn_append(args: Vec<Value>) -> Result<Value, RuntimeError> {
match (&args[0], &args[1]) {
(Value::List(a), Value::List(b)) => {
let mut newvals = b.borrow().clone();
a.borrow_mut().append(&mut newvals);
Ok(Value::Nil)
},
_ => Err("Both arguments to append must be lists".into())
}
}
fn fn_insert(args: Vec<Value>) -> Result<Value, RuntimeError> {
match (&args[0], &args[1]) {
(Value::List(a), Value::Int(i)) => {
if *i < 0 {
return Err(format!("List index {} cannot be negative", i).into())
}
if *i > a.borrow().len() as i64 {
return Err(format!("List index {} not valid for list of length {}", i, a.borrow().len()).into())
}
a.borrow_mut().insert(*i as usize, args[2].clone());
Ok(Value::Nil)
},
(Value::Map(a), b) => {
Ok(a.borrow_mut().insert(b.clone(), args[3].clone()).unwrap_or(Value::Nil))
},
(Value::List(_), _) => Err("Second argument to insert must be an integer when the first is a list".into()),
_ => Err("First argument to insert must be a list or map".into()),
}
}
fn fn_remove(args: Vec<Value>) -> Result<Value, RuntimeError> {
match (&args[0], &args[1]) {
(Value::List(a), Value::Int(i)) => {
if *i < 0 {
return Err(format!("List index {} cannot be negative", i).into())
}
if *i >= a.borrow().len() as i64 {
return Err(format!("List index {} not valid for list of length {}", i, a.borrow().len()).into())
}
Ok(a.borrow_mut().remove(*i as usize))
},
(Value::Map(a), b) => {
Ok(a.borrow_mut().remove(b).unwrap_or(Value::Nil))
},
(Value::List(_), _) => Err("Second argument to remove must be an integer when the first is a list".into()),
_ => Err("First argument to remove must be a list or map".into()),
}
}

7
examples/prime.cxpr Normal file
View file

@ -0,0 +1,7 @@
# compute the primorial of n - the product of all primes <= n
fn primorial(n) {
return range(2,n+1) |? is_prime |// fn(x,y) { return x*y; };
}
println(primorial(10)); # 2*3*5*7 = 210
println(primorial(20)); # 2*3*5*7*11*13*17*19 = 9699690