refactoring, stdlib improvements

This commit is contained in:
TriMill 2022-09-25 19:25:13 -04:00
parent 08c0cd9173
commit 443262f711
7 changed files with 443 additions and 71 deletions

31
Cargo.lock generated
View file

@ -96,6 +96,7 @@ dependencies = [
"lazy_static", "lazy_static",
"num-traits", "num-traits",
"paste", "paste",
"try-partialord",
] ]
[[package]] [[package]]
@ -204,9 +205,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.132" version = "0.2.133"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
@ -326,9 +327,9 @@ checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.43" version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -380,9 +381,9 @@ checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.35.9" version = "0.35.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72c825b8aa8010eb9ee99b75f05e10180b9278d161583034d7574c9d617aeada" checksum = "af895b90e5c071badc3136fc10ff0bcfc98747eadbaf43ed8f214e07ba8f8477"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
@ -474,9 +475,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.99" version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -485,24 +486,30 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.35" version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.35" version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
] ]
[[package]]
name = "try-partialord"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "494f2baf446447eb9b49ece9bbc391b8b251ceb4778f7362ef09dd9eadec390f"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.4" version = "1.0.4"

View file

@ -10,3 +10,4 @@ complexpr = { path = "../complexpr"}
num-traits = "0.2.15" num-traits = "0.2.15"
paste = "1.0.9" paste = "1.0.9"
lazy_static = "1.4.0" lazy_static = "1.4.0"
try-partialord = "0.1.3"

View file

