From 443262f7116c8aac6d5b89dea7fc38e2407c6d91 Mon Sep 17 00:00:00 2001 From: TriMill Date: Sun, 25 Sep 2022 19:25:13 -0400 Subject: [PATCH] refactoring, stdlib improvements --- Cargo.lock | 31 ++-- complexpr-stdlib/Cargo.toml | 1 + complexpr-stdlib/src/io.rs | 89 +++++++++-- complexpr-stdlib/src/iter.rs | 27 ++++ complexpr-stdlib/src/math.rs | 259 +++++++++++++++++++++++++++----- complexpr-stdlib/src/prelude.rs | 91 ++++++++++- complexpr/src/value/mod.rs | 16 +- 7 files changed, 443 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 263a810..df591d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/complexpr-stdlib/Cargo.toml b/complexpr-stdlib/Cargo.toml index 04981cf..a105311 100644 --- a/complexpr-stdlib/Cargo.toml +++ b/complexpr-stdlib/Cargo.toml @@ -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" diff --git a/complexpr-stdlib/src/io.rs b/complexpr-stdlib/src/io.rs index 94e7662..eaaaf63 100644 --- a/complexpr-stdlib/src/io.rs +++ b/complexpr-stdlib/src/io.rs @@ -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) -> Result { @@ -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 = 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) -> Result { 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) -> Result { 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) -> Result { 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) -> Result { + 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) -> Result { + 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()) } } diff --git a/complexpr-stdlib/src/iter.rs b/complexpr-stdlib/src/iter.rs index b6469c0..48c220a 100644 --- a/complexpr-stdlib/src/iter.rs +++ b/complexpr-stdlib/src/iter.rs @@ -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) -> Result { + 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) -> Result { + match args[0].iter()?.next() { + Some(x) => x, + None => Ok(Value::Nil) + } +} + fn fn_take(args: Vec) -> Result { let idx = RefCell::new(0); let limit = match args[0] { diff --git a/complexpr-stdlib/src/math.rs b/complexpr-stdlib/src/math.rs index a10a658..824fb29 100644 --- a/complexpr-stdlib/src/math.rs +++ b/complexpr-stdlib/src/math.rs @@ -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 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 { +fn try_into_floaty(v: &Value, name: &str) -> Result { 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 { // Misc functions // -fn fn_re(args: Vec) -> Result { - 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) -> Result { - 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) -> Result { 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) -> Result { 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { } } +fn fn_fract(args: Vec) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { 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) -> Result { _ 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) -> Result { let mut n = match &args[0] { Value::Int(n) => n.abs(), @@ -253,7 +429,7 @@ fn fn_factors(args: Vec) -> Result { 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) -> Result { 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)) diff --git a/complexpr-stdlib/src/prelude.rs b/complexpr-stdlib/src/prelude.rs index ce6a5a4..62765fc 100644 --- a/complexpr-stdlib/src/prelude.rs +++ b/complexpr-stdlib/src/prelude.rs @@ -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) -> Result { Ok(Value::Type(args[0].get_type())) } +fn fn_type_id(args: Vec) -> Result { + 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) -> Result { Ok(Value::Bool(if args[0].get_type() != args[1].get_type() { false @@ -106,6 +124,14 @@ fn fn_time(_: Vec) -> Result { Ok(Value::from(time.as_secs_f64())) } +fn fn_assert(args: Vec) -> Result { + if args[0].truthy() { + Ok(Value::Nil) + } else { + Err("Assertion failed".into()) + } +} + fn fn_list(args: Vec) -> Result { let a = args[0].iter()?; let mut res = Vec::new(); @@ -179,3 +205,64 @@ fn fn_remove(args: Vec) -> Result { _ => Err("First argument to remove must be a list or map".into()), } } + +fn fn_rev(mut args: Vec) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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()) + } +} diff --git a/complexpr/src/value/mod.rs b/complexpr/src/value/mod.rs index 2ccfde9..789633f 100644 --- a/complexpr/src/value/mod.rs +++ b/complexpr/src/value/mod.rs @@ -54,18 +54,20 @@ pub fn generate_struct_type(name: Rc, fields: Vec) -> Type { pub fn generate_builtin_types() -> Vec { 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) -> 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) -> String { fn fmt_map(map: &HashMap) -> String { let mut result: String = "{".into(); - if map.len() > 0 { + if !map.is_empty() { for (k, v) in map { result += &(k.repr() + ": " + &v.repr() + ", "); }