1use crate::value::{Global, Key, Table, Value};
14use std::collections::HashMap;
15
16#[derive(Default)]
18pub struct LoadOptions {
19 pub safe: Option<bool>,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum LoadError {
31 UnsafeCall,
33 UnexpectedByte {
35 byte: Option<u8>,
37 pos: usize,
39 },
40 Unterminated {
42 what: &'static str,
44 },
45 UnknownVariable(String),
47 BadNumber(String),
49 Syntax(String),
51}
52
53impl std::fmt::Display for LoadError {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 LoadError::UnsafeCall => write!(f, "cannot call functions"),
57 LoadError::UnexpectedByte { byte, pos } => {
58 write!(f, "unexpected byte {byte:?} at {pos}")
59 }
60 LoadError::Unterminated { what } => write!(f, "unterminated {what}"),
61 LoadError::UnknownVariable(name) => write!(f, "unknown variable {name}"),
62 LoadError::BadNumber(text) => write!(f, "bad number {text}"),
63 LoadError::Syntax(msg) => write!(f, "{msg}"),
64 }
65 }
66}
67
68impl std::error::Error for LoadError {}
69
70pub fn load(data: &str, opts: &LoadOptions) -> Result<Value, LoadError> {
81 let safe = opts.safe != Some(false);
82 let mut p = Parser::new(data, safe);
83 p.parse_chunk()
84}
85
86struct Parser<'a> {
87 src: &'a [u8],
88 pos: usize,
89 safe: bool,
90 locals: HashMap<String, Value>,
92 global_ids: HashMap<String, usize>,
94 next_global: usize,
95}
96
97impl<'a> Parser<'a> {
98 fn new(src: &'a str, safe: bool) -> Self {
99 Parser {
100 src: src.as_bytes(),
101 pos: 0,
102 safe,
103 locals: HashMap::new(),
104 global_ids: HashMap::new(),
105 next_global: 1,
106 }
107 }
108
109 fn global_value(&mut self, name: String) -> Value {
113 let id = *self.global_ids.entry(name.clone()).or_insert_with(|| {
114 let id = self.next_global;
115 self.next_global += 1;
116 id
117 });
118 Value::Global(Global { name, id })
119 }
120
121 fn parse_chunk(&mut self) -> Result<Value, LoadError> {
122 self.skip_ws();
123 let v = if self.looking_at("do") {
124 self.parse_do_block()?
125 } else {
126 self.parse_expr()?
127 };
128 self.skip_comment();
129 self.skip_ws();
130 if let Some(byte) = self.peek() {
131 return Err(LoadError::UnexpectedByte {
132 byte: Some(byte),
133 pos: self.pos,
134 });
135 }
136 Ok(v)
137 }
138
139 fn parse_do_block(&mut self) -> Result<Value, LoadError> {
144 self.expect_word("do")?;
145 self.skip_ws();
146 loop {
147 self.skip_ws();
148 if self.looking_at("return") || self.at_end_keyword() {
149 break;
150 }
151 if self.looking_at("local") {
152 self.parse_local()?;
153 } else {
154 self.parse_statement()?;
155 }
156 self.skip_sep();
157 }
158 let mut val = Value::Nil;
159 if self.looking_at("return") {
160 self.expect_word("return")?;
161 self.skip_ws();
162 let name = self.parse_name()?;
163 val = self
164 .locals
165 .get(&name)
166 .cloned()
167 .ok_or(LoadError::UnknownVariable(name))?;
168 self.skip_sep();
169 }
170 self.skip_ws();
171 self.expect_word("end")?;
172 Ok(val)
173 }
174
175 fn at_end_keyword(&self) -> bool {
178 if !self.looking_at("end") {
179 return false;
180 }
181 match self.src.get(self.pos + 3) {
182 None => true,
183 Some(b) => !(b.is_ascii_alphanumeric() || *b == b'_'),
184 }
185 }
186
187 fn parse_local(&mut self) -> Result<(), LoadError> {
189 self.expect_word("local")?;
190 self.skip_ws();
191 let name = self.parse_name()?;
192 self.skip_ws();
193 if self.peek() == Some(b'=') {
194 self.pos += 1;
195 let v = self.parse_expr()?;
196 self.locals.insert(name, v);
197 } else {
198 self.locals.insert(name, Value::Nil);
199 }
200 Ok(())
201 }
202
203 fn parse_statement(&mut self) -> Result<(), LoadError> {
207 let base = self.parse_name()?;
209 let known_local = matches!(self.locals.get(&base), Some(Value::Table(_)));
210 if !known_local {
211 return self.consume_sandbox_statement();
212 }
213 let mut table = match self.locals.get(&base) {
214 Some(Value::Table(t)) => t.clone(),
215 _ => unreachable!("checked known_local"),
216 };
217 let mut pending_key: Option<Key> = None;
218 loop {
219 self.skip_ws();
220 match self.peek() {
221 Some(b'[') => {
222 self.pos += 1;
223 let k = self.parse_expr()?;
224 self.skip_ws();
225 self.expect_byte(b']')?;
226 if let Some(prev) = pending_key.take() {
227 table = self.descend(&table, prev)?;
228 }
229 pending_key = Some(value_to_key(k)?);
230 }
231 Some(b'.') => {
232 self.pos += 1;
233 let field = self.parse_name()?;
234 if let Some(prev) = pending_key.take() {
235 table = self.descend(&table, prev)?;
236 }
237 pending_key = Some(Key::Str(field.into_bytes()));
238 }
239 _ => break,
240 }
241 }
242 self.skip_ws();
243 self.expect_byte(b'=')?;
244 let rhs = self.parse_expr()?;
245 let key = pending_key.ok_or(LoadError::Syntax("assignment with no key".to_string()))?;
246 table.set(key, rhs);
247 Ok(())
248 }
249
250 fn consume_sandbox_statement(&mut self) -> Result<(), LoadError> {
254 loop {
256 self.skip_ws();
257 match self.peek() {
258 Some(b'.') => {
259 self.pos += 1;
260 let _ = self.parse_name();
261 }
262 Some(b'[') => {
263 self.pos += 1;
264 let _ = self.parse_expr()?;
265 self.skip_ws();
266 self.expect_byte(b']')?;
267 }
268 _ => break,
269 }
270 }
271 self.skip_ws();
272 match self.peek() {
273 Some(b'(') => {
274 if self.safe {
275 return Err(LoadError::UnsafeCall);
276 }
277 self.skip_balanced_parens()?;
278 Ok(())
279 }
280 Some(b'=') if self.peek_at(1) != Some(b'=') => {
281 self.pos += 1;
282 let _ = self.parse_expr()?;
284 Ok(())
285 }
286 _ => Ok(()),
287 }
288 }
289
290 fn descend(&self, table: &Table, key: Key) -> Result<Table, LoadError> {
292 match table.get(&key) {
293 Some(Value::Table(t)) => Ok(t),
294 _ => Err(LoadError::Syntax("path does not reach a table".to_string())),
295 }
296 }
297
298 fn parse_expr(&mut self) -> Result<Value, LoadError> {
299 self.skip_ws();
300 match self.peek() {
301 Some(b'{') => self.parse_table(),
302 Some(b'"') | Some(b'\'') => self.parse_string(),
303 Some(b'-') | Some(b'0'..=b'9') => self.parse_number_or_special(),
304 Some(c) if c.is_ascii_alphabetic() || c == b'_' => self.parse_word_expr(),
305 other => Err(LoadError::UnexpectedByte {
306 byte: other,
307 pos: self.pos,
308 }),
309 }
310 }
311
312 fn parse_word_expr(&mut self) -> Result<Value, LoadError> {
315 let word = self.parse_name()?;
316 match word.as_str() {
317 "nil" => return Ok(Value::Nil),
318 "true" => return Ok(Value::Bool(true)),
319 "false" => return Ok(Value::Bool(false)),
320 "function" => return self.parse_function_stub(),
321 _ => {}
322 }
323
324 if let Some(base) = self.locals.get(&word).cloned() {
325 return self.follow_access(base);
328 }
329
330 let mut name = word;
333 self.skip_ws();
334 while matches!(self.peek(), Some(b'.') | Some(b'[')) {
335 if self.peek() == Some(b'.') {
336 self.pos += 1;
337 let field = self.parse_name()?;
338 name.push('.');
339 name.push_str(&field);
340 } else {
341 self.pos += 1;
342 let _ = self.parse_expr();
343 self.skip_ws();
344 let _ = self.expect_byte(b']');
345 name.push_str("[]");
346 }
347 self.skip_ws();
348 }
349 if self.peek() == Some(b'(') {
350 if self.safe {
351 return Err(LoadError::UnsafeCall);
352 }
353 self.skip_balanced_parens()?;
354 return Ok(Value::Nil);
355 }
356 Ok(self.global_value(name))
358 }
359
360 fn follow_access(&mut self, mut cur: Value) -> Result<Value, LoadError> {
362 loop {
363 self.skip_ws();
364 let key = match self.peek() {
365 Some(b'.') => {
366 self.pos += 1;
367 Key::Str(self.parse_name()?.into_bytes())
368 }
369 Some(b'[') => {
370 self.pos += 1;
371 let k = self.parse_expr()?;
372 self.skip_ws();
373 self.expect_byte(b']')?;
374 value_to_key(k)?
375 }
376 _ => return Ok(cur),
377 };
378 let next = match &cur {
379 Value::Table(t) => t.get(&key).unwrap_or(Value::Nil),
380 _ => Value::Nil,
381 };
382 cur = next;
383 }
384 }
385
386 fn parse_function_stub(&mut self) -> Result<Value, LoadError> {
390 self.skip_ws();
392 if self.peek() == Some(b'(') {
393 self.skip_balanced_parens()?;
394 }
395 let mut depth = 1;
397 while depth > 0 {
398 self.skip_ws();
399 if self.at_end_keyword() {
400 self.pos += 3;
401 depth -= 1;
402 } else if self.looking_at("function") || self.looking_at("do") || self.looking_at("if")
403 {
404 let _ = self.parse_name();
406 } else if self.pos < self.src.len() {
407 self.pos += 1;
408 } else {
409 break;
410 }
411 }
412 Ok(Value::Nil)
413 }
414
415 fn skip_balanced_parens(&mut self) -> Result<(), LoadError> {
416 self.expect_byte(b'(')?;
417 let mut depth = 1;
418 while depth > 0 {
419 match self.next_byte() {
420 Some(b'(') => depth += 1,
421 Some(b')') => depth -= 1,
422 Some(_) => {}
423 None => return Err(LoadError::Syntax("unbalanced parentheses".to_string())),
424 }
425 }
426 Ok(())
427 }
428
429 fn parse_table(&mut self) -> Result<Value, LoadError> {
430 self.expect_byte(b'{')?;
431 let table = Table::new();
432 let mut array_idx = 1u64;
433 loop {
434 self.skip_comment();
437 self.skip_ws();
438 match self.peek() {
439 Some(b'}') => {
440 self.pos += 1;
441 break;
442 }
443 None => return Err(LoadError::Unterminated { what: "table" }),
444 _ => {}
445 }
446 if self.peek() == Some(b'[') {
448 self.pos += 1;
449 let key = self.parse_expr()?;
450 self.skip_ws();
451 self.expect_byte(b']')?;
452 self.skip_ws();
453 self.expect_byte(b'=')?;
454 let val = self.parse_expr()?;
455 table.set(value_to_key(key)?, val);
456 } else if let Some(name) = self.try_named_field()? {
457 let val = self.parse_expr()?;
458 table.set(Key::Str(name.into_bytes()), val);
459 } else {
460 let val = self.parse_expr()?;
461 table.set(Key::Number(array_idx as f64), val);
462 array_idx += 1;
463 }
464 self.skip_comment();
467 self.skip_ws();
468 match self.peek() {
469 Some(b',') | Some(b';') => {
470 self.pos += 1;
471 }
472 Some(b'}') => {}
473 other => {
474 return Err(LoadError::Syntax(format!(
475 "expected , or }} in table, got {other:?}"
476 )))
477 }
478 }
479 }
480 Ok(Value::Table(table))
481 }
482
483 fn try_named_field(&mut self) -> Result<Option<String>, LoadError> {
487 let save = self.pos;
488 match self.peek() {
489 Some(c) if c.is_ascii_alphabetic() || c == b'_' => {}
490 _ => return Ok(None),
491 }
492 let name = self.parse_name()?;
493 self.skip_ws();
494 if self.peek() == Some(b'=') && self.peek_at(1) != Some(b'=') {
495 self.pos += 1;
496 Ok(Some(name))
497 } else {
498 self.pos = save;
499 Ok(None)
500 }
501 }
502
503 fn parse_string(&mut self) -> Result<Value, LoadError> {
504 let quote = self
505 .next_byte()
506 .ok_or(LoadError::Unterminated { what: "string" })?;
507 let mut bytes = Vec::new();
508 loop {
509 match self.next_byte() {
510 None => return Err(LoadError::Unterminated { what: "string" }),
511 Some(b) if b == quote => break,
512 Some(b'\\') => {
513 let e = self
514 .next_byte()
515 .ok_or(LoadError::Unterminated { what: "string" })?;
516 match e {
517 b'n' => bytes.push(b'\n'),
518 b'r' => bytes.push(b'\r'),
519 b't' => bytes.push(b'\t'),
520 b'a' => bytes.push(7),
521 b'b' => bytes.push(8),
522 b'f' => bytes.push(12),
523 b'v' => bytes.push(11),
524 b'\\' => bytes.push(b'\\'),
525 b'"' => bytes.push(b'"'),
526 b'\'' => bytes.push(b'\''),
527 b'\n' => bytes.push(b'\n'),
528 b'0'..=b'9' => {
529 let mut n = (e - b'0') as u32;
531 for _ in 0..2 {
532 if let Some(d @ b'0'..=b'9') = self.peek() {
533 n = n * 10 + (d - b'0') as u32;
534 self.pos += 1;
535 } else {
536 break;
537 }
538 }
539 bytes.push(n as u8);
540 }
541 other => bytes.push(other),
542 }
543 }
544 Some(b) => bytes.push(b),
545 }
546 }
547 Ok(Value::Str(bytes))
548 }
549
550 fn parse_number_or_special(&mut self) -> Result<Value, LoadError> {
552 let start = self.pos;
553 if self.peek() == Some(b'-') {
554 self.pos += 1;
555 }
556 while matches!(
557 self.peek(),
558 Some(b'0'..=b'9')
559 | Some(b'.')
560 | Some(b'e')
561 | Some(b'E')
562 | Some(b'+')
563 | Some(b'-')
564 | Some(b'x')
565 | Some(b'X')
566 | Some(b'a'..=b'f')
567 | Some(b'A'..=b'F')
568 | Some(b'p')
569 | Some(b'P')
570 ) {
571 self.pos += 1;
572 }
573 let text = std::str::from_utf8(&self.src[start..self.pos])
574 .map_err(|_| LoadError::BadNumber("bad bytes".to_string()))?;
575 self.skip_ws();
577 if self.peek() == Some(b'/') {
578 self.pos += 1;
579 self.skip_ws();
580 let dstart = self.pos;
581 if self.peek() == Some(b'-') {
582 self.pos += 1;
583 }
584 while matches!(self.peek(), Some(b'0'..=b'9') | Some(b'.')) {
585 self.pos += 1;
586 }
587 let denom = std::str::from_utf8(&self.src[dstart..self.pos]).unwrap_or("");
588 let num: f64 = text
589 .trim()
590 .parse()
591 .map_err(|_| LoadError::BadNumber(text.trim().to_string()))?;
592 let den: f64 = denom
593 .trim()
594 .parse()
595 .map_err(|_| LoadError::BadNumber(denom.trim().to_string()))?;
596 self.skip_comment();
597 return Ok(Value::Number(num / den));
598 }
599 let n: f64 = if let Some(hex) = text.strip_prefix("0x").or_else(|| text.strip_prefix("0X"))
600 {
601 i64::from_str_radix(hex, 16)
602 .map(|v| v as f64)
603 .map_err(|_| LoadError::BadNumber(text.to_string()))?
604 } else {
605 text.parse()
606 .map_err(|_| LoadError::BadNumber(text.to_string()))?
607 };
608 self.skip_comment();
609 Ok(Value::Number(n))
610 }
611
612 fn parse_name(&mut self) -> Result<String, LoadError> {
613 self.skip_ws();
614 let start = self.pos;
615 match self.peek() {
616 Some(c) if c.is_ascii_alphabetic() || c == b'_' => {}
617 _ => return Err(LoadError::Syntax(format!("expected name at {}", self.pos))),
618 }
619 while matches!(self.peek(), Some(c) if c.is_ascii_alphanumeric() || c == b'_') {
620 self.pos += 1;
621 }
622 Ok(String::from_utf8_lossy(&self.src[start..self.pos]).into_owned())
623 }
624
625 fn skip_comment(&mut self) {
626 self.skip_ws();
627 if self.looking_at("--[[") {
628 self.pos += 4;
629 while self.pos < self.src.len() && !self.looking_at("]]") {
630 self.pos += 1;
631 }
632 if self.looking_at("]]") {
633 self.pos += 2;
634 }
635 }
636 }
637
638 fn skip_sep(&mut self) {
639 self.skip_ws();
640 while matches!(self.peek(), Some(b';') | Some(b'\n')) {
641 self.pos += 1;
642 self.skip_ws();
643 }
644 }
645
646 fn skip_ws(&mut self) {
647 loop {
648 match self.peek() {
649 Some(b) if b == b' ' || b == b'\t' || b == b'\r' || b == b'\n' => {
650 self.pos += 1;
651 }
652 _ => break,
653 }
654 }
655 }
656
657 fn looking_at(&self, s: &str) -> bool {
658 self.src[self.pos..].starts_with(s.as_bytes())
659 }
660
661 fn expect_word(&mut self, w: &str) -> Result<(), LoadError> {
662 self.skip_ws();
663 if self.looking_at(w) {
664 self.pos += w.len();
665 Ok(())
666 } else {
667 Err(LoadError::Syntax(format!("expected {w}")))
668 }
669 }
670
671 fn expect_byte(&mut self, b: u8) -> Result<(), LoadError> {
672 self.skip_ws();
673 if self.peek() == Some(b) {
674 self.pos += 1;
675 Ok(())
676 } else {
677 Err(LoadError::Syntax(format!("expected {}", b as char)))
678 }
679 }
680
681 fn peek(&self) -> Option<u8> {
682 self.src.get(self.pos).copied()
683 }
684
685 fn peek_at(&self, off: usize) -> Option<u8> {
686 self.src.get(self.pos + off).copied()
687 }
688
689 fn next_byte(&mut self) -> Option<u8> {
690 let b = self.src.get(self.pos).copied();
691 if b.is_some() {
692 self.pos += 1;
693 }
694 b
695 }
696}
697
698fn value_to_key(v: Value) -> Result<Key, LoadError> {
699 match v {
700 Value::Bool(b) => Ok(Key::Bool(b)),
701 Value::Number(n) => Ok(Key::Number(n)),
702 Value::Str(s) => Ok(Key::Str(s)),
703 Value::Table(t) => Ok(Key::Table(t)),
704 Value::Function(f) => Ok(Key::Function(f)),
705 Value::Global(g) => Ok(Key::Global(g)),
706 Value::Nil => Err(LoadError::Syntax("nil is not a valid key".to_string())),
707 }
708}