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 return d.array.get((i - 1) as usize).cloned().unwrap_or(Value::Nil);
164 }
165 }
166 TableKey::from_value(key)
167 .and_then(|k| d.hash.get(&k).cloned())
168 .unwrap_or(Value::Nil)
169 }
170
171 pub fn rawget_str(&self, key: &str) -> Value {
172 self.inner.borrow().hash
173 .get(&TableKey::Str(key.to_string()))
174 .cloned()
175 .unwrap_or(Value::Nil)
176 }
177
178 pub fn set(&self, key: Value, val: Value) {
179 let mut d = self.inner.borrow_mut();
180 if let Some(i) = int_key(&key) {
181 if i >= 1 {
182 let idx = (i - 1) as usize;
183 if idx <= d.array.len() {
184 if matches!(val, Value::Nil) {
185 if idx < d.array.len() { d.array[idx] = Value::Nil; }
186 } else if idx == d.array.len() {
187 d.array.push(val);
188 } else {
189 d.array[idx] = val;
190 }
191 return;
192 }
193 }
194 }
195 if let Some(k) = TableKey::from_value(&key) {
196 match val {
197 Value::Nil => { d.hash.remove(&k); }
198 v => { d.hash.insert(k, v); }
199 }
200 }
201 }
202
203 pub fn rawset_str(&self, key: &str, val: Value) {
204 let mut d = self.inner.borrow_mut();
205 let k = TableKey::Str(key.to_string());
206 match val {
207 Value::Nil => { d.hash.remove(&k); }
208 v => { d.hash.insert(k, v); }
209 }
210 }
211
212 pub fn length(&self) -> i64 {
213 self.inner.borrow().array.len() as i64
214 }
215
216 pub fn push(&self, v: Value) {
217 self.inner.borrow_mut().array.push(v);
218 }
219
220 pub fn array_values(&self) -> Vec<Value> {
221 self.inner.borrow().array.clone()
222 }
223
224 pub fn set_metatable(&self, mt: Option<Table>) {
225 self.inner.borrow_mut().metatable = mt;
226 }
227
228 pub fn get_metatable(&self) -> Option<Table> {
229 self.inner.borrow().metatable.clone()
230 }
231
232 pub fn next(&self, after: &Value) -> Option<(Value, Value)> {
233 let d = self.inner.borrow();
234 match after {
235 Value::Nil => {
236 if let Some(v) = d.array.first() {
237 return Some((Value::Int(1), v.clone()));
238 }
239 return d.hash.iter().next().map(|(k, v)| (tk_to_val(k), v.clone()));
240 }
241 _ => {
242 if let Some(i) = int_key(after) {
243 if i >= 1 {
244 let next_i = i as usize;
245 if next_i < d.array.len() {
246 return Some((Value::Int(i + 1), d.array[next_i].clone()));
247 }
248 return d.hash.iter().next().map(|(k, v)| (tk_to_val(k), v.clone()));
249 }
250 }
251 if let Some(k) = TableKey::from_value(after) {
252 let mut found = false;
253 for (hk, hv) in &d.hash {
254 if found { return Some((tk_to_val(hk), hv.clone())); }
255 if *hk == k { found = true; }
256 }
257 }
258 None
259 }
260 }
261 }
262}
263
264fn int_key(v: &Value) -> Option<i64> {
265 match v {
266 Value::Int(i) => Some(*i),
267 Value::Float(f) if f.fract() == 0.0 => Some(*f as i64),
268 _ => None,
269 }
270}
271
272fn tk_to_val(k: &TableKey) -> Value {
273 match k {
274 TableKey::Str(s) => Value::Str(Arc::new(s.clone())),
275 TableKey::Int(i) => Value::Int(*i),
276 TableKey::Bool(b) => Value::Bool(*b),
277 }
278}
279
280#[derive(Debug)]
283pub struct Closure {
284 pub chunk: Arc<Chunk>,
285 pub upvalues: Vec<UpvalueCell>,
286}
287
288#[derive(Debug, Clone)]
289pub struct UpvalueCell(Arc<std::cell::RefCell<Value>>);
290
291impl UpvalueCell {
292 pub fn new(v: Value) -> Self { UpvalueCell(Arc::new(std::cell::RefCell::new(v))) }
293 pub fn get(&self) -> Value { self.0.borrow().clone() }
294 pub fn set(&self, v: Value) { *self.0.borrow_mut() = v; }
295}
296
297pub struct NativeFunc {
298 pub name: String,
299 pub func: Box<dyn Fn(&mut Vm, Vec<Value>) -> Result<Vec<Value>, ScriptError> + Send + Sync>,
300}
301
302impl fmt::Debug for NativeFunc {
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 write!(f, "NativeFunc({})", self.name)
305 }
306}
307
308#[derive(Debug, Clone)]
311pub struct ScriptError {
312 pub message: String,
313 pub line: Option<u32>,
314}
315
316impl ScriptError {
317 pub fn new(msg: impl Into<String>) -> Self {
318 ScriptError { message: msg.into(), line: None }
319 }
320
321 pub fn at(msg: impl Into<String>, line: u32) -> Self {
322 ScriptError { message: msg.into(), line: Some(line) }
323 }
324
325 fn runtime(msg: impl Into<String>) -> Self { ScriptError::new(msg) }
326}
327
328impl fmt::Display for ScriptError {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 if let Some(l) = self.line { write!(f, "[line {}] {}", l, self.message) }
331 else { write!(f, "{}", self.message) }
332 }
333}
334
335impl std::error::Error for ScriptError {}
336
337struct CallFrame {
340 chunk: Arc<Chunk>,
341 upvalues: Vec<UpvalueCell>,
342 ip: usize,
343 base: usize,
344}
345
346const MAX_DEPTH: usize = 200;
349
350pub struct Vm {
351 stack: Vec<Value>,
352 frames: Vec<CallFrame>,
353 globals: HashMap<String, Value>,
354 depth: usize,
355 pub output: Vec<String>,
356}
357
358impl Vm {
359 pub fn new() -> Self {
360 Vm {
361 stack: Vec::with_capacity(64),
362 frames: Vec::with_capacity(32),
363 globals: HashMap::new(),
364 depth: 0,
365 output: Vec::new(),
366 }
367 }
368
369 pub fn set_global(&mut self, name: impl Into<String>, v: Value) {
370 self.globals.insert(name.into(), v);
371 }
372
373 pub fn get_global(&self, name: &str) -> Value {
374 self.globals.get(name).cloned().unwrap_or(Value::Nil)
375 }
376
377 pub fn register_native(
378 &mut self,
379 name: impl Into<String>,
380 f: impl Fn(&mut Vm, Vec<Value>) -> Result<Vec<Value>, ScriptError> + Send + Sync + 'static,
381 ) {
382 let name = name.into();
383 let nf = Arc::new(NativeFunc { name: name.clone(), func: Box::new(f) });
384 self.globals.insert(name, Value::NativeFunction(nf));
385 }
386
387 pub fn execute(&mut self, chunk: Arc<Chunk>) -> Result<Vec<Value>, ScriptError> {
389 self.run_chunk(chunk, vec![], vec![])
390 }
391
392 pub fn call(&mut self, callee: Value, args: Vec<Value>) -> Result<Vec<Value>, ScriptError> {
394 match callee {
395 Value::Function(c) => {
396 let chunk = Arc::clone(&c.chunk);
397 let upvalues = c.upvalues.clone();
398 self.run_chunk(chunk, upvalues, args)
399 }
400 Value::NativeFunction(nf) => {
401 let func = Arc::clone(&nf);
402 (func.func)(self, args)
403 }
404 other => Err(ScriptError::runtime(format!(
405 "attempt to call a {} value", other.type_name()
406 ))),
407 }
408 }
409
410 fn run_chunk(
411 &mut self,
412 chunk: Arc<Chunk>,
413 upvalues: Vec<UpvalueCell>,
414 args: Vec<Value>,
415 ) -> Result<Vec<Value>, ScriptError> {
416 self.depth += 1;
417 if self.depth > MAX_DEPTH {
418 self.depth -= 1;
419 return Err(ScriptError::runtime("stack overflow"));
420 }
421 let base = self.stack.len();
422 for a in args { self.stack.push(a); }
423 let param_count = chunk.param_count as usize;
424 while self.stack.len() < base + param_count {
425 self.stack.push(Value::Nil);
426 }
427 self.frames.push(CallFrame { chunk, upvalues, ip: 0, base });
428 let result = self.run();
429 self.depth -= 1;
430 result
431 }
432
433 fn run(&mut self) -> Result<Vec<Value>, ScriptError> {
434 loop {
435 let fi = self.frames.len() - 1;
436 let ip = self.frames[fi].ip;
437 let code = self.frames[fi].chunk.instructions.clone();
438
439 if ip >= code.len() {
440 let base = self.frames[fi].base;
441 self.stack.truncate(base);
442 self.frames.pop();
443 return Ok(vec![]);
444 }
445
446 let instr = code[ip].clone();
447 self.frames[fi].ip += 1;
448
449 match instr {
450 Instruction::LoadNil => self.push(Value::Nil),
451
452 Instruction::LoadBool(b) => self.push(Value::Bool(b)),
453
454 Instruction::LoadInt(n) => self.push(Value::Int(n)),
455
456 Instruction::LoadFloat(n) => self.push(Value::Float(n)),
457
458 Instruction::LoadStr(s) => self.push(Value::Str(Arc::new(s))),
459
460 Instruction::LoadConst(idx) => {
461 let fi = self.frames.len() - 1;
462 let v = self.frames[fi].chunk.constants[idx].clone();
463 self.push(v);
464 }
465
466 Instruction::Pop => { self.stack.pop(); }
467 Instruction::Dup => {
468 let v = self.stack.last().cloned().unwrap_or(Value::Nil);
469 self.push(v);
470 }
471 Instruction::Swap => {
472 let len = self.stack.len();
473 if len >= 2 { self.stack.swap(len - 1, len - 2); }
474 }
475
476 Instruction::GetLocal(slot) => {
477 let fi = self.frames.len() - 1;
478 let base = self.frames[fi].base;
479 let v = self.stack.get(base + slot).cloned().unwrap_or(Value::Nil);
480 self.push(v);
481 }
482 Instruction::SetLocal(slot) => {
483 let fi = self.frames.len() - 1;
484 let base = self.frames[fi].base;
485 let v = self.pop();
486 let idx = base + slot;
487 while self.stack.len() <= idx { self.stack.push(Value::Nil); }
488 self.stack[idx] = v;
489 }
490
491 Instruction::GetUpvalue(idx) => {
492 let fi = self.frames.len() - 1;
493 let v = self.frames[fi].upvalues.get(idx)
494 .map(|uv| uv.get())
495 .unwrap_or(Value::Nil);
496 self.push(v);
497 }
498 Instruction::SetUpvalue(idx) => {
499 let v = self.pop();
500 let fi = self.frames.len() - 1;
501 if let Some(uv) = self.frames[fi].upvalues.get(idx) {
502 uv.set(v);
503 }
504 }
505
506 Instruction::GetGlobal(name) => {
507 let v = self.globals.get(&name).cloned().unwrap_or(Value::Nil);
508 self.push(v);
509 }
510 Instruction::SetGlobal(name) => {
511 let v = self.pop();
512 self.globals.insert(name, v);
513 }
514
515 Instruction::NewTable => self.push(Value::Table(Table::new())),
516
517 Instruction::SetField(name) => {
518 let val = self.pop();
519 let table = self.peek();
520 match table {
521 Value::Table(t) => t.rawset_str(&name, val),
522 _ => return Err(ScriptError::runtime("SetField on non-table")),
523 }
524 }
525 Instruction::GetField(name) => {
526 let table = self.pop();
527 let v = match &table {
528 Value::Table(t) => t.rawget_str(&name),
529 Value::Str(_) => {
530 self.globals.get("string")
531 .and_then(|s| if let Value::Table(t) = s {
532 Some(t.rawget_str(&name))
533 } else { None })
534 .unwrap_or(Value::Nil)
535 }
536 other => return Err(ScriptError::runtime(format!(
537 "attempt to index a {} value", other.type_name()
538 ))),
539 };
540 self.push(v);
541 }
542 Instruction::SetIndex => {
543 let val = self.pop();
544 let key = self.pop();
545 let table = self.pop();
546 match table {
547 Value::Table(t) => t.set(key, val),
548 other => return Err(ScriptError::runtime(format!(
549 "attempt to index a {} value", other.type_name()
550 ))),
551 }
552 }
553 Instruction::GetIndex => {
554 let key = self.pop();
555 let table = self.pop();
556 let v = match &table {
557 Value::Table(t) => t.get(&key),
558 other => return Err(ScriptError::runtime(format!(
559 "attempt to index a {} value", other.type_name()
560 ))),
561 };
562 self.push(v);
563 }
564 Instruction::TableAppend => {
565 let val = self.pop();
566 let table = self.peek();
567 if let Value::Table(t) = table { t.push(val); }
568 }
569
570 Instruction::Len => {
571 let a = self.pop();
572 let n = match a {
573 Value::Table(t) => t.length(),
574 Value::Str(s) => s.len() as i64,
575 other => return Err(ScriptError::runtime(format!(
576 "attempt to get length of {} value", other.type_name()
577 ))),
578 };
579 self.push(Value::Int(n));
580 }
581 Instruction::Neg => {
582 let a = self.pop();
583 let r = match a {
584 Value::Int(i) => Value::Int(-i),
585 Value::Float(f) => Value::Float(-f),
586 other => return Err(ScriptError::runtime(format!(
587 "unary - on {}", other.type_name()
588 ))),
589 };
590 self.push(r);
591 }
592 Instruction::Not => { let a = self.pop(); self.push(Value::Bool(!a.is_truthy())); }
593 Instruction::BitNot => {
594 let a = self.pop();
595 let i = a.to_int().ok_or_else(|| ScriptError::runtime(
596 format!("bitwise not on {}", a.type_name())))?;
597 self.push(Value::Int(!i));
598 }
599
600 Instruction::Add => self.arith2(|a, b| num_arith(a, b, i64::wrapping_add, |x, y| x + y))?,
601 Instruction::Sub => self.arith2(|a, b| num_arith(a, b, i64::wrapping_sub, |x, y| x - y))?,
602 Instruction::Mul => self.arith2(|a, b| num_arith(a, b, i64::wrapping_mul, |x, y| x * y))?,
603 Instruction::Div => {
604 let b = self.pop(); let a = self.pop();
605 let af = a.to_float().ok_or_else(|| ScriptError::runtime(format!("arith on {}", a.type_name())))?;
606 let bf = b.to_float().ok_or_else(|| ScriptError::runtime(format!("arith on {}", b.type_name())))?;
607 self.push(Value::Float(af / bf));
608 }
609 Instruction::IDiv => {
610 let b = self.pop(); let a = self.pop();
611 let ai = a.to_int().ok_or_else(|| ScriptError::runtime("floor div requires integers"))?;
612 let bi = b.to_int().ok_or_else(|| ScriptError::runtime("floor div requires integers"))?;
613 if bi == 0 { return Err(ScriptError::runtime("integer divide by zero")); }
614 self.push(Value::Int(ai.div_euclid(bi)));
615 }
616 Instruction::Mod => {
617 let b = self.pop(); let a = self.pop();
618 let r = match (&a, &b) {
619 (Value::Int(ai), Value::Int(bi)) => {
620 if *bi == 0 { return Err(ScriptError::runtime("modulo by zero")); }
621 Value::Int(ai.rem_euclid(*bi))
622 }
623 _ => {
624 let af = a.to_float().ok_or_else(|| ScriptError::runtime(format!("arith on {}", a.type_name())))?;
625 let bf = b.to_float().ok_or_else(|| ScriptError::runtime(format!("arith on {}", b.type_name())))?;
626 Value::Float(af % bf)
627 }
628 };
629 self.push(r);
630 }
631 Instruction::Pow => {
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.powf(bf)));
636 }
637 Instruction::Concat => {
638 let b = self.pop(); let a = self.pop();
639 let sa = a.to_str_repr().ok_or_else(|| ScriptError::runtime(format!("concat on {}", a.type_name())))?;
640 let sb = b.to_str_repr().ok_or_else(|| ScriptError::runtime(format!("concat on {}", b.type_name())))?;
641 self.push(Value::Str(Arc::new(sa + &sb)));
642 }
643
644 Instruction::BitAnd => self.bitwise(|a, b| a & b)?,
645 Instruction::BitOr => self.bitwise(|a, b| a | b)?,
646 Instruction::BitXor => self.bitwise(|a, b| a ^ b)?,
647 Instruction::Shl => self.bitwise(|a, b| a.wrapping_shl(b as u32))?,
648 Instruction::Shr => self.bitwise(|a, b| a.wrapping_shr(b as u32))?,
649
650 Instruction::Eq => { let b = self.pop(); let a = self.pop(); self.push(Value::Bool(a == b)); }
651 Instruction::NotEq => { let b = self.pop(); let a = self.pop(); self.push(Value::Bool(a != b)); }
652 Instruction::Lt => self.cmp(|a, b| a < b)?,
653 Instruction::LtEq => self.cmp(|a, b| a <= b)?,
654 Instruction::Gt => self.cmp(|a, b| a > b)?,
655 Instruction::GtEq => self.cmp(|a, b| a >= b)?,
656
657 Instruction::Jump(off) => {
658 let fi = self.frames.len() - 1;
659 self.frames[fi].ip = (self.frames[fi].ip as isize + off) as usize;
660 }
661 Instruction::JumpIf(off) => {
662 if self.stack.last().map(|v| v.is_truthy()).unwrap_or(false) {
663 let fi = self.frames.len() - 1;
664 self.frames[fi].ip = (self.frames[fi].ip as isize + off) as usize;
665 }
666 }
667 Instruction::JumpIfNot(off) => {
668 if !self.stack.last().map(|v| v.is_truthy()).unwrap_or(false) {
669 let fi = self.frames.len() - 1;
670 self.frames[fi].ip = (self.frames[fi].ip as isize + off) as usize;
671 }
672 }
673 Instruction::JumpIfNotPop(off) => {
674 let top = self.pop();
675 if !top.is_truthy() {
676 let fi = self.frames.len() - 1;
677 self.frames[fi].ip = (self.frames[fi].ip as isize + off) as usize;
678 } else {
679 self.push(top);
680 }
681 }
682 Instruction::JumpIfPop(off) => {
683 let top = self.pop();
684 if top.is_truthy() {
685 let fi = self.frames.len() - 1;
686 self.frames[fi].ip = (self.frames[fi].ip as isize + off) as usize;
687 } else {
688 self.push(top);
689 }
690 }
691 Instruction::JumpAbs(abs_ip) => {
692 let fi = self.frames.len() - 1;
693 self.frames[fi].ip = abs_ip;
694 }
695
696 Instruction::Call(nargs) => {
697 let top = self.stack.len();
698 let base = top.saturating_sub(nargs + 1);
699 let args: Vec<Value> = self.stack.drain(base + 1..).collect();
700 let callee = self.stack.pop().unwrap_or(Value::Nil);
701 let results = self.call(callee, args)?;
702 for r in results { self.stack.push(r); }
703 }
704
705 Instruction::CallMethod(method_name, nargs) => {
706 let top = self.stack.len();
707 let base = top.saturating_sub(nargs + 1);
708 let extra: Vec<Value> = self.stack.drain(base + 1..).collect();
709 let obj = self.stack.pop().unwrap_or(Value::Nil);
710 let method = match &obj {
711 Value::Table(t) => t.rawget_str(&method_name),
712 other => return Err(ScriptError::runtime(format!(
713 "method call on {} value", other.type_name()
714 ))),
715 };
716 let mut args = vec![obj];
717 args.extend(extra);
718 let results = self.call(method, args)?;
719 for r in results { self.stack.push(r); }
720 }
721
722 Instruction::Return(nret) => {
723 let top = self.stack.len();
724 let ret_start = if nret == 0 {
725 self.frames[self.frames.len() - 1].base
726 } else {
727 top.saturating_sub(nret)
728 };
729 let returns: Vec<Value> = self.stack.drain(ret_start..).collect();
730 let fi = self.frames.len() - 1;
731 let base = self.frames[fi].base;
732 self.stack.truncate(base);
733 self.frames.pop();
734 return Ok(returns);
735 }
736
737 Instruction::MakeFunction(idx) => {
738 let fi = self.frames.len() - 1;
739 let sub = Arc::clone(&self.frames[fi].chunk.sub_chunks[idx]);
740 let closure = Arc::new(Closure { chunk: sub, upvalues: Vec::new() });
741 self.push(Value::Function(closure));
742 }
743
744 Instruction::MakeClosure(idx, captures) => {
745 let fi = self.frames.len() - 1;
746 let sub = Arc::clone(&self.frames[fi].chunk.sub_chunks[idx]);
747 let mut upvalues = Vec::new();
748 for (is_local, slot) in captures {
749 if is_local {
750 let base = self.frames[fi].base;
751 let v = self.stack.get(base + slot).cloned().unwrap_or(Value::Nil);
752 upvalues.push(UpvalueCell::new(v));
753 } else {
754 let v = self.frames[fi].upvalues.get(slot)
755 .map(|uv| uv.clone())
756 .unwrap_or_else(|| UpvalueCell::new(Value::Nil));
757 upvalues.push(v);
758 }
759 }
760 let closure = Arc::new(Closure { chunk: sub, upvalues });
761 self.push(Value::Function(closure));
762 }
763
764 Instruction::CloseUpvalue(_slot) => {
765 }
767
768 Instruction::ForPrep(_nvars) => {
769 }
772
773 Instruction::ForStep(local_idx, jump_off) => {
774 let fi = self.frames.len() - 1;
776 let base = self.frames[fi].base;
777 let cur = self.stack.get(base + local_idx).cloned().unwrap_or(Value::Nil);
778 let limit = self.stack.get(base + local_idx + 1).cloned().unwrap_or(Value::Nil);
779 let step = self.stack.get(base + local_idx + 2).cloned().unwrap_or(Value::Nil);
780 let cv = cur.to_float().unwrap_or(0.0);
781 let lv = limit.to_float().unwrap_or(0.0);
782 let sv = step.to_float().unwrap_or(1.0);
783 let should_continue = if sv > 0.0 { cv <= lv } else { cv >= lv };
784 if !should_continue {
785 let fi = self.frames.len() - 1;
786 self.frames[fi].ip = (self.frames[fi].ip as isize + jump_off) as usize;
787 } else {
788 let next = match (&cur, &step) {
789 (Value::Int(c), Value::Int(s)) => Value::Int(c.wrapping_add(*s)),
790 _ => Value::Float(cv + sv),
791 };
792 let fi = self.frames.len() - 1;
793 let base = self.frames[fi].base;
794 let idx = base + local_idx;
795 while self.stack.len() <= idx { self.stack.push(Value::Nil); }
796 self.stack[idx] = next;
797 }
798 }
799
800 Instruction::Nop => {}
801 }
802 }
803 }
804
805 #[inline] fn push(&mut self, v: Value) { self.stack.push(v); }
808 #[inline] fn pop(&mut self) -> Value { self.stack.pop().unwrap_or(Value::Nil) }
809 #[inline] fn peek(&self) -> Value { self.stack.last().cloned().unwrap_or(Value::Nil) }
810
811 fn arith2<F>(&mut self, f: F) -> Result<(), ScriptError>
812 where F: Fn(Value, Value) -> Result<Value, ScriptError>
813 {
814 let b = self.pop(); let a = self.pop();
815 self.push(f(a, b)?);
816 Ok(())
817 }
818
819 fn cmp<F>(&mut self, op: F) -> Result<(), ScriptError>
820 where F: Fn(f64, f64) -> bool
821 {
822 let b = self.pop(); let a = self.pop();
823 let av = a.to_float().ok_or_else(|| ScriptError::runtime(format!("compare on {}", a.type_name())))?;
824 let bv = b.to_float().ok_or_else(|| ScriptError::runtime(format!("compare on {}", b.type_name())))?;
825 self.push(Value::Bool(op(av, bv)));
826 Ok(())
827 }
828
829 fn bitwise<F>(&mut self, op: F) -> Result<(), ScriptError>
830 where F: Fn(i64, i64) -> i64
831 {
832 let b = self.pop(); let a = self.pop();
833 let ai = a.to_int().ok_or_else(|| ScriptError::runtime(format!("bitwise on {}", a.type_name())))?;
834 let bi = b.to_int().ok_or_else(|| ScriptError::runtime(format!("bitwise on {}", b.type_name())))?;
835 self.push(Value::Int(op(ai, bi)));
836 Ok(())
837 }
838}
839
840fn num_arith<FI, FF>(a: Value, b: Value, fi: FI, ff: FF) -> Result<Value, ScriptError>
841where
842 FI: Fn(i64, i64) -> i64,
843 FF: Fn(f64, f64) -> f64,
844{
845 match (&a, &b) {
846 (Value::Int(ai), Value::Int(bi)) => Ok(Value::Int(fi(*ai, *bi))),
847 (Value::Float(af), Value::Float(bf)) => Ok(Value::Float(ff(*af, *bf))),
848 (Value::Int(ai), Value::Float(bf)) => Ok(Value::Float(ff(*ai as f64, *bf))),
849 (Value::Float(af), Value::Int(bi)) => Ok(Value::Float(ff(*af, *bi as f64))),
850 _ => Err(ScriptError::runtime(format!(
851 "attempt to perform arithmetic on {} and {} values",
852 a.type_name(), b.type_name()
853 ))),
854 }
855}
856
857#[cfg(test)]
860mod tests {
861 use super::*;
862 use crate::scripting::compiler::Compiler;
863 use crate::scripting::parser::Parser;
864
865 fn run(src: &str) -> Result<Vec<Value>, ScriptError> {
866 let script = Parser::from_source("test", src)
867 .map_err(|e| ScriptError::new(e.to_string()))?;
868 let chunk = Compiler::compile_script(&script);
869 let mut vm = Vm::new();
870 vm.execute(chunk)
871 }
872
873 #[test]
874 fn test_return_int() {
875 let r = run("return 42").unwrap();
876 assert_eq!(r[0], Value::Int(42));
877 }
878
879 #[test]
880 fn test_arithmetic() {
881 let r = run("return 2 + 3 * 4").unwrap();
882 assert_eq!(r[0], Value::Int(14));
883 }
884
885 #[test]
886 fn test_string_concat() {
887 let r = run("return \"hello\" .. \" world\"").unwrap();
888 assert!(matches!(&r[0], Value::Str(s) if s.as_ref() == "hello world"));
889 }
890
891 #[test]
892 fn test_local_variable() {
893 let r = run("local x = 10 return x").unwrap();
894 assert_eq!(r[0], Value::Int(10));
895 }
896
897 #[test]
898 fn test_if_else() {
899 let r = run("if true then return 1 else return 2 end").unwrap();
900 assert_eq!(r[0], Value::Int(1));
901 }
902
903 #[test]
904 fn test_function_call() {
905 let r = run("local function add(a, b) return a + b end return add(3, 4)").unwrap();
906 assert_eq!(r[0], Value::Int(7));
907 }
908}