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_keyword("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_keyword("return") || self.at_end_keyword() {
149 break;
150 }
151 if self.looking_at_keyword("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_keyword("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 self.looking_at_keyword("end")
179 }
180
181 fn parse_local(&mut self) -> Result<(), LoadError> {
183 self.expect_word("local")?;
184 self.skip_ws();
185 let name = self.parse_name()?;
186 self.skip_ws();
187 if self.peek() == Some(b'=') {
188 self.pos += 1;
189 let v = self.parse_expr()?;
190 self.locals.insert(name, v);
191 } else {
192 self.locals.insert(name, Value::Nil);
193 }
194 Ok(())
195 }
196
197 fn parse_statement(&mut self) -> Result<(), LoadError> {
201 let base = self.parse_name()?;
203 let known_local = matches!(self.locals.get(&base), Some(Value::Table(_)));
204 if !known_local {
205 return self.consume_sandbox_statement();
206 }
207 let mut table = match self.locals.get(&base) {
208 Some(Value::Table(t)) => t.clone(),
209 _ => unreachable!("checked known_local"),
210 };
211 let mut pending_key: Option<Key> = None;
212 loop {
213 self.skip_ws();
214 match self.peek() {
215 Some(b'[') => {
216 self.pos += 1;
217 let k = self.parse_expr()?;
218 self.skip_ws();
219 self.expect_byte(b']')?;
220 if let Some(prev) = pending_key.take() {
221 table = self.descend(&table, prev)?;
222 }
223 pending_key = Some(value_to_key(k)?);
224 }
225 Some(b'.') => {
226 self.pos += 1;
227 let field = self.parse_name()?;
228 if let Some(prev) = pending_key.take() {
229 table = self.descend(&table, prev)?;
230 }
231 pending_key = Some(Key::Str(field.into_bytes()));
232 }
233 _ => break,
234 }
235 }
236 self.skip_ws();
237 self.expect_byte(b'=')?;
238 let rhs = self.parse_expr()?;
239 let key = pending_key.ok_or(LoadError::Syntax("assignment with no key".to_string()))?;
240 table.set(key, rhs);
241 Ok(())
242 }
243
244 fn consume_sandbox_statement(&mut self) -> Result<(), LoadError> {
248 loop {
250 self.skip_ws();
251 match self.peek() {
252 Some(b'.') => {
253 self.pos += 1;
254 let _ = self.parse_name();
255 }
256 Some(b'[') => {
257 self.pos += 1;
258 let _ = self.parse_expr()?;
259 self.skip_ws();
260 self.expect_byte(b']')?;
261 }
262 _ => break,
263 }
264 }
265 self.skip_ws();
266 match self.peek() {
267 Some(b'(') => {
268 if self.safe {
269 return Err(LoadError::UnsafeCall);
270 }
271 self.skip_balanced_parens()?;
272 Ok(())
273 }
274 Some(b'=') if self.peek_at(1) != Some(b'=') => {
275 self.pos += 1;
276 let _ = self.parse_expr()?;
278 Ok(())
279 }
280 _ => Ok(()),
281 }
282 }
283
284 fn descend(&self, table: &Table, key: Key) -> Result<Table, LoadError> {
286 match table.get(&key) {
287 Some(Value::Table(t)) => Ok(t),
288 _ => Err(LoadError::Syntax("path does not reach a table".to_string())),
289 }
290 }
291
292 fn parse_expr(&mut self) -> Result<Value, LoadError> {
293 self.skip_ws();
294 match self.peek() {
295 Some(b'{') => self.parse_table(),
296 Some(b'"') | Some(b'\'') => self.parse_string(),
297 Some(b'-') | Some(b'0'..=b'9') => self.parse_number_or_special(),
298 Some(c) if c.is_ascii_alphabetic() || c == b'_' => self.parse_word_expr(),
299 other => Err(LoadError::UnexpectedByte {
300 byte: other,
301 pos: self.pos,
302 }),
303 }
304 }
305
306 fn parse_word_expr(&mut self) -> Result<Value, LoadError> {
309 let word = self.parse_name()?;
310 match word.as_str() {
311 "nil" => return Ok(Value::Nil),
312 "true" => return Ok(Value::Bool(true)),
313 "false" => return Ok(Value::Bool(false)),
314 "function" => return self.parse_function_stub(),
315 _ => {}
316 }
317
318 if let Some(base) = self.locals.get(&word).cloned() {
319 return self.follow_access(base);
322 }
323
324 let mut name = word;
327 self.skip_ws();
328 while matches!(self.peek(), Some(b'.') | Some(b'[')) {
329 if self.peek() == Some(b'.') {
330 self.pos += 1;
331 let field = self.parse_name()?;
332 name.push('.');
333 name.push_str(&field);
334 } else {
335 self.pos += 1;
336 self.parse_expr()?;
337 self.skip_ws();
338 self.expect_byte(b']')?;
339 name.push_str("[]");
340 }
341 self.skip_ws();
342 }
343 if self.peek() == Some(b'(') {
344 if self.safe {
345 return Err(LoadError::UnsafeCall);
346 }
347 self.skip_balanced_parens()?;
348 return Ok(Value::Nil);
349 }
350 Ok(self.global_value(name))
352 }
353
354 fn follow_access(&mut self, mut cur: Value) -> Result<Value, LoadError> {
356 loop {
357 self.skip_ws();
358 let key = match self.peek() {
359 Some(b'.') => {
360 self.pos += 1;
361 Key::Str(self.parse_name()?.into_bytes())
362 }
363 Some(b'[') => {
364 self.pos += 1;
365 let k = self.parse_expr()?;
366 self.skip_ws();
367 self.expect_byte(b']')?;
368 value_to_key(k)?
369 }
370 _ => return Ok(cur),
371 };
372 let next = match &cur {
373 Value::Table(t) => t.get(&key).unwrap_or(Value::Nil),
374 _ => Value::Nil,
375 };
376 cur = next;
377 }
378 }
379
380 fn parse_function_stub(&mut self) -> Result<Value, LoadError> {
384 self.skip_ws();
386 if self.peek() == Some(b'(') {
387 self.skip_balanced_parens()?;
388 }
389 let mut depth = 1;
391 while depth > 0 {
392 self.skip_ws();
393 match self.peek() {
394 None => return Err(LoadError::Syntax("expected end".to_string())),
395 Some(b'-') if self.peek_at(1) == Some(b'-') => {
396 self.skip_function_body_comment();
397 }
398 Some(b'[') => {
399 if !self.skip_long_bracket() {
400 self.pos += 1;
401 }
402 }
403 Some(quote @ (b'"' | b'\'')) => {
404 self.pos += 1;
405 self.skip_quoted_bytes(quote)?;
406 }
407 Some(c) if is_ident_start(c) => {
408 let word = self.parse_name()?;
409 match word.as_str() {
410 "end" => depth -= 1,
411 "function" | "do" | "if" => depth += 1,
412 _ => {}
413 }
414 }
415 Some(_) => {
416 self.pos += 1;
417 }
418 }
419 }
420 Ok(Value::Nil)
421 }
422
423 fn skip_balanced_parens(&mut self) -> Result<(), LoadError> {
424 self.expect_byte(b'(')?;
425 let mut depth = 1;
426 while depth > 0 {
427 match self.next_byte() {
428 Some(b'"') => self.skip_quoted_bytes(b'"')?,
429 Some(b'\'') => self.skip_quoted_bytes(b'\'')?,
430 Some(b'(') => depth += 1,
431 Some(b')') => depth -= 1,
432 Some(_) => {}
433 None => return Err(LoadError::Syntax("unbalanced parentheses".to_string())),
434 }
435 }
436 Ok(())
437 }
438
439 fn parse_table(&mut self) -> Result<Value, LoadError> {
440 self.expect_byte(b'{')?;
441 let table = Table::new();
442 let mut array_idx = 1u64;
443 loop {
444 self.skip_comment();
447 self.skip_ws();
448 match self.peek() {
449 Some(b'}') => {
450 self.pos += 1;
451 break;
452 }
453 None => return Err(LoadError::Unterminated { what: "table" }),
454 _ => {}
455 }
456 if self.peek() == Some(b'[') {
458 self.pos += 1;
459 let key = self.parse_expr()?;
460 self.skip_ws();
461 self.expect_byte(b']')?;
462 self.skip_ws();
463 self.expect_byte(b'=')?;
464 let val = self.parse_expr()?;
465 table.set(value_to_key(key)?, val);
466 } else if let Some(name) = self.try_named_field()? {
467 let val = self.parse_expr()?;
468 table.set(Key::Str(name.into_bytes()), val);
469 } else {
470 let val = self.parse_expr()?;
471 table.set(Key::Number(array_idx as f64), val);
472 array_idx += 1;
473 }
474 self.skip_comment();
477 self.skip_ws();
478 match self.peek() {
479 Some(b',') | Some(b';') => {
480 self.pos += 1;
481 }
482 Some(b'}') => {}
483 other => {
484 return Err(LoadError::Syntax(format!(
485 "expected , or }} in table, got {other:?}"
486 )))
487 }
488 }
489 }
490 Ok(Value::Table(table))
491 }
492
493 fn try_named_field(&mut self) -> Result<Option<String>, LoadError> {
497 let save = self.pos;
498 match self.peek() {
499 Some(c) if c.is_ascii_alphabetic() || c == b'_' => {}
500 _ => return Ok(None),
501 }
502 let name = self.parse_name()?;
503 self.skip_ws();
504 if self.peek() == Some(b'=') && self.peek_at(1) != Some(b'=') {
505 self.pos += 1;
506 Ok(Some(name))
507 } else {
508 self.pos = save;
509 Ok(None)
510 }
511 }
512
513 fn parse_string(&mut self) -> Result<Value, LoadError> {
514 let quote = self
515 .next_byte()
516 .ok_or(LoadError::Unterminated { what: "string" })?;
517 let mut bytes = Vec::new();
518 loop {
519 match self.next_byte() {
520 None => return Err(LoadError::Unterminated { what: "string" }),
521 Some(b) if b == quote => break,
522 Some(b'\\') => {
523 let e = self
524 .next_byte()
525 .ok_or(LoadError::Unterminated { what: "string" })?;
526 match e {
527 b'n' => bytes.push(b'\n'),
528 b'r' => bytes.push(b'\r'),
529 b't' => bytes.push(b'\t'),
530 b'a' => bytes.push(7),
531 b'b' => bytes.push(8),
532 b'f' => bytes.push(12),
533 b'v' => bytes.push(11),
534 b'\\' => bytes.push(b'\\'),
535 b'"' => bytes.push(b'"'),
536 b'\'' => bytes.push(b'\''),
537 b'\n' => bytes.push(b'\n'),
538 b'0'..=b'9' => {
539 let mut n = (e - b'0') as u32;
541 for _ in 0..2 {
542 if let Some(d @ b'0'..=b'9') = self.peek() {
543 n = n * 10 + (d - b'0') as u32;
544 self.pos += 1;
545 } else {
546 break;
547 }
548 }
549 bytes.push(n as u8);
550 }
551 other => bytes.push(other),
552 }
553 }
554 Some(b) => bytes.push(b),
555 }
556 }
557 Ok(Value::Str(bytes))
558 }
559
560 fn parse_number_or_special(&mut self) -> Result<Value, LoadError> {
562 let start = self.pos;
563 if self.peek() == Some(b'-') {
564 self.pos += 1;
565 }
566 while matches!(
567 self.peek(),
568 Some(b'0'..=b'9')
569 | Some(b'.')
570 | Some(b'e')
571 | Some(b'E')
572 | Some(b'+')
573 | Some(b'-')
574 | Some(b'x')
575 | Some(b'X')
576 | Some(b'a'..=b'f')
577 | Some(b'A'..=b'F')
578 | Some(b'p')
579 | Some(b'P')
580 ) {
581 self.pos += 1;
582 }
583 let text = std::str::from_utf8(&self.src[start..self.pos])
584 .map_err(|_| LoadError::BadNumber("bad bytes".to_string()))?;
585 self.skip_ws();
587 if self.peek() == Some(b'/') {
588 self.pos += 1;
589 self.skip_ws();
590 let dstart = self.pos;
591 if self.peek() == Some(b'-') {
592 self.pos += 1;
593 }
594 while matches!(self.peek(), Some(b'0'..=b'9') | Some(b'.')) {
595 self.pos += 1;
596 }
597 let denom = std::str::from_utf8(&self.src[dstart..self.pos]).unwrap_or("");
598 let num: f64 = text
599 .trim()
600 .parse()
601 .map_err(|_| LoadError::BadNumber(text.trim().to_string()))?;
602 let den: f64 = denom
603 .trim()
604 .parse()
605 .map_err(|_| LoadError::BadNumber(denom.trim().to_string()))?;
606 self.skip_comment();
607 return Ok(Value::Number(num / den));
608 }
609 let n: f64 = if let Some(hex) = text.strip_prefix("0x").or_else(|| text.strip_prefix("0X"))
610 {
611 i64::from_str_radix(hex, 16)
612 .map(|v| v as f64)
613 .map_err(|_| LoadError::BadNumber(text.to_string()))?
614 } else {
615 text.parse()
616 .map_err(|_| LoadError::BadNumber(text.to_string()))?
617 };
618 self.skip_comment();
619 Ok(Value::Number(n))
620 }
621
622 fn parse_name(&mut self) -> Result<String, LoadError> {
623 self.skip_ws();
624 let start = self.pos;
625 match self.peek() {
626 Some(c) if c.is_ascii_alphabetic() || c == b'_' => {}
627 _ => return Err(LoadError::Syntax(format!("expected name at {}", self.pos))),
628 }
629 while matches!(self.peek(), Some(c) if c.is_ascii_alphanumeric() || c == b'_') {
630 self.pos += 1;
631 }
632 Ok(String::from_utf8_lossy(&self.src[start..self.pos]).into_owned())
633 }
634
635 fn skip_comment(&mut self) {
636 self.skip_ws();
637 if self.looking_at("--[[") {
638 self.pos += 4;
639 while self.pos < self.src.len() && !self.looking_at("]]") {
640 self.pos += 1;
641 }
642 if self.looking_at("]]") {
643 self.pos += 2;
644 }
645 }
646 }
647
648 fn skip_function_body_comment(&mut self) {
649 self.pos += 2;
650 let is_long_comment = self.peek() == Some(b'[') && self.skip_long_bracket();
651 if !is_long_comment {
652 while !matches!(self.peek(), None | Some(b'\n') | Some(b'\r')) {
653 self.pos += 1;
654 }
655 }
656 }
657
658 fn skip_long_bracket(&mut self) -> bool {
659 if self.peek() != Some(b'[') {
660 return false;
661 }
662 let mut close_len = 1;
663 while self.peek_at(close_len) == Some(b'=') {
664 close_len += 1;
665 }
666 if self.peek_at(close_len) != Some(b'[') {
667 return false;
668 }
669 self.pos += close_len + 1;
670 while self.pos < self.src.len() && !self.looking_at_long_bracket_close(close_len) {
671 self.pos += 1;
672 }
673 if self.looking_at_long_bracket_close(close_len) {
674 self.pos += close_len + 1;
675 }
676 true
677 }
678
679 fn looking_at_long_bracket_close(&self, len: usize) -> bool {
680 self.peek() == Some(b']')
681 && (1..len).all(|i| self.peek_at(i) == Some(b'='))
682 && self.peek_at(len) == Some(b']')
683 }
684
685 fn skip_sep(&mut self) {
686 self.skip_ws();
687 while matches!(self.peek(), Some(b';') | Some(b'\n')) {
688 self.pos += 1;
689 self.skip_ws();
690 }
691 }
692
693 fn skip_ws(&mut self) {
694 loop {
695 match self.peek() {
696 Some(b) if b == b' ' || b == b'\t' || b == b'\r' || b == b'\n' => {
697 self.pos += 1;
698 }
699 _ => break,
700 }
701 }
702 }
703
704 fn looking_at(&self, s: &str) -> bool {
705 self.src[self.pos..].starts_with(s.as_bytes())
706 }
707
708 fn looking_at_keyword(&self, s: &str) -> bool {
709 self.looking_at(s) && !matches!(self.peek_at(s.len()), Some(b) if is_ident_byte(b))
710 }
711
712 fn expect_word(&mut self, w: &str) -> Result<(), LoadError> {
713 self.skip_ws();
714 if self.looking_at_keyword(w) {
715 self.pos += w.len();
716 Ok(())
717 } else {
718 Err(LoadError::Syntax(format!("expected {w}")))
719 }
720 }
721
722 fn expect_byte(&mut self, b: u8) -> Result<(), LoadError> {
723 self.skip_ws();
724 if self.peek() == Some(b) {
725 self.pos += 1;
726 Ok(())
727 } else {
728 Err(LoadError::Syntax(format!("expected {}", b as char)))
729 }
730 }
731
732 fn peek(&self) -> Option<u8> {
733 self.src.get(self.pos).copied()
734 }
735
736 fn peek_at(&self, off: usize) -> Option<u8> {
737 self.src.get(self.pos + off).copied()
738 }
739
740 fn next_byte(&mut self) -> Option<u8> {
741 let b = self.src.get(self.pos).copied();
742 if b.is_some() {
743 self.pos += 1;
744 }
745 b
746 }
747
748 fn skip_quoted_bytes(&mut self, quote: u8) -> Result<(), LoadError> {
749 loop {
750 match self.next_byte() {
751 Some(b) if b == quote => return Ok(()),
752 Some(b'\\') => {
753 if self.next_byte().is_none() {
754 return Err(LoadError::Unterminated { what: "string" });
755 }
756 }
757 Some(_) => {}
758 None => return Err(LoadError::Unterminated { what: "string" }),
759 }
760 }
761 }
762}
763
764fn is_ident_byte(b: u8) -> bool {
765 b.is_ascii_alphanumeric() || b == b'_'
766}
767
768fn is_ident_start(b: u8) -> bool {
769 b.is_ascii_alphabetic() || b == b'_'
770}
771
772fn value_to_key(v: Value) -> Result<Key, LoadError> {
773 match v {
774 Value::Bool(b) => Ok(Key::Bool(b)),
775 Value::Number(n) => Ok(Key::Number(n)),
776 Value::Str(s) => Ok(Key::Str(s)),
777 Value::Table(t) => Ok(Key::Table(t)),
778 Value::Function(f) => Ok(Key::Function(f)),
779 Value::Global(g) => Ok(Key::Global(g)),
780 Value::Nil => Err(LoadError::Syntax("nil is not a valid key".to_string())),
781 }
782}