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