1#[cfg(test)]
7mod tests;
8
9use crate::{
10 compiler::{Card, ForEach, Function, Module},
11 procedures::ExecutionErrorPayload,
12 value::Value,
13 vm::{runtime::cao_lang_object::CaoLangObjectBody, Vm},
14};
15
16pub fn filter() -> Function {
19 Function::default()
20 .with_arg("iterable")
21 .with_arg("callback")
22 .with_cards(vec![
23 Card::set_var("res", Card::CreateTable),
24 Card::ForEach(Box::new(ForEach {
25 i: Some("i".to_string()),
26 k: Some("k".to_string()),
27 v: Some("v".to_string()),
28 iterable: Box::new(Card::read_var("iterable")),
29 body: Box::new(Card::composite_card(
30 "_",
31 vec![Card::IfTrue(Box::new([
32 Card::dynamic_call(
33 Card::read_var("callback"),
34 vec![
35 Card::read_var("i"),
36 Card::read_var("v"),
37 Card::read_var("k"),
38 ],
39 ),
40 Card::set_property(
41 Card::read_var("v"),
42 Card::read_var("res"),
43 Card::read_var("k"),
44 ),
45 ]))],
46 )),
47 })),
48 Card::return_card(Card::read_var("res")),
49 ])
50}
51
52pub fn any() -> Function {
54 Function::default()
55 .with_arg("iterable")
56 .with_arg("callback")
57 .with_cards(vec![
58 Card::set_var("res", Card::CreateTable),
59 Card::ForEach(Box::new(ForEach {
60 i: Some("i".to_string()),
61 k: Some("k".to_string()),
62 v: Some("v".to_string()),
63 iterable: Box::new(Card::read_var("iterable")),
64 body: Box::new(Card::composite_card(
65 "_",
66 vec![Card::IfTrue(Box::new([
67 Card::dynamic_call(
68 Card::read_var("callback"),
69 vec![
70 Card::read_var("i"),
71 Card::read_var("v"),
72 Card::read_var("k"),
73 ],
74 ),
75 Card::return_card(Card::read_var("k")),
76 ]))],
77 )),
78 })),
79 Card::return_card(Card::ScalarNil),
80 ])
81}
82
83pub fn map() -> Function {
86 Function::default()
87 .with_arg("iterable")
88 .with_arg("callback")
89 .with_cards(vec![
90 Card::set_var("res", Card::CreateTable),
91 Card::ForEach(Box::new(ForEach {
92 i: Some("i".to_string()),
93 k: Some("k".to_string()),
94 v: Some("v".to_string()),
95 iterable: Box::new(Card::read_var("iterable")),
96 body: Box::new(Card::composite_card(
97 "_",
98 vec![Card::set_property(
99 Card::composite_card(
100 "",
101 vec![Card::dynamic_call(
102 Card::read_var("callback"),
103 vec![
104 Card::read_var("i"),
105 Card::read_var("v"),
106 Card::read_var("k"),
107 ],
108 )],
109 ),
110 Card::read_var("res"),
111 Card::read_var("k"),
112 )],
113 )),
114 })),
115 Card::return_card(Card::read_var("res")),
116 ])
117}
118
119fn minmax(minimax: &str) -> Function {
120 Function::default()
121 .with_arg("iterable")
122 .with_card(Card::return_card(Card::call_function(
123 minimax,
124 vec![
125 Card::function_value("row_to_value"),
126 Card::read_var("iterable"),
127 ],
128 )))
129}
130
131pub fn min() -> Function {
133 minmax("min_by_key")
134}
135
136pub fn max() -> Function {
138 minmax("max_by_key")
139}
140
141pub fn sorted() -> Function {
142 Function::default()
143 .with_arg("iterable")
144 .with_card(Card::return_card(Card::call_function(
145 "sorted_by_key",
146 vec![
147 Card::function_value("row_to_value"),
148 Card::read_var("iterable"),
149 ],
150 )))
151}
152
153pub fn native_minmax<T, const LESS: bool>(
154 vm: &mut Vm<T>,
155 iterable: Value,
156 key_fn: Value,
157) -> Result<Value, ExecutionErrorPayload> {
158 match iterable {
159 Value::Nil | Value::Integer(_) | Value::Real(_) => Ok(iterable),
160 Value::Object(o) => unsafe {
161 match &o.as_ref().body {
162 CaoLangObjectBody::Table(t) => {
163 let Some(first) = t.iter().next() else {
164 return Ok(Value::Nil);
165 };
166 vm.stack_push(*first.1)?;
167 vm.stack_push(*first.0)?;
168 let mut max_key = vm.run_function(key_fn)?;
169 let mut i = 0;
170
171 for (j, (k, value)) in t.iter().enumerate().skip(1) {
172 vm.stack_push(*value)?;
173 vm.stack_push(*k)?;
174 let key = vm.run_function(key_fn)?;
175 if if LESS { key < max_key } else { key > max_key } {
176 i = j;
177 max_key = key;
178 }
179 }
180 let k = t.nth_key(i);
181 let v = *t.get(&k).unwrap();
182 let mut result = vm.init_table()?;
183 let t = result.0.as_mut().as_table_mut().unwrap();
184 t.insert(vm.init_string("key")?, k)?;
185 t.insert(vm.init_string("value")?, v)?;
186
187 Ok(Value::Object(result.0))
188 }
189 CaoLangObjectBody::String(_)
190 | CaoLangObjectBody::Function(_)
191 | CaoLangObjectBody::Closure(_)
192 | CaoLangObjectBody::Upvalue(_)
193 | CaoLangObjectBody::NativeFunction(_) => Ok(iterable),
194 }
195 },
196 }
197}
198
199pub fn native_sorted<T>(
200 vm: &mut Vm<T>,
201 iterable: Value,
202 key_fn: Value,
203) -> Result<Value, ExecutionErrorPayload> {
204 match iterable {
205 Value::Nil | Value::Integer(_) | Value::Real(_) => Ok(iterable),
206 Value::Object(o) => unsafe {
207 match &o.as_ref().body {
208 CaoLangObjectBody::Table(t) => {
209 let mut result = Vec::with_capacity(t.len());
212 for (k, v) in t.iter() {
213 vm.stack_push(*v)?;
214 vm.stack_push(*k)?;
215 let key = vm.run_function(key_fn)?;
216 result.push((key, k, v));
217 }
218 result.sort_by(|(a, _, _), (b, _, _)| {
219 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
220 });
221
222 let mut out = vm.init_table()?;
223 let t = out.as_table_mut().unwrap();
224 for (_, k, v) in result {
225 t.insert(*k, *v)?;
226 }
227 Ok(Value::Object(out.0))
228 }
229 CaoLangObjectBody::String(_) | CaoLangObjectBody::Function(_)
231 | CaoLangObjectBody::Closure(_)
232 | CaoLangObjectBody::Upvalue(_)
233 | CaoLangObjectBody::NativeFunction(_) => Ok(iterable),
234 }
235 },
236 }
237}
238
239pub fn native_to_array<T>(vm: &mut Vm<T>, iterable: Value) -> Result<Value, ExecutionErrorPayload> {
240 match iterable {
241 Value::Nil | Value::Integer(_) | Value::Real(_) => Ok(iterable),
242 Value::Object(o) => unsafe {
243 match &o.as_ref().body {
244 CaoLangObjectBody::Table(t) => {
245 let mut out = vm.init_table()?;
246 let table = out.as_table_mut().unwrap();
247 for (i, (_, val)) in t.iter().enumerate() {
248 table.insert(i as i64, *val)?;
249 }
250 Ok(Value::Object(out.0))
251 }
252 CaoLangObjectBody::String(_)
253 | CaoLangObjectBody::Function(_)
254 | CaoLangObjectBody::Closure(_)
255 | CaoLangObjectBody::Upvalue(_)
256 | CaoLangObjectBody::NativeFunction(_) => Ok(iterable),
257 }
258 },
259 }
260}
261
262pub fn min_by_key() -> Function {
264 Function::default()
265 .with_arg("iterable")
266 .with_arg("key_function")
267 .with_card(Card::return_card(Card::call_native(
268 "__min",
269 vec![Card::read_var("iterable"), Card::read_var("key_function")],
270 )))
271}
272
273pub fn sorted_by_key() -> Function {
274 Function::default()
275 .with_arg("iterable")
276 .with_arg("key_function")
277 .with_card(Card::return_card(Card::call_native(
278 "__sort",
279 vec![Card::read_var("iterable"), Card::read_var("key_function")],
280 )))
281}
282
283pub fn max_by_key() -> Function {
284 Function::default()
285 .with_arg("iterable")
286 .with_arg("key_function")
287 .with_card(Card::return_card(Card::call_native(
288 "__max",
289 vec![Card::read_var("iterable"), Card::read_var("key_function")],
290 )))
291}
292
293pub fn value_key_fn() -> Function {
295 Function::default()
296 .with_arg("_key")
297 .with_arg("val")
298 .with_card(Card::return_card(Card::read_var("val")))
299}
300
301pub fn to_array() -> Function {
302 Function::default()
303 .with_arg("iterable")
304 .with_card(Card::return_card(Card::call_native(
305 "__to_array",
306 vec![Card::read_var("iterable")],
307 )))
308}
309
310pub fn standard_library() -> Module {
311 let mut module = Module::default();
312 module.functions.push(("to_array".to_string(), to_array()));
313 module.functions.push(("filter".to_string(), filter()));
314 module.functions.push(("any".to_string(), any()));
315 module.functions.push(("map".to_string(), map()));
316 module.functions.push(("min".to_string(), min()));
317 module.functions.push(("max".to_string(), max()));
318 module
319 .functions
320 .push(("min_by_key".to_string(), min_by_key()));
321 module
322 .functions
323 .push(("max_by_key".to_string(), max_by_key()));
324 module
325 .functions
326 .push(("sorted_by_key".to_string(), sorted_by_key()));
327 module.functions.push(("sorted".to_string(), sorted()));
328 module
329 .functions
330 .push(("row_to_value".to_string(), value_key_fn()));
331 module
332}