feat: add List.sort and List.sortBy functions (issue 9)
Add sorting support to the List module across all backends: - List.sort for natural ordering (Int, Float, String, Bool, Char) - List.sortBy for custom comparator-based sorting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,8 @@ pub enum BuiltinFn {
|
||||
ListGet,
|
||||
ListRange,
|
||||
ListForEach,
|
||||
ListSort,
|
||||
ListSortBy,
|
||||
|
||||
// String operations
|
||||
StringSplit,
|
||||
@@ -980,6 +982,11 @@ impl Interpreter {
|
||||
"forEach".to_string(),
|
||||
Value::Builtin(BuiltinFn::ListForEach),
|
||||
),
|
||||
("sort".to_string(), Value::Builtin(BuiltinFn::ListSort)),
|
||||
(
|
||||
"sortBy".to_string(),
|
||||
Value::Builtin(BuiltinFn::ListSortBy),
|
||||
),
|
||||
]));
|
||||
env.define("List", list_module);
|
||||
|
||||
@@ -2742,6 +2749,67 @@ impl Interpreter {
|
||||
Ok(EvalResult::Value(Value::Unit))
|
||||
}
|
||||
|
||||
BuiltinFn::ListSort => {
|
||||
// List.sort(list) - sort using natural ordering (Int, Float, String, Bool)
|
||||
let mut list =
|
||||
Self::expect_arg_1::<Vec<Value>>(&args, "List.sort", span)?;
|
||||
list.sort_by(|a, b| Self::compare_values(a, b));
|
||||
Ok(EvalResult::Value(Value::List(list)))
|
||||
}
|
||||
|
||||
BuiltinFn::ListSortBy => {
|
||||
// List.sortBy(list, fn(a, b) => Int) - sort with custom comparator
|
||||
// Comparator returns negative (a < b), 0 (a == b), or positive (a > b)
|
||||
let (list, func) =
|
||||
Self::expect_args_2::<Vec<Value>, Value>(&args, "List.sortBy", span)?;
|
||||
let mut indexed: Vec<(usize, Value)> =
|
||||
list.into_iter().enumerate().collect();
|
||||
let mut err: Option<RuntimeError> = None;
|
||||
let func_ref = &func;
|
||||
let self_ptr = self as *mut Self;
|
||||
indexed.sort_by(|a, b| {
|
||||
if err.is_some() {
|
||||
return std::cmp::Ordering::Equal;
|
||||
}
|
||||
// Safety: we're in a single-threaded context and the closure
|
||||
// needs mutable access to call eval_call_to_value
|
||||
let interp = unsafe { &mut *self_ptr };
|
||||
match interp.eval_call_to_value(
|
||||
func_ref.clone(),
|
||||
vec![a.1.clone(), b.1.clone()],
|
||||
span,
|
||||
) {
|
||||
Ok(Value::Int(n)) => {
|
||||
if n < 0 {
|
||||
std::cmp::Ordering::Less
|
||||
} else if n > 0 {
|
||||
std::cmp::Ordering::Greater
|
||||
} else {
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
Ok(_) => {
|
||||
err = Some(RuntimeError {
|
||||
message: "List.sortBy comparator must return Int"
|
||||
.to_string(),
|
||||
span: Some(span),
|
||||
});
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
Err(e) => {
|
||||
err = Some(e);
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
});
|
||||
if let Some(e) = err {
|
||||
return Err(e);
|
||||
}
|
||||
let result: Vec<Value> =
|
||||
indexed.into_iter().map(|(_, v)| v).collect();
|
||||
Ok(EvalResult::Value(Value::List(result)))
|
||||
}
|
||||
|
||||
// Additional String operations
|
||||
BuiltinFn::StringStartsWith => {
|
||||
let (s, prefix) = Self::expect_args_2::<String, String>(&args, "String.startsWith", span)?;
|
||||
@@ -3357,6 +3425,18 @@ impl Interpreter {
|
||||
})
|
||||
}
|
||||
|
||||
/// Compare two values for natural ordering (used by List.sort)
|
||||
fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
|
||||
match (a, b) {
|
||||
(Value::Int(x), Value::Int(y)) => x.cmp(y),
|
||||
(Value::Float(x), Value::Float(y)) => x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal),
|
||||
(Value::String(x), Value::String(y)) => x.cmp(y),
|
||||
(Value::Bool(x), Value::Bool(y)) => x.cmp(y),
|
||||
(Value::Char(x), Value::Char(y)) => x.cmp(y),
|
||||
_ => std::cmp::Ordering::Equal,
|
||||
}
|
||||
}
|
||||
|
||||
fn match_pattern(&self, pattern: &Pattern, value: &Value) -> Option<Vec<(String, Value)>> {
|
||||
match pattern {
|
||||
Pattern::Wildcard(_) => Some(Vec::new()),
|
||||
|
||||
Reference in New Issue
Block a user