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",
"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"

View File

@ -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"

View File

@ -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())
}
}

View File

@ -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] {

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};
@ -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))

View File

@ -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())
}
}

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> {
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() + ", ");
}