@ -5,12 +5,16 @@ use complexpr::{env::Environment, value::{Value, Native, TypeData}, RuntimeError
use crate::declare_fn; use crate::declare_fn;
pub fn load(env: &mut Environment) { pub fn load(env: &mut Environment) {
let file_type = FILE_TYPE.with(|x| x.clone());
env.declare(file_type.name.clone(), Value::Type(file_type));
declare_fn!(env, print, 1); declare_fn!(env, print, 1);
declare_fn!(env, println, 1); declare_fn!(env, println, 1);
declare_fn!(env, input, 0); declare_fn!(env, input, 0);
declare_fn!(env, open, 1); declare_fn!(env, open, 2);
declare_fn!(env, close, 1); declare_fn!(env, close, 1);
declare_fn!(env, read, 1); declare_fn!(env, read, 1);
declare_fn!(env, write, 2);
declare_fn!(env, flush, 1);
} }
fn fn_print(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_print(args: Vec<Value>) -> Result<Value, RuntimeError> {
@ -42,6 +46,7 @@ lazy_static::lazy_static! {
static ref FILE_TYPE_ID: usize = complexpr::value::generate_type_id(); static ref FILE_TYPE_ID: usize = complexpr::value::generate_type_id();
} }
thread_local!(static FILE_TYPE_NAME: Rc<str> = Rc::from("File")); thread_local!(static FILE_TYPE_NAME: Rc<str> = Rc::from("File"));
thread_local!(static FILE_TYPE: complexpr::value::Type = complexpr::value::Type { name: FILE_TYPE_NAME.with(Rc::clone), id: *FILE_TYPE_ID, typedata: TypeData::None });
impl Native for FileBox { impl Native for FileBox {
fn as_any(&self) -> &dyn std::any::Any { fn as_any(&self) -> &dyn std::any::Any {
@ -53,8 +58,7 @@ impl Native for FileBox {
} }
fn get_type(&self) -> complexpr::value::Type { fn get_type(&self) -> complexpr::value::Type {
complexpr::value::Type { name: FILE_TYPE_NAME.with(Rc::clone), id: *FILE_TYPE_ID, typedata: TypeData::None } FILE_TYPE.with(|x| x.clone())
} }
} }
@ -75,7 +79,25 @@ fn fn_open(args: Vec<Value>) -> Result<Value, RuntimeError> {
Value::String(s) => s, Value::String(s) => s,
_ => return Err(format!("Expected filename, got {}", args[0]).into()) _ => return Err(format!("Expected filename, got {}", args[0]).into())
}; };
let f = match OpenOptions::new().read(true).open(fname.as_ref()) { let opts = match &args[1] {
Value::String(s) => s,
_ => return Err(format!("Expected open options string, got {}", args[0]).into())
};
let mut options = OpenOptions::new();
options.append(false);
for c in opts.chars() {
match c {
'r' => options.read(true),
'w' => options.write(true).append(false).truncate(true),
'a' => options.write(true).append(true).truncate(false),
's' => options.write(true).append(false).truncate(false),
'x' => options.create_new(false).create(true),
'X' => options.create(false).create_new(true),
_ => return Err(format!("Character {} is not valid in an open options string", c).into())
};
}
println!("{:?}", options);
let f = match options.open(fname.as_ref()) {
Ok(f) => f, Ok(f) => f,
Err(e) => return Err(format!("Could not open file '{}': {}", fname, e).into()) Err(e) => return Err(format!("Could not open file '{}': {}", fname, e).into())
}; };
@ -90,10 +112,13 @@ fn fn_close(args: Vec<Value>) -> Result<Value, RuntimeError> {
Some(f) => f, Some(f) => f,
None => return Err(format!("Expected a file, got {}", args[0]).into()) None => return Err(format!("Expected a file, got {}", args[0]).into())
}; };
drop(f.f.take()); match f.f.take() {
Some(x) => drop(x),
None => return Err("File already closed".into())
}
Ok(Value::Nil) Ok(Value::Nil)
}, },
_ => return Err(format!("Expected a file, got {}", args[0]).into()) _ => Err(format!("Expected a file, got {}", args[0]).into())
} }
} }
@ -108,13 +133,59 @@ fn fn_read(args: Vec<Value>) -> Result<Value, RuntimeError> {
if let Some(file) = &mut f.f { if let Some(file) = &mut f.f {
let mut buf = String::new(); let mut buf = String::new();
match file.read_to_string(&mut buf) { match file.read_to_string(&mut buf) {
Ok(_) => return Ok(Value::from(buf)), Ok(_) => Ok(Value::from(buf)),
Err(e) => return Err(format!("Error reading file {}: {}", f, e).into()) Err(e) => Err(format!("Error reading file {}: {}", f, e).into())
} }
} else { } else {
Err("Attempt to read file that has been closed".into()) Err("Attempt to read file that has been closed".into())
} }
}, },
_ => return Err(format!("Expected a file, got {}", args[0]).into()) _ => Err(format!("Expected a file, got {}", args[0]).into())
}
}
fn fn_write(args: Vec<Value>) -> Result<Value, RuntimeError> {
let contents = match &args[1] {
Value::String(s) => s,
_ => return Err(format!("Expected string to write as second argument, got {}", args[1]).into())
};
match &args[0] {
Value::Native(s) => {
let mut bs = s.borrow_mut();
let f: &mut FileBox = match bs.as_any_mut().downcast_mut() {
Some(f) => f,
None => return Err(format!("Expected a file as first argument, got {}", args[0]).into())
};
if let Some(file) = &mut f.f {
match file.write(contents.as_bytes()) {
Ok(_) => Ok(Value::Nil),
Err(e) => Err(format!("Error writing to file {}: {}", f, e).into())
}
} else {
Err("Attempt to write to file that has been closed".into())
}
},
_ => Err(format!("Expected a file as first argument, got {}", args[0]).into())
}
}
fn fn_flush(args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::Native(s) => {
let mut bs = s.borrow_mut();
let f: &mut FileBox = match bs.as_any_mut().downcast_mut() {
Some(f) => f,
None => return Err(format!("Expected a file, got {}", args[0]).into())
};
if let Some(file) = &mut f.f {
match file.flush() {
Ok(_) => Ok(Value::Nil),
Err(e) => Err(format!("Error flushing file {}: {}", f, e).into())
}
} else {
Err("Attempt to flush file that has been closed".into())
}
},
_ => Err(format!("Expected a file, got {}", args[0]).into())
} }
} }

View file

