diff --git a/Cargo.lock b/Cargo.lock index 5c72b1b..ddff1be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,6 +124,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + [[package]] name = "argminmax" version = "0.6.2" @@ -158,7 +164,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.108", ] [[package]] @@ -169,7 +175,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.108", ] [[package]] @@ -264,7 +270,7 @@ checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.108", ] [[package]] @@ -488,7 +494,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.108", ] [[package]] @@ -637,7 +643,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.108", ] [[package]] @@ -1243,7 +1249,7 @@ dependencies = [ "rayon", "regex", "smartstring", - "thiserror", + "thiserror 1.0.61", "version_check", "xxhash-rust", ] @@ -1257,7 +1263,7 @@ dependencies = [ "polars-arrow-format", "regex", "simdutf8", - "thiserror", + "thiserror 1.0.61", ] [[package]] @@ -1552,9 +1558,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -1673,7 +1679,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" dependencies = [ "quote", - "syn 2.0.68", + "syn 2.0.108", ] [[package]] @@ -1693,7 +1699,7 @@ checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.61", ] [[package]] @@ -1729,6 +1735,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" name = "roklang" version = "0.1.0" dependencies = [ + "anyhow", "crossterm", "directories", "env_logger", @@ -1738,6 +1745,7 @@ dependencies = [ "polars", "rand", "rustyline", + "thiserror 2.0.17", ] [[package]] @@ -1816,7 +1824,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.108", ] [[package]] @@ -1979,7 +1987,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.68", + "syn 2.0.108", ] [[package]] @@ -1995,9 +2003,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.68" +version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", @@ -2030,7 +2038,16 @@ version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.61", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", ] [[package]] @@ -2041,7 +2058,18 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.108", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", ] [[package]] @@ -2148,7 +2176,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.108", "wasm-bindgen-shared", ] @@ -2170,7 +2198,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.108", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2384,7 +2412,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.108", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index febd1fa..f786542 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ license = "ISC" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.100" crossterm = "0.27.0" directories = "5.0.1" env_logger = "0.11.3" @@ -18,6 +19,7 @@ log = "0.4.19" polars = { version = "0.41.3", features = ["performant", "cse", "lazy", "parquet", "dtype-categorical", "strings"] } rand = "0.8.5" rustyline = "14.0.0" +thiserror = "2.0.17" [[bin]] name = "rok" diff --git a/src/lib.rs b/src/lib.rs index 1103035..5dadd6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,19 @@ +use anyhow::Result; use indexmap::IndexMap; use itertools::Itertools; use log::debug; use polars::prelude::*; use std::cmp::Ordering; use std::collections::HashMap; -use std::{f64, fmt}; use std::fs::File; use std::hash::{Hash, Hasher}; use std::iter::zip; use std::path::Path; -use std::{collections::VecDeque, iter::repeat, ops}; +use std::{collections::VecDeque, iter::repeat, iter::repeat_n, ops}; +use std::{f64, fmt}; +use thiserror::Error; mod verbs; - pub use verbs::*; // oK.js is 1k lines of javascript in one file for a k6 interpreter. @@ -78,6 +79,24 @@ pub enum KW /* KWords */ { FuncArgsStart, // [ but explicitly function arguments start } +#[derive(Error, Debug)] +pub enum RokError { + #[error("'domain")] + Domain, + #[error("'error\n{0}")] + Error(String), + #[error("'length")] + Length, + #[error("'nyi")] + NYI, + #[error("'parse\n{0}")] + Parse(&'static str), + #[error("'rank")] + Rank, + #[error("'type")] + Type, +} + impl K { pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -96,7 +115,7 @@ impl K { _ => 1, } } - pub fn fill(&self, n: usize) -> Result { + pub fn fill(&self, n: usize) -> Result { use K::*; match n { 0 => match self { @@ -106,10 +125,10 @@ impl K { IntArray(_) | Int(_) => Ok(Int(None)), FloatArray(_) | Float(_) => Ok(K::Float(f64::NAN)), CharArray(_) | Char(_) => Ok(Char(' ')), - Dictionary(_) => Err("type"), // TODO right? - Table(_) => Err("type"), // TODO right? + Dictionary(_) => Err(RokError::Type.into()), // TODO right? + Table(_) => Err(RokError::Type.into()), // TODO right? List(_) => Ok(List(vec![])), - Name(_) => Err("type"), // TODO right? + Name(_) => Err(RokError::Type.into()), // TODO right? }, _ => match self { Nil => todo!("List(vec![Nil, Nil, ...]) ???"), @@ -125,7 +144,7 @@ impl K { IntArray(a) => { if n > a.len() { // Ok(IntArray(a.i64().unwrap().into_iter().chain(repeat(Some(0i64))).take(n).collect())) - Ok(IntArray(a.extend_constant(AnyValue::Int64(0i64), n - a.len()).unwrap())) + Ok(IntArray(a.extend_constant(AnyValue::Int64(0i64), n - a.len())?)) } else { todo!("just take?") } @@ -230,7 +249,7 @@ impl fmt::Display for K { .collect(), ); let s = if s.len() < cols { s } else { s[..(cols - 2)].to_string() + ".." }; - if b.len() == 0 { + if b.is_empty() { write!(f, "!0") } else if b.len() == 1 { write!(f, ",{}", s) @@ -324,15 +343,8 @@ impl Hash for K { v.hash(state) } K::FloatArray(f) => { - let v: Vec> = f - .f64() - .unwrap() - .into_iter() - .map(|f| match f { - Some(f) => Some(f.to_string()), - None => None, - }) - .collect(); + let v: Vec> = + f.f64().unwrap().into_iter().map(|f| f.map(|f| f.to_string())).collect(); v.hash(state) } K::CharArray(c) => c.hash(state), @@ -353,9 +365,9 @@ impl From for K { } impl TryFrom for K { - type Error = &'static str; + type Error = anyhow::Error; - fn try_from(s: Series) -> Result { + fn try_from(s: Series) -> Result { if s.i64().is_ok() || s.i32().is_ok() || s.u64().is_ok() || s.u32().is_ok() || s.u8().is_ok() { if s.min().unwrap() == Some(0) && s.max().unwrap() == Some(1) { // Ok(K::BoolArray(s.cast(&DataType::UInt8).unwrap())) @@ -378,15 +390,15 @@ impl TryFrom for K { )) } else { todo!("try_from() nyi: {}", s); - // Err("type") + // Err(RokError::Type.into()) } } } impl TryFrom> for K { - type Error = &'static str; + type Error = anyhow::Error; - fn try_from(v: AnyValue) -> Result { + fn try_from(v: AnyValue) -> Result { match v { // AnyValue::Boolean(b) => Ok(K::Bool(match b { // true => 1, @@ -405,27 +417,27 @@ impl TryFrom> for K { AnyValue::String(s) => Ok(K::CharArray(s.to_string())), _ => { println!("try_from() nyi: {}", v); - Err("type") + Err(RokError::Type.into()) } } } } impl TryInto> for K { - type Error = &'static str; - fn try_into(self) -> Result, Self::Error> { + type Error = anyhow::Error; + fn try_into(self) -> Result> { match self { K::SymbolArray(s) => Ok(s.iter().map(|s| K::from(s.to_string())).collect()), - _ => Err("nyi"), + _ => Err(RokError::NYI.into()), } } } impl KW { - pub fn unwrap_noun(&self) -> K { + pub fn unwrap_noun(&self) -> Result { match self { - KW::Noun(n) => n.clone(), - _ => panic!("not a noun"), + KW::Noun(n) => Ok(n.clone()), + _ => Err(RokError::Error("not a noun".into()).into()), } } } @@ -482,7 +494,7 @@ macro_rules! arr { }; } -pub fn k_to_vec(k: K) -> Result, &'static str> { +pub fn k_to_vec(k: K) -> Result> { match k { K::Bool(_) | K::Int(_) | K::Float(_) | K::Char(_) | K::Symbol(_) => Ok(vec![k]), K::List(v) => Ok(v), @@ -525,13 +537,11 @@ pub fn k_to_vec(k: K) -> Result, &'static str> { .collect(), ), K::CharArray(v) => Ok(v.chars().map(K::Char).collect()), - K::SymbolArray(_v) => { - todo!("enlist(SymbolArray(...))") - } - _ => todo!("k_to_vec({})", k), + K::SymbolArray(_v) => Err(RokError::Error("nyi: enlist(SymbolArray(...))".into()).into()), + _ => Err(RokError::Error(format!("nyi: k_to_vec({})", k)).into()), } } -pub fn vec_to_list(nouns: Vec) -> Result { +pub fn vec_to_list(nouns: Vec) -> Result { if nouns.iter().all(|w| matches!(w, KW::Noun(K::Bool(_)))) { let v: Vec = nouns .iter() @@ -587,26 +597,26 @@ pub fn vec_to_list(nouns: Vec) -> Result { .collect(); Ok(K::List(v)) } else { - Err("invalid list") + Err(RokError::Error("invalid list".into()).into()) } } -type V1 = fn(K) -> Result; -type V2 = fn(K, K) -> Result; -type V3 = fn(K, K, K) -> Result; -type V4 = fn(K, K, K, K) -> Result; +type V1 = fn(K) -> Result; +type V2 = fn(K, K) -> Result; +type V3 = fn(K, K, K) -> Result; +type V4 = fn(K, K, K, K) -> Result; -pub fn v_nyi1(_x: K) -> Result { Err("nyi") } -pub fn v_nyi2(_x: K, _y: K) -> Result { Err("nyi") } -pub fn v_none1(_x: K) -> Result { Err("rank") } -pub fn v_none2(_x: K, _y: K) -> Result { Err("rank") } -pub fn v_none3(_x: K, _y: K, _z: K) -> Result { Err("rank") } -pub fn v_none4(_a: K, _b: K, _c: K, _d: K) -> Result { Err("rank") } -pub fn av_none1(_env: &mut Env, _v: KW, _x: K) -> Result { Err("rank") } -pub fn av_d_none2(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { Err("rank") } +pub fn v_nyi1(_x: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_nyi2(_x: K, _y: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_none1(_x: K) -> Result { Err(RokError::Rank.into()) } +pub fn v_none2(_x: K, _y: K) -> Result { Err(RokError::Rank.into()) } +pub fn v_none3(_x: K, _y: K, _z: K) -> Result { Err(RokError::Rank.into()) } +pub fn v_none4(_a: K, _b: K, _c: K, _d: K) -> Result { Err(RokError::Rank.into()) } +pub fn av_none1(_env: &mut Env, _v: KW, _x: K) -> Result { Err(RokError::Rank.into()) } +pub fn av_d_none2(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { Err(RokError::Rank.into()) } -type AV1 = fn(&mut Env, KW, K) -> Result; -type AV2 = fn(&mut Env, KW, K, K) -> Result; +type AV1 = fn(&mut Env, KW, K) -> Result; +type AV2 = fn(&mut Env, KW, K, K) -> Result; type VerbDispatchTable = IndexMap<&'static str, (V1, V1, V2, V2, V2, V2, V3, V4)>; @@ -736,7 +746,7 @@ pub fn adverbs_table() -> IndexMap<&'static str, (AV1, AV2)> { ]) } -pub fn apply_primitive(env: &mut Env, v: &str, l: Option, r: KW) -> Result { +pub fn apply_primitive(env: &mut Env, v: &str, l: Option, r: KW) -> Result { // See https://github.com/JohnEarnest/ok/blob/gh-pages/oK.js // a l a-a l-a a-l l-l triad tetrad // ":" : [ident, ident, rident, rident, rident, rident, null, null ], @@ -798,12 +808,12 @@ pub fn apply_primitive(env: &mut Env, v: &str, l: Option, r: KW) -> Result (if r.len() > 1 { m_l } else { m_a })(r).map(KW::Noun), - _ => { - panic!("impossible") - } + _ => Err(RokError::Error("impossible".into()).into()), }, None => { - let t = if let Some((m_a, d_a)) = adverbs.get(&v[v.len() - 2..]) { + let t = if v.len() < 2 { + None + } else if let Some((m_a, d_a)) = adverbs.get(&v[v.len() - 2..]) { Some((2, (m_a, d_a))) } else if let Some((m_a, d_a)) = adverbs.get(&v[v.len() - 1..]) { Some((1, (m_a, d_a))) @@ -818,15 +828,15 @@ pub fn apply_primitive(env: &mut Env, v: &str, l: Option, r: KW) -> Result { m_a(env, KW::Verb { name: v[..v.len() - adv_len].to_string() }, r).map(KW::Noun) } - _ => todo!("other adverb cases"), + _ => Err(RokError::Error("NYI: other adverb cases".into()).into()), }, - None => todo!("NotYetImplemented {}", v), + None => Err(RokError::Error(format!("NYI: {}", v)).into()), } } }, } } -pub fn apply_adverb(a: &str, l: KW) -> Result { +pub fn apply_adverb(a: &str, l: KW) -> Result { // Return a new Verb that implements the appropriate adverb behaviour match l { KW::Verb { name } => { @@ -838,13 +848,13 @@ pub fn apply_adverb(a: &str, l: KW) -> Result { } KW::Function { body, args, adverb: None } => match a { "\'" | "/" | "\\" => Ok(KW::Function { body, args, adverb: Some(a.to_string()) }), - _ => panic!("invalid adverb"), + _ => Err(RokError::Error("invalid adverb".into()).into()), }, KW::Function { body: _, args: _, adverb: Some(_adverb) } => todo!("function nested adverbs"), _ => panic!("verb required"), } } -pub fn apply_function(env: &mut Env, f: KW, arg: KW) -> Result { +pub fn apply_function(env: &mut Env, f: KW, arg: KW) -> Result { match f { KW::Function { body, args, adverb: Some(adverb) } => { let adverbs = adverbs_table(); @@ -853,12 +863,12 @@ pub fn apply_function(env: &mut Env, f: KW, arg: KW) -> Result Some((m_a, _d_a)) => match arg { KW::Noun(x) => m_a(env, KW::Function { body, args, adverb: None }, x).map(KW::Noun), KW::FuncArgs(_exprs) => { - todo!("dyad/triad/etc adverb modified functions") + Err(RokError::Error("nyi: dyad/triad/etc adverb modified functions".into()).into()) // d_a(env, f , l, r).map(KW::Noun) } _ => todo!("other adverb cases"), }, - None => todo!("NotYetImplemented {}", adverb), + None => Err(RokError::Error(format!("nyi: {}", adverb)).into()), } } KW::Function { body, args, adverb: None } => match arg { @@ -870,15 +880,17 @@ pub fn apply_function(env: &mut Env, f: KW, arg: KW) -> Result e.names.extend([(args[0].clone(), KW::Noun(x.clone()))]); eval(&mut e, body) } - _ => todo!("currying - body: {:?}, args: {:?}", body, args), + _ => Err( + RokError::Error(format!("nyi: currying - body: {:?}, args: {:?}", body, args)).into(), + ), } } KW::FuncArgs(exprs) => { let exprs: Vec = - exprs.iter().map(|sentence| eval(env, sentence.clone()).unwrap()).collect(); + exprs.iter().map(|sentence| eval(env, sentence.clone())).collect::>>()?; match exprs.len().cmp(&args.len()) { - Ordering::Greater => Err("rank error"), - Ordering::Less => todo!("currying: args: {:?}", args), + Ordering::Greater => Err(RokError::Rank.into()), + Ordering::Less => Err(RokError::Error(format!("nyi: currying: args: {:?}", args)).into()), Ordering::Equal => { let mut e = Env { names: HashMap::new(), parent: Some(Box::new(env.clone())) }; e.names.extend(zip(args, exprs).collect::>()); @@ -886,17 +898,17 @@ pub fn apply_function(env: &mut Env, f: KW, arg: KW) -> Result } } } - _ => todo!("apply_function other cases?"), + _ => Err(RokError::Error("nyi: apply_function other cases?".into()).into()), }, KW::Verb { name } => match arg { KW::Noun(_) => apply_primitive(env, &name, None, arg), KW::FuncArgs(exprs) => { let exprs: Vec = - exprs.iter().map(|sentence| eval(env, sentence.clone()).unwrap()).collect(); + exprs.iter().map(|sentence| eval(env, sentence.clone())).collect::>>()?; match exprs.len() { - 0 | 1 => todo!("currying: exprs: {:?}", exprs), + 0 | 1 => Err(RokError::Error(format!("currying: exprs: {:?}", exprs)).into()), 2 => apply_primitive(env, &name, Some(exprs[0].clone()), exprs[1].clone()), - _ => Err("rank error"), + _ => Err(RokError::Rank.into()), } } _ => panic!("impossible"), @@ -916,51 +928,51 @@ pub fn promote_nouns(l: K, r: K) -> (K, K) { match (&l, &r) { (K::Bool(l), K::Int(_)) => (K::Int(Some(*l as i64)), r), (K::Bool(l), K::Float(_)) => (K::Float(*l as f64), r), - (K::Bool(l), K::BoolArray(_)) => (K::BoolArray(repeat(*l).take(r.len()).collect()), r), - (K::Bool(l), K::IntArray(_)) => (K::IntArray(repeat(*l as i64).take(r.len()).collect()), r), - (K::Bool(l), K::FloatArray(_)) => (K::FloatArray(repeat(*l as f64).take(r.len()).collect()), r), + (K::Bool(l), K::BoolArray(_)) => (K::BoolArray(repeat_n(*l, r.len()).collect()), r), + (K::Bool(l), K::IntArray(_)) => (K::IntArray(repeat_n(*l as i64, r.len()).collect()), r), + (K::Bool(l), K::FloatArray(_)) => (K::FloatArray(repeat_n(*l as f64, r.len()).collect()), r), (K::Int(_), K::Bool(r)) => (l, K::Int(Some(*r as i64))), (K::Int(Some(l)), K::Float(_)) => (K::Float(*l as f64), r), - (K::Int(Some(l)), K::BoolArray(r)) => (K::IntArray(repeat(*l).take(r.len()).collect()), K::IntArray(r.cast(&DataType::Int64).unwrap())), - (K::Int(Some(l)), K::IntArray(_)) => (K::IntArray(repeat(*l).take(r.len()).collect()), r), - (K::Int(Some(l)), K::FloatArray(_)) => (K::FloatArray(repeat(*l as f64).take(r.len()).collect()), r), + (K::Int(Some(l)), K::BoolArray(r)) => (K::IntArray(repeat_n(*l, r.len()).collect()), K::IntArray(r.cast(&DataType::Int64).unwrap())), + (K::Int(Some(l)), K::IntArray(_)) => (K::IntArray(repeat_n(*l, r.len()).collect()), r), + (K::Int(Some(l)), K::FloatArray(_)) => (K::FloatArray(repeat_n(*l as f64, r.len()).collect()), r), (K::Int(None), K::Float(_)) => (K::Float(f64::NAN), r), - (K::Int(None), K::BoolArray(r)) => (K::IntArray(repeat(None::).take(r.len()).collect()), K::IntArray(r.cast(&DataType::Int64).unwrap())), - (K::Int(None), K::IntArray(_)) => (K::IntArray(repeat(None::).take(r.len()).collect()), r), - (K::Int(None), K::FloatArray(_)) => (K::FloatArray(repeat(f64::NAN).take(r.len()).collect()), r), + (K::Int(None), K::BoolArray(r)) => (K::IntArray(repeat_n(None::, r.len()).collect()), K::IntArray(r.cast(&DataType::Int64).unwrap())), + (K::Int(None), K::IntArray(_)) => (K::IntArray(repeat_n(None::, r.len()).collect()), r), + (K::Int(None), K::FloatArray(_)) => (K::FloatArray(repeat_n(f64::NAN, r.len()).collect()), r), (K::Float(_), K::Bool(r)) => (l, K::Float(*r as f64)), (K::Float(_), K::Int(Some(r))) => (l, K::Float(*r as f64)), (K::Float(_), K::Int(None)) => (l, K::Float(f64::NAN)), - (K::Float(l), K::BoolArray(r)) => (K::FloatArray(repeat(l).take(r.len()).collect()), K::FloatArray(r.cast(&DataType::Float64).unwrap())), - (K::Float(l), K::IntArray(r)) => {(K::FloatArray(repeat(l).take(r.len()).collect()), K::FloatArray(r.cast(&DataType::Float64).unwrap()))} - (K::Float(l), K::FloatArray(_)) => (K::FloatArray(repeat(l).take(r.len()).collect()), r), + (K::Float(l), K::BoolArray(r)) => (K::FloatArray(repeat_n(l, r.len()).collect()), K::FloatArray(r.cast(&DataType::Float64).unwrap())), + (K::Float(l), K::IntArray(r)) => {(K::FloatArray(repeat_n(l, r.len()).collect()), K::FloatArray(r.cast(&DataType::Float64).unwrap()))} + (K::Float(l), K::FloatArray(_)) => (K::FloatArray(repeat_n(l, r.len()).collect()), r), // (K::Char(_l), K::Int(_)) => panic!("nyi"), // TODO // (K::Char(_l), K::Float(_)) => panic!("nyi"), // TODO - (K::Char(l), K::CharArray(_)) => (K::CharArray(repeat(*l).take(r.len()).collect()), r), + (K::Char(l), K::CharArray(_)) => (K::CharArray(repeat_n(*l, r.len()).collect()), r), // (K::Char(_l), K::IntArray(_)) => panic!("nyi"), // TODO // (K::Char(_l), K::FloatArray(_)) => panic!("nyi"), // TODO - (K::BoolArray(_), K::Bool(r)) => (l.clone(), K::BoolArray(repeat(*r as f64).take(l.len()).collect())), - (K::BoolArray(l), K::Int(Some(r))) => (K::IntArray(l.cast(&DataType::Int64).unwrap()), K::IntArray(repeat(r).take(l.len()).collect())), - (K::BoolArray(l), K::Int(None)) => {(K::IntArray(l.cast(&DataType::Int64).unwrap()), K::IntArray(repeat(None::).take(l.len()).collect()))} - (K::BoolArray(l), K::Float(r)) => {(K::FloatArray(l.cast(&DataType::Float64).unwrap()), K::FloatArray(repeat(r).take(l.len()).collect()))} + (K::BoolArray(_), K::Bool(r)) => (l.clone(), K::BoolArray(repeat_n(*r as f64, l.len()).collect())), + (K::BoolArray(l), K::Int(Some(r))) => (K::IntArray(l.cast(&DataType::Int64).unwrap()), K::IntArray(repeat_n(r, l.len()).collect())), + (K::BoolArray(l), K::Int(None)) => {(K::IntArray(l.cast(&DataType::Int64).unwrap()), K::IntArray(repeat_n(None::, l.len()).collect()))} + (K::BoolArray(l), K::Float(r)) => {(K::FloatArray(l.cast(&DataType::Float64).unwrap()), K::FloatArray(repeat_n(r, l.len()).collect()))} (K::BoolArray(l), K::IntArray(_)) => (K::IntArray(l.cast(&DataType::Int64).unwrap()), r), (K::BoolArray(l), K::FloatArray(_)) => (K::FloatArray(l.cast(&DataType::Float64).unwrap()), r), - (K::IntArray(_), K::Bool(r)) => (l.clone(), K::IntArray(repeat(*r as i64).take(l.len()).collect())), - (K::IntArray(_), K::Int(Some(r))) => (l.clone(), K::IntArray(repeat(r).take(l.len()).collect())), - (K::IntArray(_), K::Int(None)) => (l.clone(), K::IntArray(repeat(None::).take(l.len()).collect())), - (K::IntArray(l), K::Float(r)) => (K::FloatArray(l.cast(&DataType::Float64).unwrap()), K::FloatArray(repeat(*r).take(l.len()).collect())), + (K::IntArray(_), K::Bool(r)) => (l.clone(), K::IntArray(repeat_n(*r as i64, l.len()).collect())), + (K::IntArray(_), K::Int(Some(r))) => (l.clone(), K::IntArray(repeat_n(r, l.len()).collect())), + (K::IntArray(_), K::Int(None)) => (l.clone(), K::IntArray(repeat_n(None::, l.len()).collect())), + (K::IntArray(l), K::Float(r)) => (K::FloatArray(l.cast(&DataType::Float64).unwrap()), K::FloatArray(repeat_n(*r, l.len()).collect())), (K::IntArray(_), K::BoolArray(r)) => (l, K::IntArray(r.cast(&DataType::Int64).unwrap())), (K::IntArray(l), K::FloatArray(_)) => (K::FloatArray(l.cast(&DataType::Float64).unwrap()), r), - (K::FloatArray(_), K::Bool(r)) => (l.clone(), K::FloatArray(repeat(*r as f64).take(l.len()).collect())), - (K::FloatArray(_), K::Int(Some(r))) => (l.clone(), K::FloatArray(repeat(*r as f64).take(l.len()).collect())), - (K::FloatArray(_), K::Int(None)) => (l.clone(), K::FloatArray(repeat(f64::NAN).take(l.len()).collect())), - (K::FloatArray(_), K::Float(r)) => (l.clone(), K::FloatArray(repeat(r).take(l.len()).collect())), + (K::FloatArray(_), K::Bool(r)) => (l.clone(), K::FloatArray(repeat_n(*r as f64, l.len()).collect())), + (K::FloatArray(_), K::Int(Some(r))) => (l.clone(), K::FloatArray(repeat_n(*r as f64, l.len()).collect())), + (K::FloatArray(_), K::Int(None)) => (l.clone(), K::FloatArray(repeat_n(f64::NAN, l.len()).collect())), + (K::FloatArray(_), K::Float(r)) => (l.clone(), K::FloatArray(repeat_n(r, l.len()).collect())), (K::FloatArray(_), K::BoolArray(r)) => (l, K::FloatArray(r.cast(&DataType::Int64).unwrap())), (K::FloatArray(_), K::IntArray(r)) => (l, K::FloatArray(r.cast(&DataType::Float64).unwrap())), @@ -1009,11 +1021,13 @@ macro_rules! impl_op { (K::BoolArray(l), K::BoolArray(r)) => K::IntArray((l.cast(&DataType::Int64).unwrap() $op r.cast(&DataType::Int64).unwrap()).unwrap()), (K::IntArray(l), K::IntArray(r)) => K::IntArray((l $op r).unwrap()), (K::FloatArray(l), K::FloatArray(r)) => K::FloatArray((l $op r).unwrap()), - (_, K::Dictionary(_)) => todo!("dict"), - (K::Dictionary(_), _) => todo!("dict"), - (_, K::Table(_)) => todo!("table"), - (K::Table(_), _) => todo!("table"), - _ => todo!("various $op pairs - LOTS MORE to do still: char/dicts/tables/etc. self: {}, r: {}", $self, $r), + // HACK: symbols instead of RokError() due to impl ops + (_, K::Dictionary(_)) => K::Symbol("nyi: dict".into()), + (K::Dictionary(_), _) => K::Symbol("nyi: dict".into()), + (_, K::Table(_)) => K::Symbol("nyi: table".into()), + (K::Table(_), _) => K::Symbol("nyi: table".into()), + // _ => todo!("various $op pairs - LOTS MORE to do still: char/dicts/tables/etc. self: {}, r: {}", $self, $r), + _ => K::Symbol(format!("nyi: various $op pairs - LOTS MORE to do still: char/dicts/tables/etc. self: {}, r: {}", $self, $r)), } }; } @@ -1032,24 +1046,24 @@ impl ops::Mul for K { } impl ops::Div for K { type Output = Self; - fn div(self, r: Self) -> Self::Output { - //TODO this doesn't gracefully handle divide by zero. in k 0n~0%0 - impl_op!(/, div, self, r) + fn div(self, r: Self) -> Self::Output { + //TODO this doesn't gracefully handle divide by zero. in k 0n~0%0 + impl_op!(/, div, self, r) } } -fn len_ok(l: &K, r: &K) -> Result { +fn len_ok(l: &K, r: &K) -> Result { match (l, r) { (K::Dictionary(_), K::Dictionary(_)) => Ok(true), (l @ K::Table(_), r @ K::Table(_)) => match l.len() == r.len() { true => Ok(true), - false => Err("length"), + false => Err(RokError::Length.into()), }, _ => { if l.len() == r.len() || l.len() == 1 || r.len() == 1 { Ok(true) } else { - Err("length") + Err(RokError::Length.into()) } } } @@ -1061,9 +1075,9 @@ pub struct Env { pub parent: Option>, } -fn resolve_names(env: Env, fragment: (KW, KW, KW, KW)) -> Result<(KW, KW, KW, KW), &'static str> { +fn resolve_names(env: Env, fragment: (KW, KW, KW, KW)) -> Result<(KW, KW, KW, KW)> { //Resolve Names only on the RHS of assignment - let words = vec![fragment.0.clone(), fragment.1.clone(), fragment.2.clone(), fragment.3.clone()]; + let words = [fragment.0.clone(), fragment.1.clone(), fragment.2.clone(), fragment.3.clone()]; let mut resolved_words = Vec::new(); for w in words.iter().rev() { match w { @@ -1092,7 +1106,7 @@ fn resolve_names(env: Env, fragment: (KW, KW, KW, KW)) -> Result<(KW, KW, KW, KW Ok(new_words.iter().cloned().collect_tuple().unwrap()) } -pub fn eval(env: &mut Env, sentence: Vec) -> Result { +pub fn eval(env: &mut Env, sentence: Vec) -> Result { let mut queue = VecDeque::from([vec![KW::StartOfLine], sentence].concat()); let mut stack: VecDeque = VecDeque::new(); @@ -1100,11 +1114,11 @@ pub fn eval(env: &mut Env, sentence: Vec) -> Result { while !converged { // debug!("stack: {stack:?}"); let fragment = resolve_names(env.clone(), get_fragment(&mut stack)).unwrap(); - let result: Result, &'static str> = match fragment { + let result: Result> = match fragment { (w1, w2 @ KW::Cond { .. }, any1, any2) if matches!(w1, KW::StartOfLine | KW::LP) => { match parse_cond(env, w2) { Ok(r) => Ok(vec![w1, r, any1, any2]), - Err(e) => Err(e), + Err(e) => Err(RokError::Error(format!("{e}")).into()), } } (w, KW::Verb { name }, x @ KW::Noun(_), any) if matches!(w, KW::StartOfLine | KW::LP) => { @@ -1163,18 +1177,18 @@ pub fn eval(env: &mut Env, sentence: Vec) -> Result { .filter(|w| !matches!(*w, KW::SC) && !matches!(*w, KW::RP)) .collect(); Ok(vec![KW::Noun( - vec_to_list([vec![KW::Noun(n1), KW::Noun(n2)], nouns.into()].concat()).unwrap(), + vec_to_list([vec![KW::Noun(n1), KW::Noun(n2)], nouns.into()].concat())?, )]) } else { - Err("invalid list syntax") + Err(RokError::Error("invalid list syntax".into()).into()) } } (w1, KW::Exprs(exprs), w3, w4) /* if matches!(w1, KW::StartOfLine | KW::LP)*/ => { - let res: Result, &'static str> = + let res: Result> = exprs.iter().map(|sentence| eval(env, sentence.clone())).collect(); match res { Ok(res) => Ok(vec![w1, res.last().unwrap().clone(), w3, w4]), - Err(e) => Err(e), + Err(e) => Err(RokError::Error(format!("{e}")).into()), } } (w1, w2, w3, w4) => match queue.pop_back() { @@ -1200,7 +1214,7 @@ pub fn eval(env: &mut Env, sentence: Vec) -> Result { Ok(r[0].clone()) } else { debug!("{:?}", r); - Err("parse error") + Err(RokError::Parse("").into()) } } @@ -1212,7 +1226,7 @@ fn get_fragment(stack: &mut VecDeque) -> (KW, KW, KW, KW) { .expect("infinite iterator can't be empty") } -fn parse_cond(env: &mut Env, kw: KW) -> Result { +fn parse_cond(env: &mut Env, kw: KW) -> Result { match kw { KW::Cond(exprs) => { // $[if;then;elif;then;...;else] @@ -1225,7 +1239,7 @@ fn parse_cond(env: &mut Env, kw: KW) -> Result { Ok(KW::Noun(K::Bool(0))) => continue, Ok(KW::Noun(K::Int(Some(0)))) => continue, Ok(_) => return eval(env, val.to_vec()), - Err(e) => return Err(e), + Err(e) => return Err(RokError::Error(format!("{e}")).into()), } } else if pv.len() == 1 { return eval(env, pv[0].clone()); // final else case @@ -1239,11 +1253,9 @@ fn parse_cond(env: &mut Env, kw: KW) -> Result { } } -pub fn scan(code: &str) -> Result, &'static str> { - scan_pass3(scan_pass2(scan_pass1(code)?)?) -} +pub fn scan(code: &str) -> Result> { scan_pass3(scan_pass2(scan_pass1(code)?)?) } -pub fn scan_pass1(code: &str) -> Result, &'static str> { +pub fn scan_pass1(code: &str) -> Result> { // First tokenization pass. let mut words = vec![]; let mut skip: usize = 0; @@ -1317,12 +1329,12 @@ pub fn scan_pass1(code: &str) -> Result, &'static str> { } } '"' => { - let (j, k) = scan_string(&code[i..]).unwrap(); + let (j, k) = scan_string(&code[i..])?; words.push(k); skip = j; } '`' => { - let (j, k) = scan_symbol(&code[i..]).unwrap(); + let (j, k) = scan_symbol(&code[i..])?; words.push(k); skip = j; } @@ -1368,19 +1380,17 @@ pub fn scan_pass1(code: &str) -> Result, &'static str> { '\'' | '/' | '\\' => words.push(KW::Adverb { name: c.to_string() }), ' ' | '\t' | '\n' => continue, 'a'..='z' | 'A'..='Z' => { - let (j, k) = scan_name(&code[i..]).unwrap(); + let (j, k) = scan_name(&code[i..])?; words.push(k); skip = j; } - _ => return Err("TODO: scan()"), + _ => return Err(RokError::Error("TODO: scan()".into()).into()), }; } Ok(words) } -pub fn split_on( - tokens: Vec, delim: KW, end_tok: KW, -) -> Result<(Vec>, Vec), &'static str> { +pub fn split_on(tokens: Vec, delim: KW, end_tok: KW) -> Result<(Vec>, Vec)> { // Split tokens on delim token until end token. // Depth of 0 meaning we keep track of nested parens, brackets, funcs etc let mut depth = 0; @@ -1388,7 +1398,8 @@ pub fn split_on( let mut start = 0; for i in 0..tokens.len() { if depth < 0 { - return Err("mismatched parens, brackets or curly brackets"); // TODO better message + return Err(RokError::Parse("mismatched parens, brackets or curly brackets").into()); + // TODO better message } if start > i { continue; @@ -1421,7 +1432,7 @@ pub fn split_on( Ok((splits, tokens[start..].to_vec())) } -pub fn scan_pass2(tokens: Vec) -> Result, &'static str> { +pub fn scan_pass2(tokens: Vec) -> Result> { // Second tokenization pass, raw tokens into basic parsed tokens. // Parse out raw tokens into "pass2" form: // - Function{_} @@ -1465,7 +1476,7 @@ pub fn scan_pass2(tokens: Vec) -> Result, &'static str> { Ok(tkns) } -pub fn scan_pass3(tokens: Vec) -> Result, &'static str> { +pub fn scan_pass3(tokens: Vec) -> Result> { // wrap un bracketed exprs in brackets // "a:2;a+2" => "[a:2;a+2]" @@ -1477,7 +1488,7 @@ pub fn scan_pass3(tokens: Vec) -> Result, &'static str> { } } -fn scan_function(tokens: Vec) -> Result<(KW, Vec), &'static str> { +fn scan_function(tokens: Vec) -> Result<(KW, Vec)> { let mut depth = 0; // nested functions depth for i in 0..tokens.len() { match tokens.get(i) { @@ -1500,10 +1511,10 @@ fn scan_function(tokens: Vec) -> Result<(KW, Vec), &'static str> { None => panic!("impossible"), } } - Err("parse error: mismatched brackets") + Err(RokError::Parse("parse error: mismatched brackets").into()) } -fn scan_function_args(body: Vec) -> Result<(Vec, Vec), &'static str> { +fn scan_function_args(body: Vec) -> Result<(Vec, Vec)> { if let Some(KW::LB) = body.first() { // TODO // - {[a;b] {x+y}[a;b]} // nested functions also need to work @@ -1517,10 +1528,10 @@ fn scan_function_args(body: Vec) -> Result<(Vec, Vec), &'static .collect(); Ok((args, body[i + 1..].to_vec())) } else { - Err("parse error: invalid function args") + Err(RokError::Parse("parse error: invalid function args").into()) } } - None => Err("parse error: mismatched square brackets"), + None => Err(RokError::Parse("parse error: mismatched square brackets").into()), } } else { // - {x + y + z} or {z * x + y} => vec!["x","y","z"] @@ -1548,7 +1559,7 @@ fn scan_function_args(body: Vec) -> Result<(Vec, Vec), &'static } } -pub fn scan_number(code: &str) -> Result<(usize, KW), &'static str> { +pub fn scan_number(code: &str) -> Result<(usize, KW)> { // read until first char outside 0123456789.- // split on space and parse to numeric // @@ -1589,11 +1600,16 @@ pub fn scan_number(code: &str) -> Result<(usize, KW), &'static str> { _ => Ok((l, KW::Noun(promote_num(nums).unwrap()))), } } else { - Err("syntax error: a sentence starting with a digit must contain a valid number") + Err( + RokError::Error( + "syntax error: a sentence starting with a digit must contain a valid number".into(), + ) + .into(), + ) } } -pub fn scan_string(code: &str) -> Result<(usize, KW), &'static str> { +pub fn scan_string(code: &str) -> Result<(usize, KW)> { // read string, accounting for C style escapes: \", \n, \t, etc if !code.starts_with('"') { panic!("called scan_string() on invalid input") @@ -1615,7 +1631,7 @@ pub fn scan_string(code: &str) -> Result<(usize, KW), &'static str> { Some('t') => s.push('\t'), Some('n') => s.push('\n'), // TODO: handle the rest - _ => return Err("parse error: invalid string"), + _ => return Err(RokError::Parse("parse error: invalid string").into()), } i += 1; // skip next char. } else { @@ -1623,11 +1639,11 @@ pub fn scan_string(code: &str) -> Result<(usize, KW), &'static str> { } i += 1; } - Err("parse error: unmatched \"") + Err(RokError::Parse("parse error: unmatched \"").into()) } } -pub fn scan_symbol(code: &str) -> Result<(usize, KW), &'static str> { +pub fn scan_symbol(code: &str) -> Result<(usize, KW)> { // read a Symbol or SymbolArray // // read until first char outside [a-z0-9._`] @@ -1667,10 +1683,10 @@ pub fn scan_symbol(code: &str) -> Result<(usize, KW), &'static str> { ss.extend(vec![c.to_string()]); i += len + 1 } - Err(e) => return Err(e), + Err(e) => return Err(RokError::Error(format!("{e}")).into()), _ => panic!("impossible"), }, - Some('"') if !b_in_sym => return Err("parse error"), + Some('"') if !b_in_sym => return Err(RokError::Parse("parse").into()), Some(c) => { if !(c.is_ascii_alphanumeric() || c.is_ascii_whitespace() @@ -1707,7 +1723,7 @@ pub fn scan_symbol(code: &str) -> Result<(usize, KW), &'static str> { } } -pub fn scan_name(code: &str) -> Result<(usize, KW), &'static str> { +pub fn scan_name(code: &str) -> Result<(usize, KW)> { // read a single Name // a Name extends until the first symbol character or space let sentence = match code.find(|c: char| !(c.is_ascii_alphanumeric() || ['.'].contains(&c))) { @@ -1717,7 +1733,7 @@ pub fn scan_name(code: &str) -> Result<(usize, KW), &'static str> { Ok((sentence.len() - 1, KW::Noun(K::Name(sentence.into())))) } -pub fn scan_num_token(term: &str) -> Result { +pub fn scan_num_token(term: &str) -> Result { let i = if !term.starts_with('-') && term.contains('-') { //handle "1-1" term.find('-').unwrap() @@ -1740,13 +1756,13 @@ pub fn scan_num_token(term: &str) -> Result { } else if let Ok(f) = term[..i].parse::() { Ok(K::Float(f)) } else { - Err("invalid num token") + Err(RokError::Error("invalid num token".into()).into()) } } } } -pub fn promote_num(nums: Vec) -> Result { +pub fn promote_num(nums: Vec) -> Result { if nums.iter().all(|k| matches!(k, K::Float(_) | K::Int(_) | K::Bool(_))) { if nums.iter().any(|k| matches!(k, K::Float(_))) { let fa: Vec = nums @@ -1806,6 +1822,6 @@ pub fn promote_num(nums: Vec) -> Result { panic!("impossible") } } else { - Err("invalid nums") + Err(RokError::Error("invalid nums".into()).into()) } } diff --git a/src/verbs.rs b/src/verbs.rs index afcde8b..6adac26 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -1,21 +1,20 @@ use crate::*; +use anyhow::Result; use rand::distributions::{Distribution, Uniform}; -use std::cmp; -use std::collections::BTreeMap; -use std::iter::repeat; +use std::{cmp, collections::BTreeMap, iter::repeat, iter::repeat_n}; -pub fn v_imat(x: K) -> Result { +pub fn v_imat(x: K) -> Result { match x { K::Int(Some(i)) => { let s = format!("{{a=/:a:!x}}{}", i); let mut env = Env { names: HashMap::new(), parent: None }; - Ok(eval(&mut env, scan(&s).unwrap()).unwrap().unwrap_noun()) + eval(&mut env, scan(&s)?)?.unwrap_noun() } - _ => Err("type"), + _ => Err(RokError::Type.into()), } } -pub fn v_group(x: K) -> Result { +pub fn v_group(x: K) -> Result { // https://docs.rs/polars/latest/polars/frame/group_by/struct.GroupBy.html#method.get_groups // https://docs.rs/polars/latest/polars/frame/group_by/struct.GroupBy.html#method.groups match x { @@ -26,7 +25,7 @@ pub fn v_group(x: K) -> Result { // let keys: K = K::try_from(g.iter().nth(0).unwrap().clone()).unwrap(); let keys: K = K::SymbolArray( g.iter() - .nth(0) + .next() .unwrap() .clone() .cast(&DataType::Categorical(None, CategoricalOrdering::Lexical)) @@ -47,7 +46,7 @@ pub fn v_group(x: K) -> Result { } Ok(K::Dictionary(d)) } - K::SymbolArray(_s) => todo!("v_group(SymbolArray(_))"), + K::SymbolArray(_s) => Err(RokError::Error("nyi: v_group(SymbolArray(_))".into()).into()), K::List(v) => { let mut im = IndexMap::new(); for (i, k) in v.iter().enumerate() { @@ -60,13 +59,13 @@ pub fn v_group(x: K) -> Result { } Ok(K::Dictionary(d)) } - K::Table(_df) => todo!("v_group(Table(_))"), + K::Table(_df) => Err(RokError::Error("nyi: v_group(Table(_))".into()).into()), - _ => Err("type"), + _ => Err(RokError::Type.into()), } } -pub fn v_equal(x: K, y: K) -> Result { +pub fn v_equal(x: K, y: K) -> Result { len_ok(&x, &y).and_then(|_| match promote_nouns(x, y) { (K::Bool(l), K::Bool(r)) => Ok(K::Bool((l == r) as u8)), (K::Int(Some(l)), K::Int(Some(r))) => Ok(K::Bool((l == r) as u8)), @@ -94,14 +93,14 @@ pub fn v_equal(x: K, y: K) -> Result { } })))) } - (_, K::Table(_)) => todo!("table"), - (K::Table(_), _) => todo!("table"), - _ => Err("nyi"), + (_, K::Table(_)) => Err(RokError::Error("nyi: table".into()).into()), + (K::Table(_), _) => Err(RokError::Error("nyi: table".into()).into()), + _ => Err(RokError::NYI.into()), }) } -pub fn v_count(x: K) -> Result { Ok(K::Int(Some(x.len().try_into().unwrap()))) } -pub fn v_take(x: K, y: K) -> Result { +pub fn v_count(x: K) -> Result { Ok(K::Int(Some(x.len().try_into().unwrap()))) } +pub fn v_take(x: K, y: K) -> Result { match x { K::Bool(0) | K::Int(Some(0)) => match y { K::Bool(_) | K::BoolArray(_) => Ok(K::BoolArray(Series::new_empty("", &DataType::Boolean))), @@ -122,7 +121,7 @@ pub fn v_take(x: K, y: K) -> Result { }, K::Bool(1) | K::Int(Some(_)) => v_at(y, v_iota(x).unwrap()), K::Int(None) => Ok(y), - _ => Err("type"), + _ => Err(RokError::Type.into()), } } @@ -137,52 +136,56 @@ macro_rules! reshape_atom_by_type { }}; } -pub fn v_reshape(l: K, r: K) -> Result { +pub fn v_reshape(l: K, r: K) -> Result { match l { K::IntArray(a) => { - let rev_shape: Vec = - a.i64().unwrap().to_vec().iter().rev().map(|i| i.unwrap()).collect(); - match r { - K::Bool(b) => { - reshape_atom_by_type!(b, K::BoolArray, rev_shape) - } - K::Int(None) => Err("nyi"), - K::Int(Some(i)) => { - reshape_atom_by_type!(i, K::IntArray, rev_shape) - } - K::Float(f) => { - reshape_atom_by_type!(f, K::FloatArray, rev_shape) - } - K::Char(_) => Err("nyi"), - K::Symbol(_) => Err("nyi"), + if a.null_count() > 0 { + Err(RokError::Error("nyi: reshape fills".into()).into()) + } else { + let rev_shape: Vec = + a.i64().unwrap().to_vec().iter().rev().map(|i| i.unwrap()).collect(); + match r { + K::Bool(b) => { + reshape_atom_by_type!(b, K::BoolArray, rev_shape) + } + K::Int(None) => Err(RokError::NYI.into()), + K::Int(Some(i)) => { + reshape_atom_by_type!(i, K::IntArray, rev_shape) + } + K::Float(f) => { + reshape_atom_by_type!(f, K::FloatArray, rev_shape) + } + K::Char(_) => Err(RokError::NYI.into()), + K::Symbol(_) => Err(RokError::NYI.into()), - K::SymbolArray(_) - | K::BoolArray(_) - | K::IntArray(_) - | K::FloatArray(_) - | K::CharArray(_) - | K::List(_) - | K::Dictionary(_) - | K::Table(_) - | K::Nil => Err("nyi"), - K::Name(_) => panic!("impossible"), + K::SymbolArray(_) + | K::BoolArray(_) + | K::IntArray(_) + | K::FloatArray(_) + | K::CharArray(_) + | K::List(_) + | K::Dictionary(_) + | K::Table(_) + | K::Nil => Err(RokError::NYI.into()), + K::Name(_) => panic!("impossible"), + } } } - K::BoolArray(_) => Err("nyi"), + K::BoolArray(_) => Err(RokError::NYI.into()), // K::Int(Some(i)) => v_reshape(K::IntArray(arr!([i])), r), // K::Bool(b) => v_reshape(K::IntArray(arr!([b])), r), - _ => Err("type"), + _ => Err(RokError::Type.into()), } } -pub fn v_ident(x: K) -> Result { Ok(x) } -pub fn v_rident(_l: K, r: K) -> Result { Ok(r) } -pub fn v_flip(x: K) -> Result { +pub fn v_ident(x: K) -> Result { Ok(x) } +pub fn v_rident(_l: K, r: K) -> Result { Ok(r) } +pub fn v_flip(x: K) -> Result { match x { K::Dictionary(ref d) => { let lengths: Vec = d.iter().map(|(_k, v)| v.len()).unique().sorted().collect(); - if lengths.len() > 2 || lengths.len() == 0 { - Err("length") + if lengths.len() > 2 || lengths.is_empty() { + Err(RokError::Length.into()) } else { // if d.iter().map(|(_k, v)| v.len()).all_equal() { let len = lengths.last().unwrap(); @@ -191,14 +194,15 @@ pub fn v_flip(x: K) -> Result { .map(|(k, v)| match v { K::SymbolArray(s) | K::BoolArray(s) | K::IntArray(s) | K::FloatArray(s) => { match s.len() { - 1 => { - Series::new(&k.to_string(), s.extend_constant(s.get(0).unwrap(), *len).unwrap()) - } - _ => Series::new(&k.to_string(), s.clone()), + 1 => Ok(Series::new( + &k.to_string(), + s.extend_constant(s.get(0).unwrap(), *len).unwrap(), + )), + _ => Ok(Series::new(&k.to_string(), s.clone())), } } // | K::CharArray(s) => Series::new(&k.to_string(), s.clone()), - K::CharArray(s) => Series::new(&k.to_string(), s), + K::CharArray(s) => Ok(Series::new(&k.to_string(), s)), K::List(v) => { if v.iter().all(|i| match i { K::CharArray(_) => true, @@ -206,35 +210,32 @@ pub fn v_flip(x: K) -> Result { _ => false, }) { let vs: Vec = v.iter().map(|s| s.to_string()).collect(); - Series::new(&k.to_string(), vs.clone()) + Ok(Series::new(&k.to_string(), vs.clone())) } else { - // Err("type") - panic!("type error?") + Err(RokError::Type.into()) + // panic!("type error?") } } - K::Bool(b) => { - Series::new(&k.to_string(), std::iter::repeat(*b).take(*len).collect::>()) - } + K::Bool(b) => Ok(Series::new(&k.to_string(), repeat_n(*b, *len).collect::>())), K::Int(Some(i)) => { - Series::new(&k.to_string(), std::iter::repeat(*i).take(*len).collect::>()) + Ok(Series::new(&k.to_string(), repeat_n(*i, *len).collect::>())) } - K::Int(None) => Series::full_null(&k.to_string(), *len, &DataType::Int64), + K::Int(None) => Ok(Series::full_null(&k.to_string(), *len, &DataType::Int64)), K::Float(f) => { - Series::new(&k.to_string(), std::iter::repeat(*f).take(*len).collect::>()) + Ok(Series::new(&k.to_string(), repeat_n(*f, *len).collect::>())) + } + K::Symbol(s) => { + Ok(Series::new(&k.to_string(), repeat_n(s.clone(), *len).collect::>())) } - K::Symbol(s) => Series::new( - &k.to_string(), - std::iter::repeat(s.clone()).take(*len).collect::>(), - ), // K::Char(c) => Series::new(&k.to_string(), std::iter::repeat(*c.to_string()).take(*len).collect::>()), - K::Char(_c) => todo!("handle char"), - K::Table(_df) => todo!("why is Table here?"), + K::Char(_c) => Err(RokError::Error("handle char".into()).into()), + K::Table(_df) => Err(RokError::Error("why is Table here?".into()).into()), _ => { println!("v_flip(x): x: {}", x); panic!("impossible") } }) - .collect(); + .collect::>>()?; Ok(K::Table(DataFrame::new(cols).unwrap())) } } @@ -261,7 +262,7 @@ pub fn v_flip(x: K) -> Result { v.iter().cloned(), )))) } - _ => todo!("flip the rest"), + _ => Err(RokError::Error("flip the rest".into()).into()), } } macro_rules! atomicdyad { @@ -321,26 +322,30 @@ macro_rules! atomicdyad { } }; } -pub fn v_plus(l: K, r: K) -> Result { atomicdyad!(+, v_plus, add, l, r) } -pub fn v_negate(x: K) -> Result { Ok(K::Int(Some(-1i64)) * x) } -pub fn v_minus(l: K, r: K) -> Result { atomicdyad!(-, v_minus, sub, l, r) } +pub fn v_plus(l: K, r: K) -> Result { atomicdyad!(+, v_plus, add, l, r) } +pub fn v_negate(x: K) -> Result { Ok(K::Int(Some(-1i64)) * x) } +pub fn v_minus(l: K, r: K) -> Result { atomicdyad!(-, v_minus, sub, l, r) } -pub fn v_first(x: K) -> Result { - match x { - K::Bool(_) => Ok(x), - K::Int(_) => Ok(x), - K::Float(_) => Ok(x), - K::Char(_) => Ok(x), - K::BoolArray(a) => Ok(K::Bool(a.bool().unwrap().get(0).unwrap() as u8)), - K::IntArray(a) => Ok(K::Int(Some(a.i64().unwrap().get(0).unwrap()))), - K::FloatArray(a) => Ok(K::Float(a.f64().unwrap().get(0).unwrap())), - K::CharArray(a) => Ok(K::Char(a.chars().nth(0).unwrap_or(' '))), - K::List(l) => Ok(l.first().unwrap().clone()), - _ => Err("nyi"), +pub fn v_first(x: K) -> Result { + if x.len() == 0 { + Ok(x) + } else { + match x { + K::Bool(_) => Ok(x), + K::Int(_) => Ok(x), + K::Float(_) => Ok(x), + K::Char(_) => Ok(x), + K::BoolArray(a) => Ok(K::Bool(a.bool().unwrap().get(0).unwrap() as u8)), + K::IntArray(a) => Ok(K::Int(Some(a.i64().unwrap().get(0).unwrap()))), + K::FloatArray(a) => Ok(K::Float(a.f64().unwrap().get(0).unwrap())), + K::CharArray(a) => Ok(K::Char(a.chars().next().unwrap_or(' '))), + K::List(l) => Ok(l.first().unwrap().clone()), + _ => Err(RokError::NYI.into()), + } } } -pub fn v_times(l: K, r: K) -> Result { +pub fn v_times(l: K, r: K) -> Result { match (l.clone(), r.clone()) { // TODO can we make this less repetitive and explicit? (K::Int(i), K::Table(df)) => Ok(K::Table( @@ -349,8 +354,8 @@ pub fn v_times(l: K, r: K) -> Result { _ => atomicdyad!(*, v_times, mul, l, r), } } -pub fn v_sqrt(_x: K) -> Result { todo!("implement sqrt") } -pub fn v_divide(l: K, r: K) -> Result { +pub fn v_sqrt(_x: K) -> Result { Err(RokError::Error("nyi: implement sqrt".into()).into()) } +pub fn v_divide(l: K, r: K) -> Result { match (&l, &r) { (K::Bool(_), K::Bool(0)) => Ok(K::Float(f64::NAN)), (K::Bool(_), K::Int(Some(0))) => Ok(K::Float(f64::NAN)), @@ -358,28 +363,28 @@ pub fn v_divide(l: K, r: K) -> Result { _ => atomicdyad!(/, v_divide, div, l, r), } } -pub fn v_keys_odometer(x: K) -> Result { +pub fn v_keys_odometer(x: K) -> Result { match x { - K::Dictionary(_) => todo!("implement keys"), + K::Dictionary(_) => Err(RokError::Error("nyi: implement keys".into()).into()), K::Table(df) => Ok(K::SymbolArray( Series::new("", df.fields().iter().cloned().map(|f| f.name.into()).collect::>()) .cast(&DataType::Categorical(None, CategoricalOrdering::Lexical)) .unwrap(), )), - K::IntArray(_) => todo!("implement odometer"), - _ => Err("nyi"), + K::IntArray(_) => Err(RokError::Error("nyi: odometer".into()).into()), + _ => Err(RokError::NYI.into()), } } -pub fn v_mod(l: K, r: K) -> Result { +pub fn v_mod(l: K, r: K) -> Result { match (l, r) { (K::Int(Some(i)), K::IntArray(a)) => Ok(K::IntArray(a % i)), (K::Int(Some(i)), K::FloatArray(a)) => Ok(K::FloatArray(a % i)), - (K::Int(Some(_i)), K::CharArray(_a)) => todo!("implement v_mod"), - _ => todo!("implement v_mod"), + (K::Int(Some(_i)), K::CharArray(_a)) => Err(RokError::Error("implement v_mod".into()).into()), + _ => Err(RokError::Error("nyi: v_mod".into()).into()), } } -pub fn v_where(x: K) -> Result { +pub fn v_where(x: K) -> Result { match x { K::BoolArray(b) => { let indices: Vec = b @@ -392,11 +397,11 @@ pub fn v_where(x: K) -> Result { .collect(); Ok(K::IntArray(arr!(indices))) } - _ => Err("nyi"), + _ => Err(RokError::NYI.into()), } } -pub fn v_reverse(x: K) -> Result { +pub fn v_reverse(x: K) -> Result { match x { K::Bool(_) => Ok(x), K::Int(_) => Ok(x), @@ -415,11 +420,11 @@ pub fn v_reverse(x: K) -> Result { Ok(K::Dictionary(d)) } K::Table(df) => Ok(K::Table(df.reverse())), - _ => Err("nyi"), + _ => Err(RokError::NYI.into()), } } -pub fn v_min(l: K, r: K) -> Result { +pub fn v_min(l: K, r: K) -> Result { //TODO fix code duplication in v_min/v_max len_ok(&l, &r).and_then(|_| match promote_nouns(l, r) { (K::Bool(l), K::Bool(r)) => Ok(K::Bool((cmp::min(l, r)) as u8)), @@ -445,14 +450,14 @@ pub fn v_min(l: K, r: K) -> Result { }) .collect(), )), - (K::List(_l), K::List(_r)) => Err("nyi"), - (K::Dictionary(_l), K::Dictionary(_r)) => Err("nyi"), - (_, K::Table(_)) => todo!("table"), - (K::Table(_), _) => todo!("table"), - _ => Err("nyi - wtf"), + (K::List(_l), K::List(_r)) => Err(RokError::NYI.into()), + (K::Dictionary(_l), K::Dictionary(_r)) => Err(RokError::NYI.into()), + (_, K::Table(_)) => Err(RokError::Error("nyi: table".into()).into()), + (K::Table(_), _) => Err(RokError::Error("nyi: table".into()).into()), + _ => Err(RokError::Error("nyi - wtf".into()).into()), }) } -pub fn v_max(l: K, r: K) -> Result { +pub fn v_max(l: K, r: K) -> Result { len_ok(&l, &r).and_then(|_| match promote_nouns(l, r) { (K::Bool(l), K::Bool(r)) => Ok(K::Bool((cmp::max(l, r)) as u8)), (K::Int(Some(l)), K::Int(Some(r))) => Ok(K::Int(Some(cmp::max(l, r)))), @@ -477,68 +482,72 @@ pub fn v_max(l: K, r: K) -> Result { }) .collect(), )), - (K::List(_l), K::List(_r)) => Err("nyi"), - (K::Dictionary(_l), K::Dictionary(_r)) => Err("nyi"), - (_, K::Table(_)) => todo!("table"), - (K::Table(_), _) => todo!("table"), - _ => Err("nyi - wtf"), + (K::List(_l), K::List(_r)) => Err(RokError::NYI.into()), + (K::Dictionary(_l), K::Dictionary(_r)) => Err(RokError::NYI.into()), + (_, K::Table(_)) => Err(RokError::Error("nyi: table".into()).into()), + (K::Table(_), _) => Err(RokError::Error("nyi: table".into()).into()), + _ => Err(RokError::Error("nyi - wtf".into()).into()), }) } -pub fn v_asc(x: K) -> Result { - match x { - K::BoolArray(x) => { - let mut map: BTreeMap, Vec> = BTreeMap::new(); - for (i, v) in x.bool().unwrap().iter().enumerate() { - if map.contains_key(&v) { - let vec = map.get(&v).unwrap(); - map.insert(v, vec.iter().chain([i].iter()).cloned().collect()); - } else { - map.insert(v, vec![i]); +pub fn v_asc(x: K) -> Result { + if x.len() < 2 { + Ok(x) + } else { + match x { + K::BoolArray(x) => { + let mut map: BTreeMap, Vec> = BTreeMap::new(); + for (i, v) in x.bool().unwrap().iter().enumerate() { + if map.contains_key(&v) { + let vec = map.get(&v).unwrap(); + map.insert(v, vec.iter().chain([i].iter()).cloned().collect()); + } else { + map.insert(v, vec![i]); + } } + let v: Vec = map.values().flat_map(|v| v.iter().map(|v| *v as i64)).collect(); + Ok(K::BoolArray(arr!(v))) } - let v: Vec = map.values().flat_map(|v| v.iter().map(|v| *v as i64)).collect(); - Ok(K::BoolArray(arr!(v))) - } - K::IntArray(x) => { - let mut map: BTreeMap, Vec> = BTreeMap::new(); - for (i, v) in x.i64().unwrap().iter().enumerate() { - if map.contains_key(&v) { - let vec = map.get(&v).unwrap(); - map.insert(v, vec.iter().chain([i].iter()).cloned().collect()); - } else { - map.insert(v, vec![i]); + K::IntArray(x) => { + let mut map: BTreeMap, Vec> = BTreeMap::new(); + for (i, v) in x.i64().unwrap().iter().enumerate() { + if map.contains_key(&v) { + let vec = map.get(&v).unwrap(); + map.insert(v, vec.iter().chain([i].iter()).cloned().collect()); + } else { + map.insert(v, vec![i]); + } } + let v: Vec = map.values().flat_map(|v| v.iter().map(|v| *v as i64)).collect(); + Ok(K::IntArray(arr!(v))) } - let v: Vec = map.values().flat_map(|v| v.iter().map(|v| *v as i64)).collect(); - Ok(K::IntArray(arr!(v))) - } - K::FloatArray(x) => { - // f64 is only PartialOrd but we need something with Ord here. - // This is a terrible hack and probably terrible for performance. - let scaled_ints: Vec> = - (x * 1e9).f64().unwrap().into_iter().map(|f| f.map(|f| f as i128)).collect(); - let mut map: BTreeMap, Vec> = BTreeMap::new(); - for (i, v) in scaled_ints.iter().enumerate() { - if map.contains_key(v) { - let vec = map.get(v).unwrap(); - map.insert(*v, vec.iter().chain([i].iter()).cloned().collect()); - } else { - map.insert(*v, vec![i]); + K::FloatArray(x) => { + // f64 is only PartialOrd but we need something with Ord here. + // This is a terrible hack and probably terrible for performance. + let scaled_ints: Vec> = + (x * 1e9).f64().unwrap().into_iter().map(|f| f.map(|f| f as i128)).collect(); + let mut map: BTreeMap, Vec> = BTreeMap::new(); + for (i, v) in scaled_ints.iter().enumerate() { + if map.contains_key(v) { + let vec = map.get(v).unwrap(); + map.insert(*v, vec.iter().chain([i].iter()).cloned().collect()); + } else { + map.insert(*v, vec![i]); + } } + let v: Vec = map.values().flat_map(|v| v.iter().map(|v| *v as i64)).collect(); + Ok(K::IntArray(arr!(v))) } - let v: Vec = map.values().flat_map(|v| v.iter().map(|v| *v as i64)).collect(); - Ok(K::IntArray(arr!(v))) + _ => Err(RokError::NYI.into()), } - _ => Err("nyi"), } } -pub fn v_desc(x: K) -> Result { - v_reverse(v_asc(x).unwrap()) /* TODO: faster */ +pub fn v_desc(x: K) -> Result { + v_reverse(v_asc(x)?) /* TODO: faster */ } -pub fn v_lesser(x: K, y: K) -> Result { +pub fn v_lesser(x: K, y: K) -> Result { len_ok(&x, &y).and_then(|_| match promote_nouns(x, y) { (K::Bool(l), K::Bool(r)) => Ok(K::Bool((l < r) as u8)), (K::Int(Some(l)), K::Int(Some(r))) => Ok(K::Bool((l < r) as u8)), @@ -587,7 +596,7 @@ pub fn v_lesser(x: K, y: K) -> Result { .collect::>()))), (K::List(l), K::List(r)) => Ok(K::List( zip(l.iter(), r.iter()) - .map(|(l, r)| v_lesser(l.clone(), r.clone()).unwrap_or(K::Bool(0 as u8))) + .map(|(l, r)| v_lesser(l.clone(), r.clone()).unwrap_or(K::Bool(0_u8))) .collect::>(), )), (K::Dictionary(l), K::Dictionary(r)) => { @@ -620,9 +629,7 @@ pub fn v_lesser(x: K, y: K) -> Result { _ => None, } })))), - (K::Table(_l), K::Table(_r)) => { - todo!("table") - } + (K::Table(_l), K::Table(_r)) => Err(RokError::Error("nyi: table".into()).into()), (l, ref r @ K::Table(_)) => { // TODO: faster. hack: flip to dict, v_lesser(l, r), flip back to table v_flip(v_lesser(l, v_flip(r.clone()).unwrap()).unwrap()) @@ -631,11 +638,11 @@ pub fn v_lesser(x: K, y: K) -> Result { // TODO: faster. hack: flip to dict, v_lesser(l, r), flip back to table v_flip(v_lesser(v_flip(l.clone()).unwrap(), r).unwrap()) } - _ => Err("nyi"), + _ => Err(RokError::NYI.into()), }) } -pub fn v_greater(x: K, y: K) -> Result { +pub fn v_greater(x: K, y: K) -> Result { len_ok(&x, &y).and_then(|_| match promote_nouns(x, y) { (K::Bool(l), K::Bool(r)) => Ok(K::Bool((l > r) as u8)), (K::Int(Some(l)), K::Int(Some(r))) => Ok(K::Bool((l > r) as u8)), @@ -680,7 +687,7 @@ pub fn v_greater(x: K, y: K) -> Result { .collect::>()))), (K::List(l), K::List(r)) => Ok(K::List( zip(l.iter(), r.iter()) - .map(|(l, r)| v_lesser(l.clone(), r.clone()).unwrap_or(K::Bool(0 as u8))) + .map(|(l, r)| v_lesser(l.clone(), r.clone()).unwrap_or(K::Bool(0_u8))) .collect::>(), )), (K::Dictionary(l), K::Dictionary(r)) => { @@ -713,9 +720,7 @@ pub fn v_greater(x: K, y: K) -> Result { _ => None, } })))), - (K::Table(_l), K::Table(_r)) => { - todo!("table") - } + (K::Table(_l), K::Table(_r)) => Err(RokError::Error("nyi: table".into()).into()), (l, ref r @ K::Table(_)) => { // TODO: faster. hack: flip to dict, v_lesser(l, r), flip back to table v_flip(v_lesser(l, v_flip(r.clone()).unwrap()).unwrap()) @@ -724,14 +729,14 @@ pub fn v_greater(x: K, y: K) -> Result { // TODO: faster. hack: flip to dict, v_lesser(l, r), flip back to table v_flip(v_lesser(v_flip(l.clone()).unwrap(), r).unwrap()) } - _ => Err("nyi"), + _ => Err(RokError::NYI.into()), }) } -pub fn v_not(_r: K) -> Result { Err("nyi") } -pub fn v_match(_l: K, _r: K) -> Result { Err("nyi") } +pub fn v_not(_r: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_match(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } -pub fn v_enlist(x: K) -> Result { +pub fn v_enlist(x: K) -> Result { match x { K::Bool(x) => Ok(K::BoolArray(arr!([x]))), K::Int(x) => Ok(K::IntArray(arr!([x]))), @@ -749,61 +754,61 @@ pub fn v_enlist(x: K) -> Result { | K::List(_) | K::Dictionary(_) | K::Table(_) => Ok(K::List(vec![x])), - _ => Err("nyi v_enlist() other cases"), + _ => Err(RokError::Error("nyi v_enlist() other cases".into()).into()), } } -pub fn v_concat(x: K, y: K) -> Result { +pub fn v_concat(x: K, y: K) -> Result { match (x.clone(), y.clone()) { (K::Bool(_) | K::Int(_) | K::Float(_), K::Bool(_) | K::Int(_) | K::Float(_)) => { promote_num(vec![x, y]) } (K::BoolArray(x) | K::IntArray(x) | K::FloatArray(x), K::FloatArray(y)) => { - Ok(K::FloatArray(x.to_float().unwrap().extend(&y).unwrap().clone())) + Ok(K::FloatArray(x.to_float()?.extend(&y)?.clone())) } (K::FloatArray(mut x), K::BoolArray(y) | K::IntArray(y)) => { - Ok(K::FloatArray(x.extend(&y.to_float().unwrap()).unwrap().clone())) + Ok(K::FloatArray(x.extend(&y.to_float()?)?.clone())) } (K::BoolArray(x) | K::IntArray(x), K::IntArray(y)) => { - Ok(K::IntArray(x.cast(&DataType::Int64).unwrap().extend(&y).unwrap().clone())) + Ok(K::IntArray(x.cast(&DataType::Int64)?.extend(&y)?.clone())) } (K::BoolArray(mut x), K::BoolArray(y)) => { - Ok(K::IntArray(x.extend(&y.cast(&DataType::Boolean).unwrap()).unwrap().clone())) + Ok(K::IntArray(x.extend(&y.cast(&DataType::Boolean)?)?.clone())) } (K::Bool(_) | K::Int(_) | K::Float(_), K::BoolArray(_) | K::IntArray(_) | K::FloatArray(_)) => { - v_concat(v_enlist(x).unwrap(), y) + v_concat(v_enlist(x)?, y) } (K::BoolArray(_) | K::IntArray(_) | K::FloatArray(_), K::Bool(_) | K::Int(_) | K::Float(_)) => { - v_concat(x, v_enlist(y).unwrap()) + v_concat(x, v_enlist(y)?) } (K::Char(x), K::Char(y)) => Ok(K::CharArray(format!("{}{}", x, y))), (K::CharArray(x), K::Char(y)) => Ok(K::CharArray(format!("{}{}", x, y))), (K::Char(x), K::CharArray(y)) => Ok(K::CharArray(format!("{}{}", x, y))), (K::List(x), K::List(y)) => Ok(K::List(x.iter().chain(y.iter()).cloned().collect())), - (K::List(x), y) => Ok(K::List(x.iter().chain(vec![y].iter()).cloned().collect())), - (x, K::List(y)) => Ok(K::List(vec![x].iter().chain(y.iter()).cloned().collect())), - _ => todo!("nyi v_concat() other cases {}, {}", x, y), + (K::List(x), y) => Ok(K::List(x.iter().chain([y].iter()).cloned().collect())), + (x, K::List(y)) => Ok(K::List([x].iter().chain(y.iter()).cloned().collect())), + _ => Err(RokError::Error(format!("nyi: v_concat() other cases {}, {}", x, y)).into()), } } -pub fn v_isnull(x: K) -> Result { +pub fn v_isnull(x: K) -> Result { match x { K::Int(None) => Ok(K::Bool(1u8)), K::Float(f) if f.is_nan() => Ok(K::Bool(1u8)), _ => Ok(K::Bool(0u8)), } } -pub fn v_fill(_l: K, _r: K) -> Result { Err("nyi") } -pub fn v_except(_l: K, _r: K) -> Result { Err("nyi") } +pub fn v_fill(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_except(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } -pub fn v_floor(_r: K) -> Result { Err("nyi") } -pub fn v_drop(x: K, y: K) -> Result { +pub fn v_floor(_r: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_drop(x: K, y: K) -> Result { match x { K::Bool(1) => v_drop(K::Int(Some(1)), y), K::Int(Some(0)) | K::Bool(0) => Ok(y), K::Int(Some(i)) => { - if i.abs() as usize >= y.len() { + if i.unsigned_abs() as usize >= y.len() { v_take(K::Int(Some(0)), y) } else if i < 0 { // drop off end. @@ -815,35 +820,35 @@ pub fn v_drop(x: K, y: K) -> Result { v_at(y.clone(), K::IntArray(arr!((i..(y.len() as i64)).collect::>()))) } } - _ => Err("type"), + _ => Err(RokError::Type.into()), } } -pub fn v_delete(_l: K, _r: K) -> Result { Err("nyi") } -pub fn v_cut(_l: K, _r: K) -> Result { Err("nyi") } +pub fn v_delete(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_cut(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } -pub fn v_string(_r: K) -> Result { Err("nyi") } -pub fn v_dfmt(_l: K, _r: K) -> Result { Err("nyi") } -pub fn v_pad(_l: K, _r: K) -> Result { Err("nyi") } -pub fn v_cast(l: K, _r: K) -> Result { +pub fn v_string(_r: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_dfmt(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_pad(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_cast(l: K, _r: K) -> Result { match l { - K::Symbol(s) if s == "c".to_string() => todo!("cast to string"), - K::Symbol(s) if s == "i".to_string() => todo!("cast to int"), - K::Symbol(s) if s == "f".to_string() => todo!("cast to float"), - _ => Err("type"), + K::Symbol(s) if s == "c" => Err(RokError::Error("nyi: cast to string".into()).into()), + K::Symbol(s) if s == "i" => Err(RokError::Error("nyi: cast to int".into()).into()), + K::Symbol(s) if s == "f" => Err(RokError::Error("nyi: cast to float".into()).into()), + _ => Err(RokError::Type.into()), } } -pub fn v_randfloat(r: K) -> Result { +pub fn v_randfloat(r: K) -> Result { match r { - K::Int(Some(0)) => Err("nyi"), - K::Int(Some(i)) if i < 0 => Err("domain"), + K::Int(Some(0)) => Err(RokError::NYI.into()), + K::Int(Some(i)) if i < 0 => Err(RokError::Domain.into()), K::Int(Some(i)) if i > 0 => Ok(K::FloatArray( ChunkedArray::::rand_uniform("", i as usize, 0.0f64, 1.0f64).into_series(), )), - _ => Err("type"), + _ => Err(RokError::Type.into()), } } -pub fn v_unique(r: K) -> Result { +pub fn v_unique(r: K) -> Result { debug!("v_unique({:?})", r); match r { @@ -854,11 +859,11 @@ pub fn v_unique(r: K) -> Result { K::CharArray(a) => Ok(K::CharArray(a.chars().unique().collect())), // TODO ?(3.14;"abc";3.14) works in ngn/k but k9 throws domain error if the list has any float item and otherwise works. // K::List(v) => Ok(K::List(v.into_iter().unique().collect())), - K::List(_v) => Err("nyi: v_unique(K::List(_))"), - _ => Err("domain"), // + K::List(_v) => Err(RokError::Error("nyi: v_unique(K::List(_))".into()).into()), + _ => Err(RokError::Domain.into()), // } } -pub fn v_rand(l: K, r: K) -> Result { +pub fn v_rand(l: K, r: K) -> Result { match (l.clone(), r.clone()) { (K::Int(Some(x)), K::Int(Some(y))) if x > 0 => { let mut rng = rand::thread_rng(); @@ -868,21 +873,27 @@ pub fn v_rand(l: K, r: K) -> Result { Ok(K::IntArray(Series::new("", v))) } (K::Int(Some(x)), K::Int(Some(_y))) if x < 0 => { - todo!("nyi v_rand with no repeats") + Err(RokError::Error("nyi: nyi v_rand with no repeats".into()).into()) } (K::Int(Some(_x)), y) if y.len() > 1 => { let idxs = v_rand(l, K::Int(Some(y.len() as i64))).unwrap(); v_at(r, idxs) } - _ => Err("nyi"), + _ => Err(RokError::NYI.into()), } } -pub fn v_find(x: K, y: K) -> Result { +pub fn v_find(x: K, y: K) -> Result { // find index of every item of y in x match (x, y) { - (K::BoolArray(_x), K::BoolArray(_y)) => todo!("BoolArray "), - (K::IntArray(_x), K::IntArray(_y)) => todo!("IntArray "), - (K::FloatArray(_x), K::FloatArray(_y)) => todo!("FloatArray "), + (K::BoolArray(_x), K::BoolArray(_y)) => { + Err(RokError::Error("nyi: v_find BoolArray".into()).into()) + } + (K::IntArray(_x), K::IntArray(_y)) => { + Err(RokError::Error("nyi: v_find IntArray".into()).into()) + } + (K::FloatArray(_x), K::FloatArray(_y)) => { + Err(RokError::Error("nyi: v_find FloatArray".into()).into()) + } (K::CharArray(x), K::CharArray(y)) => { if let K::CharArray(uniq_y) = v_unique(K::CharArray(y.clone())).unwrap() { let map: IndexMap> = uniq_y @@ -899,12 +910,12 @@ pub fn v_find(x: K, y: K) -> Result { panic!("impossible") } } - _ => Err("nyi v_find"), + _ => Err(RokError::Error("nyi v_find".into()).into()), } } -pub fn v_splice(_x: K, _y: K, _z: K) -> Result { Err("nyi") } +pub fn v_splice(_x: K, _y: K, _z: K) -> Result { Err(RokError::NYI.into()) } -pub fn v_type(r: K) -> Result { +pub fn v_type(r: K) -> Result { use K::*; // TODO allow checking type of KW::Verb etc, see ngn/k's types help \0 match r { @@ -926,16 +937,16 @@ pub fn v_type(r: K) -> Result { } } -pub fn v_at(l: K, r: K) -> Result { +pub fn v_at(l: K, r: K) -> Result { match r { K::Int(None) => match l.clone() { K::SymbolArray(_) | K::BoolArray(_) | K::IntArray(_) | K::FloatArray(_) | K::CharArray(_) => { l.fill(0) } - K::List(_v) => todo!("v_at"), - K::Dictionary(_d) => todo!("v_at"), - K::Table(_df) => todo!("v_at"), - _ => Err("type"), + K::List(_v) => Err(RokError::Error("nyi: v_at List".into()).into()), + K::Dictionary(_d) => Err(RokError::Error("nyi: v_at Dictionary".into()).into()), + K::Table(_df) => Err(RokError::Error("nyi: v_at Table".into()).into()), + _ => Err(RokError::Type.into()), }, K::Bool(i) => { // TODO remove this code duplication of K::Int(_) case below @@ -951,7 +962,7 @@ pub fn v_at(l: K, r: K) -> Result { if (i as usize) < v.len() { Ok(v[i as usize].clone()) } else { - Err("length") + Err(RokError::Length.into()) } } _ => todo!("index into l"), @@ -1003,25 +1014,37 @@ pub fn v_at(l: K, r: K) -> Result { K::SymbolArray(a) => { match a.clone().append(&Series::new_null("", 1)).unwrap().take_slice(&idcs) { Ok(a) => Ok(K::SymbolArray(a)), - _ => todo!("index out of bounds - this shouldn't be an error"), + _ => Err( + RokError::Error("nyi: index out of bounds - this shouldn't be an error".into()) + .into(), + ), } } K::BoolArray(a) => { match a.clone().append(&Series::new_null("", 1)).unwrap().take_slice(&idcs) { Ok(a) => Ok(K::BoolArray(a)), - _ => todo!("index out of bounds - this shouldn't be an error"), + _ => Err( + RokError::Error("nyi: index out of bounds - this shouldn't be an error".into()) + .into(), + ), } } K::IntArray(a) => { match a.clone().append(&Series::new_null("", 1)).unwrap().take_slice(&idcs) { Ok(a) => Ok(K::IntArray(a)), - _ => todo!("index out of bounds - this shouldn't be an error"), + _ => Err( + RokError::Error("nyi: index out of bounds - this shouldn't be an error".into()) + .into(), + ), } } K::FloatArray(a) => { match a.clone().append(&Series::new_null("", 1)).unwrap().take_slice(&idcs) { Ok(a) => Ok(K::FloatArray(a)), - _ => todo!("index out of bounds - this shouldn't be an error"), + _ => Err( + RokError::Error("nyi: index out of bounds - this shouldn't be an error".into()) + .into(), + ), } } K::CharArray(a) => Ok(K::CharArray( @@ -1045,7 +1068,7 @@ pub fn v_at(l: K, r: K) -> Result { .collect(); Ok(promote_num(r.clone()).unwrap_or(K::List(r))) } - _ => todo!("v_at"), + _ => Err(RokError::Error("nyi: v_at".into()).into()), } } K::Symbol(s) => match l.clone() { @@ -1058,7 +1081,7 @@ pub fn v_at(l: K, r: K) -> Result { } K::Table(df) => match df.get_column_index(&s) { Some(i) => K::try_from(df[i].clone()), - _ => todo!("nyi"), + _ => Err(RokError::NYI.into()), }, K::SymbolArray(ss) => match l.clone() { K::Dictionary(d) => { @@ -1076,10 +1099,10 @@ pub fn v_at(l: K, r: K) -> Result { .collect::>(), )) } - K::Table(_df) => todo!("nyi"), - _ => todo!("nyi"), + K::Table(_df) => Err(RokError::NYI.into()), + _ => Err(RokError::NYI.into()), }, - _ => Err("type"), + _ => Err(RokError::Type.into()), }, K::SymbolArray(s) => { let keys: Vec = s @@ -1091,31 +1114,31 @@ pub fn v_at(l: K, r: K) -> Result { .collect(); Ok(K::List(keys.into_iter().map(|k| v_at(l.clone(), k).unwrap()).collect())) } - _ => todo!("v_at({:?}, {:?})", l, r), + _ => Err(RokError::Error(format!("v_at({:?}, {:?})", l, r)).into()), } } // https://k.miraheze.org/wiki/Amend -pub fn v_amend3(_x: K, _y: K, _z: K) -> Result { Err("nyi") } -pub fn v_amend4(_x: K, _y: K, _f: K, _z: K) -> Result { Err("nyi") } +pub fn v_amend3(_x: K, _y: K, _z: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_amend4(_x: K, _y: K, _f: K, _z: K) -> Result { Err(RokError::NYI.into()) } -pub fn v_eval(x: K) -> Result { +pub fn v_eval(x: K) -> Result { // TODO: does this need the current Env passed in? match x { K::CharArray(s) => { let mut env = Env { names: HashMap::new(), parent: None }; - Ok(eval(&mut env, scan(&s).unwrap()).unwrap().unwrap_noun()) + eval(&mut env, scan(&s)?)?.unwrap_noun() } - _ => Err("nyi"), + _ => Err(RokError::NYI.into()), } } -pub fn v_dot(_l: K, _r: K) -> Result { Err("nyi") } +pub fn v_dot(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } // https://k.miraheze.org/wiki/Deep_amend -pub fn v_deepamend3(_x: K, _y: K, _z: K) -> Result { Err("nyi") } -pub fn v_deepamend4(_x: K, _y: K, _f: K, _z: K) -> Result { Err("nyi") } -pub fn v_try(_x: K, _y: K, _z: K) -> Result { Err("nyi") } +pub fn v_deepamend3(_x: K, _y: K, _z: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_deepamend4(_x: K, _y: K, _f: K, _z: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_try(_x: K, _y: K, _z: K) -> Result { Err(RokError::NYI.into()) } -pub fn v_join(l: K, r: K) -> Result { +pub fn v_join(l: K, r: K) -> Result { match l { K::Char(l) => match r { K::List(v) => { @@ -1123,40 +1146,40 @@ pub fn v_join(l: K, r: K) -> Result { v.iter().map(|k| if let K::CharArray(s) = k { Some(s.clone()) } else { None }).collect(); match v { Some(v) => Ok(K::CharArray(v.join(&l.to_string()))), - None => Err("type"), + None => Err(RokError::Type.into()), } } - _ => Err("type"), + _ => Err(RokError::Type.into()), }, - _ => Err("type"), + _ => Err(RokError::Type.into()), } } -pub fn v_unpack(_l: K, _r: K) -> Result { Err("nyi") } -pub fn v_split(l: K, r: K) -> Result { +pub fn v_unpack(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_split(l: K, r: K) -> Result { match l { K::Char(l) => match r { K::CharArray(r) => Ok(K::List(r.split(l).map(|s| K::CharArray(s.to_string())).collect())), - _ => Err("type"), + _ => Err(RokError::Type.into()), }, K::CharArray(l) => match r { K::CharArray(r) => Ok(K::List(r.split(&l).map(|s| K::CharArray(s.to_string())).collect())), - _ => Err("type"), + _ => Err(RokError::Type.into()), }, - _ => Err("type"), + _ => Err(RokError::Type.into()), } } -pub fn v_iota(x: K) -> Result { +pub fn v_iota(x: K) -> Result { debug!("v_iota"); match x { K::Bool(0) => Ok(K::IntArray(Series::new_empty("", &DataType::Int64))), K::Bool(1) => v_iota(K::Int(Some(1))), K::Int(Some(0)) | K::Int(None) => Ok(K::IntArray(Series::new_empty("", &DataType::Int64))), K::Int(Some(i)) => Ok(K::IntArray(arr![(0..i).collect::>()])), - _ => todo!("v_iota variants: {}", x), + _ => Err(RokError::Error(format!("nyi: v_iota variants: {}", x)).into()), } } -pub fn v_sum(x: K) -> Result { +pub fn v_sum(x: K) -> Result { match x { K::Bool(_) | K::Int(_) | K::Float(_) => Ok(x), K::BoolArray(a) => Ok(K::Int(a.sum().ok())), @@ -1165,15 +1188,18 @@ pub fn v_sum(x: K) -> Result { _ => { // Fall back to slow catchall. TODO Something nicer let mut env = Env { names: HashMap::new(), parent: None }; - let sum = eval(&mut env, scan("{x+y}/").unwrap()).unwrap(); - Ok(eval(&mut env, vec![sum, KW::Noun(x.clone())]).unwrap().unwrap_noun()) + let sum = eval(&mut env, scan("{x+y}/")?)?; + eval(&mut env, vec![sum, KW::Noun(x.clone())])?.unwrap_noun() } } } -pub fn v_d_sum(l: K, r: K) -> Result { Ok(l + v_sum(r).unwrap()) } +pub fn v_d_sum(l: K, r: K) -> Result { + let r = v_sum(r)?; + Ok(l + r) +} // TODO -// pub fn v_product(x: K) -> Result { +// pub fn v_product(x: K) -> Result { // match x { // K::BoolArray(a) => Ok(K::Int(a.product().ok().into())), // K::IntArray(a) => Ok(K::Int(a.product().ok())), @@ -1181,9 +1207,9 @@ pub fn v_d_sum(l: K, r: K) -> Result { Ok(l + v_sum(r).unwrap() // _ => todo!("other types of K"), // } // } -// pub fn v_d_product(l: K, r: K) -> Result { Ok(l + v_product(r).unwrap()) } +// pub fn v_d_product(l: K, r: K) -> Result { Ok(l + v_product(r).unwrap()) } -pub fn v_d_bang(l: K, r: K) -> Result { +pub fn v_d_bang(l: K, r: K) -> Result { match l { K::SymbolArray(_) | K::Symbol(_) @@ -1195,25 +1221,29 @@ pub fn v_d_bang(l: K, r: K) -> Result { } } -pub fn v_each(env: &mut Env, v: KW, x: K) -> Result { +pub fn v_each(env: &mut Env, v: KW, x: K) -> Result { match v { - f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(x).map(|v| { - let r: Vec = v - .iter() - .cloned() - .map(|y| + f @ KW::Verb { .. } | f @ KW::Function { .. } => { + match k_to_vec(x).map(|v| { + v.iter() + .map(|y| // apply_primitive(env, &name, None, KW::Noun(y.clone())).unwrap().unwrap_noun() - eval(env, vec![f.clone(), KW::Noun(y.clone())]).unwrap().unwrap_noun()) - .collect(); - promote_num(r.clone()).unwrap_or(K::List(r)) - }), - _ => Err("type"), + eval(env, vec![f.clone(), KW::Noun(y.clone())])?.unwrap_noun()) + .collect::>>() + })? { + Ok(r) => Ok(promote_num(r.clone()).unwrap_or(K::List(r))), + Err(e) => Err(e), + } + } + _ => Err(RokError::Type.into()), } } -pub fn v_d_each(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { todo!("each") } +pub fn v_d_each(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { + Err(RokError::Error("nyi: each".into()).into()) +} // Dispatch / based on inputs: fold, over, fixedpoint -pub fn a_slash(env: &mut Env, v: KW, x: K) -> Result { +pub fn a_slash(env: &mut Env, v: KW, x: K) -> Result { match v.clone() { KW::Verb { name } => match name.as_str().char_indices().nth_back(0).unwrap().1 { ':' | '/' | '\\' => v_fixedpoint(env, v, x), @@ -1222,13 +1252,13 @@ pub fn a_slash(env: &mut Env, v: KW, x: K) -> Result { KW::Function { body: _, args, adverb: _ } => match args.len() { 2 => v_fold(env, v, x), 1 => v_fixedpoint(env, v, x), - _ => Err("rank"), + _ => Err(RokError::Rank.into()), }, _ => panic!("impossible"), } } -pub fn a_d_slash(env: &mut Env, v: KW, x: K, y: K) -> Result { +pub fn a_d_slash(env: &mut Env, v: KW, x: K, y: K) -> Result { // TODO check the rank of v and type of x and handle the different meanings of / // https://k.miraheze.org/wiki/For // https://k.miraheze.org/wiki/While @@ -1243,33 +1273,32 @@ pub fn a_d_slash(env: &mut Env, v: KW, x: K, y: K) -> Result { KW::Function { body: _, args, adverb: _ } => match args.len() { 2 => v_d_fold(env, v, x, y), 1 => todo!("monadic v: {}", v), - _ => Err("rank"), + _ => Err(RokError::Rank.into()), }, _ => panic!("impossible"), } } -pub fn v_fixedpoint(env: &mut Env, v: KW, x: K) -> Result { +pub fn v_fixedpoint(env: &mut Env, v: KW, x: K) -> Result { // fixedpoint and scan-fixedpoint are adverbs that apply a monadic function to // a given noun y until it stops changing, or the initial value has been repeated. let mut prev_r = x.clone(); loop { - let r = eval(env, vec![v.clone(), KW::Noun(prev_r.clone())]).unwrap().unwrap_noun(); + let r = eval(env, vec![v.clone(), KW::Noun(prev_r.clone())])?.unwrap_noun()?; if r == prev_r || r == x { return Ok(r); } prev_r = r; } } -pub fn v_scan_fixedpoint(env: &mut Env, v: KW, x: K) -> Result { +pub fn v_scan_fixedpoint(env: &mut Env, v: KW, x: K) -> Result { // same as v_fixedpoint() except return a K::List of all intermediate results match v.clone() { f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(x.clone()).and_then(|_| { let mut result: Vec = vec![x.clone()]; loop { - let r = eval(env, vec![f.clone(), KW::Noun(result[result.len() - 1].clone())]) - .unwrap() - .unwrap_noun(); + let r = + eval(env, vec![f.clone(), KW::Noun(result[result.len() - 1].clone())])?.unwrap_noun()?; if r == result[result.len() - 1] || r == x { match promote_num(result.clone()) { Ok(k) => return Ok(k), @@ -1279,50 +1308,42 @@ pub fn v_scan_fixedpoint(env: &mut Env, v: KW, x: K) -> Result result.push(r); } }), - _ => Err("type"), + _ => Err(RokError::Type.into()), } } -pub fn v_fold(env: &mut Env, v: KW, x: K) -> Result { +pub fn v_fold(env: &mut Env, v: KW, x: K) -> Result { // split into list, then reduce match v { f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(x).and_then(|v| { - let r = v.iter().cloned().reduce(|x, y| { + let mut acc: K = v.get(0).ok_or(RokError::Length)?.clone(); + for y in v.iter().skip(1).cloned() { // apply_primitive(env, &name, Some(KW::Noun(x.clone())), KW::Noun(y.clone())).unwrap().unwrap_noun() - eval( + acc = eval( env, - vec![f.clone(), KW::FuncArgs(vec![vec![KW::Noun(x.clone())], vec![KW::Noun(y.clone())]])], - ) - .unwrap() - .unwrap_noun() - }); - match r { - Some(k) => Ok(k.clone()), - None => Err("TODO not sure what this error case is"), + vec![ + f.clone(), + KW::FuncArgs(vec![vec![KW::Noun(acc.clone())], vec![KW::Noun(y.clone())]]), + ], + )? + .unwrap_noun()? } + Ok(acc) }), - _ => Err("type"), + _ => Err(RokError::Type.into()), } } -pub fn v_d_fold(env: &mut Env, v: KW, x: K, y: K) -> Result { +pub fn v_d_fold(env: &mut Env, v: KW, x: K, y: K) -> Result { if let KW::Verb { ref name } = v { let mut e = Env { names: HashMap::new(), parent: Some(Box::new(env.clone())) }; // TODO This will lose names if the fold verb does global assignment - Ok( - apply_primitive( - env, - &name.clone(), - Some(KW::Noun(x.clone())), - KW::Noun(v_fold(&mut e, v, y).unwrap()), - ) - .unwrap() - .unwrap_noun(), - ) + apply_primitive(env, &name.clone(), Some(KW::Noun(x.clone())), KW::Noun(v_fold(&mut e, v, y)?))? + .unwrap_noun() } else { - Err("type") + Err(RokError::Type.into()) } } -pub fn a_bslash(env: &mut Env, v: KW, x: K) -> Result { +pub fn a_bslash(env: &mut Env, v: KW, x: K) -> Result { match v.clone() { KW::Verb { name } => match name.as_str().char_indices().nth_back(0).unwrap().1 { ':' | '/' | '\\' => v_scan_fixedpoint(env, v, x), @@ -1331,13 +1352,13 @@ pub fn a_bslash(env: &mut Env, v: KW, x: K) -> Result { KW::Function { body: _, args, adverb: _ } => match args.len() { 2 => v_scan(env, v, x), 1 => v_scan_fixedpoint(env, v, x), - _ => Err("rank"), + _ => Err(RokError::Rank.into()), }, _ => panic!("impossible"), } } -pub fn a_d_bslash(env: &mut Env, v: KW, x: K, y: K) -> Result { +pub fn a_d_bslash(env: &mut Env, v: KW, x: K, y: K) -> Result { // TODO check the rank of v and type of x and handle the different meanings of \ // https://k.miraheze.org/wiki/For // https://k.miraheze.org/wiki/While @@ -1352,13 +1373,13 @@ pub fn a_d_bslash(env: &mut Env, v: KW, x: K, y: K) -> Result { KW::Function { body: _, args, adverb: _ } => match args.len() { 2 => v_d_scan(env, v, x, y), 1 => todo!("monadic v: {}", v), - _ => Err("rank"), + _ => Err(RokError::Rank.into()), }, _ => panic!("impossible"), } } -pub fn v_scan(env: &mut Env, v: KW, x: K) -> Result { +pub fn v_scan(env: &mut Env, v: KW, x: K) -> Result { // same as v_fold() except return a K::List of intermediate results // split into list, then scan match v { @@ -1377,7 +1398,8 @@ pub fn v_scan(env: &mut Env, v: KW, x: K) -> Result { ], ) .unwrap() - .unwrap_noun(), + .unwrap_noun() + .unwrap(), ) } match promote_num(result.clone()) { @@ -1385,74 +1407,82 @@ pub fn v_scan(env: &mut Env, v: KW, x: K) -> Result { _ => K::List(result), } }), - _ => Err("type"), + _ => Err(RokError::Type.into()), } } -pub fn v_d_scan(env: &mut Env, v: KW, x: K, y: K) -> Result { +pub fn v_d_scan(env: &mut Env, v: KW, x: K, y: K) -> Result { match v.clone() { f @ KW::Verb { .. } | f @ KW::Function { .. } => { - let first: K = eval( - env, - vec![ - f.clone(), - KW::FuncArgs(vec![ - vec![KW::Noun(x.clone())], - vec![KW::Noun(v_first(y.clone()).unwrap())], - ]), - ], - ) - .unwrap() - .unwrap_noun(); + if y.is_empty() { + Ok(y) + } else { + let first: K = eval( + env, + vec![ + f.clone(), + KW::FuncArgs(vec![vec![KW::Noun(x.clone())], vec![KW::Noun(v_first(y.clone())?)]]), + ], + )? + .unwrap_noun()?; - let rest: K = v_drop(K::Int(Some(1)), y).unwrap(); - v_scan(env, v, v_concat(first, rest).unwrap()) + let rest: K = v_drop(K::Int(Some(1)), y)?; + v_scan(env, v, v_concat(first, rest)?) + } } - _ => Err("type"), + _ => Err(RokError::Type.into()), } } -pub fn v_eachprior(env: &mut Env, v: KW, x: K) -> Result { +pub fn v_eachprior(env: &mut Env, v: KW, x: K) -> Result { match v { - f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(x).map(|v| { - let first: &K = &v[0]; - let r: Vec = v - .iter() - .zip(v.iter().skip(1)) - .map(|(x, y)| { - // f[y,x] / yes y,x not x,y - eval( - env, - vec![ - f.clone(), - KW::FuncArgs(vec![vec![KW::Noun(y.clone())], vec![KW::Noun(x.clone())]]), - ], - ) - .unwrap() - .unwrap_noun() - }) - .collect(); - let r: Vec = vec![first.clone()].into_iter().chain(r).collect(); - promote_num(r.clone()).unwrap_or(K::List(r)) - }), - _ => Err("type"), + ref f @ KW::Function { ref body, ref args, ref adverb } if args.len() == 1 => { + Err(RokError::Error("nyi: peach https://code.kx.com/q/basics/peach/".into()).into()) + } + f @ KW::Verb { .. } | f @ KW::Function { .. } => { + k_to_vec(x).map(|v| { + let first: &K = &v[0]; + let r: Result> = v + .iter() + .zip(v.iter().skip(1)) + .map(|(x, y)| { + // f[y,x] / yes y,x not x,y + eval( + env, + vec![ + f.clone(), + KW::FuncArgs(vec![vec![KW::Noun(y.clone())], vec![KW::Noun(x.clone())]]), + ], + )? + .unwrap_noun() + }) + .collect(); + match r { + Ok(r) => { + let r: Vec = vec![first.clone()].into_iter().chain(r).collect(); + Ok(promote_num(r.clone()).unwrap_or(K::List(r))) + } + Err(e) => Err(e), + } + })? + } + _ => Err(RokError::Type.into()), } } -pub fn v_eachprior_d_or_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { - todo!("v_eachprior_d_or_windows()") +pub fn v_eachprior_d_or_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { + Err(RokError::Error("nyi: v_eachprior_d_or_windows()".into()).into()) } -pub fn v_eachprior_d(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { - todo!("v_eachprior_d()") +pub fn v_eachprior_d(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { + Err(RokError::Error("nyi: v_eachprior_d()".into()).into()) } -pub fn v_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { - todo!("v_windows()") +pub fn v_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { + Err(RokError::Error("nyi: v_windows()".into()).into()) } -pub fn v_d_eachright(env: &mut Env, v: KW, x: K, y: K) -> Result { +pub fn v_d_eachright(env: &mut Env, v: KW, x: K, y: K) -> Result { match v { f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(y).map(|v| { - let r: Vec = v + let r: Result> = v .iter() - .cloned() .map(|y| { eval( env, @@ -1460,22 +1490,23 @@ pub fn v_d_eachright(env: &mut Env, v: KW, x: K, y: K) -> Result Err("type"), + match r { + Ok(r) => Ok(promote_num(r.clone()).unwrap_or(K::List(r))), + Err(e) => Err(e), + } + })?, + _ => Err(RokError::Type.into()), } } -pub fn v_d_eachleft(env: &mut Env, v: KW, x: K, y: K) -> Result { +pub fn v_d_eachleft(env: &mut Env, v: KW, x: K, y: K) -> Result { match v { - f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(x).map(|v| { - let r: Vec = v + f @ KW::Verb { .. } | f @ KW::Function { .. } => { + let r: Vec = k_to_vec(x)? .iter() - .cloned() .map(|x| { eval( env, @@ -1483,14 +1514,13 @@ pub fn v_d_eachleft(env: &mut Env, v: KW, x: K, y: K) -> Result f.clone(), KW::FuncArgs(vec![vec![KW::Noun(x.clone())], vec![KW::Noun(y.clone())]]), ], - ) - .unwrap() + )? .unwrap_noun() }) - .collect(); - promote_num(r.clone()).unwrap_or(K::List(r)) - }), - _ => Err("type"), + .collect::>>()?; + Ok(promote_num(r.clone()).unwrap_or(K::List(r))) + } + _ => Err(RokError::Type.into()), } } @@ -1501,7 +1531,7 @@ pub fn strip_quotes(s: String) -> String { s } } -pub fn v_makedict(l: K, r: K) -> Result { +pub fn v_makedict(l: K, r: K) -> Result { debug!("v_makedict() l: {:?}", l); debug!("v_makedict() r: {:?}", r); match l { @@ -1518,21 +1548,21 @@ pub fn v_makedict(l: K, r: K) -> Result { repeat(v[0].clone()), )))) } else { - Err("length") + Err(RokError::Length.into()) } } K::BoolArray(_) | K::IntArray(_) | K::FloatArray(_) | K::CharArray(_) | K::SymbolArray(_) => { // `a`b`c!1 2 3 => `a`b`c!(1;2;3) Ok(K::Dictionary(IndexMap::from_iter(zip( s.iter().map(|s| strip_quotes(s.to_string())), - k_to_vec(r).unwrap().iter().cloned(), + k_to_vec(r)?.iter().cloned(), )))) } _ => { - if s.len() == 0 { - Err("length") + if s.is_empty() { + Err(RokError::Length.into()) } else if s.len() == 1 { - Ok(K::Dictionary(IndexMap::from([(strip_quotes(s.get(0).unwrap().to_string()), r)]))) + Ok(K::Dictionary(IndexMap::from([(strip_quotes(s.get(0)?.to_string()), r)]))) } else { Ok(K::Dictionary(IndexMap::from_iter(zip( s.iter().map(|s| strip_quotes(s.to_string())), @@ -1542,18 +1572,16 @@ pub fn v_makedict(l: K, r: K) -> Result { } }, K::Symbol(s) => Ok(K::Dictionary(IndexMap::from([(s, r)]))), - _ => { - todo!("modulo") - } + _ => Err(RokError::Error("nyi: modulo".into()).into()), } } -pub fn v_colon(_r: K) -> Result { todo!(": monad") } -pub fn v_d_colon(env: &mut Env, l: K, r: KW) -> Result { +pub fn v_colon(_r: K) -> Result { Err(RokError::Error("nyi: : monad".into()).into()) } +pub fn v_d_colon(env: &mut Env, l: K, r: KW) -> Result { debug!("l: {:?}, r: {:?}", l, r); match (&l, &r) { (K::Bool(0), KW::Noun(K::CharArray(a))) => Ok(KW::Noun(K::List( - std::fs::read_to_string(a).unwrap().lines().map(String::from).map(K::from).collect(), + std::fs::read_to_string(a)?.lines().map(String::from).map(K::from).collect(), ))), (K::Int(Some(2i64)), KW::Noun(K::Symbol(s))) => { let p = Path::new(&s); @@ -1564,22 +1592,20 @@ pub fn v_d_colon(env: &mut Env, l: K, r: KW) -> Result { Ok(KW::Noun(K::Table( CsvReadOptions::default() .with_has_header(true) - .try_into_reader_with_file_path(Some(p.to_path_buf())) - .unwrap() - .finish() - .unwrap(), + .try_into_reader_with_file_path(Some(p.to_path_buf()))? + .finish()?, ))) } else if e == "parquet" { // let lf1 = LazyFrame::scan_parquet(p, Default::default()).unwrap(); Ok(KW::Noun(K::Table(ParquetReader::new(File::open(p).unwrap()).finish().unwrap()))) } else { - todo!("other file types") + Err(RokError::Error("nyi: other file types".into()).into()) } } - _ => todo!("no extension"), + _ => Err(RokError::Error("nyi: no extension".into()).into()), } } else { - Err("path does not exist") + Err(RokError::Error("path does not exist".into()).into()) } } (K::Name(n), r) => { @@ -1587,11 +1613,11 @@ pub fn v_d_colon(env: &mut Env, l: K, r: KW) -> Result { Ok(r.clone()) } (_, KW::Noun(r)) => Ok(KW::Noun(v_rident(l, r.clone()).unwrap())), - _ => panic!("impossible"), + _ => Err(RokError::Error("impossible".into()).into()), } } -pub fn v_prm(_r: K) -> Result { Err("nyi") } -pub fn v_in(_l: K, _r: K) -> Result { Err("nyi") } -pub fn v_has(_l: K, _r: K) -> Result { Err("nyi") } -pub fn v_within(_l: K, _r: K) -> Result { Err("nyi") } +pub fn v_prm(_r: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_in(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_has(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_within(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } diff --git a/tests/ngnk.rs b/tests/ngnk.rs index a88727c..15bad8e 100644 --- a/tests/ngnk.rs +++ b/tests/ngnk.rs @@ -1,14 +1,14 @@ -use roklang::KW::*; +use anyhow::Result; use roklang::*; use std::collections::HashMap; -use std::fs::{self, File}; +use std::fs::File; use std::io::Read; -fn k_eval(s: &str) -> Result { +fn k_eval(s: &str) -> Result { let mut env = Env { names: HashMap::new(), parent: None }; // let r = eval(&mut env, scan(s).unwrap()).unwrap().unwrap_noun(); - match eval(&mut env, scan(s).unwrap()) { + match eval(&mut env, scan(s)?) { Ok(r) => { // let r = r.unwrap_noun(); // println!("k_eval({}) = {}", s, r); @@ -38,10 +38,12 @@ fn test_ngnk_tests() { assert!(lines.len() > 0); let mut test_count = 0; + let mut passed_tests = 0; let mut failed_tests = 0; // TODO add support for these lines - let skiplines = [9, 10, 14, 15, 28, 30, 31, 32, 34]; + // let skiplines = [9, 10, 14, 15, 28, 30, 31, 32, 34]; + let skiplines = []; for (i, l) in lines.iter().enumerate() { if skiplines.contains(&i) { @@ -56,18 +58,25 @@ fn test_ngnk_tests() { } else { test_count += 1; // assert_eq!(k_eval(t[0]), k_eval(t[1])); - let res = k_eval(t[0]); - if res != k_eval(t[1]) { + let res_l = k_eval(t[0]); + let res_r = k_eval(t[1]); + let fail = match (&res_l, &res_r) { + (Ok(l), Ok(r)) if l == r => false, + _ => true, + }; + if fail { failed_tests += 1; println!("Failed test: ({failed_tests}/{test_count}): {}", l); - match res { + match res_l { Ok(k) => println!("{}", k), - Err(e) => println!("{:?}", res), + Err(_) => println!("{:?}", res_l), } + } else { + passed_tests += 1; } } } } - println!("test_count: {}\nfailed_tests: {}", test_count, failed_tests); + println!("\ntest_count: {}\npassed: {}\nfailed: {}", test_count, passed_tests, failed_tests); assert!(failed_tests == 0); } diff --git a/tests/tests.rs b/tests/tests.rs index fa69b1d..836493f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,3 +1,5 @@ +#![feature(assert_matches)] +use std::assert_matches::assert_matches; use std::collections::HashMap; use std::fs::{self, File}; @@ -9,7 +11,7 @@ use roklang::KW::*; fn k_eval(s: &str) -> K { let mut env = Env { names: HashMap::new(), parent: None }; - let r = eval(&mut env, scan(s).unwrap()).unwrap().unwrap_noun(); + let r = eval(&mut env, scan(s).unwrap()).unwrap().unwrap_noun().unwrap(); println!("k_eval({}) = {}", s, r); r } @@ -79,16 +81,17 @@ fn test_split_on() { KW::RB, KW::Noun(K::Bool(1)), ]; + let res = split_on(tokens, KW::SC, KW::RB).unwrap(); assert_eq!( - split_on(tokens, KW::SC, KW::RB), - Ok(( + res, + ( vec![ vec![KW::Noun(K::Bool(1))], vec![KW::Noun(K::Int(Some(2)))], vec![KW::Noun(K::Int(Some(3)))] ], vec![KW::Noun(K::Bool(1)),] - )) + ) ); let tokens = vec![ @@ -105,16 +108,17 @@ fn test_split_on() { KW::RB, KW::Noun(K::Bool(1)), ]; + let res = split_on(tokens, KW::SC, KW::RB).unwrap(); assert_eq!( - split_on(tokens, KW::SC, KW::RB), - Ok(( + res, + ( vec![ vec![KW::Noun(K::Bool(1))], vec![KW::LB, KW::Noun(K::Int(Some(2))), KW::SC, KW::Noun(K::Int(Some(3))), KW::RB], vec![KW::Noun(K::Int(Some(4)))] ], vec![KW::Noun(K::Bool(1)),] - )) + ) ); } @@ -399,18 +403,55 @@ fn test_quoted_symbols() { #[test] fn test_length_errors() { let mut env = Env { names: HashMap::new(), parent: None }; - assert_eq!(eval(&mut env, scan("1 2 3 + 4 5").unwrap()), Err::("length")); - assert_eq!(eval(&mut env, scan("1 2 + 3 4 5").unwrap()), Err::("length")); - assert_eq!(eval(&mut env, scan("1.0 2.0 + 3 4 5").unwrap()), Err::("length")); - assert_eq!(eval(&mut env, scan("1 2 3 - 4 5").unwrap()), Err::("length")); - assert_eq!(eval(&mut env, scan("1 2 - 3 4 5").unwrap()), Err::("length")); - assert_eq!(eval(&mut env, scan("1.0 2.0 - 3 4 5").unwrap()), Err::("length")); - assert_eq!(eval(&mut env, scan("1 2 3 * 4 5").unwrap()), Err::("length")); - assert_eq!(eval(&mut env, scan("1 2 * 3 4 5").unwrap()), Err::("length")); - assert_eq!(eval(&mut env, scan("1.0 2.0 * 3 4 5").unwrap()), Err::("length")); - assert_eq!(eval(&mut env, scan("1 2 3 % 4 5").unwrap()), Err::("length")); - assert_eq!(eval(&mut env, scan("1 2 % 3 4 5").unwrap()), Err::("length")); - assert_eq!(eval(&mut env, scan("1.0 2.0 % 3 4 5").unwrap()), Err::("length")); + + assert_matches!( + eval(&mut env, scan("1 2 3 + 4 5").unwrap()).unwrap_err().downcast_ref::(), + Some(RokError::Length) + ); + assert_matches!( + eval(&mut env, scan("1 2 + 3 4 5").unwrap()).unwrap_err().downcast_ref::(), + Some(RokError::Length) + ); + assert_matches!( + eval(&mut env, scan("1.0 2.0 + 3 4 5").unwrap()).unwrap_err().downcast_ref::(), + Some(RokError::Length) + ); + assert_matches!( + eval(&mut env, scan("1 2 3 - 4 5").unwrap()).unwrap_err().downcast_ref::(), + Some(RokError::Length) + ); + assert_matches!( + eval(&mut env, scan("1 2 - 3 4 5").unwrap()).unwrap_err().downcast_ref::(), + Some(RokError::Length) + ); + assert_matches!( + eval(&mut env, scan("1.0 2.0 - 3 4 5").unwrap()).unwrap_err().downcast_ref::(), + Some(RokError::Length) + ); + assert_matches!( + eval(&mut env, scan("1 2 3 * 4 5").unwrap()).unwrap_err().downcast_ref::(), + Some(RokError::Length) + ); + assert_matches!( + eval(&mut env, scan("1 2 * 3 4 5").unwrap()).unwrap_err().downcast_ref::(), + Some(RokError::Length) + ); + assert_matches!( + eval(&mut env, scan("1.0 2.0 * 3 4 5").unwrap()).unwrap_err().downcast_ref::(), + Some(RokError::Length) + ); + assert_matches!( + eval(&mut env, scan("1 2 3 % 4 5").unwrap()).unwrap_err().downcast_ref::(), + Some(RokError::Length) + ); + assert_matches!( + eval(&mut env, scan("1 2 % 3 4 5").unwrap()).unwrap_err().downcast_ref::(), + Some(RokError::Length) + ); + assert_matches!( + eval(&mut env, scan("1.0 2.0 % 3 4 5").unwrap()).unwrap_err().downcast_ref::(), + Some(RokError::Length) + ); } #[test] @@ -947,7 +988,7 @@ fn test_functions() { assert_eq!(eval(&mut env, scan("f 2").unwrap()).unwrap(), Noun(K::Int(Some(4)))); assert_eq!( - eval(&mut env, scan("{x*2}!5").unwrap()).unwrap().unwrap_noun(), + eval(&mut env, scan("{x*2}!5").unwrap()).unwrap().unwrap_noun().unwrap(), K::IntArray(arr!([0, 2, 4, 6, 8i64])) ); } @@ -1484,23 +1525,23 @@ fn test_promote_nouns() { let mut env = Env { names: HashMap::new(), parent: None }; println!("promote_nouns(1, 1 2 3)"); - let l = eval(&mut env, scan("1").unwrap()).unwrap().unwrap_noun(); - let r = eval(&mut env, scan("1 2 3").unwrap()).unwrap().unwrap_noun(); + let l = eval(&mut env, scan("1").unwrap()).unwrap().unwrap_noun().unwrap(); + let r = eval(&mut env, scan("1 2 3").unwrap()).unwrap().unwrap_noun().unwrap(); assert_eq!(promote_nouns(l, r.clone()), (K::IntArray(arr!([1, 1, 1i64])), r)); println!("promote_nouns(1 2 3, 1)"); - let l = eval(&mut env, scan("1 2 3").unwrap()).unwrap().unwrap_noun(); - let r = eval(&mut env, scan("1").unwrap()).unwrap().unwrap_noun(); + let l = eval(&mut env, scan("1 2 3").unwrap()).unwrap().unwrap_noun().unwrap(); + let r = eval(&mut env, scan("1").unwrap()).unwrap().unwrap_noun().unwrap(); assert_eq!(promote_nouns(l.clone(), r), (l, K::IntArray(arr!([1, 1, 1i64])))); println!("promote_nouns(1, 1 2 3.0)"); - let l = eval(&mut env, scan("1").unwrap()).unwrap().unwrap_noun(); - let r = eval(&mut env, scan("1 2 3.0").unwrap()).unwrap().unwrap_noun(); + let l = eval(&mut env, scan("1").unwrap()).unwrap().unwrap_noun().unwrap(); + let r = eval(&mut env, scan("1 2 3.0").unwrap()).unwrap().unwrap_noun().unwrap(); assert_eq!(promote_nouns(l, r.clone()), (K::FloatArray(arr!([1.0, 1.0, 1.0f64])), r)); println!("promote_nouns(1 2 3.0, 1)"); - let l = eval(&mut env, scan("1 2 3.0").unwrap()).unwrap().unwrap_noun(); - let r = eval(&mut env, scan("1").unwrap()).unwrap().unwrap_noun(); + let l = eval(&mut env, scan("1 2 3.0").unwrap()).unwrap().unwrap_noun().unwrap(); + let r = eval(&mut env, scan("1").unwrap()).unwrap().unwrap_noun().unwrap(); assert_eq!(promote_nouns(l.clone(), r), (l, K::FloatArray(arr!([1.0, 1.0, 1.0f64])))); } @@ -1510,7 +1551,7 @@ fn test_split_strings() { let mut env = Env { names: HashMap::new(), parent: None }; //TODO fix parse error of 2 or more adverb chain - let res = eval(&mut env, scan(r#"","\'("1,2";"3,4")"#).unwrap()).unwrap().unwrap_noun(); + let res = eval(&mut env, scan(r#"","\'("1,2";"3,4")"#).unwrap()).unwrap().unwrap_noun().unwrap(); assert_eq!( res, @@ -1525,7 +1566,7 @@ fn test_split_strings() { fn test_eval_verb() { let mut env = Env { names: HashMap::new(), parent: None }; - let res = eval(&mut env, scan(r#"."42""#).unwrap()).unwrap().unwrap_noun(); + let res = eval(&mut env, scan(r#"."42""#).unwrap()).unwrap().unwrap_noun().unwrap(); assert_eq!(res, K::Int(Some(42))); } @@ -1534,10 +1575,10 @@ fn test_eval_verb() { fn test_concat() { let mut env = Env { names: HashMap::new(), parent: None }; - let res = eval(&mut env, scan("2,3").unwrap()).unwrap().unwrap_noun(); + let res = eval(&mut env, scan("2,3").unwrap()).unwrap().unwrap_noun().unwrap(); assert_eq!(res, K::IntArray(arr!([2, 3i64]))); - let res = eval(&mut env, scan("1 2,3 4.0").unwrap()).unwrap().unwrap_noun(); + let res = eval(&mut env, scan("1 2,3 4.0").unwrap()).unwrap().unwrap_noun().unwrap(); assert_eq!(res, K::FloatArray(arr!([1., 2., 3., 4.0f64]))); } @@ -1545,13 +1586,13 @@ fn test_concat() { fn test_grade() { let mut env = Env { names: HashMap::new(), parent: None }; - let res = eval(&mut env, scan("< 3 2 1").unwrap()).unwrap().unwrap_noun(); + let res = eval(&mut env, scan("< 3 2 1").unwrap()).unwrap().unwrap_noun().unwrap(); assert_eq!(res, K::IntArray(arr!([2, 1, 0i64]))); - let res = eval(&mut env, scan("< 3.0 2.5 1").unwrap()).unwrap().unwrap_noun(); + let res = eval(&mut env, scan("< 3.0 2.5 1").unwrap()).unwrap().unwrap_noun().unwrap(); assert_eq!(res, K::IntArray(arr!([2, 1, 0i64]))); - let res = eval(&mut env, scan("> 3 2 1").unwrap()).unwrap().unwrap_noun(); + let res = eval(&mut env, scan("> 3 2 1").unwrap()).unwrap().unwrap_noun().unwrap(); assert_eq!(res, K::IntArray(arr!([0i64, 1, 2]))); } @@ -1626,13 +1667,12 @@ fn test_comparisons() { assert_eq!(k_eval("1<+`a`b!(1 1 1;2 3 4)"), k_eval("+`a`b!(0 0 0;1 1 1)")); assert_eq!(k_eval("(+`a`b!(1 1 1;2 3 4))<3"), k_eval("+`a`b!(1 1 1;1 0 0)")); - println!("test_comparisons() numbers"); assert_eq!(k_eval("1>2"), k_eval("0")); assert_eq!(k_eval("1>1 2 3"), k_eval("0 0 0")); assert_eq!(k_eval("1.0>2"), k_eval("0")); - assert_eq!(k_eval("0N>1"), k_eval("0")); + assert_eq!(k_eval("0N>1"), k_eval("0")); assert_eq!(k_eval("0n>1"), k_eval("0")); assert_eq!(k_eval("0n 0n>1"), k_eval("0 0")); // 0N>0n is an annoyance to implement because of promote_nouns(). @@ -1662,5 +1702,4 @@ fn test_comparisons() { println!("test_comparisons() tables"); assert_eq!(k_eval("1>+`a`b!(1 1 1;2 3 4)"), k_eval("+`a`b!(0 0 0;1 1 1)")); assert_eq!(k_eval("(+`a`b!(1 1 1;2 3 4))>3"), k_eval("+`a`b!(1 1 1;1 0 0)")); - }