From 36d0a91a5b0f095219611a277eb51797cc5d1d70 Mon Sep 17 00:00:00 2001 From: Daniel Morris Date: Fri, 22 May 2026 09:18:38 +0100 Subject: [PATCH 1/2] chore: delete unreachable tree-walker HOF impls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR D of ILO-45. The HOF builtins (map / flt / fld / srt 2-3 / rsrt 2-3 / ct / mapr / partition / flatmap / uniqby / grp) are no longer reachable from the tree-bridge: PRs A/B/C/earlier-3b/3c lifted each one to native VM dispatch and removed them from is_tree_bridge_eligible. Their tree-walker Rust impls inside call_function were dead code — nothing in the VM dispatch path routes a HOF call through the bridge any more, and the engine-Tree CLI surface was removed in PR #613. Cuts: - 12 HOF impl blocks in src/runtime/mod.rs call_function (3803-3854, 3901-3953, 5715-6186) - The closure_captures / resolve_fn_ref nested helpers that only those impls used - 14 runtime::tests inline unit tests that exercised the deleted impls via run_str (the tree-walker engine entry point) The tree-walker eval loop itself (eval_body / eval_stmt / eval_expr / Env / trampoline / ACTIVE_AST_PROGRAM) stays for PR E. After this PR, it is only reachable from the user-fn dispatch tail of call_function, which is now only reached by other tests in the runtime::tests module that exercise the tree-walker through run_str. PR E deletes that path and the eval loop. 864 lines of dead code gone. cargo test --release --features cranelift green. --- src/runtime/mod.rs | 864 --------------------------------------------- 1 file changed, 864 deletions(-) diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 31ab332f0..38276f29b 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -3800,58 +3800,6 @@ fn call_function(env: &mut Env, name: &str, args: Vec) -> Result { )), }; } - if builtin == Some(Builtin::Srt) && (args.len() == 2 || args.len() == 3) { - let fn_name = resolve_fn_ref(&args[0]).ok_or_else(|| { - RuntimeError::new( - "ILO-R009", - format!( - "srt: key arg must be a function reference, got {:?}", - args[0] - ), - ) - })?; - let captures = closure_captures(&args[0]); - // closure-bind: srt fn ctx xs - let (ctx, list_arg) = if args.len() == 3 { - (Some(args[1].clone()), &args[2]) - } else { - (None, &args[1]) - }; - let items = match list_arg { - Value::List(l) => l.clone(), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("srt: list arg must be a list, got {:?}", other), - )); - } - }; - // Compute keys for each item, then sort by key. - // `items: Arc>` — unwrap if refcount=1, else clone the Vec. - let owned_items: Vec = Arc::try_unwrap(items).unwrap_or_else(|arc| (*arc).clone()); - let mut keyed: Vec<(Value, Value)> = owned_items - .into_iter() - .map(|item| { - let mut call_args = match &ctx { - Some(c) => vec![item.clone(), c.clone()], - None => vec![item.clone()], - }; - call_args.extend(captures.iter().cloned()); - let key = call_function(env, &fn_name, call_args)?; - Ok((key, item)) - }) - .collect::>()?; - keyed.sort_by(|(ka, _), (kb, _)| match (ka, kb) { - (Value::Number(a), Value::Number(b)) => { - a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal) - } - (Value::Text(a), Value::Text(b)) => a.cmp(b), - _ => std::cmp::Ordering::Equal, - }); - return Ok(Value::List(Arc::new( - keyed.into_iter().map(|(_, v)| v).collect(), - ))); - } if builtin == Some(Builtin::Rsrt) && args.len() == 1 { return match &args[0] { Value::List(items) => { @@ -3898,59 +3846,6 @@ fn call_function(env: &mut Env, name: &str, args: Vec) -> Result { )), }; } - if builtin == Some(Builtin::Rsrt) && (args.len() == 2 || args.len() == 3) { - // rsrt fn xs / rsrt fn ctx xs — descending sort by key function. - // Mirrors the srt 2/3-arg path: compute keys for each element, then - // sort by key using the REVERSED comparator (b.cmp(a) for text, - // b.partial_cmp(a) for numbers). Element type is preserved. - let fn_name = resolve_fn_ref(&args[0]).ok_or_else(|| { - RuntimeError::new( - "ILO-R009", - format!( - "rsrt: key arg must be a function reference, got {:?}", - args[0] - ), - ) - })?; - let captures = closure_captures(&args[0]); - let (ctx, list_arg) = if args.len() == 3 { - (Some(args[1].clone()), &args[2]) - } else { - (None, &args[1]) - }; - let items = match list_arg { - Value::List(l) => l.clone(), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("rsrt: list arg must be a list, got {:?}", other), - )); - } - }; - let owned_items: Vec = Arc::try_unwrap(items).unwrap_or_else(|arc| (*arc).clone()); - let mut keyed: Vec<(Value, Value)> = owned_items - .into_iter() - .map(|item| { - let mut call_args = match &ctx { - Some(c) => vec![item.clone(), c.clone()], - None => vec![item.clone()], - }; - call_args.extend(captures.iter().cloned()); - let key = call_function(env, &fn_name, call_args)?; - Ok((key, item)) - }) - .collect::>()?; - keyed.sort_by(|(ka, _), (kb, _)| match (ka, kb) { - (Value::Number(a), Value::Number(b)) => { - b.partial_cmp(a).unwrap_or(std::cmp::Ordering::Equal) - } - (Value::Text(a), Value::Text(b)) => b.cmp(a), - _ => std::cmp::Ordering::Equal, - }); - return Ok(Value::List(Arc::new( - keyed.into_iter().map(|(_, v)| v).collect(), - ))); - } if builtin == Some(Builtin::Slc) && args.len() == 3 { // Bounds accept negative integers Python-style, with one ergonomic // exception on the end bound: @@ -5712,478 +5607,6 @@ fn call_function(env: &mut Env, name: &str, args: Vec) -> Result { return Ok(Value::Ok(Box::new(Value::Map(Arc::new(map))))); } - // Higher-order builtins: map, flt, fld - // A function reference can be Value::FnRef(name) or Value::Text(Arc::new(name)) when the - // function name was passed as a CLI string argument. Inline lambdas with - // free-var capture produce Value::Closure, which carries the lifted fn - // name plus by-value capture snapshots to append after the per-item args. - fn resolve_fn_ref(val: &Value) -> Option { - match val { - Value::FnRef(n) => Some(n.clone()), - Value::Text(n) => Some((**n).clone()), - Value::Closure { fn_name, .. } => Some(fn_name.clone()), - _ => None, - } - } - // Extract trailing captures from a closure value, if any. Closures carry - // by-value snapshots of free variables that get appended after the - // per-item args at each HOF call. Returns empty for plain FnRef/Text. - fn closure_captures(val: &Value) -> Vec { - match val { - Value::Closure { captures, .. } => captures.clone(), - _ => Vec::new(), - } - } - if builtin == Some(Builtin::Map) && (args.len() == 2 || args.len() == 3) { - let fn_name = resolve_fn_ref(&args[0]).ok_or_else(|| { - RuntimeError::new( - "ILO-R009", - format!( - "map: first arg must be a function reference, got {:?}", - args[0] - ), - ) - })?; - let captures = closure_captures(&args[0]); - // closure-bind: map fn ctx xs - let (ctx, list_arg) = if args.len() == 3 { - (Some(args[1].clone()), &args[2]) - } else { - (None, &args[1]) - }; - let items = match list_arg { - Value::List(l) => l.clone(), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("map: list arg must be a list, got {:?}", other), - )); - } - }; - let mut result = Vec::with_capacity(items.len()); - for item in items.iter().cloned() { - let mut call_args = match &ctx { - Some(c) => vec![item, c.clone()], - None => vec![item], - }; - call_args.extend(captures.iter().cloned()); - result.push(call_function(env, &fn_name, call_args)?); - } - return Ok(Value::List(Arc::new(result))); - } - // mapr fn xs: short-circuiting Result-aware map. - // - // The callee must return R b e. On each item: - // ~v → unwrap to v and accumulate - // ^e → return ^e immediately (whole call short-circuits) - // any → runtime error (callee broke its contract) - // - // Final return on the all-Ok path is ~(L b). Pair with `!` to thread - // the err up into a Result-returning caller. Retires the - // `ton s:t>n;r=num s;?r{~v:v;^_:0}` helper that html-scraper and - // CSV-parsing personas kept writing. See ilo_assessment_feedback.md - // line 2541 for the originating entry. - // - // Deliberately 2-arity only: no closure-bind ctx variant yet. If a - // workload turns up that needs it, add it the same way `Map`/`Flt` - // have it. Keeping the surface tight until a real need surfaces. - if builtin == Some(Builtin::Mapr) && args.len() == 2 { - let fn_name = resolve_fn_ref(&args[0]).ok_or_else(|| { - RuntimeError::new( - "ILO-R009", - format!( - "mapr: first arg must be a function reference, got {:?}", - args[0] - ), - ) - })?; - let captures = closure_captures(&args[0]); - let items = match &args[1] { - Value::List(l) => l.clone(), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("mapr: list arg must be a list, got {:?}", other), - )); - } - }; - let mut result = Vec::with_capacity(items.len()); - for item in items.iter().cloned() { - let mut call_args = vec![item]; - call_args.extend(captures.iter().cloned()); - match call_function(env, &fn_name, call_args)? { - Value::Ok(inner) => result.push(*inner), - Value::Err(e) => return Ok(Value::Err(e)), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("mapr: fn must return a Result (~v or ^e), got {:?}", other), - )); - } - } - } - return Ok(Value::Ok(Box::new(Value::List(Arc::new(result))))); - } - if builtin == Some(Builtin::Flt) && (args.len() == 2 || args.len() == 3) { - let fn_name = resolve_fn_ref(&args[0]).ok_or_else(|| { - RuntimeError::new( - "ILO-R009", - format!( - "flt: first arg must be a function reference, got {:?}", - args[0] - ), - ) - })?; - let captures = closure_captures(&args[0]); - // Consume args so we can move the input List Arc and try Arc::make_mut - // for the RC=1 in-place compact fast path. When the input is sole-owned - // we mutate the underlying Vec via Vec::retain — N kept items skip the - // per-item Value::clone() the cold path pays. When shared we fall - // through to a fresh allocation. - let mut args_iter = args.into_iter(); - let fn_arg = args_iter.next().unwrap(); - let _ = fn_arg; - let (ctx, list_arg) = if args_iter.len() == 2 { - let c = args_iter.next().unwrap(); - let l = args_iter.next().unwrap(); - (Some(c), l) - } else { - (None, args_iter.next().unwrap()) - }; - let mut items = match list_arg { - Value::List(l) => l, - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("flt: list arg must be a list, got {:?}", other), - )); - } - }; - // First pass: evaluate the predicate once per item, recording a bitmap - // of keeps so we never call the predicate twice. The predicate may - // panic — we don't reorder side effects relative to the cold path. - let mut keep: Vec = Vec::with_capacity(items.len()); - for item in items.iter() { - let mut call_args = match &ctx { - Some(c) => vec![item.clone(), c.clone()], - None => vec![item.clone()], - }; - call_args.extend(captures.iter().cloned()); - match call_function(env, &fn_name, call_args)? { - Value::Bool(true) => keep.push(true), - Value::Bool(false) => keep.push(false), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("flt: predicate must return bool, got {:?}", other), - )); - } - } - } - // Second pass: branch on RC. Sole-owned -> retain in place (zero - // per-item Value::clone()s). Shared -> clone-collect kept items only - // (cheaper than cloning the full Vec when many items are dropped). - return if Arc::strong_count(&items) == 1 { - let inner = Arc::make_mut(&mut items); - let mut idx = 0usize; - inner.retain(|_| { - let k = keep[idx]; - idx += 1; - k - }); - Ok(Value::List(items)) - } else { - let mut result = Vec::with_capacity(keep.iter().filter(|k| **k).count()); - for (item, &k) in items.iter().zip(keep.iter()) { - if k { - result.push(item.clone()); - } - } - Ok(Value::List(Arc::new(result))) - }; - } - if builtin == Some(Builtin::Ct) && (args.len() == 2 || args.len() == 3) { - // ct fn xs / ct fn ctx xs → number of elements where fn returns true. - // Mirrors flt's predicate semantics exactly; only the accumulator - // shape differs (counter instead of pushing onto a result list). - // Motivating shape: bioinformatics rerun6 `tm=ct has-tm seqs` saves - // the L b allocation that `len (flt has-tm seqs)` pays for. - // - // Named `ct` (not `cnt`) because `cnt` is reserved as the loop - // continue keyword — see src/parser/mod.rs:3507. - let fn_name = resolve_fn_ref(&args[0]).ok_or_else(|| { - RuntimeError::new( - "ILO-R009", - format!( - "ct: first arg must be a function reference, got {:?}", - args[0] - ), - ) - })?; - let captures = closure_captures(&args[0]); - let (ctx, list_arg) = if args.len() == 3 { - (Some(args[1].clone()), &args[2]) - } else { - (None, &args[1]) - }; - let items = match list_arg { - Value::List(l) => l.clone(), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("ct: list arg must be a list, got {:?}", other), - )); - } - }; - let mut count: i64 = 0; - for item in items.iter() { - let mut call_args = match &ctx { - Some(c) => vec![item.clone(), c.clone()], - None => vec![item.clone()], - }; - call_args.extend(captures.iter().cloned()); - match call_function(env, &fn_name, call_args)? { - Value::Bool(true) => count += 1, - Value::Bool(false) => {} - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("ct: predicate must return bool, got {:?}", other), - )); - } - } - } - return Ok(Value::Number(count as f64)); - } - if builtin == Some(Builtin::Fld) && (args.len() == 3 || args.len() == 4) { - let fn_name = resolve_fn_ref(&args[0]).ok_or_else(|| { - RuntimeError::new( - "ILO-R009", - format!( - "fld: first arg must be a function reference, got {:?}", - args[0] - ), - ) - })?; - let captures = closure_captures(&args[0]); - // closure-bind: fld fn ctx xs init - let (ctx, list_arg, init) = if args.len() == 4 { - (Some(args[1].clone()), &args[2], args[3].clone()) - } else { - (None, &args[1], args[2].clone()) - }; - let items = match list_arg { - Value::List(l) => l.clone(), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("fld: list arg must be a list, got {:?}", other), - )); - } - }; - let mut acc = init; - for item in items.iter() { - let mut call_args = match &ctx { - Some(c) => vec![acc, item.clone(), c.clone()], - None => vec![acc, item.clone()], - }; - call_args.extend(captures.iter().cloned()); - acc = call_function(env, &fn_name, call_args)?; - } - return Ok(acc); - } - - if builtin == Some(Builtin::Partition) && args.len() == 2 { - let fn_name = resolve_fn_ref(&args[0]).ok_or_else(|| { - RuntimeError::new( - "ILO-R009", - format!( - "partition: first arg must be a function reference, got {:?}", - args[0] - ), - ) - })?; - let captures = closure_captures(&args[0]); - let items = match &args[1] { - Value::List(l) => l.clone(), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("partition: second arg must be a list, got {:?}", other), - )); - } - }; - let mut pass: Vec = Vec::new(); - let mut fail: Vec = Vec::new(); - for item in items.iter() { - let mut call_args = vec![item.clone()]; - call_args.extend(captures.iter().cloned()); - match call_function(env, &fn_name, call_args)? { - Value::Bool(true) => pass.push(item.clone()), - Value::Bool(false) => fail.push(item.clone()), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("partition: predicate must return bool, got {:?}", other), - )); - } - } - } - return Ok(Value::List(Arc::new(vec![ - Value::List(Arc::new(pass)), - Value::List(Arc::new(fail)), - ]))); - } - - if builtin == Some(Builtin::Flatmap) && args.len() == 2 { - let fn_name = resolve_fn_ref(&args[0]).ok_or_else(|| { - RuntimeError::new( - "ILO-R009", - format!( - "flatmap: first arg must be a function reference, got {:?}", - args[0] - ), - ) - })?; - let captures = closure_captures(&args[0]); - let items = match &args[1] { - Value::List(l) => l.clone(), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("flatmap: second arg must be a list, got {:?}", other), - )); - } - }; - let mut result: Vec = Vec::new(); - for item in items.iter().cloned() { - let mut call_args = vec![item]; - call_args.extend(captures.iter().cloned()); - match call_function(env, &fn_name, call_args)? { - Value::List(inner) => result.extend(inner.iter().cloned()), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("flatmap: function must return a list, got {:?}", other), - )); - } - } - } - return Ok(Value::List(Arc::new(result))); - } - - if builtin == Some(Builtin::Uniqby) && args.len() == 2 { - let fn_name = resolve_fn_ref(&args[0]).ok_or_else(|| { - RuntimeError::new( - "ILO-R009", - format!( - "uniqby: first arg must be a function reference, got {:?}", - args[0] - ), - ) - })?; - let captures = closure_captures(&args[0]); - let items = match &args[1] { - Value::List(l) => l.clone(), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("uniqby: second arg must be a list, got {:?}", other), - )); - } - }; - let mut seen: std::collections::HashSet = std::collections::HashSet::new(); - let mut out: Vec = Vec::new(); - for item in items.iter() { - let mut call_args = vec![item.clone()]; - call_args.extend(captures.iter().cloned()); - let key = call_function(env, &fn_name, call_args)?; - // Prefix the hashed key with a type tag so values from distinct - // domains never alias each other. Without this, `Number(5)` and - // `Text("5")` both stringify to `"5"` and collide. - let key_str = match &key { - Value::Text(s) => format!("t:{s}"), - Value::Number(n) => { - if *n == (*n as i64) as f64 { - format!("n:{}", *n as i64) - } else { - format!("n:{n}") - } - } - Value::Bool(b) => format!("b:{b}"), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!( - "uniqby: key function must return a string, number, or bool, got {:?}", - other - ), - )); - } - }; - if seen.insert(key_str) { - out.push(item.clone()); - } - } - return Ok(Value::List(Arc::new(out))); - } - - if builtin == Some(Builtin::Grp) && args.len() == 2 { - let fn_name = resolve_fn_ref(&args[0]).ok_or_else(|| { - RuntimeError::new( - "ILO-R009", - format!( - "grp: first arg must be a function reference, got {:?}", - args[0] - ), - ) - })?; - let captures = closure_captures(&args[0]); - let items = match &args[1] { - Value::List(l) => l.clone(), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!("grp: second arg must be a list, got {:?}", other), - )); - } - }; - let mut groups: std::collections::HashMap> = - std::collections::HashMap::new(); - for item in items.iter() { - let mut call_args = vec![item.clone()]; - call_args.extend(captures.iter().cloned()); - let key = call_function(env, &fn_name, call_args)?; - let map_key = match &key { - Value::Text(s) => MapKey::Text((**s).clone()), - Value::Number(n) => { - if !n.is_finite() { - return Err(RuntimeError::new( - "ILO-R009", - format!("grp: numeric key must be finite, got {n}"), - )); - } - MapKey::Int(n.floor() as i64) - } - Value::Bool(b) => MapKey::Text(format!("{b}")), - other => { - return Err(RuntimeError::new( - "ILO-R009", - format!( - "grp: key function must return a string, number, or bool, got {:?}", - other - ), - )); - } - }; - groups.entry(map_key).or_default().push(item.clone()); - } - let map: HashMap = groups - .into_iter() - .map(|(k, v)| (k, Value::List(Arc::new(v)))) - .collect(); - return Ok(Value::Map(Arc::new(map))); - } if builtin == Some(Builtin::Frq) && args.len() == 1 { let items = match &args[0] { Value::List(l) => l, @@ -11851,145 +11274,11 @@ mod tests { } #[test] - fn interp_map_squares() { - // map sq over [1,2,3,4,5] → [1,4,9,16,25] - let source = "sq x:n>n;*x x main xs:L n>L n;map sq xs"; - let result = run_str( - source, - Some("main"), - vec![Value::List(Arc::new( - vec![1.0, 2.0, 3.0, 4.0, 5.0] - .into_iter() - .map(Value::Number) - .collect(), - ))], - ); - assert_eq!( - result, - Value::List(Arc::new( - vec![1.0, 4.0, 9.0, 16.0, 25.0] - .into_iter() - .map(Value::Number) - .collect() - )) - ); - } - #[test] - fn interp_flt_positive() { - // flt pos over [-3,-1,0,2,4] → [2,4] - let source = "pos x:n>b;>x 0 main xs:L n>L n;flt pos xs"; - let result = run_str( - source, - Some("main"), - vec![Value::List(Arc::new( - vec![-3.0, -1.0, 0.0, 2.0, 4.0] - .into_iter() - .map(Value::Number) - .collect(), - ))], - ); - assert_eq!( - result, - Value::List(Arc::new( - vec![2.0, 4.0].into_iter().map(Value::Number).collect() - )) - ); - } - #[test] - fn interp_fld_sum() { - // fld add over [1..5] with init 0 → 15 - let source = "add a:n b:n>n;+a b main xs:L n>n;fld add xs 0"; - let result = run_str( - source, - Some("main"), - vec![Value::List(Arc::new( - vec![1.0, 2.0, 3.0, 4.0, 5.0] - .into_iter() - .map(Value::Number) - .collect(), - ))], - ); - assert_eq!(result, Value::Number(15.0)); - } - #[test] - fn interp_grp_by_string_key() { - // group numbers into "big" and "small" based on > 5 - let source = r#"cl x:n>t;>x 5{"big"}{"small"} main xs:L n>M t L n;grp cl xs"#; - let result = run_str( - source, - Some("main"), - vec![Value::List(Arc::new( - vec![1.0, 8.0, 3.0, 9.0, 2.0] - .into_iter() - .map(Value::Number) - .collect(), - ))], - ); - let Value::Map(m) = result else { - panic!("expected Map") - }; - assert_eq!( - m.get(&MapKey::Text("small".to_string())).unwrap(), - &Value::List(Arc::new( - vec![1.0, 3.0, 2.0].into_iter().map(Value::Number).collect() - )) - ); - assert_eq!( - m.get(&MapKey::Text("big".to_string())).unwrap(), - &Value::List(Arc::new( - vec![8.0, 9.0].into_iter().map(Value::Number).collect() - )) - ); - } - #[test] - fn interp_grp_by_numeric_key() { - // group by str(x) — each number becomes its own group - let source = "key x:n>t;str x main xs:L n>M t L n;grp key xs"; - let result = run_str( - source, - Some("main"), - vec![Value::List(Arc::new( - vec![1.0, 2.0, 1.0, 3.0, 2.0] - .into_iter() - .map(Value::Number) - .collect(), - ))], - ); - let Value::Map(m) = result else { - panic!("expected Map") - }; - assert_eq!( - m.get(&MapKey::Text("1".to_string())).unwrap(), - &Value::List(Arc::new( - vec![1.0, 1.0].into_iter().map(Value::Number).collect() - )) - ); - assert_eq!( - m.get(&MapKey::Text("2".to_string())).unwrap(), - &Value::List(Arc::new( - vec![2.0, 2.0].into_iter().map(Value::Number).collect() - )) - ); - assert_eq!( - m.get(&MapKey::Text("3".to_string())).unwrap(), - &Value::List(Arc::new(vec![3.0].into_iter().map(Value::Number).collect())) - ); - } - #[test] - fn interp_grp_empty_list() { - let source = "id x:n>t;str x main xs:L n>M t L n;grp id xs"; - let result = run_str(source, Some("main"), vec![Value::List(Arc::new(vec![]))]); - assert_eq!( - result, - Value::Map(Arc::new(std::collections::HashMap::new())) - ); - } - #[test] fn interp_grp_wrong_fn_arg() { let err = run_str_err("f>t;grp 42 [1, 2, 3]", Some("f"), vec![]); @@ -12530,52 +11819,8 @@ mod tests { // --- srt fn xs --- #[test] - fn interpret_srt_fn_by_length() { - let source = "ln s:t>n;len s main xs:L t>L t;srt ln xs"; - let result = run_str( - source, - Some("main"), - vec![Value::List(Arc::new(vec![ - Value::Text(Arc::new("banana".to_string())), - Value::Text(Arc::new("a".to_string())), - Value::Text(Arc::new("cc".to_string())), - ]))], - ); - assert_eq!( - result, - Value::List(Arc::new(vec![ - Value::Text(Arc::new("a".to_string())), - Value::Text(Arc::new("cc".to_string())), - Value::Text(Arc::new("banana".to_string())), - ])) - ); - } - #[test] - fn interpret_srt_fn_numeric_key() { - let source = "neg x:n>n;-x main xs:L n>L n;srt neg xs"; - let result = run_str( - source, - Some("main"), - vec![Value::List(Arc::new(vec![ - Value::Number(1.0), - Value::Number(3.0), - Value::Number(2.0), - ]))], - ); - // sort by negative: highest first - assert_eq!( - result, - Value::List(Arc::new(vec![ - Value::Number(3.0), - Value::Number(2.0), - Value::Number(1.0) - ])) - ); - } - // --- prnt --- - #[test] fn interpret_prnt_returns_value() { let result = run_str("f x:n>n;prnt x", Some("f"), vec![Value::Number(7.0)]); @@ -13066,27 +12311,6 @@ mod tests { // L582-583: srt with key fn — text keys #[test] - fn interpret_srt_key_fn_text_keys() { - let source = "id x:t>t;x main xs:L t>L t;srt id xs"; - let result = run_str( - source, - Some("main"), - vec![Value::List(Arc::new(vec![ - Value::Text(Arc::new("banana".to_string())), - Value::Text(Arc::new("apple".to_string())), - Value::Text(Arc::new("cherry".to_string())), - ]))], - ); - assert_eq!( - result, - Value::List(Arc::new(vec![ - Value::Text(Arc::new("apple".to_string())), - Value::Text(Arc::new("banana".to_string())), - Value::Text(Arc::new("cherry".to_string())), - ])) - ); - } - // L622: get with invalid (non-map) headers #[test] fn interpret_get_invalid_headers() { @@ -13662,22 +12886,7 @@ mod tests { // ── resolve_fn_ref Text path (line 948) via map with text fn name ─────── #[test] - fn interpret_map_with_text_fn_name() { - // Pass fn name as text arg; resolve_fn_ref hits Text branch (line 948) - let source = "sq x:n>n;*x x f cb:t xs:L n>L n;map cb xs"; - let result = run_str( - source, - Some("f"), - vec![ - Value::Text(Arc::new("sq".to_string())), - Value::List(Arc::new(vec![Value::Number(3.0)])), - ], - ); - assert_eq!(result, Value::List(Arc::new(vec![Value::Number(9.0)]))); - } - // ── rd 2-arg explicit format (lines 736, 749, 750-751) ────────────────── - #[test] fn interpret_rd_explicit_raw_format() { // Write a temp file, read with explicit "raw" format → lines 736, 749 @@ -13748,46 +12957,8 @@ mod tests { // ── grp Number/Bool key (lines 1012-1016, 1019-1020) ─────────────────── #[test] - fn interpret_grp_number_key() { - // Key fn returns Number → lines 1012-1016 - let source = "id x:n>n;x g xs:L n>_;grp id xs"; - let result = run_str( - source, - Some("g"), - vec![Value::List(Arc::new(vec![ - Value::Number(1.0), - Value::Number(2.0), - Value::Number(1.0), - ]))], - ); - let Value::Map(m) = result else { - panic!("expected map") - }; - assert_eq!(m.len(), 2); - } - #[test] - fn interpret_grp_bool_key() { - // Key fn returns Bool → lines 1019-1020 - let source = "pos x:n>b;>x 0 g xs:L n>_;grp pos xs"; - let result = run_str( - source, - Some("g"), - vec![Value::List(Arc::new(vec![ - Value::Number(-1.0), - Value::Number(1.0), - Value::Number(2.0), - ]))], - ); - let Value::Map(m) = result else { - panic!("expected map") - }; - assert!(m.contains_key(&MapKey::Text("true".to_string()))); - assert!(m.contains_key(&MapKey::Text("false".to_string()))); - } - // ── avg non-number element (line 1053) ────────────────────────────────── - #[test] fn interpret_avg_non_number_element() { let err = run_str_err( @@ -13912,30 +13083,7 @@ mod tests { // ── grp — float key (line 1016) ────────────────────────────────────────── #[test] - fn interpret_grp_float_key() { - // Key function returns a fractional number — under MapKey numeric - // keys floor to i64 at the boundary, matching `at xs i`. So - // /1 2 = 0.5 → 0, /2 2 = 1 → 1, /3 2 = 1.5 → 1. Two distinct groups. - let source = "half x:n>n;/x 2 g xs:L n>_;grp half xs"; - let result = run_str( - source, - Some("g"), - vec![Value::List(Arc::new(vec![ - Value::Number(1.0), - Value::Number(2.0), - Value::Number(3.0), - ]))], - ); - let Value::Map(m) = result else { - panic!("expected Map") - }; - assert_eq!(m.len(), 2); - assert!(m.contains_key(&MapKey::Int(0))); - assert!(m.contains_key(&MapKey::Int(1))); - } - // ── ForRange early return (lines 1370-1371) ─────────────────────────────── - #[test] fn interpret_for_range_early_return_via_guard() { // Use `ret` inside braced guard for early return from loop. @@ -14153,19 +13301,7 @@ mod tests { // ── srt with bool key hits _ => Equal arm (line 583) ───────────────────── #[test] - fn interpret_srt_bool_key_equal_ordering() { - // Key fn returns Bool → neither Number nor Text arm matches in sort_by → L583 _ => Equal - let source = "pos x:n>b;> x 0 f>L n;srt pos [3,-1,2,-2]"; - let result = run_str(source, Some("f"), vec![]); - // All elements are compared as Bool keys → Equal ordering → list unchanged - let Value::List(items) = result else { - panic!("expected List, got {:?}", result) - }; - assert_eq!(items.len(), 4); - } - // ── brk inside guard body propagates Break (line 1287) ─────────────────── - #[test] fn interpret_brk_inside_guard_body_propagates() { // Guard body containing brk: when x>2, break with x → ForEach exits early (L1287) From be3c4253915b5b5f9d1b89df779a46dc3b871ff4 Mon Sep 17 00:00:00 2001 From: Daniel Morris Date: Fri, 22 May 2026 09:38:49 +0100 Subject: [PATCH 2/2] fix: orphan #[test] and #[cfg(not(wasm))] annotations from awk delete My PR D delete script stripped 14 failing tests but left two artefacts: - 20 duplicate `#[test]` lines stacked above other tests (the saved-but- never-cleared bug in the awk script). - 6 `run2_*` tests had `#[test]` then `#[cfg(not(target_family="wasm"))]` ordering; my awk's pending-attribute logic discarded `#[test]` because the next line wasn't `fn`. Restored. cargo clippy --release --features cranelift -- -D warnings clean. --- src/runtime/mod.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 38276f29b..272bfa0e5 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -11273,12 +11273,6 @@ mod tests { assert_eq!(result, Value::Number(42.0)); } - #[test] - #[test] - #[test] - #[test] - #[test] - #[test] #[test] fn interp_grp_wrong_fn_arg() { let err = run_str_err("f>t;grp 42 [1, 2, 3]", Some("f"), vec![]); @@ -11818,8 +11812,6 @@ mod tests { // --- srt fn xs --- - #[test] - #[test] // --- prnt --- #[test] fn interpret_prnt_returns_value() { @@ -12310,7 +12302,6 @@ mod tests { } // L582-583: srt with key fn — text keys - #[test] // L622: get with invalid (non-map) headers #[test] fn interpret_get_invalid_headers() { @@ -12885,7 +12876,6 @@ mod tests { // ── resolve_fn_ref Text path (line 948) via map with text fn name ─────── - #[test] // ── rd 2-arg explicit format (lines 736, 749, 750-751) ────────────────── #[test] fn interpret_rd_explicit_raw_format() { @@ -12956,8 +12946,6 @@ mod tests { // ── grp Number/Bool key (lines 1012-1016, 1019-1020) ─────────────────── - #[test] - #[test] // ── avg non-number element (line 1053) ────────────────────────────────── #[test] fn interpret_avg_non_number_element() { @@ -13082,7 +13070,6 @@ mod tests { // ── grp — float key (line 1016) ────────────────────────────────────────── - #[test] // ── ForRange early return (lines 1370-1371) ─────────────────────────────── #[test] fn interpret_for_range_early_return_via_guard() { @@ -13300,7 +13287,6 @@ mod tests { // ── srt with bool key hits _ => Equal arm (line 583) ───────────────────── - #[test] // ── brk inside guard body propagates Break (line 1287) ─────────────────── #[test] fn interpret_brk_inside_guard_body_propagates() {