@ -5,6 +5,8 @@ use complexpr::{value::{Value, func::Func}, RuntimeError, env::Environment};
use crate::declare_fn; use crate::declare_fn;
pub fn load(env: &mut Environment) { pub fn load(env: &mut Environment) {
declare_fn!(env, iter, 1);
declare_fn!(env, next, 1);
declare_fn!(env, take, 2); declare_fn!(env, take, 2);
declare_fn!(env, skip, 2); declare_fn!(env, skip, 2);
declare_fn!(env, forall, 2); declare_fn!(env, forall, 2);
@ -14,6 +16,31 @@ pub fn load(env: &mut Environment) {
declare_fn!(env, void, 1); declare_fn!(env, void, 1);
} }
fn fn_iter(mut args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::Func(f) if f.arg_count() == 0 => Ok(args.pop().unwrap()),
x => {
let it = RefCell::new(x.iter()?);
Ok(Value::Func(Func::BuiltinClosure {
arg_count: 0,
func: Rc::new(move |_| {
match it.borrow_mut().next() {
Some(x) => x,
None => Ok(Value::Nil)
}
})
} ))
}
}
}
fn fn_next(args: Vec<Value>) -> Result<Value, RuntimeError> {
match args[0].iter()?.next() {
Some(x) => x,
None => Ok(Value::Nil)
}
}
fn fn_take(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_take(args: Vec<Value>) -> Result<Value, RuntimeError> {
let idx = RefCell::new(0); let idx = RefCell::new(0);
let limit = match args[0] { let limit = match args[0] {

View file

@ -1,6 +1,6 @@
use std::cmp::Ordering; use std::{cmp::Ordering, rc::Rc};
use num_traits::{ToPrimitive, Pow, Signed}; use num_traits::{ToPrimitive, Pow, Signed, Zero};
use complexpr::{value::{Value, Complex, Rational}, RuntimeError, env::Environment}; use complexpr::{value::{Value, Complex, Rational}, RuntimeError, env::Environment};
@ -23,15 +23,34 @@ impl From<Complex> for Floaty {
} }
pub fn load(env: &mut Environment) { pub fn load(env: &mut Environment) {
declare_fn!(env, re, 1); env.declare(Rc::from("inf"), Value::from(f64::INFINITY));
declare_fn!(env, im, 1); env.declare(Rc::from("nan"), Value::from(f64::NAN));
env.declare(Rc::from("tau"), Value::from(std::f64::consts::TAU));
env.declare(Rc::from("pi"), Value::from(std::f64::consts::PI));
env.declare(Rc::from("e"), Value::from(std::f64::consts::E));
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, abs, 1);
declare_fn!(env, numer, 1);
declare_fn!(env, denom, 1);
declare_fn!(env, rationalize, 1);
declare_fn!(env, re, 1);
declare_fn!(env, im, 1);
declare_fn!(env, conj, 1);
declare_fn!(env, arg, 1);
declare_fn!(env, norm, 1);
declare_fn!(env, norm_sq, 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);
declare_fn!(env, round_to, 2); declare_fn!(env, round_to, 2);
declare_fn!(env, fract, 1);
declare_fn!(env, trunc, 1);
declare_fn!(env, signum, 1);
declare_fn!(env, sin, 1); declare_fn!(env, sin, 1);
declare_fn!(env, cos, 1); declare_fn!(env, cos, 1);
declare_fn!(env, tan, 1); declare_fn!(env, tan, 1);
@ -46,6 +65,10 @@ 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, sqrt, 1);
declare_fn!(env, gcd, 2);
declare_fn!(env, lcm, 2);
declare_fn!(env, is_prime, 1); declare_fn!(env, is_prime, 1);
declare_fn!(env, factors, 1); declare_fn!(env, factors, 1);
} }
@ -54,12 +77,12 @@ pub fn load(env: &mut Environment) {
// Helper functions // Helper functions
// //
fn try_into_floaty(v: &Value, name: &'static str) -> Result<Floaty, String> { fn try_into_floaty(v: &Value, name: &str) -> Result<Floaty, String> {
match v { match v {
Value::Int(n) => Ok((*n as f64).into()),
Value::Float(f) => Ok((*f).into()), Value::Float(f) => Ok((*f).into()),
Value::Rational(r) => Ok((r.to_f64().ok_or("Could not convert rational to float")?).into()),
Value::Complex(z) => Ok((*z).into()), Value::Complex(z) => Ok((*z).into()),
Value::Int(n) => Ok((*n as f64).into()),
Value::Rational(r) => Ok((r.to_f64().ok_or("Could not convert rational to float")?).into()),
_ => Err(format!("Argument to {} must be numeric", name)) _ => Err(format!("Argument to {} must be numeric", name))
} }
} }
@ -68,25 +91,6 @@ fn try_into_floaty(v: &Value, name: &'static str) -> Result<Floaty, String> {
// Misc functions // Misc functions
// //
fn fn_re(args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::Int(x) => Ok(Value::Float(*x as f64)),
Value::Float(x) => Ok(Value::Float(*x)),
Value::Rational(x) => Ok(Value::Float(x.to_f64().unwrap())),
Value::Complex(x) => Ok(Value::Float(x.re)),
x => Err(format!("Cannot get real part of {:?}", x).into())
}
}
fn fn_im(args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::Int(_) | Value::Float(_) | Value::Rational(_)
=> Ok(Value::Float(0.0)),
Value::Complex(x) => Ok(Value::Float(x.im)),
x => Err(format!("Cannot get real part of {:?}", x).into())
}
}
fn fn_min(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_min(args: Vec<Value>) -> Result<Value, RuntimeError> {
match args[0].partial_cmp(&args[1]) { match args[0].partial_cmp(&args[1]) {
None => Err("Arguments to min must be comparable".into()), None => Err("Arguments to min must be comparable".into()),
@ -108,10 +112,109 @@ fn fn_abs(args: Vec<Value>) -> Result<Value, RuntimeError> {
Value::Int(n) => Ok(Value::Int(n.abs())), Value::Int(n) => Ok(Value::Int(n.abs())),
Value::Float(f) => Ok(Value::Float(f.abs())), Value::Float(f) => Ok(Value::Float(f.abs())),
Value::Rational(r) => Ok(Value::Rational(r.abs())), Value::Rational(r) => Ok(Value::Rational(r.abs())),
_ => Err("Argument to floor must be real".into()), _ => Err("Argument to abs must be real".into()),
} }
} }
//
// Rationals
//
fn fn_numer(args: Vec<Value>) -> Result<Value, RuntimeError> {
match args[0] {
Value::Rational(r) => Ok(Value::from(*r.numer())),
Value::Int(n) => Ok(Value::from(n)),
_ => Err("Argument to numer must be integer or rational".into()),
}
}
fn fn_denom(args: Vec<Value>) -> Result<Value, RuntimeError> {
match args[0] {
Value::Rational(r) => Ok(Value::from(*r.denom())),
Value::Int(_) => Ok(Value::from(1)),
_ => Err("Argument to denom must be integer or rational".into()),
}
}
fn fn_rationalize(args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::Float(f) => match Rational::approximate_float(*f) {
Some(r) => Ok(Value::from(r)),
None => Err("Error rationalizing float".into())
},
Value::Int(n) => Ok(Value::Rational(Rational::from(*n))),
Value::Rational(r) => Ok(Value::Rational(*r)),
x => Err(format!("Expected float, int, or rational, got {}", x).into())
}
}
//
// Complex
//
fn fn_re(args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::Complex(x) => Ok(Value::Float(x.re)),
Value::Float(x) => Ok(Value::Float(*x)),
Value::Int(x) => Ok(Value::Float(*x as f64)),
Value::Rational(x) => Ok(Value::Float(x.to_f64().unwrap())),
x => Err(format!("Cannot get real part of {:?}", x).into())
}
}
fn fn_im(args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::Complex(x) => Ok(Value::Float(x.im)),
Value::Int(_) | Value::Float(_) | Value::Rational(_)
=> Ok(Value::Float(0.0)),
x => Err(format!("Cannot get real part of {:?}", x).into())
}
}
fn fn_conj(args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::Complex(x) => Ok(Value::from(x.conj())),
Value::Float(n) => Ok(Value::from(Complex::from(*n))),
Value::Int(n) => Ok(Value::from(Complex::from(*n as f64))),
Value::Rational(n) => Ok(Value::from(Complex::from(n.to_f64().unwrap()))),
x => Err(format!("Cannot get conjugate of {:?}", x).into())
}
}
fn fn_arg(args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::Complex(x) => Ok(Value::from(x.arg())),
Value::Float(n) if *n >= 0.0 => Ok(Value::from(0.0)),
Value::Float(_) => Ok(Value::from(std::f64::consts::PI)),
Value::Int(n) if *n >= 0 => Ok(Value::from(0.0)),
Value::Int(_) => Ok(Value::from(std::f64::consts::PI)),
Value::Rational(n) if *n >= Rational::zero() => Ok(Value::from(0.0)),
Value::Rational(_) => Ok(Value::from(std::f64::consts::PI)),
x => Err(format!("Cannot get argument of {:?}", x).into())
}
}
fn fn_norm(args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::Complex(x) => Ok(Value::from(x.norm())),
Value::Float(n) => Ok(Value::from(n.abs())),
Value::Int(n) => Ok(Value::from(n.abs() as f64)),
Value::Rational(n) => Ok(Value::from(n.abs().to_f64().unwrap())),
x => Err(format!("Cannot get norm of {:?}", x).into())
}
}
fn fn_norm_sq(args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::Complex(x) => Ok(Value::from(x.norm_sqr())),
Value::Float(n) => Ok(Value::from(n*n)),
Value::Int(n) => Ok(Value::from((*n as f64)*(*n as f64))),
Value::Rational(n) => { let x = n.to_f64().unwrap(); Ok(Value::from(x*x)) },
x => Err(format!("Cannot get norm squared of {:?}", x).into())
}
}
// //
// Rounding // Rounding
// //
@ -181,6 +284,37 @@ fn fn_round_to(args: Vec<Value>) -> Result<Value, RuntimeError> {
} }
} }
fn fn_fract(args: Vec<Value>) -> Result<Value, RuntimeError> {
match args[0] {
Value::Int(_) => Ok(Value::Int(0)),
Value::Float(f) => Ok(Value::Float(f.fract())),
Value::Complex(c) => Ok(Value::Complex(Complex::new(c.re.fract(), c.im.fract()))),
Value::Rational(r) => Ok(Value::Rational(r.fract())),
_ => Err("Argument to fract must be numeric".into()),
}
}
fn fn_trunc(args: Vec<Value>) -> Result<Value, RuntimeError> {
match args[0] {
Value::Int(n) => Ok(Value::Int(n)),
Value::Float(f) => Ok(Value::Float(f.trunc())),
Value::Complex(c) => Ok(Value::Complex(Complex::new(c.re.trunc(), c.im.trunc()))),
Value::Rational(r) => Ok(Value::Rational(r.trunc())),
_ => Err("Argument to trunc must be numeric".into()),
}
}
fn fn_signum(args: Vec<Value>) -> Result<Value, RuntimeError> {
match args[0] {
Value::Int(n) => Ok(Value::Int(n.signum())),
Value::Float(f) => Ok(Value::Float(f.signum())),
Value::Complex(c) => Ok(Value::Complex(Complex::new(c.re.signum(), c.im.signum()))),
Value::Rational(r) => Ok(Value::Rational(r.signum())),
_ => Err("Argument to trunc must be numeric".into()),
}
}
// //
// Transcendental functions // Transcendental functions
// //
@ -213,15 +347,58 @@ transcendental!{ acosh }
transcendental!{ atanh } transcendental!{ atanh }
transcendental!{ exp } transcendental!{ exp }
transcendental!{ ln } transcendental!{ ln }
transcendental!{ sqrt } // not technically transcendental
// //
// Factorization // Integers
// //
fn gcd_inner(mut a: i64, mut b: i64) -> i64 {
if a == 0 {
return b
} else 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();
}
}
fn fn_gcd(args: Vec<Value>) -> Result<Value, RuntimeError> {
let (a, b) = match (&args[0], &args[1]) {
(Value::Int(a), Value::Int(b)) => (a.abs(), b.abs()),
_ => return Err("Arguments to gcd must be integers".into())
};
Ok(Value::from(gcd_inner(a, b)))
}
fn fn_lcm(args: Vec<Value>) -> Result<Value, RuntimeError> {
let (a, b) = match (&args[0], &args[1]) {
(Value::Int(a), Value::Int(b)) => (a.abs(), b.abs()),
_ => return Err("Arguments to lcm must be integers".into())
};
let gcd = gcd_inner(a, b);
Ok(Value::from(a/gcd * b))
}
fn fn_is_prime(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_is_prime(args: Vec<Value>) -> Result<Value, RuntimeError> {
let n = match &args[0] { let n = match &args[0] {
Value::Int(n) => *n, Value::Int(n) => *n,
_ => return Err("Argument to is_prime must be an integer".into()) _ => return Err("Argument to is_prime must be integers".into())
}; };
match n { match n {
_ if n <= 1 => Ok(Value::Bool(false)), _ if n <= 1 => Ok(Value::Bool(false)),
@ -229,21 +406,20 @@ fn fn_is_prime(args: Vec<Value>) -> Result<Value, RuntimeError> {
_ if (n % 2 == 0) || (n % 3 == 0) => Ok(Value::Bool(false)), _ if (n % 2 == 0) || (n % 3 == 0) => Ok(Value::Bool(false)),
_ => { _ => {
let mut i = 5; let mut i = 5;
let mut d = 2;
while i*i <= n { while i*i <= n {
if n % i == 0 { if n % i == 0 {
return Ok(Value::Bool(false)); return Ok(Value::Bool(false));
} }
i += 2; i += d;
if n % i == 0 { d = 6 - d;
return Ok(Value::Bool(false));
}
i += 4;
} }
Ok(Value::Bool(true)) Ok(Value::Bool(true))
} }
} }
} }
fn fn_factors(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_factors(args: Vec<Value>) -> Result<Value, RuntimeError> {
let mut n = match &args[0] { let mut n = match &args[0] {
Value::Int(n) => n.abs(), Value::Int(n) => n.abs(),
@ -253,7 +429,7 @@ fn fn_factors(args: Vec<Value>) -> Result<Value, RuntimeError> {
return Ok(Value::from(vec![])); return Ok(Value::from(vec![]));
} }
let mut factors = vec![]; let mut factors = vec![];
while n % 2 == 0 { while n & 1 == 0 {
factors.push(Value::Int(2)); factors.push(Value::Int(2));
n >>= 1; n >>= 1;
} }
@ -262,17 +438,18 @@ fn fn_factors(args: Vec<Value>) -> Result<Value, RuntimeError> {
n /= 3; n /= 3;
} }
let mut i = 5; let mut i = 5;
let mut d = 2;
while n != 1 { while n != 1 {
if i*i > n {
factors.push(Value::Int(n));
break;
}
while n % i == 0 { while n % i == 0 {
factors.push(Value::Int(i)); factors.push(Value::Int(i));
n /= i; n /= i;
} }
i += 2; i += d;
while n % i == 0 { d = 6 - d;
factors.push(Value::Int(i));
n /= i;
}
i += 4;
} }
Ok(Value::from(factors)) Ok(Value::from(factors))

View file

@ -1,12 +1,17 @@
use std::{time::{SystemTime, UNIX_EPOCH}, rc::Rc, cell::RefCell}; use std::{time::{SystemTime, UNIX_EPOCH}, rc::Rc, cell::RefCell};
use complexpr::{value::{Value, func::Func}, RuntimeError, env::Environment}; use complexpr::{value::{Value, func::Func, self}, RuntimeError, env::Environment};
use try_partialord::TrySort;
use crate::declare_fn; use crate::declare_fn;
pub fn load(env: &mut Environment) { pub fn load(env: &mut Environment) {
for ty in value::generate_builtin_types() {
env.declare(ty.name.clone(), Value::Type(ty));
}
declare_fn!(env, "type", fn_type, 1); declare_fn!(env, "type", fn_type, 1);
declare_fn!(env, type_eq, 1); declare_fn!(env, type_id, 1);
declare_fn!(env, type_eq, 2);
declare_fn!(env, copy, 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);
@ -16,12 +21,17 @@ pub fn load(env: &mut Environment) {
declare_fn!(env, len, 1); declare_fn!(env, len, 1);
declare_fn!(env, args, 0); declare_fn!(env, args, 0);
declare_fn!(env, time, 0); declare_fn!(env, time, 0);
declare_fn!(env, assert, 1);
declare_fn!(env, list, 1); declare_fn!(env, list, 1);
declare_fn!(env, push, 2); declare_fn!(env, push, 2);
declare_fn!(env, pop, 1); declare_fn!(env, pop, 1);
declare_fn!(env, append, 2); declare_fn!(env, append, 2);
declare_fn!(env, insert, 3); declare_fn!(env, insert, 3);
declare_fn!(env, remove, 2); declare_fn!(env, remove, 2);
declare_fn!(env, rev, 1);
declare_fn!(env, sort, 1);
declare_fn!(env, sort_by, 2);
declare_fn!(env, sort_by_key, 2);
} }
@ -29,6 +39,14 @@ fn fn_type(args: Vec<Value>) -> Result<Value, RuntimeError> {
Ok(Value::Type(args[0].get_type())) Ok(Value::Type(args[0].get_type()))
} }
fn fn_type_id(args: Vec<Value>) -> Result<Value, RuntimeError> {
if let Value::Type(ty) = &args[0] {
Ok(Value::Int(ty.id as i64))
} else {
Err("Argument to id must be a type".into())
}
}
fn fn_type_eq(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_type_eq(args: Vec<Value>) -> Result<Value, RuntimeError> {
Ok(Value::Bool(if args[0].get_type() != args[1].get_type() { Ok(Value::Bool(if args[0].get_type() != args[1].get_type() {
false false
@ -106,6 +124,14 @@ fn fn_time(_: Vec<Value>) -> Result<Value, RuntimeError> {
Ok(Value::from(time.as_secs_f64())) Ok(Value::from(time.as_secs_f64()))
} }
fn fn_assert(args: Vec<Value>) -> Result<Value, RuntimeError> {
if args[0].truthy() {
Ok(Value::Nil)
} else {
Err("Assertion failed".into())
}
}
fn fn_list(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_list(args: Vec<Value>) -> Result<Value, RuntimeError> {
let a = args[0].iter()?; let a = args[0].iter()?;
let mut res = Vec::new(); let mut res = Vec::new();
@ -179,3 +205,64 @@ fn fn_remove(args: Vec<Value>) -> Result<Value, RuntimeError> {
_ => Err("First argument to remove must be a list or map".into()), _ => Err("First argument to remove must be a list or map".into()),
} }
} }
fn fn_rev(mut args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::List(l) => {
l.borrow_mut().reverse();
Ok(args.swap_remove(0))
},
Value::String(s) => {
let s: String = s.chars().rev().collect();
Ok(Value::from(s))
},
_ => Err(format!("Expected list or string, got {}", args[0]).into())
}
}
fn fn_sort(args: Vec<Value>) -> Result<Value, RuntimeError> {
match &args[0] {
Value::List(l) => match l.borrow_mut().try_sort() {
Ok(_) => Ok(args[0].clone()),
Err(_) => Err("List contained incomparable items".into())
},
_ => Err(format!("Expected list, got {}", args[0]).into())
}
}
fn fn_sort_by(args: Vec<Value>) -> Result<Value, RuntimeError> {
let func = match &args[1] {
Value::Func(f) => f,
x => return Err(format!("Expected function as second argument, got {}", x).into())
};
let ord = move |a: &Value, b: &Value| {
match func.call(vec![a.clone(), b.clone()]) {
Ok(Value::Int(n)) => Some(n < 0),
_ => None
}
};
match &args[0] {
Value::List(l) => match l.borrow_mut().try_sort_by(ord) {
Ok(_) => Ok(args[0].clone()),
Err(_) => Err("Error occured in sort comparison function".into())
},
_ => Err(format!("Expected list as first argument, got {}", args[0]).into())
}
}
fn fn_sort_by_key(args: Vec<Value>) -> Result<Value, RuntimeError> {
let func = match &args[1] {
Value::Func(f) => f,
x => return Err(format!("Expected function as second argument, got {}", x).into())
};
let ord = move |a: &Value| {
func.call(vec![a.clone()]).ok()
};
match &args[0] {
Value::List(l) => match l.borrow_mut().try_sort_by_key(ord) {
Ok(_) => Ok(args[0].clone()),
Err(_) => Err("Error occured in sort key function".into())
},
_ => Err(format!("Expected list as first argument, got {}", args[0]).into())
}
}

View file

@ -54,18 +54,20 @@ pub fn generate_struct_type(name: Rc<str>, fields: Vec<String>) -> Type {
pub fn generate_builtin_types() -> Vec<Type> { pub fn generate_builtin_types() -> Vec<Type> {
let mut types = vec![]; let mut types = vec![];
for x in ValueDiscriminants::iter() { for x in ValueDiscriminants::iter() {
types.push(Type { if x != ValueDiscriminants::Struct && x != ValueDiscriminants::Native {
name: Rc::from(x.as_ref()), types.push(Type {
id: x as usize, name: Rc::from(x.as_ref()),
typedata: TypeData::None, id: x as usize,
}) typedata: TypeData::None,
})
}
} }
types types
} }
fn fmt_list(list: &Vec<Value>) -> String { fn fmt_list(list: &Vec<Value>) -> String {
let mut result: String = "[".into(); let mut result: String = "[".into();
if list.len() > 0 { if !list.is_empty() {
for v in list { for v in list {
result += &(v.repr() + ", "); result += &(v.repr() + ", ");
} }
@ -77,7 +79,7 @@ fn fmt_list(list: &Vec<Value>) -> String {
fn fmt_map(map: &HashMap<Value, Value>) -> String { fn fmt_map(map: &HashMap<Value, Value>) -> String {
let mut result: String = "{".into(); let mut result: String = "{".into();
if map.len() > 0 { if !map.is_empty() {
for (k, v) in map { for (k, v) in map {
result += &(k.repr() + ": " + &v.repr() + ", "); result += &(k.repr() + ": " + &v.repr() + ", ");
} }