From 7b6365a70f5014b9db4557e9e1ecf4bcf9810f17 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sun, 26 Oct 2025 08:41:15 +1000 Subject: [PATCH 01/18] add anyhow thiserror deps --- Cargo.lock | 68 ++++++++++++++++++++++++++++++++++++++---------------- Cargo.toml | 2 ++ 2 files changed, 50 insertions(+), 20 deletions(-) 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" From 305f14b45c7ab5e62154e91920dd3f68f4393965 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sun, 26 Oct 2025 08:55:30 +1000 Subject: [PATCH 02/18] fix: clippy --- src/lib.rs | 61 ++++++++++++++++++++++++--------------------------- src/verbs.rs | 46 +++++++++++++++----------------------- tests/ngnk.rs | 5 ++--- 3 files changed, 49 insertions(+), 63 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1103035..c42aa43 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ 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}; mod verbs; @@ -230,7 +230,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) @@ -328,10 +328,7 @@ impl Hash for K { .f64() .unwrap() .into_iter() - .map(|f| match f { - Some(f) => Some(f.to_string()), - None => None, - }) + .map(|f| f.map(|f| f.to_string())) .collect(); v.hash(state) } @@ -916,51 +913,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())), @@ -1063,7 +1060,7 @@ pub struct Env { fn resolve_names(env: Env, fragment: (KW, KW, KW, KW)) -> Result<(KW, KW, KW, KW), &'static str> { //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 { diff --git a/src/verbs.rs b/src/verbs.rs index afcde8b..fbc508f 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -1,8 +1,6 @@ use crate::*; 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 { match x { @@ -26,7 +24,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)) @@ -181,7 +179,7 @@ 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 { + if lengths.len() > 2 || lengths.is_empty() { Err("length") } else { // if d.iter().map(|(_k, v)| v.len()).all_equal() { @@ -212,20 +210,15 @@ pub fn v_flip(x: K) -> Result { panic!("type error?") } } - K::Bool(b) => { - Series::new(&k.to_string(), std::iter::repeat(*b).take(*len).collect::>()) - } + K::Bool(b) => 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::>()) + Series::new(&k.to_string(), repeat_n(*i, *len).collect::>()) } K::Int(None) => Series::full_null(&k.to_string(), *len, &DataType::Int64), - K::Float(f) => { - Series::new(&k.to_string(), std::iter::repeat(*f).take(*len).collect::>()) + K::Float(f) => Series::new(&k.to_string(), repeat_n(*f, *len).collect::>()), + K::Symbol(s) => { + 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?"), @@ -334,7 +327,7 @@ pub fn v_first(x: K) -> Result { 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::CharArray(a) => Ok(K::Char(a.chars().next().unwrap_or(' '))), K::List(l) => Ok(l.first().unwrap().clone()), _ => Err("nyi"), } @@ -587,7 +580,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)) => { @@ -680,7 +673,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)) => { @@ -781,8 +774,8 @@ pub fn v_concat(x: K, y: K) -> Result { (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())), + (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())), _ => todo!("nyi v_concat() other cases {}, {}", x, y), } } @@ -803,7 +796,7 @@ pub fn v_drop(x: K, y: K) -> Result { 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. @@ -826,9 +819,9 @@ 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 { 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"), + K::Symbol(s) if s == "c" => todo!("cast to string"), + K::Symbol(s) if s == "i" => todo!("cast to int"), + K::Symbol(s) if s == "f" => todo!("cast to float"), _ => Err("type"), } } @@ -1200,7 +1193,6 @@ pub fn v_each(env: &mut Env, v: KW, x: K) -> Result { f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(x).map(|v| { let r: Vec = v .iter() - .cloned() .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()) @@ -1452,7 +1444,6 @@ pub fn v_d_eachright(env: &mut Env, v: KW, x: K, y: K) -> Result k_to_vec(y).map(|v| { let r: Vec = v .iter() - .cloned() .map(|y| { eval( env, @@ -1475,7 +1466,6 @@ pub fn v_d_eachleft(env: &mut Env, v: KW, x: K, y: K) -> Result f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(x).map(|v| { let r: Vec = v .iter() - .cloned() .map(|x| { eval( env, @@ -1529,7 +1519,7 @@ pub fn v_makedict(l: K, r: K) -> Result { )))) } _ => { - if s.len() == 0 { + if s.is_empty() { Err("length") } else if s.len() == 1 { Ok(K::Dictionary(IndexMap::from([(strip_quotes(s.get(0).unwrap().to_string()), r)]))) diff --git a/tests/ngnk.rs b/tests/ngnk.rs index a88727c..60ced4d 100644 --- a/tests/ngnk.rs +++ b/tests/ngnk.rs @@ -1,7 +1,6 @@ -use roklang::KW::*; 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 { @@ -62,7 +61,7 @@ fn test_ngnk_tests() { println!("Failed test: ({failed_tests}/{test_count}): {}", l); match res { Ok(k) => println!("{}", k), - Err(e) => println!("{:?}", res), + Err(_) => println!("{:?}", res), } } } From c7679c80e3994ea665d820ab76df5b2307c4e2de Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sun, 26 Oct 2025 11:00:49 +1000 Subject: [PATCH 03/18] feat: anyhow --- src/lib.rs | 183 +++++++++++++++-------------- src/verbs.rs | 303 +++++++++++++++++++++++++------------------------ tests/tests.rs | 25 ++-- 3 files changed, 264 insertions(+), 247 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c42aa43..54faa22 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, 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\n")] + Domain, + #[error("'error\n{0}\n")] + Error(String), + #[error("'length\n")] + Length, + #[error("'nyi\n")] + NYI, + #[error("'parse\n{0}\n")] + Parse(&'static str), + #[error("'rank\n")] + Rank, + #[error("'type\n")] + 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, ...]) ???"), @@ -324,12 +343,8 @@ impl Hash for K { v.hash(state) } K::FloatArray(f) => { - let v: Vec> = f - .f64() - .unwrap() - .into_iter() - .map(|f| f.map(|f| f.to_string())) - .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), @@ -350,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())) @@ -375,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, @@ -402,18 +417,18 @@ 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()), } } } @@ -479,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), @@ -528,7 +543,7 @@ pub fn k_to_vec(k: K) -> Result, &'static str> { _ => todo!("k_to_vec({})", k), } } -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() @@ -584,26 +599,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)>; @@ -733,7 +748,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 ], @@ -823,7 +838,7 @@ pub fn apply_primitive(env: &mut Env, v: &str, l: Option, r: KW) -> Result 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 } => { @@ -841,7 +856,7 @@ pub fn apply_adverb(a: &str, l: KW) -> Result { _ => 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(); @@ -874,7 +889,7 @@ pub fn apply_function(env: &mut Env, f: KW, arg: KW) -> Result let exprs: Vec = exprs.iter().map(|sentence| eval(env, sentence.clone()).unwrap()).collect(); match exprs.len().cmp(&args.len()) { - Ordering::Greater => Err("rank error"), + Ordering::Greater => Err(RokError::Rank.into()), Ordering::Less => todo!("currying: args: {:?}", args), Ordering::Equal => { let mut e = Env { names: HashMap::new(), parent: Some(Box::new(env.clone())) }; @@ -893,7 +908,7 @@ pub fn apply_function(env: &mut Env, f: KW, arg: KW) -> Result match exprs.len() { 0 | 1 => todo!("currying: exprs: {:?}", exprs), 2 => apply_primitive(env, &name, Some(exprs[0].clone()), exprs[1].clone()), - _ => Err("rank error"), + _ => Err(RokError::Rank.into()), } } _ => panic!("impossible"), @@ -1029,24 +1044,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()) } } } @@ -1058,7 +1073,7 @@ 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 = [fragment.0.clone(), fragment.1.clone(), fragment.2.clone(), fragment.3.clone()]; let mut resolved_words = Vec::new(); @@ -1089,7 +1104,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(); @@ -1097,11 +1112,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,15 +1178,15 @@ pub fn eval(env: &mut Env, sentence: Vec) -> Result { vec_to_list([vec![KW::Noun(n1), KW::Noun(n2)], nouns.into()].concat()).unwrap(), )]) } 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() { @@ -1197,7 +1212,7 @@ pub fn eval(env: &mut Env, sentence: Vec) -> Result { Ok(r[0].clone()) } else { debug!("{:?}", r); - Err("parse error") + Err(RokError::Parse("").into()) } } @@ -1209,7 +1224,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] @@ -1222,7 +1237,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 @@ -1236,11 +1251,11 @@ fn parse_cond(env: &mut Env, kw: KW) -> Result { } } -pub fn scan(code: &str) -> Result, &'static str> { +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; @@ -1369,7 +1384,7 @@ pub fn scan_pass1(code: &str) -> Result, &'static str> { words.push(k); skip = j; } - _ => return Err("TODO: scan()"), + _ => return Err(RokError::Error("TODO: scan()".into()).into()), }; } Ok(words) @@ -1377,7 +1392,7 @@ pub fn scan_pass1(code: &str) -> Result, &'static str> { pub fn split_on( tokens: Vec, delim: KW, end_tok: KW, -) -> Result<(Vec>, Vec), &'static str> { +) -> 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; @@ -1385,7 +1400,7 @@ 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; @@ -1418,7 +1433,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{_} @@ -1462,7 +1477,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]" @@ -1474,7 +1489,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) { @@ -1497,10 +1512,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 @@ -1514,10 +1529,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"] @@ -1545,7 +1560,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 // @@ -1586,11 +1601,11 @@ 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") @@ -1612,7 +1627,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 { @@ -1620,11 +1635,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._`] @@ -1664,10 +1679,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() @@ -1704,7 +1719,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))) { @@ -1714,7 +1729,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() @@ -1737,13 +1752,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 @@ -1803,6 +1818,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 fbc508f..bc84998 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -1,19 +1,20 @@ use crate::*; +use anyhow::Result; use rand::distributions::{Distribution, Uniform}; 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()) } - _ => 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 { @@ -60,11 +61,11 @@ pub fn v_group(x: K) -> Result { } K::Table(_df) => todo!("v_group(Table(_))"), - _ => 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,12 +95,12 @@ pub fn v_equal(x: K, y: K) -> Result { } (_, K::Table(_)) => todo!("table"), (K::Table(_), _) => todo!("table"), - _ => Err("nyi"), + _ => 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))), @@ -120,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()), } } @@ -135,7 +136,7 @@ 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 = @@ -144,15 +145,15 @@ pub fn v_reshape(l: K, r: K) -> Result { K::Bool(b) => { reshape_atom_by_type!(b, K::BoolArray, rev_shape) } - K::Int(None) => Err("nyi"), + 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("nyi"), - K::Symbol(_) => Err("nyi"), + K::Char(_) => Err(RokError::NYI.into()), + K::Symbol(_) => Err(RokError::NYI.into()), K::SymbolArray(_) | K::BoolArray(_) @@ -162,25 +163,25 @@ pub fn v_reshape(l: K, r: K) -> Result { | K::List(_) | K::Dictionary(_) | K::Table(_) - | K::Nil => Err("nyi"), + | 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.is_empty() { - Err("length") + Err(RokError::Length.into()) } else { // if d.iter().map(|(_k, v)| v.len()).all_equal() { let len = lengths.last().unwrap(); @@ -206,7 +207,7 @@ pub fn v_flip(x: K) -> Result { let vs: Vec = v.iter().map(|s| s.to_string()).collect(); Series::new(&k.to_string(), vs.clone()) } else { - // Err("type") + // Err(RokError::Type.into()) panic!("type error?") } } @@ -314,11 +315,11 @@ 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 { +pub fn v_first(x: K) -> Result { match x { K::Bool(_) => Ok(x), K::Int(_) => Ok(x), @@ -329,11 +330,11 @@ pub fn v_first(x: K) -> Result { 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("nyi"), + _ => 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( @@ -342,8 +343,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 { todo!("implement sqrt") } +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)), @@ -351,7 +352,7 @@ 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::Table(df) => Ok(K::SymbolArray( @@ -360,10 +361,10 @@ pub fn v_keys_odometer(x: K) -> Result { .unwrap(), )), K::IntArray(_) => todo!("implement odometer"), - _ => Err("nyi"), + _ => 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)), @@ -372,7 +373,7 @@ pub fn v_mod(l: K, r: K) -> Result { } } -pub fn v_where(x: K) -> Result { +pub fn v_where(x: K) -> Result { match x { K::BoolArray(b) => { let indices: Vec = b @@ -385,11 +386,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), @@ -408,11 +409,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)), @@ -438,14 +439,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::List(_l), K::List(_r)) => Err(RokError::NYI.into()), + (K::Dictionary(_l), K::Dictionary(_r)) => Err(RokError::NYI.into()), (_, K::Table(_)) => todo!("table"), (K::Table(_), _) => todo!("table"), - _ => Err("nyi - wtf"), + _ => 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)))), @@ -470,15 +471,15 @@ 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::List(_l), K::List(_r)) => Err(RokError::NYI.into()), + (K::Dictionary(_l), K::Dictionary(_r)) => Err(RokError::NYI.into()), (_, K::Table(_)) => todo!("table"), (K::Table(_), _) => todo!("table"), - _ => Err("nyi - wtf"), + _ => Err(RokError::Error("nyi - wtf".into()).into()), }) } -pub fn v_asc(x: K) -> Result { +pub fn v_asc(x: K) -> Result { match x { K::BoolArray(x) => { let mut map: BTreeMap, Vec> = BTreeMap::new(); @@ -523,15 +524,15 @@ pub fn v_asc(x: K) -> Result { let v: Vec = map.values().flat_map(|v| v.iter().map(|v| *v as i64)).collect(); Ok(K::IntArray(arr!(v))) } - _ => Err("nyi"), + _ => Err(RokError::NYI.into()), } } -pub fn v_desc(x: K) -> Result { +pub fn v_desc(x: K) -> Result { v_reverse(v_asc(x).unwrap()) /* 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)), @@ -624,11 +625,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)), @@ -717,14 +718,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]))), @@ -742,11 +743,11 @@ 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]) @@ -780,18 +781,18 @@ pub fn v_concat(x: K, y: K) -> Result { } } -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), @@ -808,35 +809,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" => todo!("cast to string"), K::Symbol(s) if s == "i" => todo!("cast to int"), K::Symbol(s) if s == "f" => todo!("cast to float"), - _ => Err("type"), + _ => 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 { @@ -847,11 +848,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(); @@ -867,10 +868,10 @@ pub fn v_rand(l: K, r: K) -> Result { 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 "), @@ -892,12 +893,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 { @@ -919,7 +920,7 @@ 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(_) => { @@ -928,7 +929,7 @@ pub fn v_at(l: K, r: K) -> Result { K::List(_v) => todo!("v_at"), K::Dictionary(_d) => todo!("v_at"), K::Table(_df) => todo!("v_at"), - _ => Err("type"), + _ => Err(RokError::Type.into()), }, K::Bool(i) => { // TODO remove this code duplication of K::Int(_) case below @@ -944,7 +945,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"), @@ -1072,7 +1073,7 @@ pub fn v_at(l: K, r: K) -> Result { K::Table(_df) => todo!("nyi"), _ => todo!("nyi"), }, - _ => Err("type"), + _ => Err(RokError::Type.into()), }, K::SymbolArray(s) => { let keys: Vec = s @@ -1089,26 +1090,26 @@ pub fn v_at(l: K, r: K) -> Result { } // 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()) } - _ => 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) => { @@ -1116,30 +1117,30 @@ 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))), @@ -1149,7 +1150,7 @@ pub fn v_iota(x: K) -> Result { _ => todo!("v_iota variants: {}", x), } } -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())), @@ -1163,10 +1164,10 @@ pub fn v_sum(x: K) -> Result { } } } -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 { Ok(l + v_sum(r).unwrap()) } // 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())), @@ -1174,9 +1175,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(_) @@ -1188,7 +1189,7 @@ 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 @@ -1199,13 +1200,13 @@ pub fn v_each(env: &mut Env, v: KW, x: K) -> Result { .collect(); promote_num(r.clone()).unwrap_or(K::List(r)) }), - _ => Err("type"), + _ => 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 { todo!("each") } // 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), @@ -1214,13 +1215,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 @@ -1235,13 +1236,13 @@ 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(); @@ -1253,7 +1254,7 @@ pub fn v_fixedpoint(env: &mut Env, v: KW, x: K) -> Result { 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(|_| { @@ -1271,11 +1272,11 @@ 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| { @@ -1290,13 +1291,13 @@ pub fn v_fold(env: &mut Env, v: KW, x: K) -> Result { }); match r { Some(k) => Ok(k.clone()), - None => Err("TODO not sure what this error case is"), + None => Err(RokError::Error("TODO not sure what this error case is".into()).into()), } }), - _ => 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( @@ -1310,11 +1311,11 @@ pub fn v_d_fold(env: &mut Env, v: KW, x: K, y: K) -> Result { .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), @@ -1323,13 +1324,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 @@ -1344,13 +1345,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,10 +1378,10 @@ 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( @@ -1399,11 +1400,11 @@ pub fn v_d_scan(env: &mut Env, v: KW, x: K, y: K) -> Result { let rest: K = v_drop(K::Int(Some(1)), y).unwrap(); v_scan(env, v, v_concat(first, rest).unwrap()) } - _ => 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]; @@ -1426,20 +1427,20 @@ pub fn v_eachprior(env: &mut Env, v: KW, x: K) -> Result { let r: Vec = vec![first.clone()].into_iter().chain(r).collect(); promote_num(r.clone()).unwrap_or(K::List(r)) }), - _ => Err("type"), + _ => Err(RokError::Type.into()), } } -pub fn v_eachprior_d_or_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { +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(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { +pub fn v_eachprior_d(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { todo!("v_eachprior_d()") } -pub fn v_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { +pub fn v_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { todo!("v_windows()") } -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 @@ -1458,10 +1459,10 @@ pub fn v_d_eachright(env: &mut Env, v: KW, x: K, y: K) -> Result Err("type"), + _ => 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 @@ -1480,7 +1481,7 @@ pub fn v_d_eachleft(env: &mut Env, v: KW, x: K, y: K) -> Result .collect(); promote_num(r.clone()).unwrap_or(K::List(r)) }), - _ => Err("type"), + _ => Err(RokError::Type.into()), } } @@ -1491,7 +1492,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 { @@ -1508,7 +1509,7 @@ 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(_) => { @@ -1520,7 +1521,7 @@ pub fn v_makedict(l: K, r: K) -> Result { } _ => { if s.is_empty() { - Err("length") + Err(RokError::Length.into()) } else if s.len() == 1 { Ok(K::Dictionary(IndexMap::from([(strip_quotes(s.get(0).unwrap().to_string()), r)]))) } else { @@ -1538,8 +1539,8 @@ pub fn v_makedict(l: K, r: K) -> Result { } } -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 { todo!(": monad") } +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( @@ -1569,7 +1570,7 @@ pub fn v_d_colon(env: &mut Env, l: K, r: KW) -> Result { _ => todo!("no extension"), } } else { - Err("path does not exist") + Err(RokError::Error("path does not exist".into()).into()) } } (K::Name(n), r) => { @@ -1581,7 +1582,7 @@ pub fn v_d_colon(env: &mut Env, l: K, r: KW) -> Result { } } -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/tests.rs b/tests/tests.rs index fa69b1d..cebc6ee 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -399,18 +399,19 @@ 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")); + //TODO this doesn't build, anyhow::Error does not impl PartialEq + assert_eq!(eval(&mut env, scan("1 2 3 + 4 5").unwrap()), Err::(RokError::Length)); + assert_eq!(eval(&mut env, scan("1 2 + 3 4 5").unwrap()), Err::(RokError::Length)); + assert_eq!(eval(&mut env, scan("1.0 2.0 + 3 4 5").unwrap()), Err::(RokError::Length)); + assert_eq!(eval(&mut env, scan("1 2 3 - 4 5").unwrap()), Err::(RokError::Length)); + assert_eq!(eval(&mut env, scan("1 2 - 3 4 5").unwrap()), Err::(RokError::Length)); + assert_eq!(eval(&mut env, scan("1.0 2.0 - 3 4 5").unwrap()), Err::(RokError::Length)); + assert_eq!(eval(&mut env, scan("1 2 3 * 4 5").unwrap()), Err::(RokError::Length)); + assert_eq!(eval(&mut env, scan("1 2 * 3 4 5").unwrap()), Err::(RokError::Length)); + assert_eq!(eval(&mut env, scan("1.0 2.0 * 3 4 5").unwrap()), Err::(RokError::Length)); + assert_eq!(eval(&mut env, scan("1 2 3 % 4 5").unwrap()), Err::(RokError::Length)); + assert_eq!(eval(&mut env, scan("1 2 % 3 4 5").unwrap()), Err::(RokError::Length)); + assert_eq!(eval(&mut env, scan("1.0 2.0 % 3 4 5").unwrap()), Err::(RokError::Length)); } #[test] From 05bcba67ff523215e174209efc4f793374ad966e Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sun, 26 Oct 2025 11:32:29 +1000 Subject: [PATCH 04/18] test: fix tests --- tests/ngnk.rs | 16 +++++++--- tests/tests.rs | 82 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 71 insertions(+), 27 deletions(-) diff --git a/tests/ngnk.rs b/tests/ngnk.rs index 60ced4d..2a9fae0 100644 --- a/tests/ngnk.rs +++ b/tests/ngnk.rs @@ -1,9 +1,10 @@ use roklang::*; +use anyhow::Result; use std::collections::HashMap; 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(); @@ -55,13 +56,18 @@ 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(_) => println!("{:?}", res), + Err(_) => println!("{:?}", res_l), } } } diff --git a/tests/tests.rs b/tests/tests.rs index cebc6ee..8cbaa72 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}; @@ -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,19 +403,55 @@ fn test_quoted_symbols() { #[test] fn test_length_errors() { let mut env = Env { names: HashMap::new(), parent: None }; - //TODO this doesn't build, anyhow::Error does not impl PartialEq - assert_eq!(eval(&mut env, scan("1 2 3 + 4 5").unwrap()), Err::(RokError::Length)); - assert_eq!(eval(&mut env, scan("1 2 + 3 4 5").unwrap()), Err::(RokError::Length)); - assert_eq!(eval(&mut env, scan("1.0 2.0 + 3 4 5").unwrap()), Err::(RokError::Length)); - assert_eq!(eval(&mut env, scan("1 2 3 - 4 5").unwrap()), Err::(RokError::Length)); - assert_eq!(eval(&mut env, scan("1 2 - 3 4 5").unwrap()), Err::(RokError::Length)); - assert_eq!(eval(&mut env, scan("1.0 2.0 - 3 4 5").unwrap()), Err::(RokError::Length)); - assert_eq!(eval(&mut env, scan("1 2 3 * 4 5").unwrap()), Err::(RokError::Length)); - assert_eq!(eval(&mut env, scan("1 2 * 3 4 5").unwrap()), Err::(RokError::Length)); - assert_eq!(eval(&mut env, scan("1.0 2.0 * 3 4 5").unwrap()), Err::(RokError::Length)); - assert_eq!(eval(&mut env, scan("1 2 3 % 4 5").unwrap()), Err::(RokError::Length)); - assert_eq!(eval(&mut env, scan("1 2 % 3 4 5").unwrap()), Err::(RokError::Length)); - assert_eq!(eval(&mut env, scan("1.0 2.0 % 3 4 5").unwrap()), Err::(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) + ); + 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] @@ -1627,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(). @@ -1663,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)")); - } From c09c2f854fa2a4fb56ad47ce5882e24d5a8ba1a8 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sun, 26 Oct 2025 11:44:28 +1000 Subject: [PATCH 05/18] test: wip fix panics --- src/lib.rs | 20 ++++++++++---------- tests/ngnk.rs | 5 +++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 54faa22..7fdd7b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,19 +81,19 @@ pub enum KW /* KWords */ { #[derive(Error, Debug)] pub enum RokError { - #[error("'domain\n")] + #[error("'domain")] Domain, - #[error("'error\n{0}\n")] + #[error("'error\n{0}")] Error(String), - #[error("'length\n")] + #[error("'length")] Length, - #[error("'nyi\n")] + #[error("'nyi")] NYI, - #[error("'parse\n{0}\n")] + #[error("'parse\n{0}")] Parse(&'static str), - #[error("'rank\n")] + #[error("'rank")] Rank, - #[error("'type\n")] + #[error("'type")] Type, } @@ -1329,12 +1329,12 @@ pub fn scan_pass1(code: &str) -> Result> { } } '"' => { - 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; } @@ -1380,7 +1380,7 @@ pub fn scan_pass1(code: &str) -> Result> { '\'' | '/' | '\\' => 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; } diff --git a/tests/ngnk.rs b/tests/ngnk.rs index 2a9fae0..640bdbd 100644 --- a/tests/ngnk.rs +++ b/tests/ngnk.rs @@ -8,7 +8,7 @@ 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); @@ -41,7 +41,8 @@ fn test_ngnk_tests() { 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) { From 3cdfb091bcfaf5bb306243ad49562b3bde75a11c Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sun, 26 Oct 2025 11:54:45 +1000 Subject: [PATCH 06/18] wip: fix panics --- src/lib.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7fdd7b1..359d4b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -815,7 +815,9 @@ pub fn apply_primitive(env: &mut Env, v: &str, l: Option, r: KW) -> Result { - 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))) @@ -830,9 +832,9 @@ 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()), } } }, @@ -1251,9 +1253,7 @@ fn parse_cond(env: &mut Env, kw: KW) -> Result { } } -pub fn scan(code: &str) -> Result> { - 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> { // First tokenization pass. @@ -1390,9 +1390,7 @@ pub fn scan_pass1(code: &str) -> Result> { Ok(words) } -pub fn split_on( - tokens: Vec, delim: KW, end_tok: KW, -) -> Result<(Vec>, Vec)> { +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; @@ -1400,7 +1398,8 @@ pub fn split_on( let mut start = 0; for i in 0..tokens.len() { if depth < 0 { - return Err(RokError::Parse("mismatched parens, brackets or curly brackets").into()); // TODO better message + return Err(RokError::Parse("mismatched parens, brackets or curly brackets").into()); + // TODO better message } if start > i { continue; @@ -1601,7 +1600,12 @@ pub fn scan_number(code: &str) -> Result<(usize, KW)> { _ => Ok((l, KW::Noun(promote_num(nums).unwrap()))), } } else { - Err(RokError::Error("syntax error: a sentence starting with a digit must contain a valid number".into()).into()) + Err( + RokError::Error( + "syntax error: a sentence starting with a digit must contain a valid number".into(), + ) + .into(), + ) } } From 681456acd7dfec9b45a43cf0a1580b4e8b48743d Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sun, 26 Oct 2025 22:17:11 +1000 Subject: [PATCH 07/18] wip: fix panics --- src/lib.rs | 23 ++++++++++++++++------- src/verbs.rs | 27 +++++++++++++-------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 359d4b6..63b930b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,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?") } @@ -440,6 +440,13 @@ impl KW { _ => panic!("not a noun"), } } + //TODO get rid of panic version above + pub fn unwrap_noun_result(&self) -> Result { + match self { + KW::Noun(n) => Ok(n.clone()), + _ => Err(RokError::Error("not a noun".into()).into()), + } + } } impl fmt::Display for KW { @@ -1023,11 +1030,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)), } }; } @@ -1177,7 +1186,7 @@ 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(RokError::Error("invalid list syntax".into()).into()) diff --git a/src/verbs.rs b/src/verbs.rs index bc84998..241fed6 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -1191,15 +1191,18 @@ pub fn v_d_bang(l: K, r: 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() - .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)) - }), + eval(env, vec![f.clone(), KW::Noun(y.clone())])?.unwrap_noun_result()) + .collect::>>() + })? { + Ok(r) => Ok(promote_num(r.clone()).unwrap_or(K::List(r))), + Err(e) => Err(e), + } + } _ => Err(RokError::Type.into()), } } @@ -1433,12 +1436,8 @@ pub fn v_eachprior(env: &mut Env, v: KW, x: K) -> Result { 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(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { - todo!("v_eachprior_d()") -} -pub fn v_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { - todo!("v_windows()") -} +pub fn v_eachprior_d(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { todo!("v_eachprior_d()") } +pub fn v_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { todo!("v_windows()") } pub fn v_d_eachright(env: &mut Env, v: KW, x: K, y: K) -> Result { match v { From 02603dc8e791a3015812435bc2e32e4fb4c58511 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Mon, 27 Oct 2025 21:32:34 +1000 Subject: [PATCH 08/18] fix: unwrap_noun result --- src/lib.rs | 9 +-------- src/verbs.rs | 55 +++++++++++++++++++++++--------------------------- tests/tests.rs | 34 +++++++++++++++---------------- 3 files changed, 43 insertions(+), 55 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 63b930b..d94b3de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -434,14 +434,7 @@ impl TryInto> for K { } impl KW { - pub fn unwrap_noun(&self) -> K { - match self { - KW::Noun(n) => n.clone(), - _ => panic!("not a noun"), - } - } - //TODO get rid of panic version above - pub fn unwrap_noun_result(&self) -> Result { + pub fn unwrap_noun(&self) -> Result { match self { KW::Noun(n) => Ok(n.clone()), _ => Err(RokError::Error("not a noun".into()).into()), diff --git a/src/verbs.rs b/src/verbs.rs index 241fed6..d07f585 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -8,7 +8,7 @@ pub fn v_imat(x: K) -> Result { 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(RokError::Type.into()), } @@ -1098,7 +1098,7 @@ pub fn v_eval(x: K) -> Result { 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(RokError::NYI.into()), } @@ -1159,12 +1159,15 @@ 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 { @@ -1196,7 +1199,7 @@ pub fn v_each(env: &mut Env, v: KW, x: K) -> Result { 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_noun_result()) + eval(env, vec![f.clone(), KW::Noun(y.clone())])?.unwrap_noun()) .collect::>>() })? { Ok(r) => Ok(promote_num(r.clone()).unwrap_or(K::List(r))), @@ -1250,7 +1253,7 @@ pub fn v_fixedpoint(env: &mut Env, v: KW, x: K) -> Result { // 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); } @@ -1263,9 +1266,8 @@ pub fn v_scan_fixedpoint(env: &mut Env, v: KW, x: K) -> Result { 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), @@ -1291,6 +1293,7 @@ pub fn v_fold(env: &mut Env, v: KW, x: K) -> Result { ) .unwrap() .unwrap_noun() + .unwrap() }); match r { Some(k) => Ok(k.clone()), @@ -1303,16 +1306,8 @@ pub fn v_fold(env: &mut Env, v: KW, x: 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(RokError::Type.into()) } @@ -1373,7 +1368,8 @@ pub fn v_scan(env: &mut Env, v: KW, x: K) -> Result { ], ) .unwrap() - .unwrap_noun(), + .unwrap_noun() + .unwrap(), ) } match promote_num(result.clone()) { @@ -1391,17 +1387,13 @@ pub fn v_d_scan(env: &mut Env, v: KW, x: K, y: K) -> Result { env, vec![ f.clone(), - KW::FuncArgs(vec![ - vec![KW::Noun(x.clone())], - vec![KW::Noun(v_first(y.clone()).unwrap())], - ]), + KW::FuncArgs(vec![vec![KW::Noun(x.clone())], vec![KW::Noun(v_first(y.clone())?)]]), ], - ) - .unwrap() - .unwrap_noun(); + )? + .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(RokError::Type.into()), } @@ -1425,6 +1417,7 @@ pub fn v_eachprior(env: &mut Env, v: KW, x: K) -> Result { ) .unwrap() .unwrap_noun() + .unwrap() }) .collect(); let r: Vec = vec![first.clone()].into_iter().chain(r).collect(); @@ -1454,6 +1447,7 @@ pub fn v_d_eachright(env: &mut Env, v: KW, x: K, y: K) -> Result { ) .unwrap() .unwrap_noun() + .unwrap() }) .collect(); promote_num(r.clone()).unwrap_or(K::List(r)) @@ -1476,6 +1470,7 @@ pub fn v_d_eachleft(env: &mut Env, v: KW, x: K, y: K) -> Result { ) .unwrap() .unwrap_noun() + .unwrap() }) .collect(); promote_num(r.clone()).unwrap_or(K::List(r)) diff --git a/tests/tests.rs b/tests/tests.rs index 8cbaa72..836493f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -11,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 } @@ -988,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])) ); } @@ -1525,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])))); } @@ -1551,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, @@ -1566,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))); } @@ -1575,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]))); } @@ -1586,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]))); } From 1709988f02a5a3a795d21959cb33fc7472ac84e2 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Mon, 27 Oct 2025 22:05:05 +1000 Subject: [PATCH 09/18] wip: panics --- src/verbs.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/verbs.rs b/src/verbs.rs index d07f585..ee2318c 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -46,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() { @@ -59,7 +59,7 @@ 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(RokError::Type.into()), } @@ -93,8 +93,8 @@ pub fn v_equal(x: K, y: K) -> Result { } })))) } - (_, K::Table(_)) => todo!("table"), - (K::Table(_), _) => todo!("table"), + (_, K::Table(_)) => Err(RokError::Error("nyi: table".into()).into()), + (K::Table(_), _) => Err(RokError::Error("nyi: table".into()).into()), _ => Err(RokError::NYI.into()), }) } @@ -360,7 +360,7 @@ pub fn v_keys_odometer(x: K) -> Result { .cast(&DataType::Categorical(None, CategoricalOrdering::Lexical)) .unwrap(), )), - K::IntArray(_) => todo!("implement odometer"), + K::IntArray(_) => Err(RokError::Error("nyi: odometer".into()).into()), _ => Err(RokError::NYI.into()), } } @@ -368,8 +368,8 @@ 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()), } } @@ -1147,7 +1147,7 @@ pub fn v_iota(x: K) -> Result { 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 { @@ -1209,7 +1209,7 @@ pub fn v_each(env: &mut Env, v: KW, x: K) -> Result { _ => 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 { From 2208f72e2558a0dbc54ef3c2f6b5dc58123f914d Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Mon, 27 Oct 2025 22:19:22 +1000 Subject: [PATCH 10/18] wip: panics --- src/verbs.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/verbs.rs b/src/verbs.rs index ee2318c..c38991f 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -1039,7 +1039,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() { @@ -1052,7 +1052,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) => { @@ -1070,8 +1070,8 @@ 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(RokError::Type.into()), }, @@ -1209,7 +1209,9 @@ pub fn v_each(env: &mut Env, v: KW, x: K) -> Result { _ => Err(RokError::Type.into()), } } -pub fn v_d_each(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { Err(RokError::Error("nyi: each".into()).into()) } +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 { @@ -1457,8 +1459,8 @@ pub fn v_d_eachright(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() .map(|x| { eval( @@ -1467,14 +1469,12 @@ 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() - .unwrap() }) - .collect(); - promote_num(r.clone()).unwrap_or(K::List(r)) - }), + .collect::>>()?; + Ok(promote_num(r.clone()).unwrap_or(K::List(r))) + } _ => Err(RokError::Type.into()), } } From 444509a81ff84d1a9fa613f7938a20f2ca8e4c3b Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Tue, 28 Oct 2025 21:38:25 +1000 Subject: [PATCH 11/18] fix: more panics --- src/lib.rs | 10 ++++------ src/verbs.rs | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d94b3de..3f936ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -538,9 +538,9 @@ pub fn k_to_vec(k: K) -> Result> { ), K::CharArray(v) => Ok(v.chars().map(K::Char).collect()), K::SymbolArray(_v) => { - todo!("enlist(SymbolArray(...))") + Err(RokError::Error("nyi: enlist(SymbolArray(...))".into()).into()) } - _ => todo!("k_to_vec({})", k), + _ => Err(RokError::Error(format!("nyi: k_to_vec({})", k)).into()), } } pub fn vec_to_list(nouns: Vec) -> Result { @@ -810,9 +810,7 @@ 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 v.len() < 2 { @@ -852,7 +850,7 @@ 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"), diff --git a/src/verbs.rs b/src/verbs.rs index c38991f..5801601 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -255,7 +255,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 { @@ -777,7 +777,7 @@ pub fn v_concat(x: K, y: K) -> Result { (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([y].iter()).cloned().collect())), (x, K::List(y)) => Ok(K::List([x].iter().chain(y.iter()).cloned().collect())), - _ => todo!("nyi v_concat() other cases {}, {}", x, y), + _ => Err(RokError::Error(format!("nyi: v_concat() other cases {}, {}", x, y)).into()), } } From 272e9182cbc02b62bbe2babc4405649f46d135c3 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sat, 1 Nov 2025 09:48:35 +1000 Subject: [PATCH 12/18] fix: v_d_scan handle empty y --- src/verbs.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/verbs.rs b/src/verbs.rs index 5801601..5bd82fe 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -1385,17 +1385,21 @@ pub fn v_scan(env: &mut Env, v: KW, x: 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_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)?; - v_scan(env, v, v_concat(first, rest)?) + let rest: K = v_drop(K::Int(Some(1)), y)?; + v_scan(env, v, v_concat(first, rest)?) + } } _ => Err(RokError::Type.into()), } From 413d423a88da400c9cf52ca56b8fca77f0690162 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sat, 1 Nov 2025 10:15:45 +1000 Subject: [PATCH 13/18] wip: panics --- src/lib.rs | 4 +- src/verbs.rs | 103 ++++++++++++++++++++++++++++----------------------- 2 files changed, 58 insertions(+), 49 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3f936ab..622974e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -865,12 +865,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 { diff --git a/src/verbs.rs b/src/verbs.rs index 5bd82fe..e965437 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -139,32 +139,36 @@ macro_rules! reshape_atom_by_type { 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(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()), + 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(RokError::NYI.into()), - 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(RokError::NYI.into()), @@ -1407,28 +1411,33 @@ pub fn v_d_scan(env: &mut Env, v: KW, x: K, y: 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() - .unwrap() - }) - .collect(); - let r: Vec = vec![first.clone()].into_iter().chain(r).collect(); - promote_num(r.clone()).unwrap_or(K::List(r)) - }), + 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()), } } From 598d7d55704fef24690121d30ce5515a8d30c8be Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sat, 1 Nov 2025 10:29:53 +1000 Subject: [PATCH 14/18] todo: peach --- src/verbs.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/verbs.rs b/src/verbs.rs index e965437..4b01c79 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -1411,6 +1411,9 @@ pub fn v_d_scan(env: &mut Env, v: KW, x: K, y: K) -> Result { pub fn v_eachprior(env: &mut Env, v: KW, x: K) -> Result { match v { + 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]; From 9ae17d1f8c998a5edab74a329796d10dd15e3500 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sat, 1 Nov 2025 10:38:45 +1000 Subject: [PATCH 15/18] wip: panics --- src/verbs.rs | 70 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/verbs.rs b/src/verbs.rs index 4b01c79..1d79710 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -347,7 +347,7 @@ 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_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)), @@ -358,7 +358,7 @@ pub fn v_divide(l: K, r: 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)) @@ -445,8 +445,8 @@ pub fn v_min(l: K, r: K) -> Result { )), (K::List(_l), K::List(_r)) => Err(RokError::NYI.into()), (K::Dictionary(_l), K::Dictionary(_r)) => Err(RokError::NYI.into()), - (_, K::Table(_)) => todo!("table"), - (K::Table(_), _) => todo!("table"), + (_, 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()), }) } @@ -477,8 +477,8 @@ pub fn v_max(l: K, r: K) -> Result { )), (K::List(_l), K::List(_r)) => Err(RokError::NYI.into()), (K::Dictionary(_l), K::Dictionary(_r)) => Err(RokError::NYI.into()), - (_, K::Table(_)) => todo!("table"), - (K::Table(_), _) => todo!("table"), + (_, 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()), }) } @@ -619,7 +619,7 @@ pub fn v_lesser(x: K, y: K) -> Result { } })))), (K::Table(_l), K::Table(_r)) => { - todo!("table") + 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 @@ -712,7 +712,7 @@ pub fn v_greater(x: K, y: K) -> Result { } })))), (K::Table(_l), K::Table(_r)) => { - todo!("table") + 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 @@ -757,22 +757,22 @@ pub fn v_concat(x: K, y: K) -> Result { 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))), @@ -824,9 +824,9 @@ 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" => todo!("cast to string"), - K::Symbol(s) if s == "i" => todo!("cast to int"), - K::Symbol(s) if s == "f" => todo!("cast to float"), + 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()), } } @@ -866,7 +866,7 @@ 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(); @@ -878,9 +878,9 @@ pub fn v_rand(l: K, r: 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 @@ -930,9 +930,9 @@ pub fn v_at(l: K, r: K) -> Result { 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"), + 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) => { @@ -1001,25 +1001,25 @@ 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( @@ -1445,10 +1445,10 @@ pub fn v_eachprior(env: &mut Env, v: KW, x: K) -> Result { } } pub fn v_eachprior_d_or_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { - todo!("v_eachprior_d_or_windows()") + 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_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { todo!("v_windows()") } +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 { Err(RokError::Error("nyi: v_windows()".into()).into()) } pub fn v_d_eachright(env: &mut Env, v: KW, x: K, y: K) -> Result { match v { @@ -1544,12 +1544,12 @@ 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_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) { @@ -1574,10 +1574,10 @@ pub fn v_d_colon(env: &mut Env, l: K, r: KW) -> Result { // 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(RokError::Error("path does not exist".into()).into()) From 9ff4ded8288dc6899edc870fee65719baf27675a Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Wed, 5 Nov 2025 22:41:56 +1000 Subject: [PATCH 16/18] wip: panics --- src/lib.rs | 18 ++++++------ src/verbs.rs | 83 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 58 insertions(+), 43 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 622974e..5dadd6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -537,9 +537,7 @@ pub fn k_to_vec(k: K) -> Result> { .collect(), ), K::CharArray(v) => Ok(v.chars().map(K::Char).collect()), - K::SymbolArray(_v) => { - Err(RokError::Error("nyi: enlist(SymbolArray(...))".into()).into()) - } + K::SymbolArray(_v) => Err(RokError::Error("nyi: enlist(SymbolArray(...))".into()).into()), _ => Err(RokError::Error(format!("nyi: k_to_vec({})", k)).into()), } } @@ -882,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(RokError::Rank.into()), - Ordering::Less => todo!("currying: args: {:?}", args), + 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::>()); @@ -898,15 +898,15 @@ 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(RokError::Rank.into()), } diff --git a/src/verbs.rs b/src/verbs.rs index 1d79710..8e00293 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -618,9 +618,7 @@ pub fn v_lesser(x: K, y: K) -> Result { _ => None, } })))), - (K::Table(_l), K::Table(_r)) => { - Err(RokError::Error("nyi: table".into()).into()) - } + (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()) @@ -711,9 +709,7 @@ pub fn v_greater(x: K, y: K) -> Result { _ => None, } })))), - (K::Table(_l), K::Table(_r)) => { - Err(RokError::Error("nyi: table".into()).into()) - } + (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()) @@ -878,9 +874,15 @@ pub fn v_rand(l: K, r: 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)) => 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::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 @@ -1001,25 +1003,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)), - _ => Err(RokError::Error("nyi: index out of bounds - this shouldn't be an error".into()).into()), + _ => 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)), - _ => Err(RokError::Error("nyi: index out of bounds - this shouldn't be an error".into()).into()), + _ => 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)), - _ => Err(RokError::Error("nyi: index out of bounds - this shouldn't be an error".into()).into()), + _ => 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)), - _ => Err(RokError::Error("nyi: index out of bounds - this shouldn't be an error".into()).into()), + _ => Err( + RokError::Error("nyi: index out of bounds - this shouldn't be an error".into()) + .into(), + ), } } K::CharArray(a) => Ok(K::CharArray( @@ -1089,7 +1103,7 @@ 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()), } } @@ -1437,7 +1451,7 @@ pub fn v_eachprior(env: &mut Env, v: KW, x: K) -> Result { 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(e) => Err(e), } })? } @@ -1447,13 +1461,17 @@ pub fn v_eachprior(env: &mut Env, v: KW, x: K) -> Result { 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 { Err(RokError::Error("nyi: v_eachprior_d()".into()).into()) } -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_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 { + Err(RokError::Error("nyi: v_windows()".into()).into()) +} 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() .map(|y| { eval( @@ -1462,14 +1480,15 @@ pub fn v_d_eachright(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() - .unwrap() }) .collect(); - promote_num(r.clone()).unwrap_or(K::List(r)) - }), + match r { + Ok(r) => Ok(promote_num(r.clone()).unwrap_or(K::List(r))), + Err(e) => Err(e), + } + })?, _ => Err(RokError::Type.into()), } } @@ -1526,14 +1545,14 @@ pub fn v_makedict(l: K, r: K) -> Result { // `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.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())), @@ -1543,9 +1562,7 @@ pub fn v_makedict(l: K, r: K) -> Result { } }, K::Symbol(s) => Ok(K::Dictionary(IndexMap::from([(s, r)]))), - _ => { - Err(RokError::Error("nyi: modulo".into()).into()) - } + _ => Err(RokError::Error("nyi: modulo".into()).into()), } } @@ -1554,7 +1571,7 @@ 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); @@ -1565,10 +1582,8 @@ 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(); @@ -1588,7 +1603,7 @@ 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()), } } From 83b26aa9f6a3c088244860b75688e087ee9cbad5 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Fri, 7 Nov 2025 07:27:21 +1000 Subject: [PATCH 17/18] fix: panics --- src/verbs.rs | 164 +++++++++++++++++++++++++++------------------------ 1 file changed, 87 insertions(+), 77 deletions(-) diff --git a/src/verbs.rs b/src/verbs.rs index 8e00293..6adac26 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -194,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, @@ -209,30 +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(RokError::Type.into()) - panic!("type error?") + Err(RokError::Type.into()) + // panic!("type error?") } } - K::Bool(b) => Series::new(&k.to_string(), repeat_n(*b, *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(), repeat_n(*i, *len).collect::>()) + Ok(Series::new(&k.to_string(), repeat_n(*i, *len).collect::>())) + } + K::Int(None) => Ok(Series::full_null(&k.to_string(), *len, &DataType::Int64)), + K::Float(f) => { + Ok(Series::new(&k.to_string(), repeat_n(*f, *len).collect::>())) } - K::Int(None) => Series::full_null(&k.to_string(), *len, &DataType::Int64), - K::Float(f) => Series::new(&k.to_string(), repeat_n(*f, *len).collect::>()), K::Symbol(s) => { - Series::new(&k.to_string(), repeat_n(s.clone(), *len).collect::>()) + Ok(Series::new(&k.to_string(), repeat_n(s.clone(), *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())) } } @@ -324,17 +327,21 @@ 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().next().unwrap_or(' '))), - K::List(l) => Ok(l.first().unwrap().clone()), - _ => Err(RokError::NYI.into()), + 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()), + } } } @@ -484,56 +491,60 @@ pub fn v_max(l: K, r: K) -> Result { } 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]); + 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(RokError::NYI.into()), } } pub fn v_desc(x: K) -> Result { - v_reverse(v_asc(x).unwrap()) /* TODO: faster */ + v_reverse(v_asc(x)?) /* TODO: faster */ } pub fn v_lesser(x: K, y: K) -> Result { @@ -1305,20 +1316,19 @@ 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() - .unwrap() - }); - match r { - Some(k) => Ok(k.clone()), - None => Err(RokError::Error("TODO not sure what this error case is".into()).into()), + vec![ + f.clone(), + KW::FuncArgs(vec![vec![KW::Noun(acc.clone())], vec![KW::Noun(y.clone())]]), + ], + )? + .unwrap_noun()? } + Ok(acc) }), _ => Err(RokError::Type.into()), } From 1efbb28cc72acd11d43e9cb0237dab742f696e8a Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Fri, 7 Nov 2025 07:29:37 +1000 Subject: [PATCH 18/18] test: ngnk test cleanup --- tests/ngnk.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/ngnk.rs b/tests/ngnk.rs index 640bdbd..15bad8e 100644 --- a/tests/ngnk.rs +++ b/tests/ngnk.rs @@ -1,7 +1,7 @@ -use roklang::*; use anyhow::Result; +use roklang::*; use std::collections::HashMap; -use std::fs::{File}; +use std::fs::File; use std::io::Read; fn k_eval(s: &str) -> Result { @@ -38,6 +38,7 @@ 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 @@ -61,7 +62,7 @@ fn test_ngnk_tests() { let res_r = k_eval(t[1]); let fail = match (&res_l, &res_r) { (Ok(l), Ok(r)) if l == r => false, - _ => true + _ => true, }; if fail { failed_tests += 1; @@ -70,10 +71,12 @@ fn test_ngnk_tests() { Ok(k) => println!("{}", k), 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); }