From f2c36d1bd969fa57f14b142d3e540612b58be8e3 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Fri, 7 Nov 2025 18:02:30 +1000 Subject: [PATCH 01/10] feat: v_match --- src/verbs.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/verbs.rs b/src/verbs.rs index 6adac26..100d227 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -71,12 +71,14 @@ pub fn v_equal(x: K, y: K) -> Result { (K::Int(Some(l)), K::Int(Some(r))) => Ok(K::Bool((l == r) as u8)), (K::Int(None), K::Int(_)) | (K::Int(_), K::Int(None)) => Ok(K::Bool(0)), (K::Float(l), K::Float(r)) => Ok(K::Bool((l == r) as u8)), + (K::Symbol(l), K::Symbol(r)) => Ok(K::Bool((l == r) as u8)), (K::BoolArray(l), K::BoolArray(r)) => Ok(K::BoolArray(l.equal(&r).unwrap().into())), (K::IntArray(l), K::IntArray(r)) => Ok(K::BoolArray(l.equal(&r).unwrap().into())), (K::FloatArray(l), K::FloatArray(r)) => Ok(K::BoolArray(l.equal(&r).unwrap().into())), (K::CharArray(l), K::CharArray(r)) => { Ok(K::BoolArray(arr!(l.chars().zip(r.chars()).map(|(l, r)| l == r).collect::>()))) } + (K::SymbolArray(l), K::SymbolArray(r)) => Ok(K::BoolArray(l.equal(&r).unwrap().into())), (K::List(l), K::List(r)) => Ok(K::BoolArray(arr!(zip(l.iter(), r.iter()) .map(|(l, r)| { let (l, r) = promote_nouns(l.clone(), r.clone()); @@ -734,7 +736,24 @@ pub fn v_greater(x: K, y: K) -> Result { } 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_match(x: K, y: K) -> Result { + match (x,y) { + (K::Bool(l), K::Bool(r)) => Ok(K::Bool((l == r) as u8)), + (K::Int(l), K::Int(r)) => Ok(K::Bool((l == r) as u8)), + (K::Float(l), K::Float(r)) => Ok(K::Bool((l == r) as u8)), + (K::Char(l), K::Char(r)) => Ok(K::Bool((l == r) as u8)), + (K::Symbol(l), K::Symbol(r)) => Ok(K::Bool((l == r) as u8)), + (K::SymbolArray(l), K::SymbolArray(r)) => Ok(K::Bool((l == r) as u8)), + (K::BoolArray(l), K::BoolArray(r)) => Ok(K::Bool((l == r) as u8)), + (K::IntArray(l), K::IntArray(r)) => Ok(K::Bool((l == r) as u8)), + (K::FloatArray(l), K::FloatArray(r)) => Ok(K::Bool((l == r) as u8)), + (K::CharArray(l), K::CharArray(r)) => Ok(K::Bool((l == r) as u8)), + (K::List(l), K::List(r)) => Ok(K::Bool((l == r) as u8)), + (K::Dictionary(l), K::Dictionary(r)) => Ok(K::Bool((l == r) as u8)), + (K::Table(l), K::Table(r)) => Ok(K::Bool((l == r) as u8)), + _ => Err(RokError::NYI.into()) + } +} pub fn v_enlist(x: K) -> Result { match x { From 51bad44a776720bbb342a6d5a85b722d0dd01d92 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Fri, 7 Nov 2025 18:03:11 +1000 Subject: [PATCH 02/10] fix: vec_to_list IntArray promotion --- src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5dadd6f..bd9ab58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -553,13 +553,14 @@ pub fn vec_to_list(nouns: Vec) -> Result { Ok(K::BoolArray(arr!(v))) } else if nouns .iter() - .all(|w| matches!(w, KW::Noun(K::Bool(_))) || matches!(w, KW::Noun(K::Int(Some(_))))) + .all(|w| matches!(w, KW::Noun(K::Bool(_))) || matches!(w, KW::Noun(K::Int(_)))) { - let v: Vec = nouns + let v: Vec> = nouns .iter() .map(|w| match w { - KW::Noun(K::Bool(b)) => *b as i64, - KW::Noun(K::Int(Some(i))) => *i, + KW::Noun(K::Bool(b)) => Some(*b as i64), + KW::Noun(K::Int(Some(i))) => Some(*i), + KW::Noun(K::Int(None)) => None, _ => panic!("impossible"), }) .collect(); From 0a0a78b57cd3791fbbd3a45bc3b2f7ee48d0df59 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Fri, 7 Nov 2025 18:03:39 +1000 Subject: [PATCH 03/10] wip: ngnk tests --- tests/ngnk.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/ngnk.rs b/tests/ngnk.rs index 15bad8e..8eee709 100644 --- a/tests/ngnk.rs +++ b/tests/ngnk.rs @@ -43,7 +43,7 @@ fn test_ngnk_tests() { // TODO add support for these lines // let skiplines = [9, 10, 14, 15, 28, 30, 31, 32, 34]; - let skiplines = []; + let skiplines = [11, 12, 13]; for (i, l) in lines.iter().enumerate() { if skiplines.contains(&i) { @@ -51,7 +51,7 @@ fn test_ngnk_tests() { failed_tests += 1; println!("\nskipping line {} known failure: {}", i + 1, l); } else { - println!("\nline {}: {}", i + 1, l); + // println!("\nline {}: {}", i + 1, l); let t: Vec<&str> = l.split(" / ").collect(); if t.len() != 2 { println!("Skipping dud line: {}", l); @@ -67,6 +67,9 @@ fn test_ngnk_tests() { if fail { failed_tests += 1; println!("Failed test: ({failed_tests}/{test_count}): {}", l); + if failed_tests > 5 { + panic!("More than 5 failures: bailing out"); + } match res_l { Ok(k) => println!("{}", k), Err(_) => println!("{:?}", res_l), From d086d8f947b52e8d40c9659c8626b2d01a980511 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Fri, 7 Nov 2025 22:08:38 +1000 Subject: [PATCH 04/10] feat: string casts --- src/lib.rs | 2 +- src/verbs.rs | 34 ++++++++++++++++++++++++++++------ tests/ngnk.rs | 8 ++++---- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bd9ab58..8861e53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -641,7 +641,7 @@ pub fn primitives_table() -> VerbDispatchTable { ("^", (v_isnull, v_isnull, v_fill, v_except, v_fill, v_except, v_none3, v_none4)), ("#", (v_count, v_count, v_take, v_reshape, v_take, v_reshape, v_none3, v_none4,)), ("_", (v_floor, v_floor, v_drop, v_delete, v_drop, v_cut, v_none3, v_none4,)), - ("$", (v_string, v_string, v_dfmt, v_dfmt, v_dfmt, v_dfmt, v_none3, v_none4,)), + ("$", (v_string, v_string, v_d_dollar, v_d_dollar, v_d_dollar, v_d_dollar, v_none3, v_none4,)), ("?", (v_randfloat, v_unique, v_rand, v_find, v_rand, v_find, v_splice, v_none4,)), ("@", (v_type, v_type, v_at, v_at, v_at, v_at, v_amend3, v_amend4,)), (".", (v_eval, v_eval, v_dot, v_dot, v_dot, v_dot, v_deepamend3, v_deepamend4,)), diff --git a/src/verbs.rs b/src/verbs.rs index 100d227..6598129 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -320,12 +320,20 @@ macro_rules! atomicdyad { (l, K::List(rv)) => { Ok(K::List(rv.iter().map(|y| $v(l.clone(), y.clone()).unwrap()).collect())) } + (l@K::Char(_) | l@K::CharArray(_), r@K::Char(_) | r@K::CharArray(_)) => len_ok(&l, &r).and_then(|_| Ok(v_cast(K::Symbol("i".into()), l)? $op v_cast(K::Symbol("i".into()), r)?)), + (l, r@K::Char(_) | r@K::CharArray(_)) => len_ok(&l, &r).and_then(|_| Ok(l $op v_cast(K::Symbol("i".into()), r)?)), + (l@K::Char(_) | l@K::CharArray(_), r) => len_ok(&l, &r).and_then(|_| Ok(v_cast(K::Symbol("i".into()), l)? $op r)), (l,r) => len_ok(&l, &r).and_then(|_| Ok(l $op 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_negate(x: K) -> Result { + match x { + K::Char(_) | K::CharArray(_) => Ok(K::Int(Some(-1i64)) * v_cast(K::Symbol("i".into()), x)?), + _ => 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 { @@ -736,8 +744,8 @@ pub fn v_greater(x: K, y: K) -> Result { } pub fn v_not(_r: K) -> Result { Err(RokError::NYI.into()) } -pub fn v_match(x: K, y: K) -> Result { - match (x,y) { +pub fn v_match(x: K, y: K) -> Result { + match (x, y) { (K::Bool(l), K::Bool(r)) => Ok(K::Bool((l == r) as u8)), (K::Int(l), K::Int(r)) => Ok(K::Bool((l == r) as u8)), (K::Float(l), K::Float(r)) => Ok(K::Bool((l == r) as u8)), @@ -751,7 +759,7 @@ pub fn v_match(x: K, y: K) -> Result { (K::List(l), K::List(r)) => Ok(K::Bool((l == r) as u8)), (K::Dictionary(l), K::Dictionary(r)) => Ok(K::Bool((l == r) as u8)), (K::Table(l), K::Table(r)) => Ok(K::Bool((l == r) as u8)), - _ => Err(RokError::NYI.into()) + _ => Err(RokError::NYI.into()), } } @@ -846,12 +854,26 @@ 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(RokError::NYI.into()) } +pub fn v_d_dollar(l: K, r: K) -> Result { + match l { + K::Symbol(_) => v_cast(l, r), + _ => 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 { +pub fn v_cast(l: K, r: K) -> Result { match l { 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 == "i" => match r { + K::Char(c) => Ok(K::Int(Some((c as u32) as i64))), + K::CharArray(s) => Ok(K::IntArray(arr!(s + .chars() + .into_iter() + .map(|i| (i as u32) as i64) + .collect::>()))), + _ => 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()), } diff --git a/tests/ngnk.rs b/tests/ngnk.rs index 8eee709..9036c47 100644 --- a/tests/ngnk.rs +++ b/tests/ngnk.rs @@ -66,10 +66,10 @@ fn test_ngnk_tests() { }; if fail { failed_tests += 1; - println!("Failed test: ({failed_tests}/{test_count}): {}", l); - if failed_tests > 5 { - panic!("More than 5 failures: bailing out"); - } + println!("Failed test: line {} ({failed_tests}/{test_count}): {}", i, l); + // if failed_tests > 10 { + // panic!("More than {failed_tests} failures: bailing out"); + // } match res_l { Ok(k) => println!("{}", k), Err(_) => println!("{:?}", res_l), From 6b5af7e9b8a119a18596385f5b2d58c10db0986b Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sun, 9 Nov 2025 10:05:54 +1000 Subject: [PATCH 05/10] feat: empty list () --- src/lib.rs | 1 + tests/ngnk.rs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8861e53..6b27a02 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1169,6 +1169,7 @@ pub fn eval(env: &mut Env, sentence: Vec) -> Result { } // TODO: rest of the J (K is similar!) parse table (minus forks/hooks) https://www.jsoftware.com/help/jforc/parsing_and_execution_ii.htm#_Toc191734587 (KW::LP, w, KW::RP, any) => Ok(vec![w.clone(), any.clone()]), // 8 paren + (KW::LP, KW::RP, any1, any2 ) => Ok(vec![KW::Noun(K::List(vec![])), any1.clone(), any2.clone()]), (KW::LP, KW::Noun(n1), KW::SC, KW::Noun(n2)) => { // List if let Some(i) = stack.iter().position(|w| matches!(w, KW::RP)) { diff --git a/tests/ngnk.rs b/tests/ngnk.rs index 9036c47..1ae67dd 100644 --- a/tests/ngnk.rs +++ b/tests/ngnk.rs @@ -53,8 +53,9 @@ fn test_ngnk_tests() { } else { // println!("\nline {}: {}", i + 1, l); let t: Vec<&str> = l.split(" / ").collect(); - if t.len() != 2 { - println!("Skipping dud line: {}", l); + if l.chars().next() == Some('/') || t.len() != 2 { + // comment or other + // println!("Skipping dud line: {}", l); } else { test_count += 1; // assert_eq!(k_eval(t[0]), k_eval(t[1])); @@ -67,6 +68,7 @@ fn test_ngnk_tests() { if fail { failed_tests += 1; println!("Failed test: line {} ({failed_tests}/{test_count}): {}", i, l); + // Useful for debugging: // if failed_tests > 10 { // panic!("More than {failed_tests} failures: bailing out"); // } From 2c8ed96f19e2a4f83db4b3631caad8cf69fa56a3 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sun, 9 Nov 2025 10:22:03 +1000 Subject: [PATCH 06/10] test: track ngn/k/t/t.k tests in Tests.md --- README.md | 3 +++ Tests.md | 8 ++++++++ tests/ngnk.rs | 9 ++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 Tests.md diff --git a/README.md b/README.md index 7789a43..392630b 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,9 @@ oK.js is under 1k lines of javascript, can we do the same in under 1k lines of r RUST_LOG=rok=debug cargo run ``` +## ngn/k tests + +See [Tests.md] ## example diff --git a/Tests.md b/Tests.md new file mode 100644 index 0000000..8acbd4e --- /dev/null +++ b/Tests.md @@ -0,0 +1,8 @@ +# ngn/k tests + +The ngn/k tests come from here: + - https://codeberg.org/ngn/k/src/branch/master/t/t.k + +test_count: 842 +passed: 122 +failed: 720 diff --git a/tests/ngnk.rs b/tests/ngnk.rs index 1ae67dd..e3fa9fe 100644 --- a/tests/ngnk.rs +++ b/tests/ngnk.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use std::io::Write; use roklang::*; use std::collections::HashMap; use std::fs::File; @@ -43,7 +44,7 @@ fn test_ngnk_tests() { // TODO add support for these lines // let skiplines = [9, 10, 14, 15, 28, 30, 31, 32, 34]; - let skiplines = [11, 12, 13]; + let skiplines = []; for (i, l) in lines.iter().enumerate() { if skiplines.contains(&i) { @@ -83,5 +84,11 @@ fn test_ngnk_tests() { } } println!("\ntest_count: {}\npassed: {}\nfailed: {}", test_count, passed_tests, failed_tests); + + let mut w = File::create("Tests.md").unwrap(); + writeln!(&mut w, "# ngn/k tests\n").unwrap(); + writeln!(&mut w, "The ngn/k tests come from here: \n\t- https://codeberg.org/ngn/k/src/branch/master/t/t.k").unwrap(); + writeln!(&mut w, "\ntest_count: {}\npassed: {}\nfailed: {}", test_count, passed_tests, failed_tests).unwrap(); + assert!(failed_tests == 0); } From 945c7163e5fce1cda511f614bb7e507b66131bbc Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sun, 9 Nov 2025 10:23:35 +1000 Subject: [PATCH 07/10] misc: readme formatting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 392630b..8618976 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ RUST_LOG=rok=debug cargo run ## ngn/k tests -See [Tests.md] +See [Tests.md](Tests.md) ## example From 8859830c069956722a9f3239522b29508db1cdeb Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Sat, 22 Nov 2025 14:10:36 +1000 Subject: [PATCH 08/10] wip: n-ary special adverb forms ' case ' binarysearch etc --- src/lib.rs | 68 +++++++++++++++++++++++++++++++++++++++------------- src/verbs.rs | 24 +++++++++++++++++++ 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6b27a02..d7fd993 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,6 +63,7 @@ pub enum KW /* KWords */ { Verb { name: String }, Adverb { name: String }, // DerivedVerb { verb: Box, adverb: String }, // TODO represent modified verbs/functions like this? eg. +/ or {2*x}' + // Projection { verb: Box, args: Vec> }, // TODO Exprs(Vec>), // list of expressions: [e1;e2;e3] FuncArgs(Vec>), // function arguments: f[a1;a2;3] Cond(Vec>), // conditional form $[p;t;f] @@ -615,9 +616,12 @@ 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()) } +pub fn av_nyi3(_env: &mut Env, _v: KW, _x: KW, _y: KW) -> Result { Err(RokError::NYI.into()) } type AV1 = fn(&mut Env, KW, K) -> Result; type AV2 = fn(&mut Env, KW, K, K) -> Result; +//Adverb n-adic: Noun [Verb] Adverb FuncArgs +type AVN = fn(&mut Env, KW /*Verb Adverb */, KW /* x */, KW /* y */) -> Result; type VerbDispatchTable = IndexMap<&'static str, (V1, V1, V2, V2, V2, V2, V3, V4)>; @@ -736,14 +740,26 @@ pub fn specialcombinations_table() -> VerbDispatchTable { ]) } -pub fn adverbs_table() -> IndexMap<&'static str, (AV1, AV2)> { +// TODO: adverbs have more parse table cases than just monadic and dyadic: +// ' also has v_case, v_binarysearch, and n-adic each: +// - v_case: 0 1 0'["abc";"xyz"] / "ayc" +// - https://wiki.k-language.dev/wiki/Binary_search +// - n-adic each: {x+y-z}'[1 2 3;4 5 6;7 8 9] +// +// This will need additional cases in the parse table in eval(). +// To complicate this further, case is actually n-ary: +// https://code.kx.com/q/ref/maps/#case +// q) 0 2 0'["abc";"xyz";"123";"789"] +// "a2c" +// +pub fn adverbs_table() -> IndexMap<&'static str, (AV1, AV2, AVN)> { IndexMap::from([ - ("'", (v_each as AV1, v_d_each as AV2)), - ("/", (a_slash, a_d_slash)), // over fixedpoint for while - ("\\", (a_bslash, a_d_bslash)), // scan scan-fixedpoint scan-for scan-while - ("':", (v_eachprior, v_eachprior_d_or_windows)), - ("/:", (av_none1, v_d_eachright)), - ("\\:", (av_none1, v_d_eachleft)), + ("'", (v_each as AV1, v_d_each as AV2, v_d_quote as AVN)), + ("/", (a_slash, a_d_slash, av_nyi3)), // over fixedpoint for while + ("\\", (a_bslash, a_d_bslash, av_nyi3)), // scan scan-fixedpoint scan-for scan-while + ("':", (v_eachprior, v_eachprior_d_or_windows, av_nyi3)), + ("/:", (av_none1, v_d_eachright, av_nyi3)), + ("\\:", (av_none1, v_d_eachleft, av_nyi3)), ]) } @@ -812,26 +828,42 @@ pub fn apply_primitive(env: &mut Env, v: &str, l: Option, r: KW) -> Result Err(RokError::Error("impossible".into()).into()), }, None => { + // adverb 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))) + if let Some((m_a, d_a, avn)) = adverbs.get(&v) { + Some((1, (m_a, d_a, avn))) + } else { + None + } + } else if let Some((m_a, d_a, avn)) = adverbs.get(&v[v.len() - 2..]) { + Some((2, (m_a, d_a, avn))) + } else if let Some((m_a, d_a, avn)) = adverbs.get(&v[v.len() - 1..]) { + Some((1, (m_a, d_a, avn))) } else { None }; match t { - Some((adv_len, (m_a, d_a))) => match (l, r) { + Some((adv_len, (m_a, d_a, avn))) => match (l, r.clone()) { + (Some(l@KW::Noun(_)), KW::FuncArgs(_)) => { + if v.len() < 2 { + avn(env, KW::Verb{ name: v.to_string() }, l, r).map(KW::Noun) + } else { + Err(RokError::Error("NYI: Adverb case: Noun Verb Adverb FuncArgs".into()).into()) + } + } (Some(KW::Noun(l)), KW::Noun(r)) => { - d_a(env, KW::Verb { name: v[..v.len() - adv_len].to_string() }, l, r).map(KW::Noun) + d_a(env, KW::Verb { name: v[..v.len() - adv_len].to_string() }, l, r).map(KW::Noun) } (None, KW::Noun(r)) => { m_a(env, KW::Verb { name: v[..v.len() - adv_len].to_string() }, r).map(KW::Noun) } _ => Err(RokError::Error("NYI: other adverb cases".into()).into()), }, - None => Err(RokError::Error(format!("NYI: {}", v)).into()), + None => { + println!("Todo: extra adverb cases like case and binarysearch"); + println!("v: \"{v}\", l: {l:?}, r: {r}"); + Err(RokError::Error(format!("NYI: {}", v)).into()) + } } } }, @@ -861,7 +893,7 @@ pub fn apply_function(env: &mut Env, f: KW, arg: KW) -> Result { let adverbs = adverbs_table(); let adverb: &str = &adverb; match adverbs.get(adverb) { - Some((m_a, _d_a)) => match arg { + Some((m_a, _d_a, _av3)) => match arg { KW::Noun(x) => m_a(env, KW::Function { body, args, adverb: None }, x).map(KW::Noun), KW::FuncArgs(_exprs) => { Err(RokError::Error("nyi: dyad/triad/etc adverb modified functions".into()).into()) @@ -1167,6 +1199,10 @@ pub fn eval(env: &mut Env, sentence: Vec) -> Result { // 4 dyad adverb (special cases, they're actually verbs) apply_primitive(env, &name, Some(x.clone()), y.clone()).map(|r| vec![any, r]) } + (any, x @ KW::Noun(_), KW::Adverb { name }, y @ KW::FuncArgs(_)) => { + // 4 dyad adverb (more special cases, they're actually verbs) + apply_primitive(env, &name, Some(x.clone()), y.clone()).map(|r| vec![any, r]) + } // TODO: rest of the J (K is similar!) parse table (minus forks/hooks) https://www.jsoftware.com/help/jforc/parsing_and_execution_ii.htm#_Toc191734587 (KW::LP, w, KW::RP, any) => Ok(vec![w.clone(), any.clone()]), // 8 paren (KW::LP, KW::RP, any1, any2 ) => Ok(vec![KW::Noun(K::List(vec![])), any1.clone(), any2.clone()]), diff --git a/src/verbs.rs b/src/verbs.rs index 6598129..b72a3f7 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -1262,6 +1262,30 @@ pub fn v_d_bang(l: K, r: K) -> Result { } } +pub fn v_d_quote(_env: &mut Env, _v: KW, x: KW, y: KW) -> Result { + // dispatch dyadic verb form of ' + match (x, y) { + // (KW::Noun(K::IntArray(_)), KW::FuncArgs(_)) => todo!("todo"), + _ => Err(RokError::Error("v_d_quote(). Dispatch of dyadic ' ".into()).into()), + } +} +pub fn v_binarysearch(_l: K, _r: K) -> Result { Err(RokError::NYI.into()) } +pub fn v_case(_l: K, _r: K) -> Result { + // https://code.kx.com/q/ref/maps/#case + // https://codeberg.org/ngn/k/commit/319fcaa609e2099c33f04d5373393df46fe78810 + // 0 1 0'["abc";"xyz"] / "ayc" + // Err(RokError::NYI.into()) + todo!("AA TODO nyi") +} + +pub fn v_quote(env: &mut Env, v: KW, x: K) -> Result { + // dispatch adverb ' variants + match v { + KW::Verb { .. } | KW::Function { .. } => v_each(env, v, x), + KW::Nothing => todo!(), + _ => Err(RokError::NYI.into()), + } +} pub fn v_each(env: &mut Env, v: KW, x: K) -> Result { match v { f @ KW::Verb { .. } | f @ KW::Function { .. } => { From 51dda372606c5badd69bb75c8e69ced159756371 Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Wed, 3 Dec 2025 21:59:33 +1000 Subject: [PATCH 09/10] test: aoc 2025 day1 --- tests/aoc.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/aoc.rs b/tests/aoc.rs index f55e61d..408c166 100644 --- a/tests/aoc.rs +++ b/tests/aoc.rs @@ -122,3 +122,13 @@ fn test_aoc2015_12_03() { // let expected = eval(&mut env, scan("1234").unwrap()).unwrap(); // 4403 too high // assert_eq!(res, expected); } + +#[test] +fn test_aoc2025_12_01() { + let mut env = Env { names: HashMap::new(), parent: None }; + + let s = r#"50{100!x+y}\{.(" -"@"L"=*x),1_x}'x:("L68";"L30";"R48";"L5";"R60";"L55";"L1;""L99";"R14";"L82")"#; + let res = eval(&mut env, scan(s).unwrap()).unwrap(); + let expected = eval(&mut env, scan("3").unwrap()).unwrap(); + assert_eq!(res, expected); +} From 30101c4838f982421fbf52b8347b7a67660353de Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Wed, 3 Dec 2025 22:01:56 +1000 Subject: [PATCH 10/10] test: aoc 2025 day 1 --- tests/aoc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/aoc.rs b/tests/aoc.rs index 408c166..ae9eaa8 100644 --- a/tests/aoc.rs +++ b/tests/aoc.rs @@ -127,7 +127,7 @@ fn test_aoc2015_12_03() { fn test_aoc2025_12_01() { let mut env = Env { names: HashMap::new(), parent: None }; - let s = r#"50{100!x+y}\{.(" -"@"L"=*x),1_x}'x:("L68";"L30";"R48";"L5";"R60";"L55";"L1;""L99";"R14";"L82")"#; + let s = r#"+/0=50{100!x+y}\{.(" -"@"L"=*x),1_x}'x:("L68";"L30";"R48";"L5";"R60";"L55";"L1";"L99";"R14";"L82")"#; let res = eval(&mut env, scan(s).unwrap()).unwrap(); let expected = eval(&mut env, scan("3").unwrap()).unwrap(); assert_eq!(res, expected);