1use std::cell::RefCell;
8use std::collections::HashMap;
9use std::hash::{Hash, Hasher};
10use std::rc::Rc;
11use std::time::Instant;
12
13#[derive(Debug, Clone)]
17pub enum LuaValue {
18 Nil,
19 Bool(bool),
20 Int(i64),
21 Float(f64),
22 Str(String),
23 Table(Rc<RefCell<HashMap<TableKey, LuaValue>>>),
24 Function(Rc<LuaFunction>),
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29pub enum TableKey {
30 Bool(bool),
31 Int(i64),
32 Float(u64),
34 Str(String),
35 TablePtr(usize),
37 FuncPtr(usize),
39}
40
41impl TableKey {
42 pub fn from_value(v: &LuaValue) -> Option<TableKey> {
43 match v {
44 LuaValue::Nil => None,
45 LuaValue::Bool(b) => Some(TableKey::Bool(*b)),
46 LuaValue::Int(i) => Some(TableKey::Int(*i)),
47 LuaValue::Float(f) => Some(TableKey::Float(f.to_bits())),
48 LuaValue::Str(s) => Some(TableKey::Str(s.clone())),
49 LuaValue::Table(rc) => Some(TableKey::TablePtr(Rc::as_ptr(rc) as usize)),
50 LuaValue::Function(rc) => Some(TableKey::FuncPtr(Rc::as_ptr(rc) as usize)),
51 }
52 }
53}
54
55impl PartialEq for LuaValue {
56 fn eq(&self, other: &Self) -> bool {
57 match (self, other) {
58 (LuaValue::Nil, LuaValue::Nil) => true,
59 (LuaValue::Bool(a), LuaValue::Bool(b)) => a == b,
60 (LuaValue::Int(a), LuaValue::Int(b)) => a == b,
61 (LuaValue::Float(a), LuaValue::Float(b)) => a.to_bits() == b.to_bits(),
62 (LuaValue::Str(a), LuaValue::Str(b)) => a == b,
63 (LuaValue::Table(a), LuaValue::Table(b)) => Rc::ptr_eq(a, b),
64 (LuaValue::Function(a), LuaValue::Function(b)) => Rc::ptr_eq(a, b),
65 _ => false,
66 }
67 }
68}
69
70impl Eq for LuaValue {}
71
72impl Hash for LuaValue {
73 fn hash<H: Hasher>(&self, state: &mut H) {
74 match self {
75 LuaValue::Nil => 0u8.hash(state),
76 LuaValue::Bool(b) => {
77 1u8.hash(state);
78 b.hash(state);
79 }
80 LuaValue::Int(i) => {
81 2u8.hash(state);
82 i.hash(state);
83 }
84 LuaValue::Float(f) => {
85 3u8.hash(state);
86 f.to_bits().hash(state);
87 }
88 LuaValue::Str(s) => {
89 4u8.hash(state);
90 s.hash(state);
91 }
92 LuaValue::Table(rc) => {
93 5u8.hash(state);
94 (Rc::as_ptr(rc) as usize).hash(state);
95 }
96 LuaValue::Function(rc) => {
97 6u8.hash(state);
98 (Rc::as_ptr(rc) as usize).hash(state);
99 }
100 }
101 }
102}
103
104#[derive(Debug)]
106#[non_exhaustive]
107pub enum LuaFunction {
108 #[doc(hidden)]
109 Closure(LuaClosure),
110 #[doc(hidden)]
111 Builtin(String),
112}
113
114#[derive(Debug)]
116pub struct LuaClosure {
117 pub(crate) params: Vec<String>,
118 pub(crate) body: Vec<Stmt>,
119 pub(crate) env: Rc<RefCell<Env>>,
120}
121
122#[derive(Debug, Clone)]
124pub struct LuaConfig {
125 pub max_stack_depth: usize,
126 pub timeout_ms: u64,
127 pub sandbox: bool,
128}
129
130#[derive(Debug, Clone)]
132pub struct LuaScript {
133 pub source: String,
134 pub name: String,
135}
136
137#[derive(Debug, Clone)]
139pub struct LuaResult {
140 pub return_values: Vec<LuaValue>,
141 pub error: Option<String>,
142 pub executed: bool,
143 pub print_output: Vec<String>,
145}
146
147pub struct LuaStub {
149 pub config: LuaConfig,
150 pub globals: HashMap<String, LuaValue>,
151 pub call_count: u64,
152 pub last_print_output: Vec<String>,
154}
155
156#[derive(Debug)]
160pub(crate) enum Signal {
161 None,
162 Return(Vec<LuaValue>),
163 Break,
164}
165
166#[derive(Debug)]
168pub(crate) struct Env {
169 pub(crate) vars: HashMap<String, LuaValue>,
170 pub(crate) parent: Option<Rc<RefCell<Env>>>,
171}
172
173pub(crate) struct Interpreter {
175 max_stack_depth: usize,
176 timeout_ms: u64,
177 start_time: Instant,
178 call_depth: usize,
179 stmt_count: u64,
180 print_buf: Rc<RefCell<Vec<String>>>,
181}
182
183pub fn default_lua_config() -> LuaConfig {
187 LuaConfig {
188 max_stack_depth: 200,
189 timeout_ms: 5000,
190 sandbox: true,
191 }
192}
193
194pub fn new_lua_stub(cfg: LuaConfig) -> LuaStub {
196 LuaStub {
197 config: cfg,
198 globals: HashMap::new(),
199 call_count: 0,
200 last_print_output: Vec::new(),
201 }
202}
203
204pub fn lua_set_global(stub: &mut LuaStub, name: &str, val: LuaValue) {
206 stub.globals.insert(name.to_string(), val);
207}
208
209pub fn lua_get_global<'a>(stub: &'a LuaStub, name: &str) -> Option<&'a LuaValue> {
211 stub.globals.get(name)
212}
213
214pub fn lua_global_count(stub: &LuaStub) -> usize {
216 stub.globals.len()
217}
218
219pub fn lua_execute(stub: &mut LuaStub, script: &LuaScript) -> LuaResult {
221 stub.call_count += 1;
222
223 let tokens = match lex(&script.source) {
224 Ok(t) => t,
225 Err(e) => {
226 return LuaResult {
227 return_values: Vec::new(),
228 error: Some(format!("[{}] lexer: {}", script.name, e)),
229 executed: false,
230 print_output: Vec::new(),
231 };
232 }
233 };
234
235 let stmts = match parse(&tokens) {
236 Ok(s) => s,
237 Err(e) => {
238 return LuaResult {
239 return_values: Vec::new(),
240 error: Some(format!("[{}] parse: {}", script.name, e)),
241 executed: false,
242 print_output: Vec::new(),
243 };
244 }
245 };
246
247 let print_buf: Rc<RefCell<Vec<String>>> = Rc::new(RefCell::new(Vec::new()));
248 let env = lua_interp::build_global_env(&stub.globals, Rc::clone(&print_buf));
249 let mut interp = Interpreter {
250 max_stack_depth: stub.config.max_stack_depth,
251 timeout_ms: stub.config.timeout_ms,
252 start_time: Instant::now(),
253 call_depth: 0,
254 stmt_count: 0,
255 print_buf: Rc::clone(&print_buf),
256 };
257
258 let result = interp.exec_block(&stmts, &env);
259 let captured = print_buf.borrow().clone();
260 stub.last_print_output = captured.clone();
261
262 match result {
263 Ok(sig) => {
264 let return_values = match sig {
265 Signal::Return(vals) => vals,
266 _ => Vec::new(),
267 };
268 LuaResult {
269 return_values,
270 error: None,
271 executed: true,
272 print_output: captured,
273 }
274 }
275 Err(e) => LuaResult {
276 return_values: Vec::new(),
277 error: Some(format!("[{}] runtime: {}", script.name, e)),
278 executed: true,
279 print_output: captured,
280 },
281 }
282}
283
284pub fn new_lua_script(name: &str, source: &str) -> LuaScript {
286 LuaScript {
287 source: source.to_string(),
288 name: name.to_string(),
289 }
290}
291
292pub fn lua_value_type_name(v: &LuaValue) -> &'static str {
294 match v {
295 LuaValue::Nil => "nil",
296 LuaValue::Bool(_) => "boolean",
297 LuaValue::Int(_) => "integer",
298 LuaValue::Float(_) => "float",
299 LuaValue::Str(_) => "string",
300 LuaValue::Table(_) => "table",
301 LuaValue::Function(_) => "function",
302 }
303}
304
305pub fn lua_value_to_json(v: &LuaValue) -> String {
307 lua_value_to_json_depth(v, 0)
308}
309
310fn lua_value_to_json_depth(v: &LuaValue, depth: usize) -> String {
311 if depth > 16 {
312 return "\"[max depth]\"".to_string();
313 }
314 match v {
315 LuaValue::Nil => "null".to_string(),
316 LuaValue::Bool(b) => format!("{}", b),
317 LuaValue::Int(i) => format!("{}", i),
318 LuaValue::Float(f) => format!("{}", f),
319 LuaValue::Str(s) => {
320 format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
321 }
322 LuaValue::Table(rc) => {
323 let map = rc.borrow();
324 let entries: Vec<String> = map
325 .iter()
326 .map(|(k, val)| {
327 let key_str = match k {
328 TableKey::Int(i) => format!("\"{}\"", i),
329 TableKey::Str(s) => format!("\"{}\"", s),
330 TableKey::Bool(b) => format!("\"{}\"", b),
331 TableKey::Float(bits) => {
332 format!("\"{}\"", f64::from_bits(*bits))
333 }
334 TableKey::TablePtr(p) => format!("\"table@{}\"", p),
335 TableKey::FuncPtr(p) => format!("\"func@{}\"", p),
336 };
337 format!("{}:{}", key_str, lua_value_to_json_depth(val, depth + 1))
338 })
339 .collect();
340 format!("{{{}}}", entries.join(","))
341 }
342 LuaValue::Function(_) => "\"[function]\"".to_string(),
343 }
344}
345
346pub fn lua_result_to_json(r: &LuaResult) -> String {
348 let vals: Vec<String> = r.return_values.iter().map(lua_value_to_json).collect();
349 let vals_str = vals.join(",");
350 let err_str = match &r.error {
351 Some(e) => format!("\"{}\"", e.replace('"', "\\\"")),
352 None => "null".to_string(),
353 };
354 format!(
355 "{{\"return_values\":[{}],\"error\":{},\"executed\":{}}}",
356 vals_str, err_str, r.executed
357 )
358}
359
360pub fn lua_stub_to_json(stub: &LuaStub) -> String {
362 let globals_count = stub.globals.len();
363 format!(
364 "{{\"call_count\":{},\"globals_count\":{},\"sandbox\":{}}}",
365 stub.call_count, globals_count, stub.config.sandbox
366 )
367}
368
369#[derive(Debug, Clone, PartialEq)]
372enum Token {
373 Number(f64),
374 LuaStr(String),
375 True,
376 False,
377 Nil,
378 Ident(String),
379 Plus,
380 Minus,
381 Star,
382 Slash,
383 Percent,
384 Caret,
385 EqEq,
386 NotEq,
387 Lt,
388 Le,
389 Gt,
390 Ge,
391 Assign,
392 And,
393 Or,
394 Not,
395 Hash,
396 DotDot,
397 LParen,
398 RParen,
399 LBrace,
400 RBrace,
401 LBracket,
402 RBracket,
403 Dot,
404 Comma,
405 Semi,
406 Colon,
407 If,
408 Then,
409 Else,
410 Elseif,
411 End,
412 While,
413 Do,
414 For,
415 In,
416 Return,
417 Local,
418 Function,
419 Repeat,
420 Until,
421 Break,
422 Eof,
423}
424
425fn lex(src: &str) -> Result<Vec<Token>, String> {
426 let chars: Vec<char> = src.chars().collect();
427 let mut pos = 0;
428 let mut tokens = Vec::new();
429
430 while pos < chars.len() {
431 if chars[pos].is_whitespace() {
432 pos += 1;
433 continue;
434 }
435
436 if pos + 1 < chars.len() && chars[pos] == '-' && chars[pos + 1] == '-' {
438 pos += 2;
439 if pos + 1 < chars.len() && chars[pos] == '[' && chars[pos + 1] == '[' {
440 pos += 2;
441 loop {
442 if pos + 1 >= chars.len() {
443 return Err("unterminated long comment".to_string());
444 }
445 if chars[pos] == ']' && chars[pos + 1] == ']' {
446 pos += 2;
447 break;
448 }
449 pos += 1;
450 }
451 } else {
452 while pos < chars.len() && chars[pos] != '\n' {
453 pos += 1;
454 }
455 }
456 continue;
457 }
458
459 if chars[pos].is_ascii_digit()
461 || (chars[pos] == '.' && pos + 1 < chars.len() && chars[pos + 1].is_ascii_digit())
462 {
463 let start = pos;
464 while pos < chars.len() && (chars[pos].is_ascii_digit() || chars[pos] == '.') {
465 pos += 1;
466 }
467 if pos < chars.len() && (chars[pos] == 'e' || chars[pos] == 'E') {
468 pos += 1;
469 if pos < chars.len() && (chars[pos] == '+' || chars[pos] == '-') {
470 pos += 1;
471 }
472 while pos < chars.len() && chars[pos].is_ascii_digit() {
473 pos += 1;
474 }
475 }
476 let s: String = chars[start..pos].iter().collect();
477 let n: f64 = s.parse().map_err(|_| format!("invalid number: {}", s))?;
478 tokens.push(Token::Number(n));
479 continue;
480 }
481
482 if chars[pos] == '0'
484 && pos + 1 < chars.len()
485 && (chars[pos + 1] == 'x' || chars[pos + 1] == 'X')
486 {
487 pos += 2;
488 let start = pos;
489 while pos < chars.len() && chars[pos].is_ascii_hexdigit() {
490 pos += 1;
491 }
492 let s: String = chars[start..pos].iter().collect();
493 let n = i64::from_str_radix(&s, 16).map_err(|_| format!("invalid hex: {}", s))?;
494 tokens.push(Token::Number(n as f64));
495 continue;
496 }
497
498 if chars[pos] == '"' || chars[pos] == '\'' {
500 let quote = chars[pos];
501 pos += 1;
502 let mut s = String::new();
503 while pos < chars.len() && chars[pos] != quote {
504 if chars[pos] == '\\' {
505 pos += 1;
506 if pos >= chars.len() {
507 return Err("unterminated string escape".to_string());
508 }
509 match chars[pos] {
510 'n' => s.push('\n'),
511 't' => s.push('\t'),
512 'r' => s.push('\r'),
513 '\\' => s.push('\\'),
514 '"' => s.push('"'),
515 '\'' => s.push('\''),
516 '0' => s.push('\0'),
517 c => {
518 s.push('\\');
519 s.push(c);
520 }
521 }
522 } else {
523 s.push(chars[pos]);
524 }
525 pos += 1;
526 }
527 if pos >= chars.len() {
528 return Err("unterminated string literal".to_string());
529 }
530 pos += 1;
531 tokens.push(Token::LuaStr(s));
532 continue;
533 }
534
535 if chars[pos] == '[' && pos + 1 < chars.len() && chars[pos + 1] == '[' {
537 pos += 2;
538 let mut s = String::new();
539 loop {
540 if pos + 1 >= chars.len() {
541 return Err("unterminated long string".to_string());
542 }
543 if chars[pos] == ']' && chars[pos + 1] == ']' {
544 pos += 2;
545 break;
546 }
547 s.push(chars[pos]);
548 pos += 1;
549 }
550 tokens.push(Token::LuaStr(s));
551 continue;
552 }
553
554 if chars[pos].is_alphabetic() || chars[pos] == '_' {
556 let start = pos;
557 while pos < chars.len() && (chars[pos].is_alphanumeric() || chars[pos] == '_') {
558 pos += 1;
559 }
560 let word: String = chars[start..pos].iter().collect();
561 let tok = match word.as_str() {
562 "true" => Token::True,
563 "false" => Token::False,
564 "nil" => Token::Nil,
565 "and" => Token::And,
566 "or" => Token::Or,
567 "not" => Token::Not,
568 "if" => Token::If,
569 "then" => Token::Then,
570 "else" => Token::Else,
571 "elseif" => Token::Elseif,
572 "end" => Token::End,
573 "while" => Token::While,
574 "do" => Token::Do,
575 "for" => Token::For,
576 "in" => Token::In,
577 "return" => Token::Return,
578 "local" => Token::Local,
579 "function" => Token::Function,
580 "repeat" => Token::Repeat,
581 "until" => Token::Until,
582 "break" => Token::Break,
583 _ => Token::Ident(word),
584 };
585 tokens.push(tok);
586 continue;
587 }
588
589 if pos + 1 < chars.len() {
591 let two: String = chars[pos..pos + 2].iter().collect();
592 let tok = match two.as_str() {
593 "==" => Some(Token::EqEq),
594 "~=" => Some(Token::NotEq),
595 "<=" => Some(Token::Le),
596 ">=" => Some(Token::Ge),
597 ".." => Some(Token::DotDot),
598 _ => None,
599 };
600 if let Some(t) = tok {
601 tokens.push(t);
602 pos += 2;
603 continue;
604 }
605 }
606
607 let tok = match chars[pos] {
609 '+' => Token::Plus,
610 '-' => Token::Minus,
611 '*' => Token::Star,
612 '/' => Token::Slash,
613 '%' => Token::Percent,
614 '^' => Token::Caret,
615 '<' => Token::Lt,
616 '>' => Token::Gt,
617 '=' => Token::Assign,
618 '#' => Token::Hash,
619 '(' => Token::LParen,
620 ')' => Token::RParen,
621 '{' => Token::LBrace,
622 '}' => Token::RBrace,
623 '[' => Token::LBracket,
624 ']' => Token::RBracket,
625 '.' => Token::Dot,
626 ',' => Token::Comma,
627 ';' => Token::Semi,
628 ':' => Token::Colon,
629 c => return Err(format!("unexpected character: {:?}", c)),
630 };
631 tokens.push(tok);
632 pos += 1;
633 }
634
635 tokens.push(Token::Eof);
636 Ok(tokens)
637}
638
639#[derive(Debug, Clone)]
642pub(crate) enum Expr {
643 Nil,
644 True,
645 False,
646 Number(f64),
647 Str(String),
648 Var(String),
649 BinOp {
650 op: BinOp,
651 left: Box<Expr>,
652 right: Box<Expr>,
653 },
654 UnOp {
655 op: UnOp,
656 operand: Box<Expr>,
657 },
658 TableCtor(Vec<TableField>),
659 FieldAccess {
660 table: Box<Expr>,
661 field: String,
662 },
663 IndexAccess {
664 table: Box<Expr>,
665 key: Box<Expr>,
666 },
667 FuncCall {
668 func: Box<Expr>,
669 args: Vec<Expr>,
670 },
671 MethodCall {
672 obj: Box<Expr>,
673 method: String,
674 args: Vec<Expr>,
675 },
676 FuncDef {
677 params: Vec<String>,
678 body: Vec<Stmt>,
679 },
680}
681
682#[derive(Debug, Clone)]
683pub(crate) enum TableField {
684 Indexed { key: Expr, val: Expr },
685 Named { name: String, val: Expr },
686 Positional(Expr),
687}
688
689#[derive(Debug, Clone, Copy, PartialEq)]
690pub(crate) enum BinOp {
691 Add,
692 Sub,
693 Mul,
694 Div,
695 Mod,
696 Pow,
697 Eq,
698 Ne,
699 Lt,
700 Le,
701 Gt,
702 Ge,
703 And,
704 Or,
705 Concat,
706}
707
708#[derive(Debug, Clone, Copy, PartialEq)]
709pub(crate) enum UnOp {
710 Neg,
711 Not,
712 Len,
713}
714
715#[derive(Debug, Clone)]
716pub(crate) enum Stmt {
717 Assign {
718 targets: Vec<Expr>,
719 values: Vec<Expr>,
720 },
721 Local {
722 names: Vec<String>,
723 values: Vec<Expr>,
724 },
725 Do(Vec<Stmt>),
726 While {
727 cond: Expr,
728 body: Vec<Stmt>,
729 },
730 Repeat {
731 body: Vec<Stmt>,
732 cond: Expr,
733 },
734 If {
735 cond: Expr,
736 then_block: Vec<Stmt>,
737 elseif_blocks: Vec<(Expr, Vec<Stmt>)>,
738 else_block: Option<Vec<Stmt>>,
739 },
740 NumFor {
741 var: String,
742 start: Expr,
743 limit: Expr,
744 step: Option<Expr>,
745 body: Vec<Stmt>,
746 },
747 GenFor {
748 vars: Vec<String>,
749 iterators: Vec<Expr>,
750 body: Vec<Stmt>,
751 },
752 FuncDef {
753 name: Vec<String>,
754 method: Option<String>,
755 params: Vec<String>,
756 body: Vec<Stmt>,
757 },
758 LocalFunc {
759 name: String,
760 params: Vec<String>,
761 body: Vec<Stmt>,
762 },
763 Return(Vec<Expr>),
764 Break,
765 Expr(Expr),
766}
767
768struct Parser<'a> {
771 tokens: &'a [Token],
772 pos: usize,
773}
774
775impl<'a> Parser<'a> {
776 fn new(tokens: &'a [Token]) -> Self {
777 Parser { tokens, pos: 0 }
778 }
779
780 fn peek(&self) -> &Token {
781 &self.tokens[self.pos]
782 }
783
784 fn peek_at(&self, offset: usize) -> &Token {
785 let idx = self.pos + offset;
786 if idx < self.tokens.len() {
787 &self.tokens[idx]
788 } else {
789 &Token::Eof
790 }
791 }
792
793 fn advance(&mut self) -> &Token {
794 let t = &self.tokens[self.pos];
795 if self.pos + 1 < self.tokens.len() {
796 self.pos += 1;
797 }
798 t
799 }
800
801 fn expect(&mut self, expected: &Token) -> Result<(), String> {
802 let t = self.advance();
803 if t == expected {
804 Ok(())
805 } else {
806 Err(format!("expected {:?}, got {:?}", expected, t))
807 }
808 }
809
810 fn expect_ident(&mut self) -> Result<String, String> {
811 match self.advance().clone() {
812 Token::Ident(name) => Ok(name),
813 t => Err(format!("expected identifier, got {:?}", t)),
814 }
815 }
816
817 fn parse_block(&mut self) -> Result<Vec<Stmt>, String> {
818 let mut stmts = Vec::new();
819 loop {
820 while self.peek() == &Token::Semi {
821 self.advance();
822 }
823 match self.peek() {
824 Token::Eof | Token::End | Token::Else | Token::Elseif | Token::Until => break,
825 Token::Return => {
826 self.advance();
827 let vals = self.parse_expr_list()?;
828 if self.peek() == &Token::Semi {
829 self.advance();
830 }
831 stmts.push(Stmt::Return(vals));
832 break;
833 }
834 _ => {
835 let stmt = self.parse_stmt()?;
836 stmts.push(stmt);
837 }
838 }
839 }
840 Ok(stmts)
841 }
842
843 fn parse_stmt(&mut self) -> Result<Stmt, String> {
844 match self.peek().clone() {
845 Token::Local => {
846 self.advance();
847 if self.peek() == &Token::Function {
848 self.advance();
849 let name = self.expect_ident()?;
850 let (params, body) = self.parse_func_body()?;
851 Ok(Stmt::LocalFunc { name, params, body })
852 } else {
853 let mut names = vec![self.expect_ident()?];
854 while self.peek() == &Token::Comma {
855 self.advance();
856 names.push(self.expect_ident()?);
857 }
858 let values = if self.peek() == &Token::Assign {
859 self.advance();
860 self.parse_expr_list()?
861 } else {
862 Vec::new()
863 };
864 Ok(Stmt::Local { names, values })
865 }
866 }
867 Token::Function => {
868 self.advance();
869 let mut name_parts = vec![self.expect_ident()?];
870 let mut method = None;
871 loop {
872 match self.peek() {
873 Token::Dot => {
874 self.advance();
875 name_parts.push(self.expect_ident()?);
876 }
877 Token::Colon => {
878 self.advance();
879 method = Some(self.expect_ident()?);
880 break;
881 }
882 _ => break,
883 }
884 }
885 let (mut params, body) = self.parse_func_body()?;
886 if method.is_some() {
887 params.insert(0, "self".to_string());
888 }
889 Ok(Stmt::FuncDef {
890 name: name_parts,
891 method,
892 params,
893 body,
894 })
895 }
896 Token::If => self.parse_if(),
897 Token::While => {
898 self.advance();
899 let cond = self.parse_expr()?;
900 self.expect(&Token::Do)?;
901 let body = self.parse_block()?;
902 self.expect(&Token::End)?;
903 Ok(Stmt::While { cond, body })
904 }
905 Token::Repeat => {
906 self.advance();
907 let body = self.parse_block()?;
908 self.expect(&Token::Until)?;
909 let cond = self.parse_expr()?;
910 Ok(Stmt::Repeat { body, cond })
911 }
912 Token::For => {
913 self.advance();
914 let first_var = self.expect_ident()?;
915 if self.peek() == &Token::Assign {
916 self.advance();
917 let start = self.parse_expr()?;
918 self.expect(&Token::Comma)?;
919 let limit = self.parse_expr()?;
920 let step = if self.peek() == &Token::Comma {
921 self.advance();
922 Some(self.parse_expr()?)
923 } else {
924 None
925 };
926 self.expect(&Token::Do)?;
927 let body = self.parse_block()?;
928 self.expect(&Token::End)?;
929 Ok(Stmt::NumFor {
930 var: first_var,
931 start,
932 limit,
933 step,
934 body,
935 })
936 } else {
937 let mut vars = vec![first_var];
938 while self.peek() == &Token::Comma {
939 self.advance();
940 vars.push(self.expect_ident()?);
941 }
942 self.expect(&Token::In)?;
943 let iterators = self.parse_expr_list()?;
944 self.expect(&Token::Do)?;
945 let body = self.parse_block()?;
946 self.expect(&Token::End)?;
947 Ok(Stmt::GenFor {
948 vars,
949 iterators,
950 body,
951 })
952 }
953 }
954 Token::Do => {
955 self.advance();
956 let block = self.parse_block()?;
957 self.expect(&Token::End)?;
958 Ok(Stmt::Do(block))
959 }
960 Token::Break => {
961 self.advance();
962 Ok(Stmt::Break)
963 }
964 _ => {
965 let expr = self.parse_suffix_expr()?;
966 let mut targets = vec![expr];
967 while self.peek() == &Token::Comma {
968 self.advance();
969 targets.push(self.parse_suffix_expr()?);
970 }
971 if self.peek() == &Token::Assign {
972 self.advance();
973 let values = self.parse_expr_list()?;
974 Ok(Stmt::Assign { targets, values })
975 } else if targets.len() == 1 {
976 match &targets[0] {
977 Expr::FuncCall { .. } | Expr::MethodCall { .. } => {
978 Ok(Stmt::Expr(targets.remove(0)))
979 }
980 _ => Err(format!(
981 "unexpected expression as statement: {:?}",
982 targets[0]
983 )),
984 }
985 } else {
986 Err("multi-target without assignment".to_string())
987 }
988 }
989 }
990 }
991
992 fn parse_if(&mut self) -> Result<Stmt, String> {
993 self.expect(&Token::If)?;
994 let cond = self.parse_expr()?;
995 self.expect(&Token::Then)?;
996 let then_block = self.parse_block()?;
997 let mut elseif_blocks = Vec::new();
998 let mut else_block = None;
999 loop {
1000 match self.peek() {
1001 Token::Elseif => {
1002 self.advance();
1003 let ec = self.parse_expr()?;
1004 self.expect(&Token::Then)?;
1005 let eb = self.parse_block()?;
1006 elseif_blocks.push((ec, eb));
1007 }
1008 Token::Else => {
1009 self.advance();
1010 else_block = Some(self.parse_block()?);
1011 break;
1012 }
1013 _ => break,
1014 }
1015 }
1016 self.expect(&Token::End)?;
1017 Ok(Stmt::If {
1018 cond,
1019 then_block,
1020 elseif_blocks,
1021 else_block,
1022 })
1023 }
1024
1025 fn parse_func_body(&mut self) -> Result<(Vec<String>, Vec<Stmt>), String> {
1026 self.expect(&Token::LParen)?;
1027 let mut params = Vec::new();
1028 if self.peek() != &Token::RParen {
1029 params.push(self.expect_ident()?);
1030 while self.peek() == &Token::Comma {
1031 self.advance();
1032 if let Token::DotDot = self.peek() {
1033 self.advance();
1034 if self.peek() == &Token::Dot {
1035 self.advance();
1036 }
1037 break;
1038 }
1039 params.push(self.expect_ident()?);
1040 }
1041 }
1042 self.expect(&Token::RParen)?;
1043 let body = self.parse_block()?;
1044 self.expect(&Token::End)?;
1045 Ok((params, body))
1046 }
1047
1048 fn parse_expr_list(&mut self) -> Result<Vec<Expr>, String> {
1049 let first = self.parse_expr()?;
1050 let mut exprs = vec![first];
1051 while self.peek() == &Token::Comma {
1052 self.advance();
1053 exprs.push(self.parse_expr()?);
1054 }
1055 Ok(exprs)
1056 }
1057
1058 fn parse_expr(&mut self) -> Result<Expr, String> {
1059 self.parse_or_expr()
1060 }
1061
1062 fn parse_or_expr(&mut self) -> Result<Expr, String> {
1063 let mut left = self.parse_and_expr()?;
1064 while self.peek() == &Token::Or {
1065 self.advance();
1066 let right = self.parse_and_expr()?;
1067 left = Expr::BinOp {
1068 op: BinOp::Or,
1069 left: Box::new(left),
1070 right: Box::new(right),
1071 };
1072 }
1073 Ok(left)
1074 }
1075
1076 fn parse_and_expr(&mut self) -> Result<Expr, String> {
1077 let mut left = self.parse_compare_expr()?;
1078 while self.peek() == &Token::And {
1079 self.advance();
1080 let right = self.parse_compare_expr()?;
1081 left = Expr::BinOp {
1082 op: BinOp::And,
1083 left: Box::new(left),
1084 right: Box::new(right),
1085 };
1086 }
1087 Ok(left)
1088 }
1089
1090 fn parse_compare_expr(&mut self) -> Result<Expr, String> {
1091 let left = self.parse_concat_expr()?;
1092 let op = match self.peek() {
1093 Token::EqEq => BinOp::Eq,
1094 Token::NotEq => BinOp::Ne,
1095 Token::Lt => BinOp::Lt,
1096 Token::Le => BinOp::Le,
1097 Token::Gt => BinOp::Gt,
1098 Token::Ge => BinOp::Ge,
1099 _ => return Ok(left),
1100 };
1101 self.advance();
1102 let right = self.parse_concat_expr()?;
1103 Ok(Expr::BinOp {
1104 op,
1105 left: Box::new(left),
1106 right: Box::new(right),
1107 })
1108 }
1109
1110 fn parse_concat_expr(&mut self) -> Result<Expr, String> {
1111 let left = self.parse_add_expr()?;
1112 if self.peek() == &Token::DotDot {
1113 self.advance();
1114 let right = self.parse_concat_expr()?;
1115 return Ok(Expr::BinOp {
1116 op: BinOp::Concat,
1117 left: Box::new(left),
1118 right: Box::new(right),
1119 });
1120 }
1121 Ok(left)
1122 }
1123
1124 fn parse_add_expr(&mut self) -> Result<Expr, String> {
1125 let mut left = self.parse_mul_expr()?;
1126 loop {
1127 let op = match self.peek() {
1128 Token::Plus => BinOp::Add,
1129 Token::Minus => BinOp::Sub,
1130 _ => break,
1131 };
1132 self.advance();
1133 let right = self.parse_mul_expr()?;
1134 left = Expr::BinOp {
1135 op,
1136 left: Box::new(left),
1137 right: Box::new(right),
1138 };
1139 }
1140 Ok(left)
1141 }
1142
1143 fn parse_mul_expr(&mut self) -> Result<Expr, String> {
1144 let mut left = self.parse_unary_expr()?;
1145 loop {
1146 let op = match self.peek() {
1147 Token::Star => BinOp::Mul,
1148 Token::Slash => BinOp::Div,
1149 Token::Percent => BinOp::Mod,
1150 _ => break,
1151 };
1152 self.advance();
1153 let right = self.parse_unary_expr()?;
1154 left = Expr::BinOp {
1155 op,
1156 left: Box::new(left),
1157 right: Box::new(right),
1158 };
1159 }
1160 Ok(left)
1161 }
1162
1163 fn parse_unary_expr(&mut self) -> Result<Expr, String> {
1164 match self.peek().clone() {
1165 Token::Minus => {
1166 self.advance();
1167 let e = self.parse_unary_expr()?;
1168 Ok(Expr::UnOp {
1169 op: UnOp::Neg,
1170 operand: Box::new(e),
1171 })
1172 }
1173 Token::Not => {
1174 self.advance();
1175 let e = self.parse_unary_expr()?;
1176 Ok(Expr::UnOp {
1177 op: UnOp::Not,
1178 operand: Box::new(e),
1179 })
1180 }
1181 Token::Hash => {
1182 self.advance();
1183 let e = self.parse_unary_expr()?;
1184 Ok(Expr::UnOp {
1185 op: UnOp::Len,
1186 operand: Box::new(e),
1187 })
1188 }
1189 _ => self.parse_power_expr(),
1190 }
1191 }
1192
1193 fn parse_power_expr(&mut self) -> Result<Expr, String> {
1194 let base = self.parse_suffix_expr()?;
1195 if self.peek() == &Token::Caret {
1196 self.advance();
1197 let exp = self.parse_unary_expr()?;
1198 return Ok(Expr::BinOp {
1199 op: BinOp::Pow,
1200 left: Box::new(base),
1201 right: Box::new(exp),
1202 });
1203 }
1204 Ok(base)
1205 }
1206
1207 fn parse_suffix_expr(&mut self) -> Result<Expr, String> {
1208 let mut expr = self.parse_primary_expr()?;
1209 loop {
1210 match self.peek().clone() {
1211 Token::Dot => {
1212 self.advance();
1213 let field = self.expect_ident()?;
1214 expr = Expr::FieldAccess {
1215 table: Box::new(expr),
1216 field,
1217 };
1218 }
1219 Token::LBracket => {
1220 self.advance();
1221 let key = self.parse_expr()?;
1222 self.expect(&Token::RBracket)?;
1223 expr = Expr::IndexAccess {
1224 table: Box::new(expr),
1225 key: Box::new(key),
1226 };
1227 }
1228 Token::Colon => {
1229 self.advance();
1230 let method = self.expect_ident()?;
1231 let args = self.parse_call_args()?;
1232 expr = Expr::MethodCall {
1233 obj: Box::new(expr),
1234 method,
1235 args,
1236 };
1237 }
1238 Token::LParen | Token::LBrace | Token::LuaStr(_) => {
1239 let args = self.parse_call_args()?;
1240 expr = Expr::FuncCall {
1241 func: Box::new(expr),
1242 args,
1243 };
1244 }
1245 _ => break,
1246 }
1247 }
1248 Ok(expr)
1249 }
1250
1251 fn parse_call_args(&mut self) -> Result<Vec<Expr>, String> {
1252 match self.peek().clone() {
1253 Token::LParen => {
1254 self.advance();
1255 if self.peek() == &Token::RParen {
1256 self.advance();
1257 return Ok(Vec::new());
1258 }
1259 let args = self.parse_expr_list()?;
1260 self.expect(&Token::RParen)?;
1261 Ok(args)
1262 }
1263 Token::LBrace => {
1264 let tbl = self.parse_table_ctor()?;
1265 Ok(vec![tbl])
1266 }
1267 Token::LuaStr(s) => {
1268 let s = s.clone();
1269 self.advance();
1270 Ok(vec![Expr::Str(s)])
1271 }
1272 t => Err(format!("expected function call args, got {:?}", t)),
1273 }
1274 }
1275
1276 fn parse_primary_expr(&mut self) -> Result<Expr, String> {
1277 match self.peek().clone() {
1278 Token::Nil => {
1279 self.advance();
1280 Ok(Expr::Nil)
1281 }
1282 Token::True => {
1283 self.advance();
1284 Ok(Expr::True)
1285 }
1286 Token::False => {
1287 self.advance();
1288 Ok(Expr::False)
1289 }
1290 Token::Number(n) => {
1291 let num = n;
1292 self.advance();
1293 Ok(Expr::Number(num))
1294 }
1295 Token::LuaStr(s) => {
1296 let s = s.clone();
1297 self.advance();
1298 Ok(Expr::Str(s))
1299 }
1300 Token::Ident(name) => {
1301 let name = name.clone();
1302 self.advance();
1303 Ok(Expr::Var(name))
1304 }
1305 Token::LParen => {
1306 self.advance();
1307 let e = self.parse_expr()?;
1308 self.expect(&Token::RParen)?;
1309 Ok(e)
1310 }
1311 Token::LBrace => self.parse_table_ctor(),
1312 Token::Function => {
1313 self.advance();
1314 let (params, body) = self.parse_func_body()?;
1315 Ok(Expr::FuncDef { params, body })
1316 }
1317 t => Err(format!("unexpected token in expression: {:?}", t)),
1318 }
1319 }
1320
1321 fn parse_table_ctor(&mut self) -> Result<Expr, String> {
1322 self.expect(&Token::LBrace)?;
1323 let mut fields = Vec::new();
1324 while self.peek() != &Token::RBrace {
1325 let field = match self.peek().clone() {
1326 Token::LBracket => {
1327 self.advance();
1328 let key = self.parse_expr()?;
1329 self.expect(&Token::RBracket)?;
1330 self.expect(&Token::Assign)?;
1331 let val = self.parse_expr()?;
1332 TableField::Indexed { key, val }
1333 }
1334 Token::Ident(_) if self.peek_at(1) == &Token::Assign => {
1335 if let Token::Ident(name) = self.advance().clone() {
1336 self.advance(); let val = self.parse_expr()?;
1338 TableField::Named { name, val }
1339 } else {
1340 unreachable!()
1341 }
1342 }
1343 _ => {
1344 let val = self.parse_expr()?;
1345 TableField::Positional(val)
1346 }
1347 };
1348 fields.push(field);
1349 match self.peek() {
1350 Token::Comma | Token::Semi => {
1351 self.advance();
1352 }
1353 _ => break,
1354 }
1355 }
1356 self.expect(&Token::RBrace)?;
1357 Ok(Expr::TableCtor(fields))
1358 }
1359}
1360
1361fn parse(tokens: &[Token]) -> Result<Vec<Stmt>, String> {
1362 let mut p = Parser::new(tokens);
1363 let block = p.parse_block()?;
1364 if p.peek() != &Token::Eof {
1365 return Err(format!("unexpected token at end: {:?}", p.peek()));
1366 }
1367 Ok(block)
1368}
1369
1370#[path = "lua_interp.rs"]
1373mod lua_interp;
1374
1375#[cfg(test)]
1378mod tests {
1379 use super::*;
1380
1381 fn run(src: &str) -> LuaResult {
1382 let cfg = default_lua_config();
1383 let mut stub = new_lua_stub(cfg);
1384 let script = new_lua_script("test", src);
1385 lua_execute(&mut stub, &script)
1386 }
1387
1388 #[test]
1389 fn test_default_config() {
1390 let cfg = default_lua_config();
1391 assert_eq!(cfg.max_stack_depth, 200);
1392 assert_eq!(cfg.timeout_ms, 5000);
1393 assert!(cfg.sandbox);
1394 }
1395
1396 #[test]
1397 fn test_new_stub() {
1398 let cfg = default_lua_config();
1399 let stub = new_lua_stub(cfg);
1400 assert_eq!(stub.call_count, 0);
1401 assert_eq!(lua_global_count(&stub), 0);
1402 }
1403
1404 #[test]
1405 fn test_set_get_global() {
1406 let cfg = default_lua_config();
1407 let mut stub = new_lua_stub(cfg);
1408 lua_set_global(&mut stub, "x", LuaValue::Int(42));
1409 let v = lua_get_global(&stub, "x");
1410 assert!(v.is_some());
1411 if let Some(LuaValue::Int(i)) = v {
1412 assert_eq!(*i, 42);
1413 } else {
1414 panic!("expected Int");
1415 }
1416 }
1417
1418 #[test]
1419 fn test_execute_increments_count() {
1420 let cfg = default_lua_config();
1421 let mut stub = new_lua_stub(cfg);
1422 let script = new_lua_script("test", "return 1");
1423 let result = lua_execute(&mut stub, &script);
1424 assert!(result.executed);
1425 assert!(result.error.is_none());
1426 assert_eq!(stub.call_count, 1);
1427 }
1428
1429 #[test]
1430 fn test_value_type_names() {
1431 assert_eq!(lua_value_type_name(&LuaValue::Nil), "nil");
1432 assert_eq!(lua_value_type_name(&LuaValue::Bool(true)), "boolean");
1433 assert_eq!(lua_value_type_name(&LuaValue::Int(0)), "integer");
1434 assert_eq!(lua_value_type_name(&LuaValue::Float(0.0)), "float");
1435 assert_eq!(lua_value_type_name(&LuaValue::Str(String::new())), "string");
1436 }
1437
1438 #[test]
1439 fn test_global_count_and_update() {
1440 let cfg = default_lua_config();
1441 let mut stub = new_lua_stub(cfg);
1442 lua_set_global(&mut stub, "a", LuaValue::Bool(false));
1443 lua_set_global(&mut stub, "b", LuaValue::Float(std::f64::consts::PI));
1444 assert_eq!(lua_global_count(&stub), 2);
1445 lua_set_global(&mut stub, "a", LuaValue::Bool(true));
1446 assert_eq!(lua_global_count(&stub), 2);
1447 }
1448
1449 #[test]
1450 fn test_result_to_json() {
1451 let r = LuaResult {
1452 return_values: vec![LuaValue::Nil],
1453 error: None,
1454 executed: true,
1455 print_output: Vec::new(),
1456 };
1457 let json = lua_result_to_json(&r);
1458 assert!(json.contains("return_values"));
1459 assert!(json.contains("null"));
1460 }
1461
1462 #[test]
1463 fn test_stub_to_json() {
1464 let cfg = default_lua_config();
1465 let stub = new_lua_stub(cfg);
1466 let json = lua_stub_to_json(&stub);
1467 assert!(json.contains("call_count"));
1468 assert!(json.contains("sandbox"));
1469 }
1470
1471 #[test]
1472 fn test_execute_empty_script() {
1473 let r = run("");
1474 assert!(r.error.is_none());
1475 assert!(r.executed);
1476 }
1477
1478 #[test]
1479 fn test_execute_literal_return() {
1480 let r = run("return 42");
1481 assert!(r.error.is_none());
1482 assert_eq!(r.return_values.len(), 1);
1483 assert!(matches!(r.return_values[0], LuaValue::Int(42)));
1484 }
1485
1486 #[test]
1487 fn test_execute_arithmetic() {
1488 let r = run("return 2 + 3 * 4");
1489 assert!(r.error.is_none());
1490 assert_eq!(r.return_values.len(), 1);
1491 assert!(matches!(r.return_values[0], LuaValue::Int(14)));
1492 }
1493
1494 #[test]
1495 fn test_execute_string_concat() {
1496 let r = run(r#"return "hello" .. " " .. "world""#);
1497 assert!(r.error.is_none());
1498 if let LuaValue::Str(s) = &r.return_values[0] {
1499 assert_eq!(s, "hello world");
1500 } else {
1501 panic!("expected string");
1502 }
1503 }
1504
1505 #[test]
1506 fn test_execute_if_then_else() {
1507 let r = run("if 1 > 0 then return 1 else return 0 end");
1508 assert!(r.error.is_none());
1509 assert!(matches!(r.return_values[0], LuaValue::Int(1)));
1510 }
1511
1512 #[test]
1513 fn test_execute_while_loop() {
1514 let r = run("local s = 0\nfor i = 1, 10 do s = s + i end\nreturn s");
1515 assert!(r.error.is_none(), "error: {:?}", r.error);
1516 assert!(matches!(r.return_values[0], LuaValue::Int(55)));
1517 }
1518
1519 #[test]
1520 fn test_global_set_get() {
1521 let cfg = default_lua_config();
1522 let mut stub = new_lua_stub(cfg);
1523 lua_set_global(&mut stub, "myval", LuaValue::Int(99));
1524 let script = new_lua_script("test", "return myval");
1525 let r = lua_execute(&mut stub, &script);
1526 assert!(r.error.is_none());
1527 assert!(matches!(r.return_values[0], LuaValue::Int(99)));
1528 }
1529
1530 #[test]
1531 fn test_execute_function_def_call() {
1532 let r = run("function add(a, b) return a + b end\nreturn add(3, 4)");
1533 assert!(r.error.is_none(), "error: {:?}", r.error);
1534 assert!(matches!(r.return_values[0], LuaValue::Int(7)));
1535 }
1536
1537 #[test]
1538 fn test_error_on_syntax_error() {
1539 let r = run("return )(garbage");
1540 assert!(r.error.is_some());
1541 assert!(!r.executed);
1542 }
1543
1544 #[test]
1545 fn test_max_stack_depth() {
1546 let cfg = LuaConfig {
1549 max_stack_depth: 20,
1550 timeout_ms: 5000,
1551 sandbox: true,
1552 };
1553 let mut stub = new_lua_stub(cfg);
1554 let script = new_lua_script("test", "function inf() return inf() end\ninf()");
1555 let r = lua_execute(&mut stub, &script);
1556 assert!(r.error.is_some());
1557 let err = r.error.as_deref().unwrap_or("");
1558 assert!(
1559 err.contains("stack overflow") || err.contains("stack"),
1560 "error was: {}",
1561 err
1562 );
1563 }
1564
1565 #[test]
1566 fn test_print_output_captured() {
1567 let r = run(r#"print("hello")"#);
1568 assert!(r.error.is_none(), "error: {:?}", r.error);
1569 assert_eq!(r.print_output.len(), 1);
1570 assert!(r.print_output[0].contains("hello"));
1571 }
1572
1573 #[test]
1574 fn test_table_operations() {
1575 let r = run("local t = {10, 20, 30}\nreturn t[1] + t[2] + t[3]");
1576 assert!(r.error.is_none(), "error: {:?}", r.error);
1577 assert!(matches!(r.return_values[0], LuaValue::Int(60)));
1578 }
1579
1580 #[test]
1581 fn test_elseif_chain() {
1582 let r = run(
1583 "local x = 5\nif x == 1 then return 1\nelseif x == 5 then return 5\nelse return 0 end",
1584 );
1585 assert!(r.error.is_none());
1586 assert!(matches!(r.return_values[0], LuaValue::Int(5)));
1587 }
1588
1589 #[test]
1590 fn test_string_concat_and_tostring() {
1591 let r = run(r#"return tostring(42) .. " bottles""#);
1592 assert!(r.error.is_none(), "error: {:?}", r.error);
1593 if let LuaValue::Str(s) = &r.return_values[0] {
1594 assert_eq!(s, "42 bottles");
1595 } else {
1596 panic!("expected string");
1597 }
1598 }
1599
1600 #[test]
1601 fn test_math_floor_ceil() {
1602 let r = run("return math.floor(3.7)");
1603 assert!(r.error.is_none(), "error: {:?}", r.error);
1604 assert!(matches!(r.return_values[0], LuaValue::Int(3)));
1605 }
1606
1607 #[test]
1608 fn test_ipairs_loop() {
1609 let r = run(
1610 "local s = 0\nlocal t = {1,2,3,4,5}\nfor i,v in ipairs(t) do s = s + v end\nreturn s",
1611 );
1612 assert!(r.error.is_none(), "error: {:?}", r.error);
1613 assert!(matches!(r.return_values[0], LuaValue::Int(15)));
1614 }
1615}