1use std::collections::HashMap;
6use std::fmt;
7use std::sync::Arc;
8
9use super::compiler::{Chunk, Constant, Instruction};
10
11#[derive(Clone, Debug)]
15pub enum Value {
16 Nil,
17 Bool(bool),
18 Int(i64),
19 Float(f64),
20 Str(Arc<String>),
21 Table(Table),
22 Function(Arc<Closure>),
23 NativeFunction(Arc<NativeFunc>),
24}
25
26impl PartialEq for Value {
27 fn eq(&self, other: &Self) -> bool {
28 match (self, other) {
29 (Value::Nil, Value::Nil) => true,
30 (Value::Bool(a), Value::Bool(b)) => a == b,
31 (Value::Int(a), Value::Int(b)) => a == b,
32 (Value::Float(a), Value::Float(b)) => a == b,
33 (Value::Str(a), Value::Str(b)) => a == b,
34 (Value::Int(a), Value::Float(b)) => (*a as f64) == *b,
35 (Value::Float(a), Value::Int(b)) => *a == (*b as f64),
36 (Value::Table(a), Value::Table(b)) => Arc::ptr_eq(&a.inner, &b.inner),
37 (Value::Function(a), Value::Function(b)) => Arc::ptr_eq(a, b),
38 _ => false,
39 }
40 }
41}
42
43impl fmt::Display for Value {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 match self {
46 Value::Nil => write!(f, "nil"),
47 Value::Bool(b) => write!(f, "{}", b),
48 Value::Int(n) => write!(f, "{}", n),
49 Value::Float(n) => write!(f, "{}", n),
50 Value::Str(s) => write!(f, "{}", s),
51 Value::Table(_) => write!(f, "table"),
52 Value::Function(_) => write!(f, "function"),
53 Value::NativeFunction(n) => write!(f, "function: {}", n.name),
54 }
55 }
56}
57
58impl Value {
59 pub fn is_truthy(&self) -> bool {
60 !matches!(self, Value::Nil | Value::Bool(false))
61 }
62
63 pub fn type_name(&self) -> &'static str {
64 match self {
65 Value::Nil => "nil",
66 Value::Bool(_) => "boolean",
67 Value::Int(_) => "integer",
68 Value::Float(_) => "float",
69 Value::Str(_) => "string",
70 Value::Table(_) => "table",
71 Value::Function(_) => "function",
72 Value::NativeFunction(_) => "function",
73 }
74 }
75
76 pub fn to_float(&self) -> Option<f64> {
77 match self {
78 Value::Float(f) => Some(*f),
79 Value::Int(i) => Some(*i as f64),
80 Value::Str(s) => s.parse::<f64>().ok(),
81 _ => None,
82 }
83 }
84
85 pub fn to_int(&self) -> Option<i64> {
86 match self {
87 Value::Int(i) => Some(*i),
88 Value::Float(f) => if f.fract() == 0.0 { Some(*f as i64) } else { None },
89 Value::Str(s) => s.parse::<i64>().ok(),
90 _ => None,
91 }
92 }
93
94 pub fn to_str_repr(&self) -> Option<String> {
95 match self {
96 Value::Str(s) => Some(s.as_ref().clone()),
97 Value::Int(i) => Some(i.to_string()),
98 Value::Float(f) => Some(f.to_string()),
99 _ => None,
100 }
101 }
102}
103
104impl From<&Constant> for Value {
105 fn from(c: &Constant) -> Self {
106 match c {
107 Constant::Nil => Value::Nil,
108 Constant::Bool(b) => Value::Bool(*b),
109 Constant::Int(i) => Value::Int(*i),
110 Constant::Float(f) => Value::Float(*f),
111 Constant::Str(s) => Value::Str(Arc::new(s.clone())),
112 }
113 }
114}
115
116#[derive(Clone, Debug)]
119pub struct Table {
120 pub(crate) inner: Arc<std::cell::RefCell<TableData>>,
121}
122
123#[derive(Debug)]
124struct TableData {
125 hash: HashMap<TableKey, Value>,
126 array: Vec<Value>,
127 metatable: Option<Table>,
128}
129
130#[derive(Debug, Clone, PartialEq, Eq, Hash)]
131enum TableKey {
132 Str(String),
133 Int(i64),
134 Bool(bool),
135}
136
137impl TableKey {
138 fn from_value(v: &Value) -> Option<Self> {
139 match v {
140 Value::Str(s) => Some(TableKey::Str(s.as_ref().clone())),
141 Value::Int(i) => Some(TableKey::Int(*i)),
142 Value::Bool(b) => Some(TableKey::Bool(*b)),
143 Value::Float(f) => {
144 let i = *f as i64;
145 if i as f64 == *f { Some(TableKey::Int(i)) } else { None }
146 }
147 _ => None,
148 }
149 }
150}
151
152impl Table {
153 pub fn new() -> Self {
154 Table { inner: Arc::new(std::cell::RefCell::new(TableData {
155 hash: HashMap::new(), array: Vec::new(), metatable: None,
156 }))}
157 }
158
159 pub fn get(&self, key: &Value) -> Value {
160 let d = self.inner.borrow();
161 if let Some(i) = int_key(key) {
162 if i >= 1 {
163 if let Some(v) = d.array.get((i - 1) as usize) {
164 return v.clone();
165 }
166 }
170 }
171 TableKey::from_value(key)
172 .and_then(|k| d.hash.get(&k).cloned())
173 .unwrap_or(Value::Nil)
174 }
175
176 pub fn rawget_str(&self, key: &str) -> Value {
177 self.inner.borrow().hash
178 .get(&TableKey::Str(key.to_string()))
179 .cloned()
180 .unwrap_or(Value::Nil)
181 }
182
183 pub fn set(&self, key: Value, val: Value) {
184 let mut d = self.inner.borrow_mut();
185 if let Some(i) = int_key(&key) {
186 if i >= 1 {
187 let idx = (i - 1) as usize;
188 if idx <= d.array.len() {
189 if matches!(val, Value::Nil) {
190 if idx < d.array.len() { d.array[idx] = Value::Nil; }
191 } else if idx == d.array.len() {
192 d.array.push(val);
193 } else {
194 d.array[idx] = val;
195 }
196 return;
197 }
198 }
199 }
200 if let Some(k) = TableKey::from_value(&key) {
201 match val {
202 Value::Nil => { d.hash.remove(&k); }
203 v => { d.hash.insert(k, v); }
204 }
205 }
206 }
207
208 pub fn rawset_str(&self, key: &str, val: Value) {
209 let mut d = self.inner.borrow_mut();
210 let k = TableKey::Str(key.to_string());
211 match val {
212 Value::Nil => { d.hash.remove(&k); }
213 v => { d.hash.insert(k, v); }
214 }
215 }
216
217 pub fn length(&self) -> i64 {
218 self.inner.borrow().array.len() as i64
219 }
220
221 pub fn push(&self, v: Value) {
222 self.inner.borrow_mut().array.push(v);
223 }
224
225 pub fn array_values(&self) -> Vec<Value> {
226 self.inner.borrow().array.clone()
227 }
228
229 pub fn set_metatable(&self, mt: Option<Table>) {
230 self.inner.borrow_mut().metatable = mt;
231 }
232
233 pub fn get_metatable(&self) -> Option<Table> {
234 self.inner.borrow().metatable.clone()
235 }
236
237 pub fn next(&self, after: &Value) -> Option<(Value, Value)> {
238 let d = self.inner.borrow();
239 match after {
240 Value::Nil => {
241 if let Some(v) = d.array.first() {
242 return Some((Value::Int(1), v.clone()));
243 }
244 return d.hash.iter().next().map(|(k, v)| (tk_to_val(k), v.clone()));
245 }
246 _ => {
247 if let Some(i) = int_key(after) {
248 if i >= 1 {
249 let next_i = i as usize;
250 if next_i < d.array.len() {
251 return Some((Value::Int(i + 1), d.array[next_i].clone()));
252 }
253 return d.hash.iter().next().map(|(k, v)| (tk_to_val(k), v.clone()));
254 }
255 }
256 if let Some(k) = TableKey::from_value(after) {
257 let mut found = false;
258 for (hk, hv) in &d.hash {
259 if found { return Some((tk_to_val(hk), hv.clone())); }
260 if *hk == k { found = true; }
261 }
262 }
263 None
264 }
265 }
266 }
267}
268
269fn int_key(v: &Value) -> Option<i64> {
270 match v {
271 Value::Int(i) => Some(*i),
272 Value::Float(f) if f.fract() == 0.0 => Some(*f as i64),
273 _ => None,
274 }
275}
276
277fn tk_to_val(k: &TableKey) -> Value {
278 match k {
279 TableKey::Str(s) => Value::Str(Arc::new(s.clone())),
280 TableKey::Int(i) => Value::Int(*i),
281 TableKey::Bool(b) => Value::Bool(*b),
282 }
283}
284
285#[derive(Debug)]
288pub struct Closure {
289 pub chunk: Arc<Chunk>,
290 pub upvalues: Vec<UpvalueCell>,
291}
292
293#[derive(Debug, Clone)]
294pub struct UpvalueCell(Arc<std::cell::RefCell<Value>>);
295
296impl UpvalueCell {
297 pub fn new(v: Value) -> Self { UpvalueCell(Arc::new(std::cell::RefCell::new(v))) }
298 pub fn get(&self) -> Value { self.0.borrow().clone() }
299 pub fn set(&self, v: Value) { *self.0.borrow_mut() = v; }
300}
301
302pub struct NativeFunc {
303 pub name: String,
304 pub func: Box<dyn Fn(&mut Vm, Vec<Value>) -> Result<Vec<Value>, ScriptError> + Send + Sync>,
305}
306
307impl fmt::Debug for NativeFunc {
308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309 write!(f, "NativeFunc({})", self.name)
310 }
311}
312
313#[derive(Debug, Clone)]
316pub struct ScriptError {
317 pub message: String,
318 pub line: Option<u32>,
319}
320
321impl ScriptError {
322 pub fn new(msg: impl Into<String>) -> Self {
323 ScriptError { message: msg.into(), line: None }
324 }
325
326 pub fn at(msg: impl Into<String>, line: u32) -> Self {
327 ScriptError { message: msg.into(), line: Some(line) }
328 }
329
330 fn runtime(msg: impl Into<String>) -> Self { ScriptError::new(msg) }
331}
332
333impl fmt::Display for ScriptError {
334 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335 if let Some(l) = self.line { write!(f, "[line {}] {}", l, self.message) }
336 else { write!(f, "{}", self.message) }
337 }
338}
339
340impl std::error::Error for ScriptError {}
341
342struct CallFrame {
345 chunk: Arc<Chunk>,
346 upvalues: Vec<UpvalueCell>,
347 ip: usize,
348 base: usize,
349 ret_mark: usize,
351}
352
353const MAX_DEPTH: usize = 200;
356
357pub struct Vm {
358 stack: Vec<Value>,
359 frames: Vec<CallFrame>,
360 globals: HashMap<String, Value>,
361 depth: usize,
362 pub output: Vec<String>,
363}
364
365impl Vm {
366 fn push_results(stack: &mut Vec<Value>, results: Vec<Value>, nret: usize) {
368 if nret == 0 {
369 stack.extend(results);
370 return;
371 }
372 let mut it = results.into_iter();
373 for _ in 0..nret {
374 stack.push(it.next().unwrap_or(Value::Nil));
375 }
376 }
377
378 pub fn new() -> Self {
379 Vm {
380 stack: Vec::with_capacity(64),
381 frames: Vec::with_capacity(32),
382 globals: HashMap::new(),
383 depth: 0,
384 output: Vec::new(),
385 }
386 }
387
388 pub fn set_global(&mut self, name: impl Into<String>, v: Value) {
389 self.globals.insert(name.into(), v);
390 }
391
392 pub fn get_global(&self, name: &str) -> Value {
393 self.globals.get(name).cloned().unwrap_or(Value::Nil)
394 }
395
396 pub fn register_native(
397 &mut self,
398 name: impl Into<String>,
399 f: impl Fn(&mut Vm, Vec<Value>) -> Result<Vec<Value>, ScriptError> + Send + Sync + 'static,
400 ) {
401 let name = name.into();
402 let nf = Arc::new(NativeFunc { name: name.clone(), func: Box::new(f) });
403 self.globals.insert(name, Value::NativeFunction(nf));
404 }
405
406 pub fn execute(&mut self, chunk: Arc<Chunk>) -> Result<Vec<Value>, ScriptError> {
408 self.run_chunk(chunk, vec![], vec![])
409 }
410
411 pub fn call(&mut self, callee: Value, args: Vec<Value>) -> Result<Vec<Value>, ScriptError> {
413 match callee {
414 Value::Function(c) => {
415 let chunk = Arc::clone(&c.chunk);
416 let upvalues = c.upvalues.clone();
417 self.run_chunk(chunk, upvalues, args)
418 }
419 Value::NativeFunction(nf) => {
420 let func = Arc::clone(&nf);
421 (func.func)(self, args)
422 }
423 other => Err(ScriptError::runtime(format!(
424 "attempt to call a {} value", other.type_name()
425 ))),
426 }
427 }
428
429 fn run_chunk(
430 &mut self,
431 chunk: Arc<Chunk>,
432 upvalues: Vec<UpvalueCell>,
433 args: Vec<Value>,
434 ) -> Result<Vec<Value>, ScriptError> {
435 self.depth += 1;
436 if self.depth > MAX_DEPTH {
437 self.depth -= 1;
438 return Err(ScriptError::runtime("stack overflow"));
439 }
440 let base = self.stack.len();
441 for a in args { self.stack.push(a); }
442 let param_count = chunk.param_count as usize;
443 while self.stack.len() < base + param_count {
444 self.stack.push(Value::Nil);
445 }
446 let frame_count = self.frames.len();
447 self.frames.push(CallFrame { chunk, upvalues, ip: 0, base, ret_mark: base });
448 let result = self.run();
449 self.depth -= 1;
450 if result.is_err() {
451 self.frames.truncate(frame_count);
456 self.stack.truncate(base);
457 }
458 result
459 }
460
461 fn run(&mut self) -> Result<Vec<Value>, ScriptError> {
462 loop {
463 let fi = self.frames.len() - 1;
464 let ip = self.frames[fi].ip;
465 let code = self.frames[fi].chunk.instructions.clone();
466
467 if ip >= code.len() {
468 let base = self.frames[fi].base;
469 self.stack.truncate(base);
470 self.frames.pop();
471 return Ok(vec![]);
472 }
473
474 let instr = code[ip].clone();
475 self.frames[fi].ip += 1;
476
477 match instr {
478 Instruction::LoadNil => self.push(Value::Nil),
479
480 Instruction::LoadBool(b) => self.push(Value::Bool(b)),
481
482 Instruction::LoadInt(n) => self.push(Value::Int(n)),
483
484 Instruction::LoadFloat(n) => self.push(Value::Float(n)),
485
486 Instruction::LoadStr(s) => self.push(Value::Str(Arc::new(s))),
487
488 Instruction::LoadConst(idx) => {
489 let fi = self.frames.len() - 1;
490 let v = self.frames[fi].chunk.constants[idx].clone();
491 self.push(v);
492 }
493
494 Instruction::Pop => { self.stack.pop(); }
495 Instruction::Dup => {
496 let v = self.stack.last().cloned().unwrap_or(Value::Nil);
497 self.push(v);
498 }
499 Instruction::Swap => {
500 let len = self.stack.len();
501 if len >= 2 { self.stack.swap(len - 1, len - 2); }
502 }
503
504 Instruction::GetLocal(slot) => {
505 let fi = self.frames.len() - 1;
506 let base = self.frames[fi].base;
507 let v = self.stack.get(base + slot).cloned().unwrap_or(Value::Nil);
508 self.push(v);
509 }
510 Instruction::SetLocal(slot) => {
511 let fi = self.frames.len() - 1;
512 let base = self.frames[fi].base;
513 let v = self.pop();
514 let idx = base + slot;
515 while self.stack.len() <= idx { self.stack.push(Value::Nil); }
516 self.stack[idx] = v;
517 }
518
519 Instruction::GetUpvalue(idx) => {
520 let fi = self.frames.len() - 1;
521 let v = self.frames[fi].upvalues.get(idx)
522 .map(|uv| uv.get())
523 .unwrap_or(Value::Nil);
524 self.push(v);
525 }
526 Instruction::SetUpvalue(idx) => {
527 let v = self.pop();
528 let fi = self.frames.len() - 1;
529 if let Some(uv) = self.frames[fi].upvalues.get(idx) {
530 uv.set(v);
531 }
532 }
533
534 Instruction::GetGlobal(name) => {
535 let v = self.globals.get(&name).cloned().unwrap_or(Value::Nil);
536 self.push(v);
537 }
538 Instruction::SetGlobal(name) => {
539 let v = self.pop();
540 self.globals.insert(name, v);
541 }
542
543 Instruction::NewTable => self.push(Value::Table(Table::new())),
544
545 Instruction::SetField(name) => {
546 let val = self.pop();
547 let table = self.peek();
548 match table {
549 Value::Table(t) => t.rawset_str(&name, val),
550 _ => return Err(ScriptError::runtime("SetField on non-table")),
551 }
552 }
553 Instruction::GetField(name) => {
554 let table = self.pop();
555 let v = match &table {
556 Value::Table(t) => t.rawget_str(&name),
557 Value::Str(_) => {
558 self.globals.get("string")
559 .and_then(|s| if let Value::Table(t) = s {
560 Some(t.rawget_str(&name))
561 } else { None })
562 .unwrap_or(Value::Nil)
563 }
564 other => return Err(ScriptError::runtime(format!(
565 "attempt to index a {} value", other.type_name()
566 ))),
567 };
568 self.push(v);
569 }
570 Instruction::SetIndex => {
571 let val = self.pop();
572 let key = self.pop();
573 let table = self.pop();
574 match table {
575 Value::Table(t) => t.set(key, val),
576 other => return Err(ScriptError::runtime(format!(
577 "attempt to index a {} value", other.type_name()
578 ))),
579 }
580 }
581 Instruction::GetIndex => {
582 let key = self.pop();
583 let table = self.pop();
584 let v = match &table {
585 Value::Table(t) => t.get(&key),
586 other => return Err(ScriptError::runtime(format!(
587 "attempt to index a {} value", other.type_name()
588 ))),
589 };
590 self.push(v);
591 }
592 Instruction::TableAppend => {
593 let val = self.pop();
594 let table = self.peek();
595 if let Value::Table(t) = table { t.push(val); }
596 }
597
598 Instruction::Len => {
599 let a = self.pop();
600 let n = match a {
601 Value::Table(t) => t.length(),
602 Value::Str(s) => s.len() as i64,
603 other => return Err(ScriptError::runtime(format!(
604 "attempt to get length of {} value", other.type_name()
605 ))),
606 };
607 self.push(Value::Int(n));
608 }
609 Instruction::Neg => {
610 let a = self.pop();
611 let r = match a {
612 Value::Int(i) => Value::Int(-i),
613 Value::Float(f) => Value::Float(-f),
614 other => return Err(ScriptError::runtime(format!(
615 "unary - on {}", other.type_name()
616 ))),
617 };
618 self.push(r);
619 }
620 Instruction::Not => { let a = self.pop(); self.push(Value::Bool(!a.is_truthy())); }
621 Instruction::BitNot => {
622 let a = self.pop();
623 let i = a.to_int().ok_or_else(|| ScriptError::runtime(
624 format!("bitwise not on {}", a.type_name())))?;
625 self.push(Value::Int(!i));
626 }
627
628 Instruction::Add => self.arith2(|a, b| num_arith(a, b, i64::wrapping_add, |x, y| x + y))?,
629 Instruction::Sub => self.arith2(|a, b| num_arith(a, b, i64::wrapping_sub, |x, y| x - y))?,
630 Instruction::Mul => self.arith2(|a, b| num_arith(a, b, i64::wrapping_mul, |x, y| x * y))?,
631 Instruction::Div => {
632 let b = self.pop(); let a = self.pop();
633 let af = a.to_float().ok_or_else(|| ScriptError::runtime(format!("arith on {}", a.type_name())))?;
634 let bf = b.to_float().ok_or_else(|| ScriptError::runtime(format!("arith on {}", b.type_name())))?;
635 self.push(Value::Float(af / bf));
636 }
637 Instruction::IDiv => {
638 let b = self.pop(); let a = self.pop();
639 let ai = a.to_int().ok_or_else(|| ScriptError::runtime("floor div requires integers"))?;
640 let bi = b.to_int().ok_or_else(|| ScriptError::runtime("floor div requires integers"))?;
641 if bi == 0 { return Err(ScriptError::runtime("integer divide by zero")); }
642 self.push(Value::Int(ai.div_euclid(bi)));
643 }
644 Instruction::Mod => {
645 let b = self.pop(); let a = self.pop();
646 let r = match (&a, &b) {
647 (Value::Int(ai), Value::Int(bi)) => {
648 if *bi == 0 { return Err(ScriptError::runtime("modulo by zero")); }
649 Value::Int(ai.rem_euclid(*bi))
650 }
651 _ => {
652 let af = a.to_float().ok_or_else(|| ScriptError::runtime(format!("arith on {}", a.type_name())))?;
653 let bf = b.to_float().ok_or_else(|| ScriptError::runtime(format!("arith on {}", b.type_name())))?;
654 Value::Float(af % bf)
655 }
656 };
657 self.push(r);
658 }
659 Instruction::Pow => {
660 let b = self.pop(); let a = self.pop();
661 let af = a.to_float().ok_or_else(|| ScriptError::runtime(format!("arith on {}", a.type_name())))?;
662 let bf = b.to_float().ok_or_else(|| ScriptError::runtime(format!("arith on {}", b.type_name())))?;
663 self.push(Value::Float(af.powf(bf)));
664 }
665 Instruction::Concat => {
666 let b = self.pop(); let a = self.pop();
667 let sa = a.to_str_repr().ok_or_else(|| ScriptError::runtime(format!("concat on {}", a.type_name())))?;
668 let sb = b.to_str_repr().ok_or_else(|| ScriptError::runtime(format!("concat on {}", b.type_name())))?;
669 self.push(Value::Str(Arc::new(sa + sb.as_str())));
670 }
671
672 Instruction::BitAnd => self.bitwise(|a, b| a & b)?,
673 Instruction::BitOr => self.bitwise(|a, b| a | b)?,
674 Instruction::BitXor => self.bitwise(|a, b| a ^ b)?,
675 Instruction::Shl => self.bitwise(|a, b| a.wrapping_shl(b as u32))?,
676 Instruction::Shr => self.bitwise(|a, b| a.wrapping_shr(b as u32))?,
677
678 Instruction::Eq => { let b = self.pop(); let a = self.pop(); self.push(Value::Bool(a == b)); }
679 Instruction::NotEq => { let b = self.pop(); let a = self.pop(); self.push(Value::Bool(a != b)); }
680 Instruction::Lt => self.cmp(|a, b| a < b)?,
681 Instruction::LtEq => self.cmp(|a, b| a <= b)?,
682 Instruction::Gt => self.cmp(|a, b| a > b)?,
683 Instruction::GtEq => self.cmp(|a, b| a >= b)?,
684
685 Instruction::Jump(off) => {
686 let fi = self.frames.len() - 1;
687 self.frames[fi].ip = (self.frames[fi].ip as isize + off) as usize;
688 }
689 Instruction::JumpIf(off) => {
690 if self.stack.last().map(|v| v.is_truthy()).unwrap_or(false) {
691 let fi = self.frames.len() - 1;
692 self.frames[fi].ip = (self.frames[fi].ip as isize + off) as usize;
693 }
694 }
695 Instruction::JumpIfNot(off) => {
696 if !self.stack.last().map(|v| v.is_truthy()).unwrap_or(false) {
697 let fi = self.frames.len() - 1;
698 self.frames[fi].ip = (self.frames[fi].ip as isize + off) as usize;
699 }
700 }
701 Instruction::JumpIfNotPop(off) => {
702 let top = self.pop();
703 if !top.is_truthy() {
704 let fi = self.frames.len() - 1;
705 self.frames[fi].ip = (self.frames[fi].ip as isize + off) as usize;
706 } else {
707 self.push(top);
708 }
709 }
710 Instruction::JumpIfPop(off) => {
711 let top = self.pop();
712 if top.is_truthy() {
713 let fi = self.frames.len() - 1;
714 self.frames[fi].ip = (self.frames[fi].ip as isize + off) as usize;
715 } else {
716 self.push(top);
717 }
718 }
719 Instruction::JumpAbs(abs_ip) => {
720 let fi = self.frames.len() - 1;
721 self.frames[fi].ip = abs_ip;
722 }
723
724 Instruction::Call(nargs, nret) => {
725 let top = self.stack.len();
726 let base = top.saturating_sub(nargs + 1);
727 let args: Vec<Value> = self.stack.drain(base + 1..).collect();
728 let callee = self.stack.pop().unwrap_or(Value::Nil);
729 let results = self.call(callee, args)?;
730 Self::push_results(&mut self.stack, results, nret);
735 }
736
737 Instruction::CallMethod(method_name, nargs, nret) => {
738 let top = self.stack.len();
739 let base = top.saturating_sub(nargs + 1);
740 let extra: Vec<Value> = self.stack.drain(base + 1..).collect();
741 let obj = self.stack.pop().unwrap_or(Value::Nil);
742 let method = match &obj {
743 Value::Table(t) => t.rawget_str(&method_name),
744 other => return Err(ScriptError::runtime(format!(
745 "method call on {} value", other.type_name()
746 ))),
747 };
748 let mut args = vec![obj];
749 args.extend(extra);
750 let results = self.call(method, args)?;
751 Self::push_results(&mut self.stack, results, nret);
752 }
753
754 Instruction::MarkReturn => {
755 let fi = self.frames.len() - 1;
756 self.frames[fi].ret_mark = self.stack.len();
757 }
758
759 Instruction::ReturnFromMark => {
760 let fi = self.frames.len() - 1;
761 let mark = self.frames[fi].ret_mark.min(self.stack.len());
762 let returns: Vec<Value> = self.stack.drain(mark..).collect();
763 let base = self.frames[fi].base;
764 self.stack.truncate(base);
765 self.frames.pop();
766 return Ok(returns);
767 }
768
769 Instruction::Return(nret) => {
770 let top = self.stack.len();
771 let ret_start = if nret == 0 {
772 self.frames[self.frames.len() - 1].base
773 } else {
774 top.saturating_sub(nret)
775 };
776 let returns: Vec<Value> = self.stack.drain(ret_start..).collect();
777 let fi = self.frames.len() - 1;
778 let base = self.frames[fi].base;
779 self.stack.truncate(base);
780 self.frames.pop();
781 return Ok(returns);
782 }
783
784 Instruction::MakeFunction(idx) => {
785 let fi = self.frames.len() - 1;
786 let sub = Arc::clone(&self.frames[fi].chunk.sub_chunks[idx]);
787 let closure = Arc::new(Closure { chunk: sub, upvalues: Vec::new() });
788 self.push(Value::Function(closure));
789 }
790
791 Instruction::MakeClosure(idx, captures) => {
792 let fi = self.frames.len() - 1;
793 let sub = Arc::clone(&self.frames[fi].chunk.sub_chunks[idx]);
794 let mut upvalues = Vec::new();
795 for (is_local, slot) in captures {
796 if is_local {
797 let base = self.frames[fi].base;
798 let v = self.stack.get(base + slot).cloned().unwrap_or(Value::Nil);
799 upvalues.push(UpvalueCell::new(v));
800 } else {
801 let v = self.frames[fi].upvalues.get(slot)
802 .map(|uv| uv.clone())
803 .unwrap_or_else(|| UpvalueCell::new(Value::Nil));
804 upvalues.push(v);
805 }
806 }
807 let closure = Arc::new(Closure { chunk: sub, upvalues });
808 self.push(Value::Function(closure));
809 }
810
811 Instruction::CloseUpvalue(_slot) => {
812 }
814
815 Instruction::ForPrep(_nvars) => {
816 }
819
820 Instruction::ForStep(local_idx, jump_off) => {
821 let fi = self.frames.len() - 1;
823 let base = self.frames[fi].base;
824 let cur = self.stack.get(base + local_idx).cloned().unwrap_or(Value::Nil);
825 let limit = self.stack.get(base + local_idx + 1).cloned().unwrap_or(Value::Nil);
826 let step = self.stack.get(base + local_idx + 2).cloned().unwrap_or(Value::Nil);
827 let cv = cur.to_float().unwrap_or(0.0);
828 let lv = limit.to_float().unwrap_or(0.0);
829 let sv = step.to_float().unwrap_or(1.0);
830 let should_continue = if sv > 0.0 { cv <= lv } else { cv >= lv };
831 if !should_continue {
832 let fi = self.frames.len() - 1;
833 self.frames[fi].ip = (self.frames[fi].ip as isize + jump_off) as usize;
834 } else {
835 let next = match (&cur, &step) {
836 (Value::Int(c), Value::Int(s)) => Value::Int(c.wrapping_add(*s)),
837 _ => Value::Float(cv + sv),
838 };
839 let fi = self.frames.len() - 1;
840 let base = self.frames[fi].base;
841 let idx = base + local_idx;
842 while self.stack.len() <= idx { self.stack.push(Value::Nil); }
843 self.stack[idx] = next;
844 }
845 }
846
847 Instruction::Nop => {}
848 }
849 }
850 }
851
852 #[inline] fn push(&mut self, v: Value) { self.stack.push(v); }
855 #[inline] fn pop(&mut self) -> Value { self.stack.pop().unwrap_or(Value::Nil) }
856 #[inline] fn peek(&self) -> Value { self.stack.last().cloned().unwrap_or(Value::Nil) }
857
858 fn arith2<F>(&mut self, f: F) -> Result<(), ScriptError>
859 where F: Fn(Value, Value) -> Result<Value, ScriptError>
860 {
861 let b = self.pop(); let a = self.pop();
862 self.push(f(a, b)?);
863 Ok(())
864 }
865
866 fn cmp<F>(&mut self, op: F) -> Result<(), ScriptError>
867 where F: Fn(f64, f64) -> bool
868 {
869 let b = self.pop(); let a = self.pop();
870 let av = a.to_float().ok_or_else(|| ScriptError::runtime(format!("compare on {}", a.type_name())))?;
871 let bv = b.to_float().ok_or_else(|| ScriptError::runtime(format!("compare on {}", b.type_name())))?;
872 self.push(Value::Bool(op(av, bv)));
873 Ok(())
874 }
875
876 fn bitwise<F>(&mut self, op: F) -> Result<(), ScriptError>
877 where F: Fn(i64, i64) -> i64
878 {
879 let b = self.pop(); let a = self.pop();
880 let ai = a.to_int().ok_or_else(|| ScriptError::runtime(format!("bitwise on {}", a.type_name())))?;
881 let bi = b.to_int().ok_or_else(|| ScriptError::runtime(format!("bitwise on {}", b.type_name())))?;
882 self.push(Value::Int(op(ai, bi)));
883 Ok(())
884 }
885}
886
887fn num_arith<FI, FF>(a: Value, b: Value, fi: FI, ff: FF) -> Result<Value, ScriptError>
888where
889 FI: Fn(i64, i64) -> i64,
890 FF: Fn(f64, f64) -> f64,
891{
892 match (&a, &b) {
893 (Value::Int(ai), Value::Int(bi)) => Ok(Value::Int(fi(*ai, *bi))),
894 (Value::Float(af), Value::Float(bf)) => Ok(Value::Float(ff(*af, *bf))),
895 (Value::Int(ai), Value::Float(bf)) => Ok(Value::Float(ff(*ai as f64, *bf))),
896 (Value::Float(af), Value::Int(bi)) => Ok(Value::Float(ff(*af, *bi as f64))),
897 _ => Err(ScriptError::runtime(format!(
898 "attempt to perform arithmetic on {} and {} values",
899 a.type_name(), b.type_name()
900 ))),
901 }
902}
903
904#[cfg(test)]
907mod tests {
908 use super::*;
909 use crate::scripting::compiler::Compiler;
910 use crate::scripting::parser::Parser;
911
912 fn run(src: &str) -> Result<Vec<Value>, ScriptError> {
913 let script = Parser::from_source("test", src)
914 .map_err(|e| ScriptError::new(e.to_string()))?;
915 let chunk = Compiler::compile_script(&script);
916 let mut vm = Vm::new();
917 vm.execute(chunk)
918 }
919
920 #[test]
921 fn test_return_int() {
922 let r = run("return 42").unwrap();
923 assert_eq!(r[0], Value::Int(42));
924 }
925
926 #[test]
927 fn test_arithmetic() {
928 let r = run("return 2 + 3 * 4").unwrap();
929 assert_eq!(r[0], Value::Int(14));
930 }
931
932 #[test]
933 fn test_string_concat() {
934 let r = run("return \"hello\" .. \" world\"").unwrap();
935 assert!(matches!(&r[0], Value::Str(s) if s.as_ref() == "hello world"));
936 }
937
938 #[test]
939 fn test_local_variable() {
940 let r = run("local x = 10 return x").unwrap();
941 assert_eq!(r[0], Value::Int(10));
942 }
943
944 #[test]
945 fn test_if_else() {
946 let r = run("if true then return 1 else return 2 end").unwrap();
947 assert_eq!(r[0], Value::Int(1));
948 }
949
950 #[test]
951 fn test_function_call() {
952 let r = run("local function add(a, b) return a + b end return add(3, 4)").unwrap();
953 assert_eq!(r[0], Value::Int(7));
954 }
955}