#[cfg(test)]
mod tests;
use crate::{
compiler::{Card, ForEach, Function, Module},
procedures::ExecutionErrorPayload,
value::Value,
vm::{runtime::cao_lang_object::CaoLangObjectBody, Vm},
};
pub fn filter() -> Function {
Function::default()
.with_arg("iterable")
.with_arg("callback")
.with_cards(vec![
Card::set_var("res", Card::CreateTable),
Card::ForEach(Box::new(ForEach {
i: Some("i".to_string()),
k: Some("k".to_string()),
v: Some("v".to_string()),
iterable: Box::new(Card::read_var("iterable")),
body: Box::new(Card::composite_card(
"_",
vec![Card::IfTrue(Box::new([
Card::dynamic_call(
Card::read_var("callback"),
vec![
Card::read_var("i"),
Card::read_var("v"),
Card::read_var("k"),
],
),
Card::set_property(
Card::read_var("v"),
Card::read_var("res"),
Card::read_var("k"),
),
]))],
)),
})),
Card::return_card(Card::read_var("res")),
])
}
pub fn any() -> Function {
Function::default()
.with_arg("iterable")
.with_arg("callback")
.with_cards(vec![
Card::set_var("res", Card::CreateTable),
Card::ForEach(Box::new(ForEach {
i: Some("i".to_string()),
k: Some("k".to_string()),
v: Some("v".to_string()),
iterable: Box::new(Card::read_var("iterable")),
body: Box::new(Card::composite_card(
"_",
vec![Card::IfTrue(Box::new([
Card::dynamic_call(
Card::read_var("callback"),
vec![
Card::read_var("i"),
Card::read_var("v"),
Card::read_var("k"),
],
),
Card::return_card(Card::read_var("k")),
]))],
)),
})),
Card::return_card(Card::ScalarNil),
])
}
pub fn map() -> Function {
Function::default()
.with_arg("iterable")
.with_arg("callback")
.with_cards(vec![
Card::set_var("res", Card::CreateTable),
Card::ForEach(Box::new(ForEach {
i: Some("i".to_string()),
k: Some("k".to_string()),
v: Some("v".to_string()),
iterable: Box::new(Card::read_var("iterable")),
body: Box::new(Card::composite_card(
"_",
vec![Card::set_property(
Card::composite_card(
"",
vec![Card::dynamic_call(
Card::read_var("callback"),
vec![
Card::read_var("i"),
Card::read_var("v"),
Card::read_var("k"),
],
)],
),
Card::read_var("res"),
Card::read_var("k"),
)],
)),
})),
Card::return_card(Card::read_var("res")),
])
}
fn minmax(minimax: &str) -> Function {
Function::default()
.with_arg("iterable")
.with_card(Card::return_card(Card::call_function(
minimax,
vec![
Card::function_value("row_to_value"),
Card::read_var("iterable"),
],
)))
}
pub fn min() -> Function {
minmax("min_by_key")
}
pub fn max() -> Function {
minmax("max_by_key")
}
pub fn sorted() -> Function {
Function::default()
.with_arg("iterable")
.with_card(Card::return_card(Card::call_function(
"sorted_by_key",
vec![
Card::function_value("row_to_value"),
Card::read_var("iterable"),
],
)))
}
pub fn native_minmax<T, const LESS: bool>(
vm: &mut Vm<T>,
iterable: Value,
key_fn: Value,
) -> Result<Value, ExecutionErrorPayload> {
match iterable {
Value::Nil | Value::Integer(_) | Value::Real(_) => Ok(iterable),
Value::Object(o) => unsafe {
match &o.as_ref().body {
CaoLangObjectBody::Table(t) => {
let Some(first) = t.iter().next() else {
return Ok(Value::Nil);
};
vm.stack_push(*first.1)?;
vm.stack_push(*first.0)?;
let mut max_key = vm.run_function(key_fn)?;
let mut i = 0;
for (j, (k, value)) in t.iter().enumerate().skip(1) {
vm.stack_push(*value)?;
vm.stack_push(*k)?;
let key = vm.run_function(key_fn)?;
if if LESS { key < max_key } else { key > max_key } {
i = j;
max_key = key;
}
}
let k = t.nth_key(i);
let v = *t.get(&k).unwrap();
let mut result = vm.init_table()?;
let t = result.0.as_mut().as_table_mut().unwrap();
t.insert(vm.init_string("key")?, k)?;
t.insert(vm.init_string("value")?, v)?;
Ok(Value::Object(result.0))
}
CaoLangObjectBody::String(_)
| CaoLangObjectBody::Function(_)
| CaoLangObjectBody::Closure(_)
| CaoLangObjectBody::Upvalue(_)
| CaoLangObjectBody::NativeFunction(_) => Ok(iterable),
}
},
}
}
pub fn native_sorted<T>(
vm: &mut Vm<T>,
iterable: Value,
key_fn: Value,
) -> Result<Value, ExecutionErrorPayload> {
match iterable {
Value::Nil | Value::Integer(_) | Value::Real(_) => Ok(iterable),
Value::Object(o) => unsafe {
match &o.as_ref().body {
CaoLangObjectBody::Table(t) => {
let mut result = Vec::with_capacity(t.len());
for (k, v) in t.iter() {
vm.stack_push(*v)?;
vm.stack_push(*k)?;
let key = vm.run_function(key_fn)?;
result.push((key, k, v));
}
result.sort_by(|(a, _, _), (b, _, _)| {
a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
});
let mut out = vm.init_table()?;
let t = out.as_table_mut().unwrap();
for (_, k, v) in result {
t.insert(*k, *v)?;
}
Ok(Value::Object(out.0))
}
CaoLangObjectBody::String(_) | CaoLangObjectBody::Function(_)
| CaoLangObjectBody::Closure(_)
| CaoLangObjectBody::Upvalue(_)
| CaoLangObjectBody::NativeFunction(_) => Ok(iterable),
}
},
}
}
pub fn native_to_array<T>(vm: &mut Vm<T>, iterable: Value) -> Result<Value, ExecutionErrorPayload> {
match iterable {
Value::Nil | Value::Integer(_) | Value::Real(_) => Ok(iterable),
Value::Object(o) => unsafe {
match &o.as_ref().body {
CaoLangObjectBody::Table(t) => {
let mut out = vm.init_table()?;
let table = out.as_table_mut().unwrap();
for (i, (_, val)) in t.iter().enumerate() {
table.insert(i as i64, *val)?;
}
Ok(Value::Object(out.0))
}
CaoLangObjectBody::String(_)
| CaoLangObjectBody::Function(_)
| CaoLangObjectBody::Closure(_)
| CaoLangObjectBody::Upvalue(_)
| CaoLangObjectBody::NativeFunction(_) => Ok(iterable),
}
},
}
}
pub fn min_by_key() -> Function {
Function::default()
.with_arg("iterable")
.with_arg("key_function")
.with_card(Card::return_card(Card::call_native(
"__min",
vec![Card::read_var("iterable"), Card::read_var("key_function")],
)))
}
pub fn sorted_by_key() -> Function {
Function::default()
.with_arg("iterable")
.with_arg("key_function")
.with_card(Card::return_card(Card::call_native(
"__sort",
vec![Card::read_var("iterable"), Card::read_var("key_function")],
)))
}
pub fn max_by_key() -> Function {
Function::default()
.with_arg("iterable")
.with_arg("key_function")
.with_card(Card::return_card(Card::call_native(
"__max",
vec![Card::read_var("iterable"), Card::read_var("key_function")],
)))
}
pub fn value_key_fn() -> Function {
Function::default()
.with_arg("_key")
.with_arg("val")
.with_card(Card::return_card(Card::read_var("val")))
}
pub fn to_array() -> Function {
Function::default()
.with_arg("iterable")
.with_card(Card::return_card(Card::call_native(
"__to_array",
vec![Card::read_var("iterable")],
)))
}
pub fn standard_library() -> Module {
let mut module = Module::default();
module.functions.push(("to_array".to_string(), to_array()));
module.functions.push(("filter".to_string(), filter()));
module.functions.push(("any".to_string(), any()));
module.functions.push(("map".to_string(), map()));
module.functions.push(("min".to_string(), min()));
module.functions.push(("max".to_string(), max()));
module
.functions
.push(("min_by_key".to_string(), min_by_key()));
module
.functions
.push(("max_by_key".to_string(), max_by_key()));
module
.functions
.push(("sorted_by_key".to_string(), sorted_by_key()));
module.functions.push(("sorted".to_string(), sorted()));
module
.functions
.push(("row_to_value".to_string(), value_key_fn()));
module
}