refactoring, stdlib improvements
This commit is contained in:
parent
08c0cd9173
commit
443262f711
7 changed files with 443 additions and 71 deletions
31
Cargo.lock
generated
31
Cargo.lock
generated
|
@ -96,6 +96,7 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"num-traits",
|
||||
"paste",
|
||||
"try-partialord",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -204,9 +205,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.132"
|
||||
version = "0.2.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||
checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
|
@ -326,9 +327,9 @@ checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.43"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -380,9 +381,9 @@ checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.35.9"
|
||||
version = "0.35.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72c825b8aa8010eb9ee99b75f05e10180b9278d161583034d7574c9d617aeada"
|
||||
checksum = "af895b90e5c071badc3136fc10ff0bcfc98747eadbaf43ed8f214e07ba8f8477"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
|
@ -474,9 +475,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.99"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||
checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -485,24 +486,30 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.35"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85"
|
||||
checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.35"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783"
|
||||
checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-partialord"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "494f2baf446447eb9b49ece9bbc391b8b251ceb4778f7362ef09dd9eadec390f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.4"
|
||||
|
|
|
@ -10,3 +10,4 @@ complexpr = { path = "../complexpr"}
|
|||
num-traits = "0.2.15"
|
||||
paste = "1.0.9"
|
||||
lazy_static = "1.4.0"
|
||||
try-partialord = "0.1.3"
|
||||
|
|
|
@ -5,12 +5,16 @@ use complexpr::{env::Environment, value::{Value, Native, TypeData}, RuntimeError
|
|||
use crate::declare_fn;
|
||||
|
||||
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, println, 1);
|
||||
declare_fn!(env, input, 0);
|
||||
declare_fn!(env, open, 1);
|
||||
declare_fn!(env, open, 2);
|
||||
declare_fn!(env, close, 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> {
|
||||
|
@ -42,6 +46,7 @@ lazy_static::lazy_static! {
|
|||
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: complexpr::value::Type = complexpr::value::Type { name: FILE_TYPE_NAME.with(Rc::clone), id: *FILE_TYPE_ID, typedata: TypeData::None });
|
||||
|
||||
impl Native for FileBox {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
|
@ -53,8 +58,7 @@ impl Native for FileBox {
|
|||
}
|
||||
|
||||
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,
|
||||
_ => 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,
|
||||
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,
|
||||
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)
|
||||
},
|
||||
_ => 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 {
|
||||
let mut buf = String::new();
|
||||
match file.read_to_string(&mut buf) {
|
||||
Ok(_) => return Ok(Value::from(buf)),
|
||||
Err(e) => return Err(format!("Error reading file {}: {}", f, e).into())
|
||||
Ok(_) => Ok(Value::from(buf)),
|
||||
Err(e) => Err(format!("Error reading file {}: {}", f, e).into())
|
||||
}
|
||||
} else {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ use complexpr::{value::{Value, func::Func}, RuntimeError, env::Environment};
|
|||
use crate::declare_fn;
|
||||
|
||||
pub fn load(env: &mut Environment) {
|
||||
declare_fn!(env, iter, 1);
|
||||
declare_fn!(env, next, 1);
|
||||
declare_fn!(env, take, 2);
|
||||
declare_fn!(env, skip, 2);
|
||||
declare_fn!(env, forall, 2);
|
||||
|
@ -14,6 +16,31 @@ pub fn load(env: &mut Environment) {
|
|||
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> {
|
||||
let idx = RefCell::new(0);
|
||||
let limit = match args[0] {
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
@ -23,15 +23,34 @@ impl From<Complex> for Floaty {
|
|||
}
|
||||
|
||||
pub fn load(env: &mut Environment) {
|
||||
declare_fn!(env, re, 1);
|
||||
declare_fn!(env, im, 1);
|
||||
env.declare(Rc::from("inf"), Value::from(f64::INFINITY));
|
||||
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, max, 2);
|
||||
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, ceil, 1);
|
||||
declare_fn!(env, round, 1);
|
||||
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, cos, 1);
|
||||
declare_fn!(env, tan, 1);
|
||||
|
@ -46,6 +65,10 @@ pub fn load(env: &mut Environment) {
|
|||
declare_fn!(env, atanh, 1);
|
||||
declare_fn!(env, exp, 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, factors, 1);
|
||||
}
|
||||
|
@ -54,12 +77,12 @@ pub fn load(env: &mut Environment) {
|
|||
// 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 {
|
||||
Value::Int(n) => Ok((*n as f64).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::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))
|
||||
}
|
||||
}
|
||||
|
@ -68,25 +91,6 @@ fn try_into_floaty(v: &Value, name: &'static str) -> Result<Floaty, String> {
|
|||
// 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> {
|
||||
match args[0].partial_cmp(&args[1]) {
|
||||
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::Float(f) => Ok(Value::Float(f.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
|
||||
//
|
||||
|
@ -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
|
||||
//
|
||||
|
@ -213,15 +347,58 @@ transcendental!{ acosh }
|
|||
transcendental!{ atanh }
|
||||
transcendental!{ exp }
|
||||
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> {
|
||||
let n = match &args[0] {
|
||||
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 {
|
||||
_ 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)),
|
||||
_ => {
|
||||
let mut i = 5;
|
||||
let mut d = 2;
|
||||
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;
|
||||
i += d;
|
||||
d = 6 - d;
|
||||
}
|
||||
Ok(Value::Bool(true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn fn_factors(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
let mut n = match &args[0] {
|
||||
Value::Int(n) => n.abs(),
|
||||
|
@ -253,7 +429,7 @@ fn fn_factors(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
|||
return Ok(Value::from(vec![]));
|
||||
}
|
||||
let mut factors = vec![];
|
||||
while n % 2 == 0 {
|
||||
while n & 1 == 0 {
|
||||
factors.push(Value::Int(2));
|
||||
n >>= 1;
|
||||
}
|
||||
|
@ -262,17 +438,18 @@ fn fn_factors(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
|||
n /= 3;
|
||||
}
|
||||
let mut i = 5;
|
||||
let mut d = 2;
|
||||
while n != 1 {
|
||||
if i*i > n {
|
||||
factors.push(Value::Int(n));
|
||||
break;
|
||||
}
|
||||
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;
|
||||
i += d;
|
||||
d = 6 - d;
|
||||
}
|
||||
|
||||
Ok(Value::from(factors))
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
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;
|
||||
|
||||
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_eq, 1);
|
||||
declare_fn!(env, type_id, 1);
|
||||
declare_fn!(env, type_eq, 2);
|
||||
declare_fn!(env, copy, 1);
|
||||
declare_fn!(env, str, 1);
|
||||
declare_fn!(env, repr, 1);
|
||||
|
@ -16,12 +21,17 @@ pub fn load(env: &mut Environment) {
|
|||
declare_fn!(env, len, 1);
|
||||
declare_fn!(env, args, 0);
|
||||
declare_fn!(env, time, 0);
|
||||
declare_fn!(env, assert, 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);
|
||||
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()))
|
||||
}
|
||||
|
||||
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> {
|
||||
Ok(Value::Bool(if args[0].get_type() != args[1].get_type() {
|
||||
false
|
||||
|
@ -106,6 +124,14 @@ fn fn_time(_: Vec<Value>) -> Result<Value, RuntimeError> {
|
|||
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> {
|
||||
let a = args[0].iter()?;
|
||||
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()),
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,18 +54,20 @@ pub fn generate_struct_type(name: Rc<str>, fields: Vec<String>) -> Type {
|
|||
pub fn generate_builtin_types() -> Vec<Type> {
|
||||
let mut types = vec![];
|
||||
for x in ValueDiscriminants::iter() {
|
||||
types.push(Type {
|
||||
name: Rc::from(x.as_ref()),
|
||||
id: x as usize,
|
||||
typedata: TypeData::None,
|
||||
})
|
||||
if x != ValueDiscriminants::Struct && x != ValueDiscriminants::Native {
|
||||
types.push(Type {
|
||||
name: Rc::from(x.as_ref()),
|
||||
id: x as usize,
|
||||
typedata: TypeData::None,
|
||||
})
|
||||
}
|
||||
}
|
||||
types
|
||||
}
|
||||
|
||||
fn fmt_list(list: &Vec<Value>) -> String {
|
||||
let mut result: String = "[".into();
|
||||
if list.len() > 0 {
|
||||
if !list.is_empty() {
|
||||
for v in list {
|
||||
result += &(v.repr() + ", ");
|
||||
}
|
||||
|
@ -77,7 +79,7 @@ fn fmt_list(list: &Vec<Value>) -> String {
|
|||
|
||||
fn fmt_map(map: &HashMap<Value, Value>) -> String {
|
||||
let mut result: String = "{".into();
|
||||
if map.len() > 0 {
|
||||
if !map.is_empty() {
|
||||
for (k, v) in map {
|
||||
result += &(k.repr() + ": " + &v.repr() + ", ");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue