Skip to main content

lust/vm/
tasks.rs

1use super::*;
2use crate::bytecode::{LustMap, ValueKey};
3use crate::vm::task::TaskKind;
4use crate::LustInt;
5use alloc::{format, string::ToString};
6use core::{cell::RefCell, mem};
7impl VM {
8    pub(super) fn run_task_internal(
9        &mut self,
10        task_id: TaskId,
11        resume_value: Option<Value>,
12    ) -> Result<()> {
13        let mut task = match self.task_manager.detach(task_id) {
14            Some(task) => task,
15            None => {
16                return Err(LustError::RuntimeError {
17                    message: format!("Invalid task handle {}", task_id.as_u64()),
18                })
19            }
20        };
21        if matches!(task.kind(), TaskKind::NativeFuture { .. }) {
22            let message = format!(
23                "Task {} is managed by the host runtime and cannot be resumed manually",
24                task_id.as_u64()
25            );
26            self.task_manager.attach(task);
27            return Err(LustError::RuntimeError { message });
28        }
29
30        match task.state {
31            TaskState::Completed | TaskState::Failed | TaskState::Stopped => {
32                let message = format!(
33                    "Task {} cannot be resumed (state: {})",
34                    task_id.as_u64(),
35                    task.state.as_str()
36                );
37                self.task_manager.attach(task);
38                return Err(LustError::RuntimeError { message });
39            }
40
41            TaskState::Running => {
42                self.task_manager.attach(task);
43                return Err(LustError::RuntimeError {
44                    message: format!("Task {} is already running", task_id.as_u64()),
45                });
46            }
47
48            _ => {}
49        }
50
51        task.state = TaskState::Running;
52        task.last_yield = None;
53        let mut resume_value_opt = resume_value;
54        if let Some(dest) = task.yield_dest.take() {
55            let value = resume_value_opt.take().unwrap_or(Value::Nil);
56            if let Some(frame) = task.call_stack.last_mut() {
57                frame.registers[dest as usize] = value;
58            }
59        } else if resume_value_opt.is_some() {
60            let message = format!(
61                "Task {} is not waiting for a resume value",
62                task_id.as_u64()
63            );
64            self.task_manager.attach(task);
65            return Err(LustError::RuntimeError { message });
66        }
67
68        mem::swap(&mut self.call_stack, &mut task.call_stack);
69        mem::swap(
70            &mut self.pending_return_value,
71            &mut task.pending_return_value,
72        );
73        mem::swap(&mut self.pending_return_dest, &mut task.pending_return_dest);
74        self.current_task = Some(task_id);
75        self.last_task_signal = None;
76        let run_result = self.run();
77        let signal = self.last_task_signal.take();
78        self.current_task = None;
79        let mut error_result: Option<LustError> = None;
80        match run_result {
81            Ok(value) => {
82                if let Some(signal) = signal {
83                    match signal {
84                        TaskSignal::Yield {
85                            dest,
86                            value: yielded,
87                        } => {
88                            task.state = TaskState::Yielded;
89                            task.last_yield = Some(yielded);
90                            task.last_result = None;
91                            task.yield_dest = Some(dest);
92                        }
93
94                        TaskSignal::Stop { value: stop_value } => {
95                            task.state = TaskState::Stopped;
96                            task.last_result = Some(stop_value);
97                            task.last_yield = None;
98                            task.call_stack.clear();
99                            task.pending_return_value = None;
100                            task.pending_return_dest = None;
101                        }
102                    }
103                } else {
104                    task.state = TaskState::Completed;
105                    task.last_result = Some(value);
106                    task.last_yield = None;
107                }
108            }
109
110            Err(err) => {
111                let annotated = self.annotate_runtime_error(err);
112                task.state = TaskState::Failed;
113                task.error = Some(annotated.clone());
114                task.last_yield = None;
115                error_result = Some(annotated);
116            }
117        }
118
119        mem::swap(&mut self.call_stack, &mut task.call_stack);
120        mem::swap(
121            &mut self.pending_return_value,
122            &mut task.pending_return_value,
123        );
124        mem::swap(&mut self.pending_return_dest, &mut task.pending_return_dest);
125        self.task_manager.attach(task);
126        if let Some(err) = error_result {
127            Err(err)
128        } else {
129            Ok(())
130        }
131    }
132
133    pub(super) fn task_id_from_handle(&self, handle: TaskHandle) -> Result<TaskId> {
134        let id = TaskId(handle.id());
135        if self.task_manager.contains(id) {
136            Ok(id)
137        } else {
138            Err(LustError::RuntimeError {
139                message: format!("Invalid task handle {}", handle.id()),
140            })
141        }
142    }
143
144    pub(super) fn prepare_task_frame(
145        &mut self,
146        func: Value,
147        args: Vec<Value>,
148    ) -> Result<CallFrame> {
149        match func {
150            Value::Function(func_idx) => {
151                let function = &self.functions[func_idx];
152                if args.len() != function.param_count as usize {
153                    return Err(LustError::RuntimeError {
154                        message: format!(
155                            "Task entry expects {} arguments, got {}",
156                            function.param_count,
157                            args.len()
158                        ),
159                    });
160                }
161                let register_count = function.register_count;
162
163                let mut frame = CallFrame::new(func_idx, None, register_count);
164                for (i, arg) in args.into_iter().enumerate() {
165                    frame.registers[i] = arg;
166                }
167
168                Ok(frame)
169            }
170
171            Value::Closure {
172                function_idx,
173                upvalues,
174            } => {
175                let function = &self.functions[function_idx];
176                if args.len() != function.param_count as usize {
177                    return Err(LustError::RuntimeError {
178                        message: format!(
179                            "Task entry expects {} arguments, got {}",
180                            function.param_count,
181                            args.len()
182                        ),
183                    });
184                }
185                let register_count = function.register_count;
186
187                let captured: Vec<Value> = upvalues.iter().map(|uv| uv.get()).collect();
188                let mut frame = CallFrame::new(function_idx, None, register_count);
189                frame.upvalues = captured;
190                for (i, arg) in args.into_iter().enumerate() {
191                    frame.registers[i] = arg;
192                }
193
194                Ok(frame)
195            }
196
197            other => Err(LustError::RuntimeError {
198                message: format!("task.run() expects a function or closure, got {:?}", other),
199            }),
200        }
201    }
202
203    pub(super) fn create_task_value(
204        &mut self,
205        func: Value,
206        args: Vec<Value>,
207    ) -> Result<TaskHandle> {
208        let frame = self.prepare_task_frame(func, args)?;
209        let task_id = self.task_manager.next_id();
210        let task = TaskInstance::new(task_id, frame);
211        self.task_manager.insert(task);
212        Ok(task_id.to_handle())
213    }
214
215    pub fn spawn_task_value(&mut self, func: Value, args: Vec<Value>) -> Result<TaskHandle> {
216        let handle = self.create_task_value(func, args)?;
217        let task_id = TaskId(handle.id());
218        if let Err(err) = self.run_task_internal(task_id, None) {
219            let _ = self.task_manager.detach(task_id);
220            return Err(err);
221        }
222
223        Ok(handle)
224    }
225
226    pub fn spawn_tick_task(&mut self, function_name: &str) -> Result<TaskHandle> {
227        let canonical = if function_name.contains("::") {
228            function_name.replace("::", ".")
229        } else {
230            function_name.to_string()
231        };
232        let func_idx = self
233            .functions
234            .iter()
235            .position(|f| f.name == canonical)
236            .ok_or_else(|| LustError::RuntimeError {
237                message: format!("Function not found: {}", function_name),
238            })?;
239
240        let yield_fn = self
241            .globals
242            .get("task")
243            .cloned()
244            .and_then(|task| match task {
245                Value::Map(map) => map
246                    .borrow()
247                    .get(&ValueKey::string("yield".to_string()))
248                    .cloned(),
249                _ => None,
250            })
251            .ok_or_else(|| LustError::RuntimeError {
252                message: "Missing corelib 'task.yield' (task module not installed?)".to_string(),
253            })?;
254
255        if !matches!(yield_fn, Value::NativeFunction(_)) {
256            return Err(LustError::RuntimeError {
257                message: "corelib 'task.yield' is not a native function".to_string(),
258            });
259        }
260
261        let wrapper_name = format!("__jit_tick_driver_{}", func_idx);
262        let wrapper_idx = match self.functions.iter().position(|f| f.name == wrapper_name) {
263            Some(existing) => existing,
264            None => {
265                let target = &self.functions[func_idx];
266                let arg_count = target.param_count;
267
268                let mut wrapper = Function::new(wrapper_name, 0, false);
269
270                let resume_reg: Register = 0;
271                let tick_fn_reg: Register = 1;
272                let yield_fn_reg: Register = 2;
273                let idx_reg: Register = 3;
274                let arg_base: Register = 4;
275                let result_reg: Register = arg_base.saturating_add(arg_count);
276
277                let required_registers = (result_reg as u16 + 1).min(256) as u8;
278                wrapper.set_register_count(required_registers);
279
280                let tick_const = wrapper.chunk.add_constant(Value::Function(func_idx));
281                let yield_const = wrapper.chunk.add_constant(yield_fn);
282                let mut index_consts = Vec::new();
283                if arg_count > 1 {
284                    for i in 0..(arg_count as LustInt) {
285                        index_consts.push(wrapper.chunk.add_constant(Value::Int(i)));
286                    }
287                }
288
289                wrapper
290                    .chunk
291                    .emit(Instruction::LoadConst(tick_fn_reg, tick_const), 0);
292                wrapper
293                    .chunk
294                    .emit(Instruction::LoadConst(yield_fn_reg, yield_const), 0);
295
296                // Prime the task so the host can immediately supply the first tick's argument via resume().
297                wrapper
298                    .chunk
299                    .emit(Instruction::Call(yield_fn_reg, 0, 0, resume_reg), 0);
300
301                let loop_start = wrapper.chunk.instructions.len();
302
303                match arg_count {
304                    0 => {
305                        wrapper
306                            .chunk
307                            .emit(Instruction::Call(tick_fn_reg, 0, 0, result_reg), 0);
308                    }
309                    1 => {
310                        wrapper.chunk.emit(
311                            Instruction::Call(tick_fn_reg, resume_reg, 1, result_reg),
312                            0,
313                        );
314                    }
315                    _ => {
316                        // For N>1, expect resume() to pass an Array of arguments.
317                        for (i, const_idx) in index_consts.iter().enumerate() {
318                            wrapper
319                                .chunk
320                                .emit(Instruction::LoadConst(idx_reg, *const_idx), 0);
321                            wrapper.chunk.emit(
322                                Instruction::GetIndex(arg_base + i as u8, resume_reg, idx_reg),
323                                0,
324                            );
325                        }
326                        wrapper.chunk.emit(
327                            Instruction::Call(tick_fn_reg, arg_base, arg_count, result_reg),
328                            0,
329                        );
330                    }
331                }
332
333                // Yield the on_tick() result back to the host; resume() will write the next tick arg into resume_reg.
334                wrapper.chunk.emit(
335                    Instruction::Call(yield_fn_reg, result_reg, 1, resume_reg),
336                    0,
337                );
338
339                let jump_idx = wrapper.chunk.emit(Instruction::Jump(0), 0);
340                wrapper.chunk.patch_jump(jump_idx, loop_start);
341
342                let new_idx = self.functions.len();
343                self.functions.push(wrapper);
344                new_idx
345            }
346        };
347
348        self.spawn_task_value(Value::Function(wrapper_idx), Vec::new())
349    }
350
351    pub fn tick_task(&mut self, handle: TaskHandle, resume_value: Value) -> Result<Value> {
352        self.resume_task_handle(handle, Some(resume_value))?;
353        let task = self.get_task_instance(handle)?;
354        match task.state {
355            TaskState::Yielded => Ok(task.last_yield.clone().unwrap_or(Value::Nil)),
356            TaskState::Completed | TaskState::Stopped => Ok(task.last_result.clone().unwrap_or(Value::Nil)),
357            TaskState::Ready | TaskState::Running => Ok(Value::Nil),
358            TaskState::Failed => Err(task.error.clone().unwrap_or_else(|| LustError::RuntimeError {
359                message: "Task failed".to_string(),
360            })),
361        }
362    }
363
364    pub fn resume_task_handle(
365        &mut self,
366        handle: TaskHandle,
367        resume_value: Option<Value>,
368    ) -> Result<()> {
369        let task_id = self.task_id_from_handle(handle)?;
370        self.run_task_internal(task_id, resume_value)
371    }
372
373    pub(super) fn stop_task_handle(&mut self, handle: TaskHandle) -> Result<()> {
374        let task_id = self.task_id_from_handle(handle)?;
375        let mut task = match self.task_manager.detach(task_id) {
376            Some(task) => task,
377            None => {
378                return Err(LustError::RuntimeError {
379                    message: format!("Invalid task handle {}", handle.id()),
380                })
381            }
382        };
383        match task.state {
384            TaskState::Stopped | TaskState::Completed | TaskState::Failed => {
385                self.task_manager.attach(task);
386                return Ok(());
387            }
388
389            TaskState::Running => {
390                self.task_manager.attach(task);
391                return Err(LustError::RuntimeError {
392                    message: format!("Task {} is currently running", handle.id()),
393                });
394            }
395
396            _ => {}
397        }
398
399        task.state = TaskState::Stopped;
400        task.call_stack.clear();
401        task.pending_return_value = None;
402        task.pending_return_dest = None;
403        task.yield_dest = None;
404        task.last_yield = None;
405        self.task_manager.attach(task);
406        Ok(())
407    }
408
409    pub(super) fn restart_task_handle(&mut self, handle: TaskHandle) -> Result<()> {
410        let task_id = self.task_id_from_handle(handle)?;
411        let mut task = match self.task_manager.detach(task_id) {
412            Some(task) => task,
413            None => {
414                return Err(LustError::RuntimeError {
415                    message: format!("Invalid task handle {}", handle.id()),
416                })
417            }
418        };
419        task.reset();
420        self.task_manager.insert(task);
421        if let Err(err) = self.run_task_internal(task_id, None) {
422            return Err(err);
423        }
424
425        Ok(())
426    }
427
428    pub fn get_task_instance(&self, handle: TaskHandle) -> Result<&TaskInstance> {
429        let task_id = self.task_id_from_handle(handle)?;
430        self.task_manager
431            .get(task_id)
432            .ok_or_else(|| LustError::RuntimeError {
433                message: format!("Invalid task handle {}", handle.id()),
434            })
435    }
436
437    pub fn current_task_handle(&self) -> Option<TaskHandle> {
438        self.current_task.map(|id| id.to_handle())
439    }
440
441    pub fn create_native_future_task(&mut self) -> TaskHandle {
442        let id = self.task_manager.next_id();
443        let task = TaskInstance::new_native_future(id);
444        let handle = task.handle();
445        self.task_manager.insert(task);
446        handle
447    }
448
449    pub fn complete_native_future_task(
450        &mut self,
451        handle: TaskHandle,
452        outcome: core::result::Result<Value, String>,
453    ) -> Result<()> {
454        let task_id = self.task_id_from_handle(handle)?;
455        let mut task = match self.task_manager.detach(task_id) {
456            Some(task) => task,
457            None => {
458                return Err(LustError::RuntimeError {
459                    message: format!("Invalid task handle {}", handle.id()),
460                })
461            }
462        };
463
464        match task.kind_mut() {
465            TaskKind::NativeFuture { .. } => {
466                match outcome {
467                    Ok(value) => {
468                        task.state = TaskState::Completed;
469                        task.last_result = Some(value);
470                        task.error = None;
471                    }
472                    Err(err_msg) => {
473                        task.state = TaskState::Failed;
474                        task.last_result = None;
475                        task.error = Some(LustError::RuntimeError { message: err_msg });
476                    }
477                }
478                task.last_yield = None;
479                task.pending_return_value = None;
480                task.pending_return_dest = None;
481                task.yield_dest = None;
482                self.task_manager.attach(task);
483                Ok(())
484            }
485
486            TaskKind::Script => {
487                self.task_manager.attach(task);
488                Err(LustError::RuntimeError {
489                    message: "Attempted to complete a script task using native future completion"
490                        .to_string(),
491                })
492            }
493        }
494    }
495
496    pub(super) fn call_builtin_method(
497        &mut self,
498        object: &Value,
499        method_name: &str,
500        args: Vec<Value>,
501    ) -> Result<Value> {
502        #[cfg(feature = "std")]
503        if std::env::var_os("LUST_LUA_SOCKET_TRACE").is_some() && method_name == "settimeout" {
504            match object {
505                Value::Enum {
506                    enum_name,
507                    variant,
508                    ..
509                } => {
510                    eprintln!(
511                        "[lua-socket] CallMethod enum={} variant={} method={}",
512                        enum_name, variant, method_name
513                    );
514                }
515                other => {
516                    eprintln!(
517                        "[lua-socket] CallMethod type={:?} method={}",
518                        other.type_of(),
519                        method_name
520                    );
521                }
522            }
523        }
524
525        if let Value::Enum {
526            enum_name,
527            variant,
528            ..
529        } = object
530        {
531            if enum_name == "LuaValue" && variant == "Userdata" {
532                if let Some(result) = self.try_call_lua_dynamic_method(object, method_name, &args)?
533                {
534                    return Ok(result);
535                }
536                #[cfg(feature = "std")]
537                if std::env::var_os("LUST_LUA_SOCKET_TRACE").is_some() {
538                    let indexer = self.lua_index_metamethod(object);
539                    eprintln!(
540                        "[lua-socket] userdata missing method '{}' indexer={:?} userdata={:?}",
541                        method_name,
542                        indexer.as_ref().map(|v| v.type_of()),
543                        object
544                    );
545                }
546            }
547        }
548
549        if let Value::Struct { name, .. } = object {
550            if name == "LuaTable" {
551                if let Some(result) = self.try_call_lua_dynamic_method(object, method_name, &args)?
552                {
553                    return Ok(result);
554                }
555            }
556        }
557
558        if let Value::Enum {
559            enum_name,
560            variant,
561            values,
562        } = object
563        {
564            if enum_name == "LuaValue" && variant == "Table" {
565                if let Some(inner) = values.as_ref().and_then(|vals| vals.get(0)) {
566                    return self.call_builtin_method(inner, method_name, args);
567                }
568            }
569        }
570
571        if let Value::Struct {
572            name: struct_name, ..
573        } = object
574        {
575            let mangled_name = format!("{}:{}", struct_name, method_name);
576            if let Some(func_idx) = self.functions.iter().position(|f| f.name == mangled_name) {
577                let mut method_args = vec![object.clone()];
578                method_args.extend(args.clone());
579                return self.call_value(&Value::Function(func_idx), method_args);
580            }
581
582            let mut candidate_names = vec![mangled_name.clone()];
583            if let Some(simple) = struct_name.rsplit(|c| c == '.' || c == ':').next() {
584                candidate_names.push(format!("{}:{}", simple, method_name));
585            }
586
587            for candidate in candidate_names {
588                let mut resolved = None;
589                for variant in [candidate.clone(), candidate.replace('.', "::")] {
590                    if let Some(value) = self.get_global(&variant) {
591                        resolved = Some(value);
592                        break;
593                    }
594                }
595
596                if let Some(global_func) = resolved {
597                    let mut method_args = vec![object.clone()];
598                    method_args.extend(args.clone());
599                    return self.call_value(&global_func, method_args);
600                }
601            }
602        }
603
604        match object {
605            Value::Struct { name, .. } if name == "LuaTable" => {
606                let Some(map_rc) = lua_table_map_rc(object) else {
607                    return Err(LustError::RuntimeError {
608                        message: "LuaTable is missing 'table' map field".to_string(),
609                    });
610                };
611                match method_name {
612                    "len" => {
613                        if !args.is_empty() {
614                            return Err(LustError::RuntimeError {
615                                message: "len() takes no arguments".to_string(),
616                            });
617                        }
618                        let seq = lua_table_read_sequence(&map_rc.borrow());
619                        Ok(Value::Int(seq.len() as LustInt))
620                    }
621                    "push" => {
622                        if args.len() != 1 {
623                            return Err(LustError::RuntimeError {
624                                message: "push() requires 1 argument (value)".to_string(),
625                            });
626                        }
627                        let value = super::corelib::unwrap_lua_value(args[0].clone());
628                        let mut map = map_rc.borrow_mut();
629                        let len = lua_table_sequence_len(&map);
630                        let key = ValueKey::from_value(&Value::Int((len as LustInt) + 1));
631                        if self.budgets.mem_budget_enabled() && !map.contains_key(&key) {
632                            self.budgets.charge_map_entry_estimate()?;
633                        }
634                        map.insert(key, value);
635                        Ok(Value::Nil)
636                    }
637                    "insert" => {
638                        if args.len() != 2 {
639                            return Err(LustError::RuntimeError {
640                                message: "insert() requires 2 arguments (pos, value)".to_string(),
641                            });
642                        }
643                        let pos_raw = super::corelib::unwrap_lua_value(args[0].clone());
644                        let pos = pos_raw.as_int().unwrap_or(0).max(1) as usize;
645                        let value = super::corelib::unwrap_lua_value(args[1].clone());
646                        let mut seq = lua_table_read_sequence(&map_rc.borrow());
647                        let idx = pos.saturating_sub(1);
648                        if idx > seq.len() {
649                            seq.push(value);
650                        } else {
651                            seq.insert(idx, value);
652                        }
653                        lua_table_write_sequence(&map_rc, &seq);
654                        Ok(Value::Nil)
655                    }
656                    "remove" => {
657                        if args.len() != 1 {
658                            return Err(LustError::RuntimeError {
659                                message: "remove() requires 1 argument (pos)".to_string(),
660                            });
661                        }
662                        let pos_raw = super::corelib::unwrap_lua_value(args[0].clone());
663                        let mut seq = lua_table_read_sequence(&map_rc.borrow());
664                        if seq.is_empty() {
665                            return Ok(Value::Nil);
666                        }
667                        let pos = pos_raw.as_int().unwrap_or(seq.len() as LustInt);
668                        let idx =
669                            ((pos - 1).max(0) as usize).min(seq.len().saturating_sub(1));
670                        let removed = seq.remove(idx);
671                        lua_table_write_sequence(&map_rc, &seq);
672                        Ok(removed)
673                    }
674                    "concat" => {
675                        if args.len() != 3 {
676                            return Err(LustError::RuntimeError {
677                                message: "concat() requires 3 arguments (sep, i, j)".to_string(),
678                            });
679                        }
680                        let sep_raw = super::corelib::unwrap_lua_value(args[0].clone());
681                        let sep = sep_raw.as_string().unwrap_or_default();
682                        let seq = lua_table_read_sequence(&map_rc.borrow());
683                        let start = super::corelib::unwrap_lua_value(args[1].clone())
684                            .as_int()
685                            .unwrap_or(1);
686                        let end = super::corelib::unwrap_lua_value(args[2].clone())
687                            .as_int()
688                            .unwrap_or(seq.len() as LustInt);
689                        let start_idx = (start - 1).max(0) as usize;
690                        let end_idx = end.max(0) as usize;
691                        let mut pieces: Vec<String> = Vec::new();
692                        for (i, val) in seq.iter().enumerate() {
693                            if i < start_idx || i >= end_idx {
694                                continue;
695                            }
696                            let raw = super::corelib::unwrap_lua_value(val.clone());
697                            pieces.push(format!("{}", raw));
698                        }
699                        Ok(Value::string(pieces.join(&sep)))
700                    }
701                    #[cfg(feature = "std")]
702                    "unpack" => {
703                        if args.len() != 2 {
704                            return Err(LustError::RuntimeError {
705                                message: "unpack() requires 2 arguments (i, j)".to_string(),
706                            });
707                        }
708                        let unpack = super::stdlib::create_table_unpack_fn();
709                        let Value::NativeFunction(func) = unpack else {
710                            return Err(LustError::RuntimeError {
711                                message: "unpack() builtin is not a native function".to_string(),
712                            });
713                        };
714                        let call_args = vec![object.clone(), args[0].clone(), args[1].clone()];
715                        match func(&call_args).map_err(|e| LustError::RuntimeError { message: e })? {
716                            NativeCallResult::Return(value) => Ok(value),
717                            NativeCallResult::Yield(_) => Err(LustError::RuntimeError {
718                                message: "unpack() unexpectedly yielded".to_string(),
719                            }),
720                            NativeCallResult::Stop(_) => Err(LustError::RuntimeError {
721                                message: "unpack() unexpectedly stopped execution".to_string(),
722                            }),
723                        }
724                    }
725                    "sort" => {
726                        if args.len() != 1 {
727                            return Err(LustError::RuntimeError {
728                                message: "sort() requires 1 argument (comp)".to_string(),
729                            });
730                        }
731                        let mut seq = lua_table_read_sequence(&map_rc.borrow());
732                        seq.sort_by(|a, b| {
733                            let la = format!("{}", super::corelib::unwrap_lua_value(a.clone()));
734                            let lb = format!("{}", super::corelib::unwrap_lua_value(b.clone()));
735                            la.cmp(&lb)
736                        });
737                        lua_table_write_sequence(&map_rc, &seq);
738                        Ok(Value::Nil)
739                    }
740                    "maxn" => {
741                        if !args.is_empty() {
742                            return Err(LustError::RuntimeError {
743                                message: "maxn() takes no arguments".to_string(),
744                            });
745                        }
746                        let map = map_rc.borrow();
747                        let mut max_idx: LustInt = 0;
748                        for key in map.keys() {
749                            if let Value::Int(i) = key.to_value() {
750                                if i > max_idx && i > 0 {
751                                    max_idx = i;
752                                }
753                            }
754                        }
755                        Ok(Value::Int(max_idx))
756                    }
757                    _ => Err(LustError::RuntimeError {
758                        message: format!("LuaTable has no method '{}'", method_name),
759                    }),
760                }
761            }
762            Value::Enum {
763                enum_name,
764                variant,
765                values,
766            } if enum_name == "Option" => match method_name {
767                "is_some" => Ok(Value::Bool(variant == "Some")),
768                "is_none" => Ok(Value::Bool(variant == "None")),
769                "unwrap" => {
770                    if variant == "Some" {
771                        if let Some(vals) = values {
772                            if !vals.is_empty() {
773                                Ok(vals[0].clone())
774                            } else {
775                                Err(LustError::RuntimeError {
776                                    message: "Option::Some has no value".to_string(),
777                                })
778                            }
779                        } else {
780                            Err(LustError::RuntimeError {
781                                message: "Option::Some has no value".to_string(),
782                            })
783                        }
784                    } else {
785                        Err(LustError::RuntimeError {
786                            message: "Called unwrap() on Option::None".to_string(),
787                        })
788                    }
789                }
790
791                "unwrap_or" => {
792                    if args.is_empty() {
793                        return Err(LustError::RuntimeError {
794                            message: "unwrap_or requires a default value".to_string(),
795                        });
796                    }
797
798                    if variant == "Some" {
799                        if let Some(vals) = values {
800                            if !vals.is_empty() {
801                                Ok(vals[0].clone())
802                            } else {
803                                Ok(args[0].clone())
804                            }
805                        } else {
806                            Ok(args[0].clone())
807                        }
808                    } else {
809                        Ok(args[0].clone())
810                    }
811                }
812
813                _ => Err(LustError::RuntimeError {
814                    message: format!("Option has no method '{}'", method_name),
815                }),
816            },
817            Value::Enum {
818                enum_name,
819                variant,
820                values,
821            } if enum_name == "Result" => match method_name {
822                "is_ok" => Ok(Value::Bool(variant == "Ok")),
823                "is_err" => Ok(Value::Bool(variant == "Err")),
824                "unwrap" => {
825                    if variant == "Ok" {
826                        if let Some(vals) = values {
827                            if !vals.is_empty() {
828                                Ok(vals[0].clone())
829                            } else {
830                                Err(LustError::RuntimeError {
831                                    message: "Result::Ok has no value".to_string(),
832                                })
833                            }
834                        } else {
835                            Err(LustError::RuntimeError {
836                                message: "Result::Ok has no value".to_string(),
837                            })
838                        }
839                    } else {
840                        Err(LustError::RuntimeError {
841                            message: "Called unwrap() on Result::Err".to_string(),
842                        })
843                    }
844                }
845
846                "unwrap_or" => {
847                    if args.is_empty() {
848                        return Err(LustError::RuntimeError {
849                            message: "unwrap_or requires a default value".to_string(),
850                        });
851                    }
852
853                    if variant == "Ok" {
854                        if let Some(vals) = values {
855                            if !vals.is_empty() {
856                                Ok(vals[0].clone())
857                            } else {
858                                Ok(args[0].clone())
859                            }
860                        } else {
861                            Ok(args[0].clone())
862                        }
863                    } else {
864                        Ok(args[0].clone())
865                    }
866                }
867
868                _ => Err(LustError::RuntimeError {
869                    message: format!("Result has no method '{}'", method_name),
870                }),
871            },
872            Value::Array(arr) => match method_name {
873                "iter" => {
874                    let items = arr.borrow().clone();
875                    let iter = crate::bytecode::value::IteratorState::Array { items, index: 0 };
876                    Ok(Value::Iterator(Rc::new(RefCell::new(iter))))
877                }
878
879                "len" => Ok(Value::Int(int_from_usize(arr.borrow().len()))),
880                "get" => {
881                    if args.is_empty() {
882                        return Err(LustError::RuntimeError {
883                            message: "get requires an index argument".to_string(),
884                        });
885                    }
886
887                    let index = args[0].as_int().ok_or_else(|| LustError::RuntimeError {
888                        message: "Array index must be an integer".to_string(),
889                    })?;
890                    let borrowed = arr.borrow();
891                    if index < 0 || index as usize >= borrowed.len() {
892                        Ok(Value::none())
893                    } else {
894                        Ok(Value::some(borrowed[index as usize].clone()))
895                    }
896                }
897
898                "first" => {
899                    let borrowed = arr.borrow();
900                    if borrowed.is_empty() {
901                        Ok(Value::none())
902                    } else {
903                        Ok(Value::some(borrowed[0].clone()))
904                    }
905                }
906
907                "last" => {
908                    let borrowed = arr.borrow();
909                    if borrowed.is_empty() {
910                        Ok(Value::none())
911                    } else {
912                        Ok(Value::some(borrowed[borrowed.len() - 1].clone()))
913                    }
914                }
915
916                "push" => {
917                    if args.is_empty() {
918                        return Err(LustError::RuntimeError {
919                            message: "push requires a value argument".to_string(),
920                        });
921                    }
922
923                    let mut borrowed = arr.borrow_mut();
924                    if self.budgets.mem_budget_enabled() {
925                        let len = borrowed.len();
926                        let cap = borrowed.capacity();
927                        if len == cap {
928                            let new_cap = if cap == 0 { 4 } else { cap.saturating_mul(2) };
929                            self.budgets.charge_vec_growth::<Value>(cap, new_cap)?;
930                        }
931                    }
932                    borrowed.push(args[0].clone());
933                    Ok(Value::Nil)
934                }
935
936                "pop" => {
937                    let popped = arr.borrow_mut().pop();
938                    match popped {
939                        Some(val) => Ok(Value::some(val)),
940                        None => Ok(Value::none()),
941                    }
942                }
943
944                "map" => {
945                    if args.is_empty() {
946                        return Err(LustError::RuntimeError {
947                            message: "map requires a function argument".to_string(),
948                        });
949                    }
950
951                    let func = &args[0];
952                    let borrowed = arr.borrow();
953                    self.budgets.charge_value_vec(borrowed.len())?;
954                    let mut result = Vec::with_capacity(borrowed.len());
955                    for elem in borrowed.iter() {
956                        let mapped_value = self.call_value(func, vec![elem.clone()])?;
957                        result.push(mapped_value);
958                    }
959
960                    Ok(Value::array(result))
961                }
962
963                "filter" => {
964                    if args.is_empty() {
965                        return Err(LustError::RuntimeError {
966                            message: "filter requires a function argument".to_string(),
967                        });
968                    }
969
970                    let func = &args[0];
971                    let borrowed = arr.borrow();
972                    self.budgets.charge_value_vec(borrowed.len())?;
973                    let mut result = Vec::with_capacity(borrowed.len());
974                    for elem in borrowed.iter() {
975                        let keep = self.call_value(func, vec![elem.clone()])?;
976                        if keep.is_truthy() {
977                            result.push(elem.clone());
978                        }
979                    }
980
981                    Ok(Value::array(result))
982                }
983
984                "reduce" => {
985                    if args.len() < 2 {
986                        return Err(LustError::RuntimeError {
987                            message: "reduce requires an initial value and function".to_string(),
988                        });
989                    }
990
991                    let init_value = &args[0];
992                    let func = &args[1];
993                    let borrowed = arr.borrow();
994                    let mut accumulator = init_value.clone();
995                    for elem in borrowed.iter() {
996                        accumulator = self.call_value(func, vec![accumulator, elem.clone()])?;
997                    }
998
999                    Ok(accumulator)
1000                }
1001
1002                "slice" => {
1003                    if args.len() < 2 {
1004                        return Err(LustError::RuntimeError {
1005                            message: "slice requires start and end indices".to_string(),
1006                        });
1007                    }
1008
1009                    let start = args[0].as_int().ok_or_else(|| LustError::RuntimeError {
1010                        message: "Start index must be an integer".to_string(),
1011                    })? as usize;
1012                    let end = args[1].as_int().ok_or_else(|| LustError::RuntimeError {
1013                        message: "End index must be an integer".to_string(),
1014                    })? as usize;
1015                    let borrowed = arr.borrow();
1016                    if start > borrowed.len() || end > borrowed.len() || start > end {
1017                        return Err(LustError::RuntimeError {
1018                            message: "Invalid slice indices".to_string(),
1019                        });
1020                    }
1021
1022                    let sliced = borrowed[start..end].to_vec();
1023                    Ok(Value::array(sliced))
1024                }
1025
1026                "clear" => {
1027                    arr.borrow_mut().clear();
1028                    Ok(Value::Nil)
1029                }
1030
1031                "is_empty" => Ok(Value::Bool(arr.borrow().is_empty())),
1032                _ => Err(LustError::RuntimeError {
1033                    message: format!("Array has no method '{}'", method_name),
1034                }),
1035            },
1036            Value::String(s) => match method_name {
1037                "iter" => {
1038                    let items: Vec<Value> =
1039                        s.chars().map(|c| Value::string(c.to_string())).collect();
1040                    let iter = crate::bytecode::value::IteratorState::Array { items, index: 0 };
1041                    Ok(Value::Iterator(Rc::new(RefCell::new(iter))))
1042                }
1043
1044                "len" => Ok(Value::Int(int_from_usize(s.len()))),
1045                "substring" => {
1046                    if args.len() < 2 {
1047                        return Err(LustError::RuntimeError {
1048                            message: "substring requires start and end indices".to_string(),
1049                        });
1050                    }
1051
1052                    let start = args[0].as_int().ok_or_else(|| LustError::RuntimeError {
1053                        message: "Start index must be an integer".to_string(),
1054                    })? as usize;
1055                    let end = args[1].as_int().ok_or_else(|| LustError::RuntimeError {
1056                        message: "End index must be an integer".to_string(),
1057                    })? as usize;
1058                    if start > s.len() || end > s.len() || start > end {
1059                        return Err(LustError::RuntimeError {
1060                            message: "Invalid substring indices".to_string(),
1061                        });
1062                    }
1063
1064                    Ok(Value::string(&s[start..end]))
1065                }
1066
1067                "find" => {
1068                    if args.is_empty() {
1069                        return Err(LustError::RuntimeError {
1070                            message: "find requires a search string".to_string(),
1071                        });
1072                    }
1073
1074                    let search = args[0].as_string().ok_or_else(|| LustError::RuntimeError {
1075                        message: "Search string must be a string".to_string(),
1076                    })?;
1077                    match s.find(search) {
1078                        Some(pos) => Ok(Value::some(Value::Int(int_from_usize(pos)))),
1079                        None => Ok(Value::none()),
1080                    }
1081                }
1082
1083                "starts_with" => {
1084                    if args.is_empty() {
1085                        return Err(LustError::RuntimeError {
1086                            message: "starts_with requires a prefix string".to_string(),
1087                        });
1088                    }
1089
1090                    let prefix = args[0].as_string().ok_or_else(|| LustError::RuntimeError {
1091                        message: "Prefix must be a string".to_string(),
1092                    })?;
1093                    Ok(Value::Bool(s.starts_with(prefix)))
1094                }
1095
1096                "ends_with" => {
1097                    if args.is_empty() {
1098                        return Err(LustError::RuntimeError {
1099                            message: "ends_with requires a suffix string".to_string(),
1100                        });
1101                    }
1102
1103                    let suffix = args[0].as_string().ok_or_else(|| LustError::RuntimeError {
1104                        message: "Suffix must be a string".to_string(),
1105                    })?;
1106                    Ok(Value::Bool(s.ends_with(suffix)))
1107                }
1108
1109                "split" => {
1110                    if args.is_empty() {
1111                        return Err(LustError::RuntimeError {
1112                            message: "split requires a separator string".to_string(),
1113                        });
1114                    }
1115
1116                    let separator = args[0].as_string().ok_or_else(|| LustError::RuntimeError {
1117                        message: "Separator must be a string".to_string(),
1118                    })?;
1119                    let parts: Vec<Value> =
1120                        s.split(separator).map(|part| Value::string(part)).collect();
1121                    Ok(Value::array(parts))
1122                }
1123
1124                "trim" => Ok(Value::string(s.trim())),
1125                "trim_start" => Ok(Value::string(s.trim_start())),
1126                "trim_end" => Ok(Value::string(s.trim_end())),
1127                "replace" => {
1128                    if args.len() < 2 {
1129                        return Err(LustError::RuntimeError {
1130                            message: "replace requires 'from' and 'to' string arguments"
1131                                .to_string(),
1132                        });
1133                    }
1134
1135                    let from = args[0].as_string().ok_or_else(|| LustError::RuntimeError {
1136                        message: "First argument must be a string".to_string(),
1137                    })?;
1138                    let to = args[1].as_string().ok_or_else(|| LustError::RuntimeError {
1139                        message: "Second argument must be a string".to_string(),
1140                    })?;
1141                    Ok(Value::string(&s.replace(from, to)))
1142                }
1143
1144                "to_upper" => Ok(Value::string(&s.to_uppercase())),
1145                "to_lower" => Ok(Value::string(&s.to_lowercase())),
1146                "contains" => {
1147                    if args.is_empty() {
1148                        return Err(LustError::RuntimeError {
1149                            message: "contains requires a search string".to_string(),
1150                        });
1151                    }
1152
1153                    let search = args[0].as_string().ok_or_else(|| LustError::RuntimeError {
1154                        message: "Search string must be a string".to_string(),
1155                    })?;
1156                    Ok(Value::Bool(s.contains(search)))
1157                }
1158
1159                "is_empty" => Ok(Value::Bool(s.is_empty())),
1160                "chars" => {
1161                    let chars: Vec<Value> =
1162                        s.chars().map(|c| Value::string(&c.to_string())).collect();
1163                    Ok(Value::array(chars))
1164                }
1165
1166                "lines" => {
1167                    let lines: Vec<Value> = s.lines().map(|line| Value::string(line)).collect();
1168                    Ok(Value::array(lines))
1169                }
1170
1171                _ => Err(LustError::RuntimeError {
1172                    message: format!("String has no method '{}'", method_name),
1173                }),
1174            },
1175            Value::Map(map) => {
1176                use crate::bytecode::ValueKey;
1177                match method_name {
1178                    "iter" => {
1179                        let items: Vec<(ValueKey, Value)> = map
1180                            .borrow()
1181                            .iter()
1182                            .map(|(k, v)| (k.clone(), v.clone()))
1183                            .collect();
1184                        let iter =
1185                            crate::bytecode::value::IteratorState::MapPairs { items, index: 0 };
1186                        return Ok(Value::Iterator(Rc::new(RefCell::new(iter))));
1187                    }
1188
1189                    "len" => Ok(Value::Int(int_from_usize(map.borrow().len()))),
1190                    "get" => {
1191                        if args.is_empty() {
1192                            return Err(LustError::RuntimeError {
1193                                message: "get requires a key argument".to_string(),
1194                            });
1195                        }
1196
1197                        let key = self.make_hash_key(&args[0])?;
1198                        match map.borrow().get(&key) {
1199                            Some(value) => Ok(Value::some(value.clone())),
1200                            None => Ok(Value::none()),
1201                        }
1202                    }
1203
1204                    "set" => {
1205                        if args.len() < 2 {
1206                            return Err(LustError::RuntimeError {
1207                                message: "set requires key and value arguments".to_string(),
1208                            });
1209                        }
1210
1211                        let key = self.make_hash_key(&args[0])?;
1212                        let value = args[1].clone();
1213                        if self.budgets.mem_budget_enabled() && !map.borrow().contains_key(&key) {
1214                            self.budgets.charge_map_entry_estimate()?;
1215                        }
1216                        map.borrow_mut().insert(key, value);
1217                        Ok(Value::Nil)
1218                    }
1219
1220                    "has" => {
1221                        if args.is_empty() {
1222                            return Err(LustError::RuntimeError {
1223                                message: "has requires a key argument".to_string(),
1224                            });
1225                        }
1226
1227                        let key = self.make_hash_key(&args[0])?;
1228                        Ok(Value::Bool(map.borrow().contains_key(&key)))
1229                    }
1230
1231                    "delete" => {
1232                        if args.is_empty() {
1233                            return Err(LustError::RuntimeError {
1234                                message: "delete requires a key argument".to_string(),
1235                            });
1236                        }
1237
1238                        let key = self.make_hash_key(&args[0])?;
1239                        match map.borrow_mut().remove(&key) {
1240                            Some(value) => Ok(Value::some(value)),
1241                            None => Ok(Value::none()),
1242                        }
1243                    }
1244
1245                    "keys" => {
1246                        let keys: Vec<Value> = map.borrow().keys().map(|k| k.to_value()).collect();
1247                        Ok(Value::array(keys))
1248                    }
1249
1250                    "values" => {
1251                        let values: Vec<Value> = map.borrow().values().cloned().collect();
1252                        Ok(Value::array(values))
1253                    }
1254
1255                    _ => Err(LustError::RuntimeError {
1256                        message: format!("Map has no method '{}'", method_name),
1257                    }),
1258                }
1259            }
1260
1261            Value::Iterator(state_rc) => match method_name {
1262                "iter" => Ok(Value::Iterator(state_rc.clone())),
1263                "next" => {
1264                    use crate::bytecode::value::IteratorState;
1265                    let mut state = state_rc.borrow_mut();
1266                    match &mut *state {
1267                        IteratorState::Array { items, index } => {
1268                            if *index < items.len() {
1269                                let v = items[*index].clone();
1270                                *index += 1;
1271                                Ok(Value::some(v))
1272                            } else {
1273                                Ok(Value::none())
1274                            }
1275                        }
1276
1277                        IteratorState::MapPairs { items, index } => {
1278                            if *index < items.len() {
1279                                let (k, v) = items[*index].clone();
1280                                *index += 1;
1281                                Ok(Value::some(Value::array(vec![k.to_value(), v])))
1282                            } else {
1283                                Ok(Value::none())
1284                            }
1285                        }
1286                    }
1287                }
1288
1289                _ => Err(LustError::RuntimeError {
1290                    message: format!("Iterator has no method '{}'", method_name),
1291                }),
1292            },
1293            Value::Float(f) => match method_name {
1294                "to_int" => {
1295                    if !args.is_empty() {
1296                        return Err(LustError::RuntimeError {
1297                            message: "to_int() takes no arguments".to_string(),
1298                        });
1299                    }
1300
1301                    Ok(Value::Int(int_from_float(*f)))
1302                }
1303
1304                "floor" => {
1305                    if !args.is_empty() {
1306                        return Err(LustError::RuntimeError {
1307                            message: "floor() takes no arguments".to_string(),
1308                        });
1309                    }
1310
1311                    Ok(Value::Float(float_floor(*f)))
1312                }
1313
1314                "ceil" => {
1315                    if !args.is_empty() {
1316                        return Err(LustError::RuntimeError {
1317                            message: "ceil() takes no arguments".to_string(),
1318                        });
1319                    }
1320
1321                    Ok(Value::Float(float_ceil(*f)))
1322                }
1323
1324                "round" => {
1325                    if !args.is_empty() {
1326                        return Err(LustError::RuntimeError {
1327                            message: "round() takes no arguments".to_string(),
1328                        });
1329                    }
1330
1331                    Ok(Value::Float(float_round(*f)))
1332                }
1333
1334                "sqrt" => {
1335                    if !args.is_empty() {
1336                        return Err(LustError::RuntimeError {
1337                            message: "sqrt() takes no arguments".to_string(),
1338                        });
1339                    }
1340
1341                    if *f < 0.0 {
1342                        return Err(LustError::RuntimeError {
1343                            message: "sqrt() requires a non-negative number".to_string(),
1344                        });
1345                    }
1346
1347                    Ok(Value::Float(float_sqrt(*f)))
1348                }
1349
1350                "abs" => {
1351                    if !args.is_empty() {
1352                        return Err(LustError::RuntimeError {
1353                            message: "abs() takes no arguments".to_string(),
1354                        });
1355                    }
1356
1357                    Ok(Value::Float(float_abs(*f)))
1358                }
1359
1360                "sin" => {
1361                    if !args.is_empty() {
1362                        return Err(LustError::RuntimeError {
1363                            message: "sin() takes no arguments".to_string(),
1364                        });
1365                    }
1366
1367                    Ok(Value::Float(float_sin(*f)))
1368                }
1369
1370                "cos" => {
1371                    if !args.is_empty() {
1372                        return Err(LustError::RuntimeError {
1373                            message: "cos() takes no arguments".to_string(),
1374                        });
1375                    }
1376
1377                    Ok(Value::Float(float_cos(*f)))
1378                }
1379
1380                "tan" => {
1381                    if !args.is_empty() {
1382                        return Err(LustError::RuntimeError {
1383                            message: "tan() takes no arguments".to_string(),
1384                        });
1385                    }
1386
1387                    Ok(Value::Float(float_tan(*f)))
1388                }
1389
1390                "asin" => {
1391                    if !args.is_empty() {
1392                        return Err(LustError::RuntimeError {
1393                            message: "asin() takes no arguments".to_string(),
1394                        });
1395                    }
1396
1397                    Ok(Value::Float(float_asin(*f)))
1398                }
1399
1400                "acos" => {
1401                    if !args.is_empty() {
1402                        return Err(LustError::RuntimeError {
1403                            message: "acos() takes no arguments".to_string(),
1404                        });
1405                    }
1406
1407                    Ok(Value::Float(float_acos(*f)))
1408                }
1409
1410                "atan" => {
1411                    if !args.is_empty() {
1412                        return Err(LustError::RuntimeError {
1413                            message: "atan() takes no arguments".to_string(),
1414                        });
1415                    }
1416
1417                    Ok(Value::Float(float_atan(*f)))
1418                }
1419
1420                "atan2" => {
1421                    if args.len() != 1 {
1422                        return Err(LustError::RuntimeError {
1423                            message: "atan2() requires 1 argument (other)".to_string(),
1424                        });
1425                    }
1426                    let other = args[0].as_float().ok_or_else(|| LustError::RuntimeError {
1427                        message: "atan2() other must be a number".to_string(),
1428                    })?;
1429                    Ok(Value::Float(float_atan2(*f, other)))
1430                }
1431
1432                "min" => {
1433                    if args.len() != 1 {
1434                        return Err(LustError::RuntimeError {
1435                            message: "min() requires 1 argument (other)".to_string(),
1436                        });
1437                    }
1438                    let other = args[0]
1439                        .as_float()
1440                        .or_else(|| args[0].as_int().map(float_from_int))
1441                        .ok_or_else(|| LustError::RuntimeError {
1442                            message: "min() other must be a number".to_string(),
1443                        })?;
1444                    Ok(Value::Float(f.min(other)))
1445                }
1446
1447                "max" => {
1448                    if args.len() != 1 {
1449                        return Err(LustError::RuntimeError {
1450                            message: "max() requires 1 argument (other)".to_string(),
1451                        });
1452                    }
1453                    let other = args[0]
1454                        .as_float()
1455                        .or_else(|| args[0].as_int().map(float_from_int))
1456                        .ok_or_else(|| LustError::RuntimeError {
1457                            message: "max() other must be a number".to_string(),
1458                        })?;
1459                    Ok(Value::Float(f.max(other)))
1460                }
1461
1462                "clamp" => {
1463                    if args.len() != 2 {
1464                        return Err(LustError::RuntimeError {
1465                            message: "clamp() requires 2 arguments (min, max)".to_string(),
1466                        });
1467                    }
1468
1469                    let min = args[0].as_float().ok_or_else(|| LustError::RuntimeError {
1470                        message: "clamp() min must be a number".to_string(),
1471                    })?;
1472                    let max = args[1].as_float().ok_or_else(|| LustError::RuntimeError {
1473                        message: "clamp() max must be a number".to_string(),
1474                    })?;
1475                    if min > max {
1476                        return Err(LustError::RuntimeError {
1477                            message: "clamp() min must be less than or equal to max".to_string(),
1478                        });
1479                    }
1480
1481                    Ok(Value::Float(float_clamp(*f, min, max)))
1482                }
1483
1484                _ => Err(LustError::RuntimeError {
1485                    message: format!("Float has no method '{}'", method_name),
1486                }),
1487            },
1488            Value::Int(i) => match method_name {
1489                "to_float" => {
1490                    if !args.is_empty() {
1491                        return Err(LustError::RuntimeError {
1492                            message: "to_float() takes no arguments".to_string(),
1493                        });
1494                    }
1495
1496                    Ok(Value::Float(float_from_int(*i)))
1497                }
1498
1499                "abs" => {
1500                    if !args.is_empty() {
1501                        return Err(LustError::RuntimeError {
1502                            message: "abs() takes no arguments".to_string(),
1503                        });
1504                    }
1505
1506                    Ok(Value::Int(i.abs()))
1507                }
1508
1509                "min" => {
1510                    if args.len() != 1 {
1511                        return Err(LustError::RuntimeError {
1512                            message: "min() requires 1 argument (other)".to_string(),
1513                        });
1514                    }
1515                    if let Some(other) = args[0].as_int() {
1516                        return Ok(Value::Int((*i).min(other)));
1517                    }
1518                    if let Some(other) = args[0].as_float() {
1519                        return Ok(Value::Float(float_from_int(*i).min(other)));
1520                    }
1521                    Err(LustError::RuntimeError {
1522                        message: "min() other must be a number".to_string(),
1523                    })
1524                }
1525
1526                "max" => {
1527                    if args.len() != 1 {
1528                        return Err(LustError::RuntimeError {
1529                            message: "max() requires 1 argument (other)".to_string(),
1530                        });
1531                    }
1532                    if let Some(other) = args[0].as_int() {
1533                        return Ok(Value::Int((*i).max(other)));
1534                    }
1535                    if let Some(other) = args[0].as_float() {
1536                        return Ok(Value::Float(float_from_int(*i).max(other)));
1537                    }
1538                    Err(LustError::RuntimeError {
1539                        message: "max() other must be a number".to_string(),
1540                    })
1541                }
1542
1543                "clamp" => {
1544                    if args.len() != 2 {
1545                        return Err(LustError::RuntimeError {
1546                            message: "clamp() requires 2 arguments (min, max)".to_string(),
1547                        });
1548                    }
1549
1550                    let min = args[0].as_int().ok_or_else(|| LustError::RuntimeError {
1551                        message: "clamp() min must be an integer".to_string(),
1552                    })?;
1553                    let max = args[1].as_int().ok_or_else(|| LustError::RuntimeError {
1554                        message: "clamp() max must be an integer".to_string(),
1555                    })?;
1556                    if min > max {
1557                        return Err(LustError::RuntimeError {
1558                            message: "clamp() min must be less than or equal to max".to_string(),
1559                        });
1560                    }
1561
1562                    Ok(Value::Int((*i).clamp(min, max)))
1563                }
1564
1565                _ => Err(LustError::RuntimeError {
1566                    message: format!("Int has no method '{}'", method_name),
1567                }),
1568            },
1569            _ => Err(LustError::RuntimeError {
1570                message: format!(
1571                    "Type {:?} has no method '{}'",
1572                    object.type_of(),
1573                    method_name
1574                ),
1575            }),
1576        }
1577    }
1578
1579    fn try_call_lua_dynamic_method(
1580        &mut self,
1581        receiver: &Value,
1582        method_name: &str,
1583        args: &[Value],
1584    ) -> Result<Option<Value>> {
1585        let key = Value::string(method_name.to_string());
1586        let method = self.lua_resolve_index(receiver, &key, 8)?;
1587        if matches!(method, Value::Nil) {
1588            return Ok(None);
1589        }
1590
1591        let mut call_args = Vec::with_capacity(1 + args.len());
1592        call_args.push(receiver.clone());
1593        call_args.extend_from_slice(args);
1594        let result = self.call_value(&method, call_args)?;
1595        Ok(Some(result))
1596    }
1597
1598    fn lua_resolve_index(&mut self, receiver: &Value, key: &Value, depth: usize) -> Result<Value> {
1599        if depth == 0 {
1600            return Ok(Value::Nil);
1601        }
1602
1603        if let Some(direct) = self.lua_direct_index(receiver, key) {
1604            if !matches!(direct, Value::Nil) {
1605                return Ok(direct);
1606            }
1607        }
1608
1609        let Some(indexer) = self.lua_index_metamethod(receiver) else {
1610            return Ok(Value::Nil);
1611        };
1612        if matches!(indexer, Value::Nil) {
1613            return Ok(Value::Nil);
1614        }
1615
1616        let is_callable = matches!(
1617            indexer,
1618            Value::Function(_) | Value::Closure { .. } | Value::NativeFunction(_)
1619        ) || matches!(
1620            &indexer,
1621            Value::Enum {
1622                enum_name,
1623                variant,
1624                ..
1625            } if enum_name == "LuaValue" && variant == "Function"
1626        );
1627
1628        if is_callable {
1629            self.call_value(&indexer, vec![receiver.clone(), key.clone()])
1630        } else {
1631            self.lua_resolve_index(&indexer, key, depth - 1)
1632        }
1633    }
1634
1635    fn lua_direct_index(&self, receiver: &Value, key: &Value) -> Option<Value> {
1636        if let Value::Enum {
1637            enum_name,
1638            variant,
1639            values,
1640        } = receiver
1641        {
1642            if enum_name == "LuaValue" && variant == "Table" {
1643                if let Some(inner) = values.as_ref().and_then(|vals| vals.get(0)) {
1644                    return self.lua_direct_index(inner, key);
1645                }
1646            }
1647        }
1648
1649        match receiver {
1650            Value::Struct { name, .. } if name == "LuaTable" => {
1651                let Some(Value::Map(map_rc)) = receiver.struct_get_field("table") else {
1652                    return None;
1653                };
1654                let raw_key = super::corelib::unwrap_lua_value(key.clone());
1655                let lookup_key = ValueKey::from_value(&raw_key);
1656                let value = map_rc.borrow().get(&lookup_key).cloned();
1657                value
1658            }
1659            Value::Map(map_rc) => {
1660                let raw_key = super::corelib::unwrap_lua_value(key.clone());
1661                let lookup_key = ValueKey::from_value(&raw_key);
1662                let value = map_rc.borrow().get(&lookup_key).cloned();
1663                value
1664            }
1665            _ => None,
1666        }
1667    }
1668
1669    fn lua_index_metamethod(&self, receiver: &Value) -> Option<Value> {
1670        if let Value::Enum {
1671            enum_name,
1672            variant,
1673            values,
1674        } = receiver
1675        {
1676            if enum_name == "LuaValue" && variant == "Table" {
1677                if let Some(inner) = values.as_ref().and_then(|vals| vals.get(0)) {
1678                    return self.lua_index_metamethod(inner);
1679                }
1680            }
1681            if enum_name == "LuaValue" && variant == "Userdata" {
1682                if let Some(inner) = values.as_ref().and_then(|vals| vals.get(0)) {
1683                    return self.lua_index_metamethod(inner);
1684                }
1685            }
1686        }
1687
1688        let Value::Struct { name, .. } = receiver else {
1689            return None;
1690        };
1691        let Some(Value::Map(meta_rc)) = receiver.struct_get_field("metamethods") else {
1692            return None;
1693        };
1694        if name != "LuaTable" && name != "LuaUserdata" {
1695            return None;
1696        }
1697        let value = meta_rc
1698            .borrow()
1699            .get(&ValueKey::string("__index".to_string()))
1700            .cloned();
1701        value
1702    }
1703}
1704
1705fn lua_table_map_rc(table: &Value) -> Option<Rc<RefCell<LustMap>>> {
1706    match table.struct_get_field("table") {
1707        Some(Value::Map(map_rc)) => Some(map_rc),
1708        _ => None,
1709    }
1710}
1711
1712fn lua_table_sequence_len(map: &LustMap) -> usize {
1713    let mut idx: LustInt = 1;
1714    loop {
1715        let key = ValueKey::from_value(&Value::Int(idx));
1716        if map.contains_key(&key) {
1717            idx += 1;
1718        } else {
1719            break;
1720        }
1721    }
1722    (idx - 1) as usize
1723}
1724
1725fn lua_table_read_sequence(map: &LustMap) -> Vec<Value> {
1726    let mut seq: Vec<Value> = Vec::new();
1727    let mut idx: LustInt = 1;
1728    loop {
1729        let key = ValueKey::from_value(&Value::Int(idx));
1730        if let Some(val) = map.get(&key) {
1731            seq.push(val.clone());
1732            idx += 1;
1733        } else {
1734            break;
1735        }
1736    }
1737    seq
1738}
1739
1740fn lua_table_write_sequence(map_rc: &Rc<RefCell<LustMap>>, seq: &[Value]) {
1741    let mut map = map_rc.borrow_mut();
1742    let mut idx: LustInt = 1;
1743    loop {
1744        let key = ValueKey::from_value(&Value::Int(idx));
1745        if map.remove(&key).is_some() {
1746            idx += 1;
1747        } else {
1748            break;
1749        }
1750    }
1751    for (i, val) in seq.iter().enumerate() {
1752        let key = ValueKey::from_value(&Value::Int((i as LustInt) + 1));
1753        map.insert(key, val.clone());
1754    }
1755}