luna_core/frontend/error.rs
1//! Syntax-error type produced by the lexer and parser.
2
3use std::fmt;
4
5/// Syntax error, formatted PUC-style: `chunkname:line: msg near 'tok'`.
6/// The `near` part is already baked into `msg` at construction time.
7///
8/// `msg` is a raw byte string — PUC 5.1 reports `near '\xff'`-style errors
9/// with the offending source byte verbatim, and `errors.lua` 5.1 :20 grep-
10/// matches that pattern. Carrying the message as `Vec<u8>` lets the lexer
11/// emit those bytes without UTF-8 enforcement getting in the way.
12#[derive(Clone, PartialEq, Eq, Debug)]
13pub struct SyntaxError {
14 /// 1-based source line where the error was detected. `0` marks a
15 /// message that PUC raises without a position prefix (e.g. 5.4's
16 /// "C stack overflow" while parsing, or a memory error).
17 pub line: u32,
18 /// Message bytes (PUC-style; may contain non-UTF-8 source bytes).
19 pub msg: Vec<u8>,
20}
21
22impl SyntaxError {
23 /// Build a `SyntaxError` at the given line with the given message bytes.
24 pub fn new(line: u32, msg: impl Into<Vec<u8>>) -> Self {
25 SyntaxError {
26 line,
27 msg: msg.into(),
28 }
29 }
30
31 /// An error PUC reports with no `chunk:line:` prefix.
32 pub fn unpositioned(msg: impl Into<Vec<u8>>) -> Self {
33 SyntaxError {
34 line: 0,
35 msg: msg.into(),
36 }
37 }
38
39 /// The complete message as `load` returns it: `<chunkid>:<line>: msg`,
40 /// or the bare message for an unpositioned error. `chunkid` is the
41 /// chunk name already rendered by `luaO_chunkid`.
42 pub fn render(&self, chunkid: &[u8]) -> Vec<u8> {
43 if self.line == 0 {
44 return self.msg.clone();
45 }
46 let mut out = chunkid.to_vec();
47 out.push(b':');
48 out.extend_from_slice(self.line.to_string().as_bytes());
49 out.extend_from_slice(b": ");
50 out.extend_from_slice(&self.msg);
51 out
52 }
53
54 /// Lossy `&str` for Rust-side display (PUC `luaG_addinfo` only cares
55 /// about the bytes; this is for unit tests / panic messages).
56 pub fn msg_str(&self) -> std::borrow::Cow<'_, str> {
57 String::from_utf8_lossy(&self.msg)
58 }
59}
60
61impl fmt::Display for SyntaxError {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 if self.line == 0 {
64 return f.write_str(&self.msg_str());
65 }
66 write!(f, "{}: {}", self.line, self.msg_str())
67 }
68}
69
70impl std::error::Error for SyntaxError {}