1use std::cell::RefCell;
9use std::sync::Arc;
10
11use super::vm::{NativeFunc, ScriptError, Table, Value, Vm};
12
13pub struct Stdlib;
17
18impl Stdlib {
19 pub fn register_all(vm: &mut Vm) {
21 register_all(vm);
22 }
23}
24
25pub fn register_all(vm: &mut Vm) {
29 register_globals(vm);
30 register_math(vm);
31 register_string(vm);
32 register_table(vm);
33 register_io(vm);
34 register_os(vm);
35 register_bit(vm);
36}
37
38fn native(name: &str, f: impl Fn(&mut Vm, Vec<Value>) -> Result<Vec<Value>, ScriptError> + Send + Sync + 'static) -> Value {
41 Value::NativeFunction(Arc::new(NativeFunc {
42 name: name.to_string(),
43 func: Box::new(f),
44 }))
45}
46
47fn register_globals(vm: &mut Vm) {
50 vm.register_native("print", |vm, args| {
52 let out: Vec<String> = args.iter().map(|v| v.to_string()).collect();
53 let line = out.join("\t");
54 vm.output.push(line.clone());
55 Ok(vec![])
56 });
57
58 vm.register_native("tostring", |_vm, args| {
60 let s = args.into_iter().next().unwrap_or(Value::Nil).to_string();
61 Ok(vec![Value::Str(Arc::new(s))])
62 });
63
64 vm.register_native("tonumber", |_vm, args| {
66 let v = args.first().cloned().unwrap_or(Value::Nil);
67 let base = args.get(1).and_then(|x| x.to_int()).unwrap_or(10);
68 let result = match &v {
69 Value::Int(i) => Value::Int(*i),
70 Value::Float(f) => Value::Float(*f),
71 Value::Str(s) => {
72 let s = s.trim();
73 if base == 10 {
74 if let Ok(i) = s.parse::<i64>() {
75 Value::Int(i)
76 } else if let Ok(f) = s.parse::<f64>() {
77 Value::Float(f)
78 } else {
79 Value::Nil
80 }
81 } else {
82 match i64::from_str_radix(s.trim_start_matches("0x").trim_start_matches("0X"), base as u32) {
83 Ok(i) => Value::Int(i),
84 Err(_) => Value::Nil,
85 }
86 }
87 }
88 _ => Value::Nil,
89 };
90 Ok(vec![result])
91 });
92
93 vm.register_native("type", |_vm, args| {
95 let t = args.into_iter().next().unwrap_or(Value::Nil).type_name();
96 Ok(vec![Value::Str(Arc::new(t.to_string()))])
97 });
98
99 vm.register_native("assert", |_vm, args| {
101 let cond = args.first().cloned().unwrap_or(Value::Nil);
102 if !cond.is_truthy() {
103 let msg = args.get(1)
104 .map(|v| v.to_string())
105 .unwrap_or_else(|| "assertion failed!".to_string());
106 return Err(ScriptError::new(msg));
107 }
108 Ok(args)
109 });
110
111 vm.register_native("error", |_vm, args| {
113 let msg = args.into_iter().next().unwrap_or(Value::Nil).to_string();
114 Err(ScriptError::new(msg))
115 });
116
117 vm.register_native("pcall", |vm, mut args| {
119 if args.is_empty() {
120 return Ok(vec![Value::Bool(false), Value::Str(Arc::new("no function".to_string()))]);
121 }
122 let func = args.remove(0);
123 match vm.call(func, args) {
124 Ok(mut results) => {
125 results.insert(0, Value::Bool(true));
126 Ok(results)
127 }
128 Err(e) => Ok(vec![Value::Bool(false), Value::Str(Arc::new(e.message))]),
129 }
130 });
131
132 vm.register_native("xpcall", |vm, mut args| {
134 if args.len() < 2 {
135 return Ok(vec![Value::Bool(false)]);
136 }
137 let func = args.remove(0);
138 let handler = args.remove(0);
139 match vm.call(func, args) {
140 Ok(mut results) => {
141 results.insert(0, Value::Bool(true));
142 Ok(results)
143 }
144 Err(e) => {
145 let err_val = Value::Str(Arc::new(e.message.clone()));
146 let handler_result = vm.call(handler, vec![err_val.clone()]);
147 let msg = match handler_result {
148 Ok(r) => r.into_iter().next().unwrap_or(err_val),
149 Err(_) => err_val,
150 };
151 Ok(vec![Value::Bool(false), msg])
152 }
153 }
154 });
155
156 vm.register_native("ipairs", |_vm, args| {
158 let table = args.into_iter().next().unwrap_or(Value::Nil);
159 let iter_fn = Arc::new(NativeFunc {
160 name: "ipairs_iter".to_string(),
161 func: Box::new(|_vm, args| {
162 let table = args.first().cloned().unwrap_or(Value::Nil);
163 let idx = args.get(1).and_then(|v| v.to_int()).unwrap_or(0);
164 let next_idx = idx + 1;
165 match &table {
166 Value::Table(t) => {
167 let v = t.get(&Value::Int(next_idx));
168 if matches!(v, Value::Nil) {
169 Ok(vec![Value::Nil])
170 } else {
171 Ok(vec![Value::Int(next_idx), v])
172 }
173 }
174 _ => Ok(vec![Value::Nil]),
175 }
176 }),
177 });
178 Ok(vec![Value::NativeFunction(iter_fn), table, Value::Int(0)])
179 });
180
181 vm.register_native("pairs", |_vm, args| {
183 let table = args.into_iter().next().unwrap_or(Value::Nil);
184 let iter_fn = Arc::new(NativeFunc {
185 name: "pairs_iter".to_string(),
186 func: Box::new(|_vm, args| {
187 let table = args.first().cloned().unwrap_or(Value::Nil);
188 let key = args.get(1).cloned().unwrap_or(Value::Nil);
189 match &table {
190 Value::Table(t) => match t.next(&key) {
191 Some((k, v)) => Ok(vec![k, v]),
192 None => Ok(vec![Value::Nil]),
193 },
194 _ => Ok(vec![Value::Nil]),
195 }
196 }),
197 });
198 Ok(vec![Value::NativeFunction(iter_fn), table, Value::Nil])
199 });
200
201 vm.register_native("next", |_vm, args| {
203 let table = args.first().cloned().unwrap_or(Value::Nil);
204 let key = args.get(1).cloned().unwrap_or(Value::Nil);
205 match &table {
206 Value::Table(t) => match t.next(&key) {
207 Some((k, v)) => Ok(vec![k, v]),
208 None => Ok(vec![Value::Nil]),
209 },
210 _ => Err(ScriptError::new("next: not a table")),
211 }
212 });
213
214 vm.register_native("select", |_vm, args| {
216 let selector = args.first().cloned().unwrap_or(Value::Nil);
217 match selector {
218 Value::Str(ref s) if s.as_ref() == "#" => {
219 Ok(vec![Value::Int((args.len() as i64) - 1)])
220 }
221 Value::Int(i) if i > 0 => {
222 let rest: Vec<Value> = args.into_iter().skip(i as usize).collect();
223 Ok(rest)
224 }
225 Value::Int(i) if i < 0 => {
226 let total = args.len() as i64 - 1;
227 let start = (total + i).max(0) as usize + 1;
228 let rest: Vec<Value> = args.into_iter().skip(start).collect();
229 Ok(rest)
230 }
231 _ => Err(ScriptError::new("select: invalid index")),
232 }
233 });
234
235 vm.register_native("unpack", |_vm, args| {
237 let table = args.first().cloned().unwrap_or(Value::Nil);
238 let i = args.get(1).and_then(|v| v.to_int()).unwrap_or(1);
239 let j_opt = args.get(2).and_then(|v| v.to_int());
240 match table {
241 Value::Table(t) => {
242 let j = j_opt.unwrap_or_else(|| t.length());
243 let mut result = Vec::new();
244 for idx in i..=j {
245 result.push(t.get(&Value::Int(idx)));
246 }
247 Ok(result)
248 }
249 _ => Err(ScriptError::new("unpack: not a table")),
250 }
251 });
252
253 vm.register_native("rawget", |_vm, args| {
255 let table = args.first().cloned().unwrap_or(Value::Nil);
256 let key = args.get(1).cloned().unwrap_or(Value::Nil);
257 match table {
258 Value::Table(t) => Ok(vec![t.get(&key)]),
259 _ => Err(ScriptError::new("rawget: not a table")),
260 }
261 });
262
263 vm.register_native("rawset", |_vm, args| {
265 let table = args.first().cloned().unwrap_or(Value::Nil);
266 let key = args.get(1).cloned().unwrap_or(Value::Nil);
267 let val = args.get(2).cloned().unwrap_or(Value::Nil);
268 match &table {
269 Value::Table(t) => { t.set(key, val); Ok(vec![table]) }
270 _ => Err(ScriptError::new("rawset: not a table")),
271 }
272 });
273
274 vm.register_native("rawequal", |_vm, args| {
276 let a = args.first().cloned().unwrap_or(Value::Nil);
277 let b = args.get(1).cloned().unwrap_or(Value::Nil);
278 Ok(vec![Value::Bool(a == b)])
279 });
280
281 vm.register_native("rawlen", |_vm, args| {
283 let v = args.into_iter().next().unwrap_or(Value::Nil);
284 let n = match v {
285 Value::Table(t) => t.length(),
286 Value::Str(s) => s.len() as i64,
287 _ => return Err(ScriptError::new("rawlen: not a table or string")),
288 };
289 Ok(vec![Value::Int(n)])
290 });
291
292 vm.register_native("setmetatable", |_vm, args| {
294 let table = args.first().cloned().unwrap_or(Value::Nil);
295 let mt = args.get(1).cloned().unwrap_or(Value::Nil);
296 if let Value::Table(t) = &table {
297 match mt {
298 Value::Table(mt_table) => t.set_metatable(Some(mt_table)),
299 Value::Nil => t.set_metatable(None),
300 _ => return Err(ScriptError::new("setmetatable: metatable must be a table or nil")),
301 }
302 Ok(vec![table])
303 } else {
304 Err(ScriptError::new("setmetatable: not a table"))
305 }
306 });
307
308 vm.register_native("getmetatable", |_vm, args| {
310 let v = args.into_iter().next().unwrap_or(Value::Nil);
311 if let Value::Table(t) = &v {
312 match t.get_metatable() {
313 Some(mt) => Ok(vec![Value::Table(mt)]),
314 None => Ok(vec![Value::Nil]),
315 }
316 } else {
317 Ok(vec![Value::Nil])
318 }
319 });
320
321 vm.register_native("require", |_vm, args| {
323 let _path = args.into_iter().next();
324 Ok(vec![Value::Nil])
325 });
326
327 vm.register_native("collectgarbage", |_vm, _args| {
329 Ok(vec![Value::Int(0)])
330 });
331}
332
333thread_local! {
337 static RAND_STATE: RefCell<u64> = RefCell::new(0x853c49e6748fea9b);
338}
339
340fn xorshift64() -> u64 {
341 RAND_STATE.with(|s| {
342 let mut x = s.borrow().wrapping_add(1);
343 if x == 0 { x = 0x853c49e6748fea9b; }
344 x ^= x << 13;
345 x ^= x >> 7;
346 x ^= x << 17;
347 *s.borrow_mut() = x;
348 x
349 })
350}
351
352fn register_math(vm: &mut Vm) {
353 let math = Table::new();
354
355 math.rawset_str("pi", Value::Float(std::f64::consts::PI));
357 math.rawset_str("tau", Value::Float(std::f64::consts::TAU));
358 math.rawset_str("huge", Value::Float(f64::INFINITY));
359 math.rawset_str("nan", Value::Float(f64::NAN));
360 math.rawset_str("maxinteger", Value::Int(i64::MAX));
361 math.rawset_str("mininteger", Value::Int(i64::MIN));
362
363 math_fn1(&math, "abs", |a| a.abs());
365 math_fn1(&math, "ceil", |a| a.ceil());
366 math_fn1(&math, "floor", |a| a.floor());
367 math_fn1(&math, "sqrt", |a| a.sqrt());
368 math_fn1(&math, "cbrt", |a| a.cbrt());
369 math_fn1(&math, "exp", |a| a.exp());
370 math_fn1(&math, "ln", |a| a.ln());
371 math_fn1(&math, "log2", |a| a.log2());
372 math_fn1(&math, "log10", |a| a.log10());
373 math_fn1(&math, "sin", |a| a.sin());
374 math_fn1(&math, "cos", |a| a.cos());
375 math_fn1(&math, "tan", |a| a.tan());
376 math_fn1(&math, "asin", |a| a.asin());
377 math_fn1(&math, "acos", |a| a.acos());
378 math_fn1(&math, "atan", |a| a.atan());
379
380 math.rawset_str("round", native("math.round", |_vm, args| {
382 match args.first().cloned().unwrap_or(Value::Nil) {
383 Value::Int(i) => Ok(vec![Value::Int(i)]),
384 Value::Float(f) => Ok(vec![Value::Float(f.round())]),
385 v => Err(ScriptError::new(format!("math.round: not a number (got {})", v.type_name()))),
386 }
387 }));
388
389 math.rawset_str("atan2", native("math.atan2", |_vm, args| {
391 let y = args.first().and_then(|v| v.to_float()).unwrap_or(0.0);
392 let x = args.get(1).and_then(|v| v.to_float()).unwrap_or(1.0);
393 Ok(vec![Value::Float(y.atan2(x))])
394 }));
395
396 math.rawset_str("hypot", native("math.hypot", |_vm, args| {
398 let x = args.first().and_then(|v| v.to_float()).unwrap_or(0.0);
399 let y = args.get(1).and_then(|v| v.to_float()).unwrap_or(0.0);
400 Ok(vec![Value::Float(x.hypot(y))])
401 }));
402
403 math.rawset_str("pow", native("math.pow", |_vm, args| {
405 let x = args.first().and_then(|v| v.to_float()).unwrap_or(0.0);
406 let y = args.get(1).and_then(|v| v.to_float()).unwrap_or(1.0);
407 Ok(vec![Value::Float(x.powf(y))])
408 }));
409
410 math.rawset_str("log", native("math.log", |_vm, args| {
412 let x = args.first().and_then(|v| v.to_float()).unwrap_or(0.0);
413 let base = args.get(1).and_then(|v| v.to_float());
414 let r = match base {
415 Some(b) => x.log(b),
416 None => x.ln(),
417 };
418 Ok(vec![Value::Float(r)])
419 }));
420
421 math.rawset_str("max", native("math.max", |_vm, args| {
423 if args.is_empty() { return Err(ScriptError::new("math.max: no arguments")); }
424 let mut best = args[0].clone();
425 for a in &args[1..] {
426 let bv = best.to_float().unwrap_or(f64::NEG_INFINITY);
427 let av = a.to_float().unwrap_or(f64::NEG_INFINITY);
428 if av > bv { best = a.clone(); }
429 }
430 Ok(vec![best])
431 }));
432
433 math.rawset_str("min", native("math.min", |_vm, args| {
435 if args.is_empty() { return Err(ScriptError::new("math.min: no arguments")); }
436 let mut best = args[0].clone();
437 for a in &args[1..] {
438 let bv = best.to_float().unwrap_or(f64::INFINITY);
439 let av = a.to_float().unwrap_or(f64::INFINITY);
440 if av < bv { best = a.clone(); }
441 }
442 Ok(vec![best])
443 }));
444
445 math.rawset_str("clamp", native("math.clamp", |_vm, args| {
447 let x = args.first().and_then(|v| v.to_float()).unwrap_or(0.0);
448 let lo = args.get(1).and_then(|v| v.to_float()).unwrap_or(f64::NEG_INFINITY);
449 let hi = args.get(2).and_then(|v| v.to_float()).unwrap_or(f64::INFINITY);
450 Ok(vec![Value::Float(x.clamp(lo, hi))])
451 }));
452
453 math.rawset_str("fmod", native("math.fmod", |_vm, args| {
455 let a = args.first().and_then(|v| v.to_float()).unwrap_or(0.0);
456 let b = args.get(1).and_then(|v| v.to_float()).unwrap_or(1.0);
457 Ok(vec![Value::Float(a % b)])
458 }));
459
460 math.rawset_str("modf", native("math.modf", |_vm, args| {
462 let a = args.first().and_then(|v| v.to_float()).unwrap_or(0.0);
463 Ok(vec![Value::Float(a.trunc()), Value::Float(a.fract())])
464 }));
465
466 math.rawset_str("type", native("math.type", |_vm, args| {
468 let t = match args.into_iter().next().unwrap_or(Value::Nil) {
469 Value::Int(_) => "integer",
470 Value::Float(_) => "float",
471 _ => "other",
472 };
473 Ok(vec![Value::Str(Arc::new(t.to_string()))])
474 }));
475
476 math.rawset_str("tointeger", native("math.tointeger", |_vm, args| {
478 let v = args.into_iter().next().unwrap_or(Value::Nil);
479 Ok(vec![v.to_int().map(Value::Int).unwrap_or(Value::Nil)])
480 }));
481
482 math.rawset_str("isnan", native("math.isnan", |_vm, args| {
484 let f = args.first().and_then(|v| v.to_float()).unwrap_or(0.0);
485 Ok(vec![Value::Bool(f.is_nan())])
486 }));
487
488 math.rawset_str("isinf", native("math.isinf", |_vm, args| {
490 let f = args.first().and_then(|v| v.to_float()).unwrap_or(0.0);
491 Ok(vec![Value::Bool(f.is_infinite())])
492 }));
493
494 math.rawset_str("random", native("math.random", |_vm, args| {
496 let r = (xorshift64() as f64) / (u64::MAX as f64);
497 let result = match args.len() {
498 0 => Value::Float(r),
499 1 => {
500 let m = args[0].to_int().unwrap_or(1).max(1);
501 Value::Int(1 + (r * m as f64) as i64 % m)
502 }
503 _ => {
504 let lo = args[0].to_int().unwrap_or(1);
505 let hi = args[1].to_int().unwrap_or(1);
506 if hi < lo { return Err(ScriptError::new("math.random: bad argument")); }
507 let range = (hi - lo + 1).max(1);
508 Value::Int(lo + (r * range as f64) as i64 % range)
509 }
510 };
511 Ok(vec![result])
512 }));
513
514 math.rawset_str("randomseed", native("math.randomseed", |_vm, args| {
516 let seed = args.first().and_then(|v| v.to_int()).unwrap_or(0) as u64;
517 RAND_STATE.with(|s| {
518 *s.borrow_mut() = if seed == 0 { 0x853c49e6748fea9b } else { seed };
519 });
520 Ok(vec![])
521 }));
522
523 vm.set_global("math", Value::Table(math));
524}
525
526fn math_fn1(table: &Table, name: &'static str, f: fn(f64) -> f64) {
527 table.rawset_str(name, Value::NativeFunction(Arc::new(NativeFunc {
528 name: format!("math.{}", name),
529 func: Box::new(move |_vm, args| {
530 let a = args.first().and_then(|v| v.to_float()).unwrap_or(0.0);
531 Ok(vec![Value::Float(f(a))])
532 }),
533 })));
534}
535
536fn register_string(vm: &mut Vm) {
539 let string = Table::new();
540
541 string.rawset_str("len", native("string.len", |_vm, args| {
543 let s = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
544 Ok(vec![Value::Int(s.len() as i64)])
545 }));
546
547 string.rawset_str("sub", native("string.sub", |_vm, args| {
549 let s = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
550 let len = s.len() as i64;
551 let i = args.get(1).and_then(|v| v.to_int()).unwrap_or(1);
552 let j = args.get(2).and_then(|v| v.to_int()).unwrap_or(-1);
553 let from = if i < 0 { (len + i).max(0) } else { (i - 1).max(0) } as usize;
554 let to = if j < 0 { (len + j + 1).max(0) } else { j.min(len) } as usize;
555 let result = if from <= to && from < s.len() {
556 s.get(from..to.min(s.len())).unwrap_or("").to_string()
557 } else {
558 String::new()
559 };
560 Ok(vec![Value::Str(Arc::new(result))])
561 }));
562
563 string.rawset_str("rep", native("string.rep", |_vm, args| {
565 let s = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
566 let n = args.get(1).and_then(|v| v.to_int()).unwrap_or(0).max(0) as usize;
567 let sep = args.get(2).and_then(|v| v.to_str_repr()).unwrap_or_default();
568 let result = if n == 0 {
569 String::new()
570 } else {
571 let parts: Vec<&str> = std::iter::repeat(s.as_str()).take(n).collect();
572 parts.join(&sep)
573 };
574 Ok(vec![Value::Str(Arc::new(result))])
575 }));
576
577 string.rawset_str("rev", native("string.rev", |_vm, args| {
579 let s = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
580 Ok(vec![Value::Str(Arc::new(s.chars().rev().collect()))])
581 }));
582
583 string.rawset_str("upper", native("string.upper", |_vm, args| {
585 let s = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
586 Ok(vec![Value::Str(Arc::new(s.to_uppercase()))])
587 }));
588
589 string.rawset_str("lower", native("string.lower", |_vm, args| {
591 let s = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
592 Ok(vec![Value::Str(Arc::new(s.to_lowercase()))])
593 }));
594
595 string.rawset_str("byte", native("string.byte", |_vm, args| {
597 let s = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
598 let i = args.get(1).and_then(|v| v.to_int()).unwrap_or(1);
599 let j = args.get(2).and_then(|v| v.to_int()).unwrap_or(i);
600 let bytes = s.as_bytes();
601 let mut result = Vec::new();
602 for idx in i..=j {
603 if idx >= 1 && (idx as usize) <= bytes.len() {
604 result.push(Value::Int(bytes[idx as usize - 1] as i64));
605 }
606 }
607 Ok(result)
608 }));
609
610 string.rawset_str("char", native("string.char", |_vm, args| {
612 let mut s = String::new();
613 for a in &args {
614 if let Some(i) = a.to_int() {
615 if let Some(c) = char::from_u32(i as u32) {
616 s.push(c);
617 }
618 }
619 }
620 Ok(vec![Value::Str(Arc::new(s))])
621 }));
622
623 string.rawset_str("dump", native("string.dump", |_vm, args| {
625 let v = args.into_iter().next().unwrap_or(Value::Nil);
626 let hex = match &v {
627 Value::Function(f) => format!("{:016x}", Arc::as_ptr(f) as usize),
628 Value::NativeFunction(f) => format!("{:016x}", Arc::as_ptr(f) as usize),
629 _ => return Err(ScriptError::new("string.dump: function expected")),
630 };
631 Ok(vec![Value::Str(Arc::new(hex))])
632 }));
633
634 string.rawset_str("split", native("string.split", |_vm, args| {
636 let s = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
637 let sep = args.get(1).and_then(|v| v.to_str_repr()).unwrap_or_else(|| "\t".to_string());
638 let tbl = Table::new();
639 let parts: Vec<&str> = if sep.is_empty() {
640 s.chars().map(|_| "").collect() } else {
642 s.split(sep.as_str()).collect()
643 };
644 if sep.is_empty() {
645 for (i, ch) in s.chars().enumerate() {
646 tbl.set(Value::Int(i as i64 + 1), Value::Str(Arc::new(ch.to_string())));
647 }
648 } else {
649 for (i, p) in parts.into_iter().enumerate() {
650 tbl.set(Value::Int(i as i64 + 1), Value::Str(Arc::new(p.to_string())));
651 }
652 }
653 Ok(vec![Value::Table(tbl)])
654 }));
655
656 string.rawset_str("format", native("string.format", |_vm, args| {
658 let fmt_str = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
659 let result = string_format(&fmt_str, &args[1.min(args.len())..]);
660 Ok(vec![Value::Str(Arc::new(result))])
661 }));
662
663 string.rawset_str("find", native("string.find", |_vm, args| {
665 let s = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
666 let pattern = args.get(1).and_then(|v| v.to_str_repr()).unwrap_or_default();
667 let init = args.get(2).and_then(|v| v.to_int()).unwrap_or(1);
668 let plain = args.get(3).map(|v| v.is_truthy()).unwrap_or(false);
669 let start = lua_idx_to_usize(init, s.len());
670 if plain {
671 if let Some(pos) = s[start..].find(pattern.as_str()) {
672 let abs_start = start + pos + 1;
673 let abs_end = abs_start + pattern.len() - 1;
674 return Ok(vec![Value::Int(abs_start as i64), Value::Int(abs_end as i64)]);
675 }
676 return Ok(vec![Value::Nil]);
677 }
678 match lua_pattern_find(&s, &pattern, start) {
679 Some((ms, me, caps)) => {
680 let mut result = vec![Value::Int(ms as i64 + 1), Value::Int(me as i64)];
681 for cap in caps {
682 result.push(Value::Str(Arc::new(cap)));
683 }
684 Ok(result)
685 }
686 None => Ok(vec![Value::Nil]),
687 }
688 }));
689
690 string.rawset_str("match", native("string.match", |_vm, args| {
692 let s = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
693 let pattern = args.get(1).and_then(|v| v.to_str_repr()).unwrap_or_default();
694 let init = args.get(2).and_then(|v| v.to_int()).unwrap_or(1);
695 let start = lua_idx_to_usize(init, s.len());
696 match lua_pattern_find(&s, &pattern, start) {
697 Some((ms, me, caps)) => {
698 if caps.is_empty() {
699 Ok(vec![Value::Str(Arc::new(s[ms..me].to_string()))])
700 } else {
701 Ok(caps.into_iter().map(|c| Value::Str(Arc::new(c))).collect())
702 }
703 }
704 None => Ok(vec![Value::Nil]),
705 }
706 }));
707
708 string.rawset_str("gmatch", native("string.gmatch", |_vm, args| {
710 let s = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
711 let pattern = args.get(1).and_then(|v| v.to_str_repr()).unwrap_or_default();
712 let matches: Vec<Vec<String>> = gmatch_collect(&s, &pattern);
714 let matches_arc = Arc::new(std::sync::Mutex::new((matches, 0usize)));
715 let iter_fn = Arc::new(NativeFunc {
716 name: "gmatch_iter".to_string(),
717 func: Box::new(move |_vm, _args| {
718 let mut guard = matches_arc.lock().unwrap();
719 let (ref matches, ref mut idx) = *guard;
720 if *idx >= matches.len() {
721 return Ok(vec![Value::Nil]);
722 }
723 let m = &matches[*idx];
724 *idx += 1;
725 Ok(m.iter().map(|s| Value::Str(Arc::new(s.clone()))).collect())
726 }),
727 });
728 Ok(vec![Value::NativeFunction(iter_fn)])
729 }));
730
731 string.rawset_str("gsub", native("string.gsub", |vm, args| {
733 let s = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
734 let pattern = args.get(1).and_then(|v| v.to_str_repr()).unwrap_or_default();
735 let repl = args.get(2).cloned().unwrap_or(Value::Nil);
736 let max_n = args.get(3).and_then(|v| v.to_int());
737
738 let (result, count) = gsub_impl(vm, &s, &pattern, &repl, max_n)?;
739 Ok(vec![Value::Str(Arc::new(result)), Value::Int(count as i64)])
740 }));
741
742 vm.set_global("string", Value::Table(string));
743}
744
745fn lua_idx_to_usize(i: i64, len: usize) -> usize {
748 if i >= 1 { (i as usize - 1).min(len) }
749 else if i < 0 { (len as i64 + i).max(0) as usize }
750 else { 0 }
751}
752
753fn lua_pattern_find(s: &str, pattern: &str, start: usize) -> Option<(usize, usize, Vec<String>)> {
756 let sb = s.as_bytes();
757 let pb = pattern.as_bytes();
758
759 let anchored = pb.first() == Some(&b'^');
760 let pb = if anchored { &pb[1..] } else { pb };
761
762 let search_start = start;
763 let search_end = if anchored { search_start + 1 } else { sb.len() + 1 };
764
765 for si in search_start..search_end {
766 if si > sb.len() { break; }
767 let mut caps: Vec<(usize, usize)> = Vec::new();
768 if let Some(end) = pat_match(sb, si, pb, 0, &mut caps) {
769 let captures: Vec<String> = caps.iter()
770 .map(|(cs, ce)| String::from_utf8_lossy(&sb[*cs..*ce]).to_string())
771 .collect();
772 return Some((si, end, captures));
773 }
774 }
775 None
776}
777
778fn pat_match(s: &[u8], mut si: usize, p: &[u8], mut pi: usize, caps: &mut Vec<(usize, usize)>) -> Option<usize> {
779 loop {
780 if pi >= p.len() {
781 return Some(si);
783 }
784 if p[pi] == b'$' && pi + 1 == p.len() {
785 return if si == s.len() { Some(si) } else { None };
786 }
787 if p[pi] == b'(' {
789 let cap_idx = caps.len();
790 caps.push((si, si));
791 let result = pat_match(s, si, p, pi + 1, caps);
792 if result.is_none() { caps.pop(); }
793 return result;
794 }
795 if p[pi] == b')' {
796 let cap_idx = caps.len() - 1;
797 let old = caps[cap_idx];
798 caps[cap_idx] = (old.0, si);
799 let result = pat_match(s, si, p, pi + 1, caps);
800 if result.is_none() { caps[cap_idx] = old; }
801 return result;
802 }
803 let (cls_len, cls_end) = pat_class_len(p, pi);
805 let quantifier = p.get(pi + cls_len).copied();
806 match quantifier {
807 Some(b'*') => {
808 let mut count = 0;
810 while si + count < s.len() && pat_class_match(s[si + count], p, pi) {
811 count += 1;
812 }
813 for c in (0..=count).rev() {
814 if let Some(end) = pat_match(s, si + c, p, pi + cls_len + 1, caps) {
815 return Some(end);
816 }
817 }
818 return None;
819 }
820 Some(b'+') => {
821 let mut count = 0;
822 while si + count < s.len() && pat_class_match(s[si + count], p, pi) {
823 count += 1;
824 }
825 if count == 0 { return None; }
826 for c in (1..=count).rev() {
827 if let Some(end) = pat_match(s, si + c, p, pi + cls_len + 1, caps) {
828 return Some(end);
829 }
830 }
831 return None;
832 }
833 Some(b'?') => {
834 if si < s.len() && pat_class_match(s[si], p, pi) {
835 if let Some(end) = pat_match(s, si + 1, p, pi + cls_len + 1, caps) {
836 return Some(end);
837 }
838 }
839 pi += cls_len + 1;
840 continue;
842 }
843 _ => {
844 if si >= s.len() { return None; }
846 if !pat_class_match(s[si], p, pi) { return None; }
847 si += 1;
848 pi += cls_len;
849 }
850 }
851 }
852}
853
854fn pat_class_len(p: &[u8], pi: usize) -> (usize, usize) {
855 if p[pi] == b'%' && pi + 1 < p.len() { (2, pi + 2) }
856 else { (1, pi + 1) }
857}
858
859fn pat_class_match(c: u8, p: &[u8], pi: usize) -> bool {
860 if p[pi] == b'%' && pi + 1 < p.len() {
861 match_percent_class(c, p[pi + 1])
862 } else if p[pi] == b'.' {
863 true
864 } else {
865 p[pi] == c
866 }
867}
868
869fn match_percent_class(c: u8, cls: u8) -> bool {
870 let ch = c as char;
871 match cls {
872 b'd' => ch.is_ascii_digit(),
873 b'D' => !ch.is_ascii_digit(),
874 b'a' => ch.is_ascii_alphabetic(),
875 b'A' => !ch.is_ascii_alphabetic(),
876 b'l' => ch.is_ascii_lowercase(),
877 b'L' => !ch.is_ascii_lowercase(),
878 b'u' => ch.is_ascii_uppercase(),
879 b'U' => !ch.is_ascii_uppercase(),
880 b's' => ch.is_ascii_whitespace(),
881 b'S' => !ch.is_ascii_whitespace(),
882 b'w' => ch.is_ascii_alphanumeric(),
883 b'W' => !ch.is_ascii_alphanumeric(),
884 b'p' => ch.is_ascii_punctuation(),
885 b'P' => !ch.is_ascii_punctuation(),
886 b'c' => ch.is_ascii_control(),
887 b'C' => !ch.is_ascii_control(),
888 b'x' => ch.is_ascii_hexdigit(),
889 b'X' => !ch.is_ascii_hexdigit(),
890 _ => c == cls, }
892}
893
894fn gmatch_collect(s: &str, pattern: &str) -> Vec<Vec<String>> {
895 let sb = s.as_bytes();
896 let pb = pattern.as_bytes();
897 let anchored = pb.first() == Some(&b'^');
898 let pb2 = if anchored { &pb[1..] } else { pb };
899 let mut results = Vec::new();
900 let mut si = 0;
901 while si <= sb.len() {
902 let mut caps: Vec<(usize, usize)> = Vec::new();
903 if let Some(end) = pat_match(sb, si, pb2, 0, &mut caps) {
904 if caps.is_empty() {
905 results.push(vec![String::from_utf8_lossy(&sb[si..end]).to_string()]);
906 } else {
907 results.push(caps.iter().map(|(cs, ce)| {
908 String::from_utf8_lossy(&sb[*cs..*ce]).to_string()
909 }).collect());
910 }
911 if end == si { si += 1; } else { si = end; }
912 if anchored { break; }
913 } else {
914 si += 1;
915 }
916 }
917 results
918}
919
920fn gsub_impl(vm: &mut Vm, s: &str, pattern: &str, repl: &Value, max_n: Option<i64>) -> Result<(String, usize), ScriptError> {
921 let sb = s.as_bytes();
922 let pb = pattern.as_bytes();
923 let anchored = pb.first() == Some(&b'^');
924 let pb2 = if anchored { &pb[1..] } else { pb };
925 let mut result = String::new();
926 let mut si = 0usize;
927 let mut count = 0usize;
928 let limit = max_n.unwrap_or(i64::MAX) as usize;
929
930 while si <= sb.len() && count < limit {
931 let mut caps: Vec<(usize, usize)> = Vec::new();
932 if let Some(end) = pat_match(sb, si, pb2, 0, &mut caps) {
933 let whole = &s[si..end];
934 let replacement = match repl {
935 Value::Str(r) => {
936 let mut rep = String::new();
938 let rb = r.as_bytes();
939 let mut ri = 0;
940 while ri < rb.len() {
941 if rb[ri] == b'%' && ri + 1 < rb.len() {
942 let next = rb[ri + 1];
943 if next == b'%' { rep.push('%'); ri += 2; }
944 else if next >= b'0' && next <= b'9' {
945 let ci = (next - b'0') as usize;
946 let cap_str = if ci == 0 { whole.to_string() }
947 else { caps.get(ci - 1).map(|(cs, ce)| s[*cs..*ce].to_string()).unwrap_or_default() };
948 rep.push_str(&cap_str);
949 ri += 2;
950 } else { rep.push('%'); ri += 1; }
951 } else { rep.push(rb[ri] as char); ri += 1; }
952 }
953 rep
954 }
955 Value::Table(t) => {
956 let key = if caps.is_empty() {
957 Value::Str(Arc::new(whole.to_string()))
958 } else {
959 let (cs, ce) = caps[0];
960 Value::Str(Arc::new(s[cs..ce].to_string()))
961 };
962 let v = t.get(&key);
963 if v.is_truthy() { v.to_string() } else { whole.to_string() }
964 }
965 Value::Function(_) | Value::NativeFunction(_) => {
966 let call_args: Vec<Value> = if caps.is_empty() {
967 vec![Value::Str(Arc::new(whole.to_string()))]
968 } else {
969 caps.iter().map(|(cs, ce)| Value::Str(Arc::new(s[*cs..*ce].to_string()))).collect()
970 };
971 let res = vm.call(repl.clone(), call_args)?;
972 let v = res.into_iter().next().unwrap_or(Value::Nil);
973 if v.is_truthy() { v.to_string() } else { whole.to_string() }
974 }
975 _ => whole.to_string(),
976 };
977 result.push_str(&s[si..si]); result.push_str(&replacement);
979 count += 1;
980 if end == si {
981 if si < sb.len() { result.push(sb[si] as char); }
982 si += 1;
983 } else {
984 si = end;
985 }
986 if anchored { break; }
987 } else {
988 if si < sb.len() { result.push(sb[si] as char); }
989 si += 1;
990 }
991 }
992 if si <= s.len() { result.push_str(&s[si..]); }
994 Ok((result, count))
995}
996
997fn string_format(fmt: &str, args: &[Value]) -> String {
1000 let mut result = String::new();
1001 let mut chars = fmt.chars().peekable();
1002 let mut arg_i = 0usize;
1003
1004 while let Some(c) = chars.next() {
1005 if c != '%' { result.push(c); continue; }
1006 let mut flags_str = String::new();
1008 loop {
1009 match chars.peek() {
1010 Some(&'-') | Some(&'+') | Some(&' ') | Some(&'0') | Some(&'#') => {
1011 flags_str.push(chars.next().unwrap());
1012 }
1013 _ => break,
1014 }
1015 }
1016 let mut width_str = String::new();
1018 while chars.peek().map(|c| c.is_ascii_digit()).unwrap_or(false) {
1019 width_str.push(chars.next().unwrap());
1020 }
1021 let mut prec_str = String::new();
1023 if chars.peek() == Some(&'.') {
1024 chars.next();
1025 while chars.peek().map(|c| c.is_ascii_digit()).unwrap_or(false) {
1026 prec_str.push(chars.next().unwrap());
1027 }
1028 }
1029 let width: usize = width_str.parse().unwrap_or(0);
1030 let prec: Option<usize> = if prec_str.is_empty() { None } else { prec_str.parse().ok() };
1031 let left_align = flags_str.contains('-');
1032 let zero_pad = flags_str.contains('0') && !left_align;
1033 let plus_sign = flags_str.contains('+');
1034
1035 match chars.next() {
1036 Some('%') => result.push('%'),
1037 Some(spec @ ('d' | 'i')) => {
1038 let v = args.get(arg_i).and_then(|v| v.to_int()).unwrap_or(0);
1039 arg_i += 1;
1040 let s = if plus_sign && v >= 0 { format!("+{}", v) } else { v.to_string() };
1041 result.push_str(&pad_str(&s, width, left_align, if zero_pad { '0' } else { ' ' }));
1042 let _ = spec;
1043 }
1044 Some('u') => {
1045 let v = args.get(arg_i).and_then(|v| v.to_int()).unwrap_or(0) as u64;
1046 arg_i += 1;
1047 let s = v.to_string();
1048 result.push_str(&pad_str(&s, width, left_align, if zero_pad { '0' } else { ' ' }));
1049 }
1050 Some('o') => {
1051 let v = args.get(arg_i).and_then(|v| v.to_int()).unwrap_or(0) as u64;
1052 arg_i += 1;
1053 let s = format!("{:o}", v);
1054 result.push_str(&pad_str(&s, width, left_align, if zero_pad { '0' } else { ' ' }));
1055 }
1056 Some('x') => {
1057 let v = args.get(arg_i).and_then(|v| v.to_int()).unwrap_or(0) as u64;
1058 arg_i += 1;
1059 let s = format!("{:x}", v);
1060 result.push_str(&pad_str(&s, width, left_align, if zero_pad { '0' } else { ' ' }));
1061 }
1062 Some('X') => {
1063 let v = args.get(arg_i).and_then(|v| v.to_int()).unwrap_or(0) as u64;
1064 arg_i += 1;
1065 let s = format!("{:X}", v);
1066 result.push_str(&pad_str(&s, width, left_align, if zero_pad { '0' } else { ' ' }));
1067 }
1068 Some('f') => {
1069 let v = args.get(arg_i).and_then(|v| v.to_float()).unwrap_or(0.0);
1070 arg_i += 1;
1071 let p = prec.unwrap_or(6);
1072 let s = if plus_sign && v >= 0.0 { format!("+{:.prec$}", v, prec = p) } else { format!("{:.prec$}", v, prec = p) };
1073 result.push_str(&pad_str(&s, width, left_align, if zero_pad { '0' } else { ' ' }));
1074 }
1075 Some('e') => {
1076 let v = args.get(arg_i).and_then(|v| v.to_float()).unwrap_or(0.0);
1077 arg_i += 1;
1078 let p = prec.unwrap_or(6);
1079 let s = format!("{:.prec$e}", v, prec = p);
1080 result.push_str(&pad_str(&s, width, left_align, ' '));
1081 }
1082 Some('g') => {
1083 let v = args.get(arg_i).and_then(|v| v.to_float()).unwrap_or(0.0);
1084 arg_i += 1;
1085 let s = format!("{}", v);
1086 result.push_str(&pad_str(&s, width, left_align, ' '));
1087 }
1088 Some('s') => {
1089 let v = args.get(arg_i).map(|v| v.to_string()).unwrap_or_default();
1090 arg_i += 1;
1091 let s = if let Some(p) = prec { v.chars().take(p).collect() } else { v };
1092 result.push_str(&pad_str(&s, width, left_align, ' '));
1093 }
1094 Some('q') => {
1095 let v = args.get(arg_i).and_then(|v| v.to_str_repr()).unwrap_or_default();
1096 arg_i += 1;
1097 result.push('"');
1098 for ch in v.chars() {
1099 match ch {
1100 '"' => result.push_str("\\\""),
1101 '\\' => result.push_str("\\\\"),
1102 '\n' => result.push_str("\\n"),
1103 '\r' => result.push_str("\\r"),
1104 '\0' => result.push_str("\\0"),
1105 c => result.push(c),
1106 }
1107 }
1108 result.push('"');
1109 }
1110 Some(x) => { result.push('%'); result.push(x); }
1111 None => result.push('%'),
1112 }
1113 }
1114 result
1115}
1116
1117fn pad_str(s: &str, width: usize, left: bool, pad: char) -> String {
1118 if s.len() >= width { return s.to_string(); }
1119 let padding: String = std::iter::repeat(pad).take(width - s.len()).collect();
1120 if left { format!("{}{}", s, padding) } else { format!("{}{}", padding, s) }
1121}
1122
1123fn register_table(vm: &mut Vm) {
1126 let tbl = Table::new();
1127
1128 tbl.rawset_str("insert", native("table.insert", |_vm, args| {
1130 let table = args.first().cloned().unwrap_or(Value::Nil);
1131 match table {
1132 Value::Table(t) => {
1133 if args.len() == 2 {
1134 t.push(args[1].clone());
1135 } else if args.len() >= 3 {
1136 let pos = args[1].to_int().unwrap_or(t.length() + 1);
1137 let val = args[2].clone();
1138 let len = t.length();
1139 for i in (pos..=len).rev() {
1141 let v = t.get(&Value::Int(i));
1142 t.set(Value::Int(i + 1), v);
1143 }
1144 t.set(Value::Int(pos), val);
1145 }
1146 Ok(vec![])
1147 }
1148 _ => Err(ScriptError::new("table.insert: not a table")),
1149 }
1150 }));
1151
1152 tbl.rawset_str("remove", native("table.remove", |_vm, args| {
1154 let table = args.first().cloned().unwrap_or(Value::Nil);
1155 match &table {
1156 Value::Table(t) => {
1157 let len = t.length();
1158 let pos = args.get(1).and_then(|v| v.to_int()).unwrap_or(len);
1159 if len == 0 { return Ok(vec![Value::Nil]); }
1160 let removed = t.get(&Value::Int(pos));
1161 for i in pos..len {
1162 let next = t.get(&Value::Int(i + 1));
1163 t.set(Value::Int(i), next);
1164 }
1165 t.set(Value::Int(len), Value::Nil);
1166 Ok(vec![removed])
1167 }
1168 _ => Err(ScriptError::new("table.remove: not a table")),
1169 }
1170 }));
1171
1172 tbl.rawset_str("concat", native("table.concat", |_vm, args| {
1174 let table = args.first().cloned().unwrap_or(Value::Nil);
1175 let sep = args.get(1).and_then(|v| v.to_str_repr()).unwrap_or_default();
1176 let i = args.get(2).and_then(|v| v.to_int()).unwrap_or(1);
1177 let j_opt = args.get(3).and_then(|v| v.to_int());
1178 match &table {
1179 Value::Table(t) => {
1180 let j = j_opt.unwrap_or_else(|| t.length());
1181 let mut parts = Vec::new();
1182 for idx in i..=j {
1183 let v = t.get(&Value::Int(idx));
1184 parts.push(v.to_str_repr().unwrap_or_else(|| v.to_string()));
1185 }
1186 Ok(vec![Value::Str(Arc::new(parts.join(&sep)))])
1187 }
1188 _ => Err(ScriptError::new("table.concat: not a table")),
1189 }
1190 }));
1191
1192 tbl.rawset_str("sort", native("table.sort", |vm, args| {
1194 let table = args.first().cloned().unwrap_or(Value::Nil);
1195 let comp = args.get(1).cloned();
1196 match &table {
1197 Value::Table(t) => {
1198 let mut v = t.array_values();
1199 let mut err: Option<ScriptError> = None;
1201 match comp {
1202 Some(Value::Function(_)) | Some(Value::NativeFunction(_)) => {
1203 let comp_val = comp.unwrap();
1204 let n = v.len();
1206 for i in 1..n {
1207 let mut j = i;
1208 while j > 0 {
1209 let res = vm.call(comp_val.clone(), vec![v[j].clone(), v[j-1].clone()]);
1210 match res {
1211 Ok(r) => {
1212 if r.first().map(|x| x.is_truthy()).unwrap_or(false) {
1213 v.swap(j, j - 1);
1214 j -= 1;
1215 } else {
1216 break;
1217 }
1218 }
1219 Err(e) => { err = Some(e); break; }
1220 }
1221 }
1222 if err.is_some() { break; }
1223 }
1224 }
1225 _ => {
1226 v.sort_by(|a, b| {
1227 match (a, b) {
1228 (Value::Int(ai), Value::Int(bi)) => ai.cmp(bi),
1229 (Value::Float(af), Value::Float(bf)) => af.partial_cmp(bf).unwrap_or(std::cmp::Ordering::Equal),
1230 (Value::Str(sa), Value::Str(sb)) => sa.as_ref().cmp(sb.as_ref()),
1231 _ => {
1232 let af = a.to_float().unwrap_or(0.0);
1233 let bf = b.to_float().unwrap_or(0.0);
1234 af.partial_cmp(&bf).unwrap_or(std::cmp::Ordering::Equal)
1235 }
1236 }
1237 });
1238 }
1239 }
1240 if let Some(e) = err { return Err(e); }
1241 for (i, val) in v.into_iter().enumerate() {
1242 t.set(Value::Int(i as i64 + 1), val);
1243 }
1244 Ok(vec![])
1245 }
1246 _ => Err(ScriptError::new("table.sort: not a table")),
1247 }
1248 }));
1249
1250 tbl.rawset_str("unpack", native("table.unpack", |_vm, args| {
1252 let table = args.first().cloned().unwrap_or(Value::Nil);
1253 let i = args.get(1).and_then(|v| v.to_int()).unwrap_or(1);
1254 let j_opt = args.get(2).and_then(|v| v.to_int());
1255 match table {
1256 Value::Table(t) => {
1257 let j = j_opt.unwrap_or_else(|| t.length());
1258 let mut result = Vec::new();
1259 for idx in i..=j { result.push(t.get(&Value::Int(idx))); }
1260 Ok(result)
1261 }
1262 _ => Err(ScriptError::new("table.unpack: not a table")),
1263 }
1264 }));
1265
1266 tbl.rawset_str("pack", native("table.pack", |_vm, args| {
1268 let t = Table::new();
1269 let n = args.len() as i64;
1270 for (i, v) in args.into_iter().enumerate() {
1271 t.set(Value::Int(i as i64 + 1), v);
1272 }
1273 t.rawset_str("n", Value::Int(n));
1274 Ok(vec![Value::Table(t)])
1275 }));
1276
1277 tbl.rawset_str("move", native("table.move", |_vm, args| {
1279 let a1 = args.first().cloned().unwrap_or(Value::Nil);
1280 let f = args.get(1).and_then(|v| v.to_int()).unwrap_or(1);
1281 let e = args.get(2).and_then(|v| v.to_int()).unwrap_or(0);
1282 let t_p = args.get(3).and_then(|v| v.to_int()).unwrap_or(1);
1283 let a2 = args.get(4).cloned().unwrap_or_else(|| a1.clone());
1284 if let (Value::Table(src), Value::Table(dst)) = (&a1, &a2) {
1285 let mut vals = Vec::new();
1286 for i in f..=e { vals.push(src.get(&Value::Int(i))); }
1287 for (offset, val) in vals.into_iter().enumerate() {
1288 dst.set(Value::Int(t_p + offset as i64), val);
1289 }
1290 }
1291 Ok(vec![a2])
1292 }));
1293
1294 tbl.rawset_str("clone", native("table.clone", |_vm, args| {
1296 let v = args.into_iter().next().unwrap_or(Value::Nil);
1297 match &v {
1298 Value::Table(t) => {
1299 let new_t = Table::new();
1300 let arr = t.array_values();
1301 for (i, val) in arr.into_iter().enumerate() {
1302 new_t.set(Value::Int(i as i64 + 1), val);
1303 }
1304 let mut key = Value::Nil;
1305 loop {
1306 match t.next(&key) {
1307 Some((k, val)) => {
1308 if !matches!(&k, Value::Int(_)) {
1310 new_t.set(k.clone(), val);
1311 }
1312 key = k;
1313 }
1314 None => break,
1315 }
1316 }
1317 Ok(vec![Value::Table(new_t)])
1318 }
1319 _ => Err(ScriptError::new("table.clone: not a table")),
1320 }
1321 }));
1322
1323 tbl.rawset_str("deep_clone", native("table.deep_clone", |_vm, args| {
1325 let v = args.into_iter().next().unwrap_or(Value::Nil);
1326 Ok(vec![deep_clone_value(v, 0)])
1327 }));
1328
1329 tbl.rawset_str("keys", native("table.keys", |_vm, args| {
1331 let v = args.into_iter().next().unwrap_or(Value::Nil);
1332 match &v {
1333 Value::Table(t) => {
1334 let kt = Table::new();
1335 let mut key = Value::Nil;
1336 let mut idx = 1i64;
1337 loop {
1338 match t.next(&key) {
1339 Some((k, _)) => { kt.set(Value::Int(idx), k.clone()); idx += 1; key = k; }
1340 None => break,
1341 }
1342 }
1343 Ok(vec![Value::Table(kt)])
1344 }
1345 _ => Err(ScriptError::new("table.keys: not a table")),
1346 }
1347 }));
1348
1349 tbl.rawset_str("values", native("table.values", |_vm, args| {
1351 let v = args.into_iter().next().unwrap_or(Value::Nil);
1352 match &v {
1353 Value::Table(t) => {
1354 let vt = Table::new();
1355 let mut key = Value::Nil;
1356 let mut idx = 1i64;
1357 loop {
1358 match t.next(&key) {
1359 Some((k, val)) => { vt.set(Value::Int(idx), val); idx += 1; key = k; }
1360 None => break,
1361 }
1362 }
1363 Ok(vec![Value::Table(vt)])
1364 }
1365 _ => Err(ScriptError::new("table.values: not a table")),
1366 }
1367 }));
1368
1369 tbl.rawset_str("pairs", native("table.pairs", |_vm, args| {
1371 let table = args.into_iter().next().unwrap_or(Value::Nil);
1372 let iter_fn = Arc::new(NativeFunc {
1373 name: "table.pairs_iter".to_string(),
1374 func: Box::new(|_vm, args| {
1375 let table = args.first().cloned().unwrap_or(Value::Nil);
1376 let key = args.get(1).cloned().unwrap_or(Value::Nil);
1377 match &table {
1378 Value::Table(t) => match t.next(&key) {
1379 Some((k, v)) => Ok(vec![k, v]),
1380 None => Ok(vec![Value::Nil]),
1381 },
1382 _ => Ok(vec![Value::Nil]),
1383 }
1384 }),
1385 });
1386 Ok(vec![Value::NativeFunction(iter_fn), table, Value::Nil])
1387 }));
1388
1389 tbl.rawset_str("ipairs", native("table.ipairs", |_vm, args| {
1391 let table = args.into_iter().next().unwrap_or(Value::Nil);
1392 let iter_fn = Arc::new(NativeFunc {
1393 name: "table.ipairs_iter".to_string(),
1394 func: Box::new(|_vm, args| {
1395 let table = args.first().cloned().unwrap_or(Value::Nil);
1396 let idx = args.get(1).and_then(|v| v.to_int()).unwrap_or(0) + 1;
1397 match &table {
1398 Value::Table(t) => {
1399 let v = t.get(&Value::Int(idx));
1400 if matches!(v, Value::Nil) {
1401 Ok(vec![Value::Nil])
1402 } else {
1403 Ok(vec![Value::Int(idx), v])
1404 }
1405 }
1406 _ => Ok(vec![Value::Nil]),
1407 }
1408 }),
1409 });
1410 Ok(vec![Value::NativeFunction(iter_fn), table, Value::Int(0)])
1411 }));
1412
1413 tbl.rawset_str("len", native("table.len", |_vm, args| {
1415 match args.into_iter().next().unwrap_or(Value::Nil) {
1416 Value::Table(t) => Ok(vec![Value::Int(t.length())]),
1417 _ => Err(ScriptError::new("table.len: not a table")),
1418 }
1419 }));
1420
1421 tbl.rawset_str("contains", native("table.contains", |_vm, args| {
1423 let table = args.first().cloned().unwrap_or(Value::Nil);
1424 let needle = args.get(1).cloned().unwrap_or(Value::Nil);
1425 match &table {
1426 Value::Table(t) => {
1427 let mut key = Value::Nil;
1428 loop {
1429 match t.next(&key) {
1430 Some((k, v)) => {
1431 if v == needle { return Ok(vec![Value::Bool(true)]); }
1432 key = k;
1433 }
1434 None => break,
1435 }
1436 }
1437 Ok(vec![Value::Bool(false)])
1438 }
1439 _ => Err(ScriptError::new("table.contains: not a table")),
1440 }
1441 }));
1442
1443 tbl.rawset_str("merge", native("table.merge", |_vm, args| {
1445 let t1 = args.first().cloned().unwrap_or(Value::Nil);
1446 let t2 = args.get(1).cloned().unwrap_or(Value::Nil);
1447 let result = Table::new();
1448 for src in &[&t1, &t2] {
1449 if let Value::Table(t) = src {
1450 let mut key = Value::Nil;
1451 loop {
1452 match t.next(&key) {
1453 Some((k, v)) => { result.set(k.clone(), v); key = k; }
1454 None => break,
1455 }
1456 }
1457 }
1458 }
1459 Ok(vec![Value::Table(result)])
1460 }));
1461
1462 vm.set_global("table", Value::Table(tbl));
1463}
1464
1465fn deep_clone_value(v: Value, depth: usize) -> Value {
1466 if depth > 32 { return v; }
1467 match v {
1468 Value::Table(t) => {
1469 let new_t = Table::new();
1470 let mut key = Value::Nil;
1471 loop {
1472 match t.next(&key) {
1473 Some((k, val)) => {
1474 let new_k = deep_clone_value(k.clone(), depth + 1);
1475 let new_val = deep_clone_value(val, depth + 1);
1476 new_t.set(new_k, new_val);
1477 key = k;
1478 }
1479 None => break,
1480 }
1481 }
1482 Value::Table(new_t)
1483 }
1484 other => other,
1485 }
1486}
1487
1488fn register_io(vm: &mut Vm) {
1492 let io = Table::new();
1493
1494 io.rawset_str("write", native("io.write", |vm, args| {
1496 let s: String = args.iter().map(|v| v.to_string()).collect::<Vec<_>>().join("");
1497 vm.output.push(s);
1498 Ok(vec![])
1499 }));
1500
1501 io.rawset_str("read", native("io.read", |_vm, _args| {
1503 Ok(vec![Value::Nil])
1504 }));
1505
1506 io.rawset_str("lines", native("io.lines", |_vm, _args| {
1508 let done = Arc::new(std::sync::atomic::AtomicBool::new(true));
1509 let iter_fn = Arc::new(NativeFunc {
1510 name: "io.lines_iter".to_string(),
1511 func: Box::new(move |_vm, _args| {
1512 Ok(vec![Value::Nil])
1513 }),
1514 });
1515 Ok(vec![Value::NativeFunction(iter_fn)])
1516 }));
1517
1518 io.rawset_str("flush", native("io.flush", |_vm, _args| {
1520 Ok(vec![Value::Bool(true)])
1521 }));
1522
1523 io.rawset_str("open", native("io.open", |_vm, args| {
1525 let path = args.first().and_then(|v| v.to_str_repr()).unwrap_or_default();
1526 let mode = args.get(1).and_then(|v| v.to_str_repr()).unwrap_or_else(|| "r".to_string());
1527 let fh = Table::new();
1528 fh.rawset_str("__path", Value::Str(Arc::new(path)));
1529 fh.rawset_str("__mode", Value::Str(Arc::new(mode)));
1530 fh.rawset_str("__buf", Value::Str(Arc::new(String::new())));
1531 fh.rawset_str("read", native("io.handle.read", |_vm, _args| Ok(vec![Value::Nil])));
1532 fh.rawset_str("write", native("io.handle.write", |_vm, _args| Ok(vec![Value::Nil])));
1533 fh.rawset_str("close", native("io.handle.close", |_vm, _args| Ok(vec![Value::Bool(true)])));
1534 fh.rawset_str("lines", native("io.handle.lines", |_vm, _args| {
1535 let iter_fn = Arc::new(NativeFunc {
1536 name: "io.handle.lines_iter".to_string(),
1537 func: Box::new(|_vm, _args| Ok(vec![Value::Nil])),
1538 });
1539 Ok(vec![Value::NativeFunction(iter_fn)])
1540 }));
1541 Ok(vec![Value::Table(fh)])
1542 }));
1543
1544 vm.set_global("io", Value::Table(io));
1545}
1546
1547thread_local! {
1550 static START_INSTANT: std::time::Instant = std::time::Instant::now();
1551 static EXIT_REQUESTED: RefCell<bool> = RefCell::new(false);
1552}
1553
1554fn register_os(vm: &mut Vm) {
1555 let os = Table::new();
1556
1557 os.rawset_str("time", native("os.time", |_vm, _args| {
1559 Ok(vec![Value::Int(0)])
1560 }));
1561
1562 os.rawset_str("clock", native("os.clock", |_vm, _args| {
1564 let elapsed = START_INSTANT.with(|s| s.elapsed().as_secs_f64());
1565 Ok(vec![Value::Float(elapsed)])
1566 }));
1567
1568 os.rawset_str("date", native("os.date", |_vm, args| {
1570 let fmt = args.first().and_then(|v| v.to_str_repr()).unwrap_or_else(|| "%c".to_string());
1571 if fmt.starts_with('*') && fmt.contains('t') {
1572 let t = Table::new();
1574 t.rawset_str("year", Value::Int(1970));
1575 t.rawset_str("month", Value::Int(1));
1576 t.rawset_str("day", Value::Int(1));
1577 t.rawset_str("hour", Value::Int(0));
1578 t.rawset_str("min", Value::Int(0));
1579 t.rawset_str("sec", Value::Int(0));
1580 t.rawset_str("wday", Value::Int(5)); t.rawset_str("yday", Value::Int(1));
1582 t.rawset_str("isdst", Value::Bool(false));
1583 Ok(vec![Value::Table(t)])
1584 } else {
1585 Ok(vec![Value::Str(Arc::new("Thu Jan 1 00:00:00 1970".to_string()))])
1586 }
1587 }));
1588
1589 os.rawset_str("exit", native("os.exit", |_vm, args| {
1591 let _code = args.first().and_then(|v| v.to_int()).unwrap_or(0);
1592 EXIT_REQUESTED.with(|e| *e.borrow_mut() = true);
1593 Err(ScriptError::new("os.exit called"))
1594 }));
1595
1596 os.rawset_str("getenv", native("os.getenv", |_vm, _args| {
1598 Ok(vec![Value::Nil])
1599 }));
1600
1601 os.rawset_str("difftime", native("os.difftime", |_vm, _args| {
1603 Ok(vec![Value::Int(0)])
1604 }));
1605
1606 vm.set_global("os", Value::Table(os));
1607}
1608
1609fn register_bit(vm: &mut Vm) {
1612 let bit = Table::new();
1613
1614 bit.rawset_str("band", native("bit.band", |_vm, args| {
1616 let mut result = args.first().and_then(|v| v.to_int()).unwrap_or(0);
1617 for a in args.iter().skip(1) {
1618 result &= a.to_int().unwrap_or(0);
1619 }
1620 Ok(vec![Value::Int(result)])
1621 }));
1622
1623 bit.rawset_str("bor", native("bit.bor", |_vm, args| {
1625 let mut result = args.first().and_then(|v| v.to_int()).unwrap_or(0);
1626 for a in args.iter().skip(1) {
1627 result |= a.to_int().unwrap_or(0);
1628 }
1629 Ok(vec![Value::Int(result)])
1630 }));
1631
1632 bit.rawset_str("bxor", native("bit.bxor", |_vm, args| {
1634 let mut result = args.first().and_then(|v| v.to_int()).unwrap_or(0);
1635 for a in args.iter().skip(1) {
1636 result ^= a.to_int().unwrap_or(0);
1637 }
1638 Ok(vec![Value::Int(result)])
1639 }));
1640
1641 bit.rawset_str("bnot", native("bit.bnot", |_vm, args| {
1643 let a = args.first().and_then(|v| v.to_int()).unwrap_or(0);
1644 Ok(vec![Value::Int(!a)])
1645 }));
1646
1647 bit.rawset_str("lshift", native("bit.lshift", |_vm, args| {
1649 let a = args.first().and_then(|v| v.to_int()).unwrap_or(0);
1650 let n = args.get(1).and_then(|v| v.to_int()).unwrap_or(0);
1651 let result = if n >= 64 || n <= -64 { 0 } else if n >= 0 {
1652 a.wrapping_shl(n as u32)
1653 } else {
1654 ((a as u64).wrapping_shr((-n) as u32)) as i64
1655 };
1656 Ok(vec![Value::Int(result)])
1657 }));
1658
1659 bit.rawset_str("rshift", native("bit.rshift", |_vm, args| {
1661 let a = args.first().and_then(|v| v.to_int()).unwrap_or(0) as u64;
1662 let n = args.get(1).and_then(|v| v.to_int()).unwrap_or(0);
1663 let result = if n >= 64 || n <= -64 { 0u64 } else if n >= 0 {
1664 a.wrapping_shr(n as u32)
1665 } else {
1666 a.wrapping_shl((-n) as u32)
1667 };
1668 Ok(vec![Value::Int(result as i64)])
1669 }));
1670
1671 bit.rawset_str("arshift", native("bit.arshift", |_vm, args| {
1673 let a = args.first().and_then(|v| v.to_int()).unwrap_or(0);
1674 let n = args.get(1).and_then(|v| v.to_int()).unwrap_or(0);
1675 let result = if n >= 64 { if a < 0 { -1 } else { 0 } }
1676 else if n <= 0 { a.wrapping_shl((-n) as u32) }
1677 else { a.wrapping_shr(n as u32) };
1678 Ok(vec![Value::Int(result)])
1679 }));
1680
1681 bit.rawset_str("tobit", native("bit.tobit", |_vm, args| {
1683 let a = args.first().and_then(|v| v.to_int()).unwrap_or(0);
1684 Ok(vec![Value::Int((a as i32) as i64)])
1685 }));
1686
1687 bit.rawset_str("tohex", native("bit.tohex", |_vm, args| {
1689 let a = args.first().and_then(|v| v.to_int()).unwrap_or(0) as u32;
1690 let n = args.get(1).and_then(|v| v.to_int()).unwrap_or(8).abs() as usize;
1691 let s = format!("{:0>width$x}", a, width = n);
1692 let trimmed: String = s.chars().rev().take(n).collect::<String>().chars().rev().collect();
1693 Ok(vec![Value::Str(Arc::new(trimmed))])
1694 }));
1695
1696 bit.rawset_str("bswap", native("bit.bswap", |_vm, args| {
1698 let a = args.first().and_then(|v| v.to_int()).unwrap_or(0) as u32;
1699 Ok(vec![Value::Int(a.swap_bytes() as i64)])
1700 }));
1701
1702 vm.set_global("bit", Value::Table(bit));
1703}
1704
1705#[cfg(test)]
1708mod tests {
1709 use super::*;
1710 use crate::scripting::{compiler::Compiler, parser::Parser};
1711
1712 fn run(src: &str) -> Vec<Value> {
1713 let script = Parser::from_source("test", src).expect("parse error");
1714 let chunk = Compiler::compile_script(&script);
1715 let mut vm = Vm::new();
1716 register_all(&mut vm);
1717 let result = vm.execute(chunk).expect("runtime error");
1718 eprintln!("[DEBUG run] src={:?} => {:?}", src, result);
1719 result
1720 }
1721
1722 fn run_err(src: &str) -> String {
1723 let script = Parser::from_source("test", src).expect("parse error");
1724 let chunk = Compiler::compile_script(&script);
1725 let mut vm = Vm::new();
1726 register_all(&mut vm);
1727 vm.execute(chunk).unwrap_err().message
1728 }
1729
1730 #[test]
1731 fn test_math_abs() {
1732 let r = run("return math.abs(-5)");
1733 assert!(matches!(&r[0], Value::Float(f) if (*f - 5.0).abs() < 1e-9));
1734 }
1735
1736 #[test]
1737 fn test_math_sqrt() {
1738 let r = run("return math.sqrt(9)");
1739 assert!(matches!(&r[0], Value::Float(f) if (*f - 3.0).abs() < 1e-9));
1740 }
1741
1742 #[test]
1743 fn test_math_clamp() {
1744 let r = run("return math.clamp(10, 0, 5)");
1745 assert!(matches!(&r[0], Value::Float(f) if (*f - 5.0).abs() < 1e-9));
1746 }
1747
1748 #[test]
1749 fn test_math_random_range() {
1750 let r = run("return math.random(1, 10)");
1751 if let Value::Int(n) = r[0] { assert!(n >= 1 && n <= 10); } else { panic!("expected int"); }
1752 }
1753
1754 #[test]
1755 fn test_string_format_d() {
1756 let r = run("return string.format(\"%05d\", 42)");
1757 assert!(matches!(&r[0], Value::Str(s) if s.as_ref() == "00042"));
1758 }
1759
1760 #[test]
1761 fn test_string_format_s() {
1762 let r = run("return string.format(\"%-10s|\", \"hi\")");
1763 assert!(matches!(&r[0], Value::Str(s) if s.as_ref() == "hi |"));
1764 }
1765
1766 #[test]
1767 fn test_string_rep() {
1768 let r = run("return string.rep(\"ab\", 3, \"-\")");
1769 assert!(matches!(&r[0], Value::Str(s) if s.as_ref() == "ab-ab-ab"));
1770 }
1771
1772 #[test]
1773 fn test_string_split() {
1774 let r = run("local t = string.split(\"a,b,c\", \",\") return t[1], t[2], t[3]");
1775 assert!(matches!(&r[0], Value::Str(s) if s.as_ref() == "a"));
1776 assert!(matches!(&r[1], Value::Str(s) if s.as_ref() == "b"));
1777 assert!(matches!(&r[2], Value::Str(s) if s.as_ref() == "c"));
1778 }
1779
1780 #[test]
1781 fn test_table_sort_default() {
1782 let r = run("local t = {3,1,2} table.sort(t) return t[1], t[2], t[3]");
1783 assert_eq!(r[0], Value::Int(1));
1784 assert_eq!(r[1], Value::Int(2));
1785 assert_eq!(r[2], Value::Int(3));
1786 }
1787
1788 #[test]
1789 fn test_table_pack_unpack() {
1790 let r = run("local t = table.pack(10, 20, 30) return table.unpack(t, 1, t.n)");
1791 assert_eq!(r[0], Value::Int(10));
1792 assert_eq!(r[2], Value::Int(30));
1793 }
1794
1795 #[test]
1796 fn test_table_merge() {
1797 let r = run("local a = {x=1} local b = {y=2} local c = table.merge(a, b) return c.x, c.y");
1798 assert_eq!(r[0], Value::Int(1));
1799 assert_eq!(r[1], Value::Int(2));
1800 }
1801
1802 #[test]
1803 fn test_bit_band() {
1804 let r = run("return bit.band(0xFF, 0x0F)");
1805 assert_eq!(r[0], Value::Int(0x0F));
1806 }
1807
1808 #[test]
1809 fn test_bit_bxor() {
1810 let r = run("return bit.bxor(0xFF, 0x0F)");
1811 assert_eq!(r[0], Value::Int(0xF0));
1812 }
1813
1814 #[test]
1815 fn test_bit_lshift() {
1816 let r = run("return bit.lshift(1, 4)");
1817 assert_eq!(r[0], Value::Int(16));
1818 }
1819
1820 #[test]
1821 fn test_pcall_success() {
1822 let r = run("return pcall(function() return 42 end)");
1823 assert_eq!(r[0], Value::Bool(true));
1824 assert_eq!(r[1], Value::Int(42));
1825 }
1826
1827 #[test]
1828 fn test_pcall_error() {
1829 let r = run("return pcall(function() error(\"oops\") end)");
1830 assert_eq!(r[0], Value::Bool(false));
1831 assert!(matches!(&r[1], Value::Str(s) if s.as_ref() == "oops"));
1832 }
1833
1834 #[test]
1835 fn test_select_hash() {
1836 let r = run("return select(\"#\", 1, 2, 3)");
1837 assert_eq!(r[0], Value::Int(3));
1838 }
1839
1840 #[test]
1841 fn test_tonumber_base16() {
1842 let r = run("return tonumber(\"ff\", 16)");
1843 assert_eq!(r[0], Value::Int(255));
1844 }
1845
1846 #[test]
1847 fn test_string_byte_char() {
1848 let r = run("return string.char(65, 66, 67)");
1849 assert!(matches!(&r[0], Value::Str(s) if s.as_ref() == "ABC"));
1850 }
1851
1852 #[test]
1853 fn test_type_function() {
1854 let r = run("return type(42), type(3.14), type(\"hi\"), type(nil), type(true)");
1855 assert!(matches!(&r[0], Value::Str(s) if s.as_ref() == "integer"));
1856 assert!(matches!(&r[1], Value::Str(s) if s.as_ref() == "float"));
1857 assert!(matches!(&r[2], Value::Str(s) if s.as_ref() == "string"));
1858 assert!(matches!(&r[3], Value::Str(s) if s.as_ref() == "nil"));
1859 assert!(matches!(&r[4], Value::Str(s) if s.as_ref() == "boolean"));
1860 }
1861}