use crate::frontend::span::Span;
use crate::version::LuaVersion;
#[derive(Clone, PartialEq, Debug)]
pub enum Token {
And,
Break,
Do,
Else,
Elseif,
End,
False,
For,
Function,
Global,
Goto,
If,
In,
Local,
Nil,
Not,
Or,
Repeat,
Return,
Then,
True,
Until,
While,
Plus,
Minus,
Star,
Slash,
DSlash,
Percent,
Caret,
Hash,
Amp,
Tilde,
Pipe,
Shl,
Shr,
Eq,
Ne,
Le,
Ge,
Lt,
Gt,
Assign,
LParen,
RParen,
LBrace,
RBrace,
LBracket,
RBracket,
DColon,
Semi,
Colon,
Comma,
Dot,
Concat,
Ellipsis,
Int(
i64,
),
Float(
f64,
),
Str(
Vec<u8>,
),
Name(
Box<str>,
),
At,
MacroBraceOpen,
MacroBraceClose,
MacroQuote(
Box<[TokenInfo]>,
),
Eof,
}
impl Token {
pub fn describe(&self, src: &[u8], span: Span, version: LuaVersion) -> String {
String::from_utf8_lossy(&self.near_bytes(src, span, version)).into_owned()
}
pub(crate) fn near_bytes(&self, src: &[u8], span: Span, version: LuaVersion) -> Vec<u8> {
match self {
Token::Eof => near_text(version, Near::Eof),
Token::Str(content) => {
let raw = span.slice(src);
let mut text = Vec::with_capacity(content.len() + 4);
if raw.first() == Some(&b'[') {
let level = raw[1..].iter().take_while(|&&c| c == b'=').count();
text.push(b'[');
text.extend(std::iter::repeat_n(b'=', level));
text.push(b'[');
text.extend_from_slice(content);
text.push(b']');
text.extend(std::iter::repeat_n(b'=', level));
text.push(b']');
} else {
text.push(raw[0]);
text.extend_from_slice(content);
text.push(raw[0]);
}
near_text(version, Near::Text(&text))
}
Token::Name(_) | Token::Int(_) | Token::Float(_) => {
near_text(version, Near::Text(span.slice(src)))
}
_ => near_text(version, Near::Fixed(span.slice(src))),
}
}
}
#[derive(Clone, Copy)]
pub(crate) enum Near<'a> {
Eof,
Char(u8),
Fixed(&'a [u8]),
Text(&'a [u8]),
}
pub(crate) fn near_text(version: LuaVersion, near: Near<'_>) -> Vec<u8> {
let mut out = Vec::new();
let quoted = |out: &mut Vec<u8>, s: &[u8]| {
out.push(b'\'');
out.extend_from_slice(s);
out.push(b'\'');
};
if version <= LuaVersion::Lua51 {
let inner: Vec<u8> = match near {
Near::Eof => b"<eof>".to_vec(),
Near::Char(c) if c.is_ascii_control() => format!("char({c})").into_bytes(),
Near::Char(c) => vec![c],
Near::Fixed(s) | Near::Text(s) => c_str(s).to_vec(),
};
quoted(&mut out, &inner);
return out;
}
match near {
Near::Eof => out.extend_from_slice(b"<eof>"),
Near::Char(c) if (0x20..0x7f).contains(&c) => quoted(&mut out, &[c]),
Near::Char(c) if version == LuaVersion::Lua52 => {
out.extend_from_slice(format!("char({c})").as_bytes())
}
Near::Char(c) => quoted(&mut out, format!("<\\{c}>").as_bytes()),
Near::Fixed(s) | Near::Text(s) => quoted(&mut out, c_str(s)),
}
out
}
fn c_str(s: &[u8]) -> &[u8] {
&s[..s.iter().position(|&c| c == 0).unwrap_or(s.len())]
}
#[derive(Clone, Debug, PartialEq)]
pub struct TokenInfo {
pub tok: Token,
pub span: Span,
pub line: u32,
}