use std::io::Write as IoWrite;
use lua_types::gc::GcRef;
pub use lua_types::LuaError;
pub use lua_types::LuaString;
pub use lua_vm::state::LuaState;
pub use lua_vm::table::LuaTable;
pub struct LexBuffer {
buffer: Vec<u8>,
}
impl LexBuffer {
pub fn new() -> Self {
LexBuffer { buffer: Vec::new() }
}
pub fn len(&self) -> usize {
self.buffer.len()
}
pub fn capacity(&self) -> usize {
self.buffer.capacity()
}
pub fn as_slice(&self) -> &[u8] {
&self.buffer
}
pub fn clear(&mut self) {
self.buffer.clear();
}
pub fn truncate_by(&mut self, i: usize) {
let new_len = self.buffer.len().saturating_sub(i);
self.buffer.truncate(new_len);
}
pub fn resize(&mut self, _state: &mut LuaState, size: usize) -> Result<(), LuaError> {
if size < self.buffer.len() {
self.buffer.truncate(size);
}
if size > self.buffer.capacity() {
let extra = size - self.buffer.capacity();
self.buffer.reserve_exact(extra);
}
Ok(())
}
fn push_byte(&mut self, c: u8) {
self.buffer.push(c);
}
}
impl Default for LexBuffer {
fn default() -> Self {
Self::new()
}
}
pub struct ZIO {
reader: Box<dyn FnMut() -> Option<Vec<u8>>>,
n: usize,
p: usize,
current_chunk: Vec<u8>,
}
impl ZIO {
pub fn new(reader: Box<dyn FnMut() -> Option<Vec<u8>>>) -> Self {
ZIO { reader, n: 0, p: 0, current_chunk: Vec::new() }
}
pub fn from_bytes(bytes: Vec<u8>) -> Self {
let mut once = Some(bytes);
ZIO::new(Box::new(move || once.take()))
}
pub fn getc(&mut self) -> i32 {
if self.n > 0 {
self.n -= 1;
let b = self.current_chunk[self.p] as u8;
self.p += 1;
b as i32
} else {
self.fill()
}
}
fn fill(&mut self) -> i32 {
match (self.reader)() {
None => EOZ,
Some(chunk) if chunk.is_empty() => EOZ,
Some(chunk) => {
self.n = chunk.len() - 1;
self.current_chunk = chunk;
self.p = 0;
let b = self.current_chunk[self.p] as u8;
self.p += 1;
b as i32
}
}
}
}
pub const FIRST_RESERVED: i32 = 257;
pub const LUA_ENV: &[u8] = b"_ENV";
pub const NUM_RESERVED: usize = (TK_WHILE - FIRST_RESERVED + 1) as usize;
pub const EOZ: i32 = -1;
const MAX_SIZE: usize = if std::mem::size_of::<usize>() < std::mem::size_of::<i64>() {
usize::MAX
} else {
i64::MAX as usize
};
const LUA_MIN_BUFFER: usize = 32;
pub const TK_AND: i32 = 257;
pub const TK_BREAK: i32 = 258;
pub const TK_DO: i32 = 259;
pub const TK_ELSE: i32 = 260;
pub const TK_ELSEIF: i32 = 261;
pub const TK_END: i32 = 262;
pub const TK_FALSE: i32 = 263;
pub const TK_FOR: i32 = 264;
pub const TK_FUNCTION: i32 = 265;
pub const TK_GOTO: i32 = 266;
pub const TK_IF: i32 = 267;
pub const TK_IN: i32 = 268;
pub const TK_LOCAL: i32 = 269;
pub const TK_NIL: i32 = 270;
pub const TK_NOT: i32 = 271;
pub const TK_OR: i32 = 272;
pub const TK_REPEAT: i32 = 273;
pub const TK_RETURN: i32 = 274;
pub const TK_THEN: i32 = 275;
pub const TK_TRUE: i32 = 276;
pub const TK_UNTIL: i32 = 277;
pub const TK_WHILE: i32 = 278;
pub const TK_IDIV: i32 = 279;
pub const TK_CONCAT: i32 = 280;
pub const TK_DOTS: i32 = 281;
pub const TK_EQ: i32 = 282;
pub const TK_GE: i32 = 283;
pub const TK_LE: i32 = 284;
pub const TK_NE: i32 = 285;
pub const TK_SHL: i32 = 286;
pub const TK_SHR: i32 = 287;
pub const TK_DBCOLON: i32 = 288;
pub const TK_EOS: i32 = 289;
pub const TK_FLT: i32 = 290;
pub const TK_INT: i32 = 291;
pub const TK_NAME: i32 = 292;
pub const TK_STRING: i32 = 293;
pub static LUAX_TOKENS: &[&[u8]] = &[
b"and", b"break", b"do", b"else", b"elseif",
b"end", b"false", b"for", b"function", b"goto", b"if",
b"in", b"local", b"nil", b"not", b"or", b"repeat",
b"return", b"then", b"true", b"until", b"while",
b"//", b"..", b"...", b"==", b">=", b"<=", b"~=",
b"<<", b">>", b"::", b"<eof>",
b"<number>", b"<integer>", b"<name>", b"<string>",
];
#[derive(Clone)]
pub enum TokenValue {
None,
Float(f64),
Int(i64),
Str(GcRef<LuaString>),
}
#[derive(Clone)]
pub struct Token {
pub kind: i32,
pub value: TokenValue,
}
impl Token {
pub fn new(kind: i32) -> Self {
Token { kind, value: TokenValue::None }
}
pub fn eos() -> Self {
Token::new(TK_EOS)
}
}
pub struct LexState {
pub current: i32,
pub linenumber: i32,
pub lastline: i32,
pub t: Token,
pub lookahead: Token,
pub fs: Option<()>,
pub z: ZIO,
pub buff: LexBuffer,
pub h: Option<GcRef<LuaTable>>,
pub long_str_anchor: std::collections::HashMap<Vec<u8>, GcRef<LuaString>>,
pub dyd: Option<()>,
pub source: GcRef<LuaString>,
pub envn: GcRef<LuaString>,
}
#[inline]
fn is_digit(c: i32) -> bool {
c >= b'0' as i32 && c <= b'9' as i32
}
#[inline]
fn is_xdigit(c: i32) -> bool {
(c >= b'0' as i32 && c <= b'9' as i32)
|| (c >= b'a' as i32 && c <= b'f' as i32)
|| (c >= b'A' as i32 && c <= b'F' as i32)
}
#[inline]
fn is_lalpha(c: i32) -> bool {
(c >= b'a' as i32 && c <= b'z' as i32)
|| (c >= b'A' as i32 && c <= b'Z' as i32)
|| c == b'_' as i32
}
#[inline]
fn is_lalnum(c: i32) -> bool {
is_lalpha(c) || is_digit(c)
}
#[inline]
fn is_space(c: i32) -> bool {
matches!(c, 9 | 10 | 11 | 12 | 13 | 32) }
#[inline]
fn is_print(c: i32) -> bool {
c >= 0x20 && c <= 0x7E
}
#[inline]
fn curr_is_newline(ls: &LexState) -> bool {
ls.current == b'\n' as i32 || ls.current == b'\r' as i32
}
#[inline]
fn advance(ls: &mut LexState) {
ls.current = ls.z.getc();
}
fn save(ls: &mut LexState, state: &mut LuaState, c: i32) -> Result<(), LuaError> {
if ls.buff.len() + 1 > ls.buff.capacity() {
if ls.buff.capacity() >= MAX_SIZE / 2 {
return Err(lex_error(ls, b"lexical element too long", 0));
}
let newsize = ls.buff.capacity() * 2;
ls.buff.resize(state, newsize)?;
}
ls.buff.push_byte(c as u8);
Ok(())
}
#[inline]
fn save_and_next(ls: &mut LexState, state: &mut LuaState) -> Result<(), LuaError> {
let c = ls.current;
save(ls, state, c)?;
advance(ls);
Ok(())
}
pub fn lex_error(ls: &mut LexState, msg: &[u8], token: i32) -> LuaError {
const LUA_IDSIZE: usize = 60;
let mut buff = [0u8; LUA_IDSIZE];
let n = lua_vm::object::chunk_id(&mut buff[..], ls.source.as_bytes());
let src_part = &buff[..n];
let mut full_msg: Vec<u8> = Vec::new();
full_msg.extend_from_slice(src_part);
let _ = write!(full_msg, ":{}: ", ls.linenumber);
full_msg.extend_from_slice(msg);
if token != 0 {
let tok_text = txt_token(ls, token);
full_msg.extend_from_slice(b" near ");
full_msg.extend_from_slice(&tok_text);
}
LuaError::syntax_raw(&full_msg)
}
pub fn syntax_error(ls: &mut LexState, msg: &[u8]) -> LuaError {
let token = ls.t.kind;
lex_error(ls, msg, token)
}
fn txt_token(ls: &mut LexState, token: i32) -> Vec<u8> {
match token {
t if t == TK_NAME || t == TK_STRING || t == TK_FLT || t == TK_INT => {
let mut v: Vec<u8> = Vec::new();
v.push(b'\'');
let buff = ls.buff.as_slice();
let trimmed = if buff.last() == Some(&0) { &buff[..buff.len() - 1] } else { buff };
v.extend_from_slice(trimmed);
v.push(b'\'');
v
}
_ => token2str_raw(token),
}
}
pub fn token2str(_ls: &LexState, token: i32) -> Vec<u8> {
token2str_raw(token)
}
fn token2str_raw(token: i32) -> Vec<u8> {
if token < FIRST_RESERVED {
if is_print(token) {
vec![b'\'', token as u8, b'\'']
} else {
let mut v: Vec<u8> = Vec::new();
v.extend_from_slice(b"'<\\");
let _ = write!(&mut v, "{}", token);
v.extend_from_slice(b">'");
v
}
} else {
let idx = (token - FIRST_RESERVED) as usize;
let s = LUAX_TOKENS[idx];
if token < TK_EOS {
let mut v: Vec<u8> = Vec::with_capacity(s.len() + 2);
v.push(b'\'');
v.extend_from_slice(s);
v.push(b'\'');
v
} else {
s.to_vec()
}
}
}
pub fn init(state: &mut LuaState) -> Result<(), LuaError> {
let _e = intern_str_stub(state, LUA_ENV)?;
for i in 0..NUM_RESERVED {
let ts = intern_str_stub(state, LUAX_TOKENS[i])?;
let _ = ts; }
Ok(())
}
pub fn set_input(
state: &mut LuaState,
ls: &mut LexState,
z: ZIO,
source: GcRef<LuaString>,
firstchar: i32,
) -> Result<(), LuaError> {
ls.t = Token::new(0);
ls.current = firstchar;
ls.lookahead = Token::eos();
ls.z = z;
ls.fs = None;
ls.linenumber = 1;
ls.lastline = 1;
ls.source = source;
ls.envn = intern_str_stub(state, LUA_ENV)?;
ls.buff.resize(state, LUA_MIN_BUFFER)?;
Ok(())
}
pub(crate) fn new_string(
state: &mut LuaState,
ls: &mut LexState,
bytes: &[u8],
) -> Result<GcRef<LuaString>, LuaError> {
if let Some(existing) = ls.long_str_anchor.get(bytes) {
return Ok(existing.clone());
}
let ts = intern_str_stub(state, bytes)?;
ls.long_str_anchor.insert(bytes.to_vec(), ts.clone());
Ok(ts)
}
pub fn next(
state: &mut LuaState,
ls: &mut LexState,
) -> Result<(), LuaError> {
ls.lastline = ls.linenumber;
if ls.lookahead.kind != TK_EOS {
ls.t = ls.lookahead.clone();
ls.lookahead = Token::eos();
} else {
let mut val = TokenValue::None;
let kind = llex(state, ls, &mut val)?;
ls.t = Token { kind, value: val };
}
Ok(())
}
pub fn lookahead(
state: &mut LuaState,
ls: &mut LexState,
) -> Result<i32, LuaError> {
debug_assert!(
ls.lookahead.kind == TK_EOS,
"luaX_lookahead: lookahead already set"
);
let mut val = TokenValue::None;
let kind = llex(state, ls, &mut val)?;
ls.lookahead = Token { kind, value: val };
Ok(ls.lookahead.kind)
}
fn check_next1(ls: &mut LexState, c: i32) -> bool {
if ls.current == c {
advance(ls);
true
} else {
false
}
}
fn check_next2(
ls: &mut LexState,
state: &mut LuaState,
set: &[u8; 2],
) -> Result<bool, LuaError> {
if ls.current == set[0] as i32 || ls.current == set[1] as i32 {
save_and_next(ls, state)?;
Ok(true)
} else {
Ok(false)
}
}
fn inc_line_number(ls: &mut LexState, _state: &mut LuaState) -> Result<(), LuaError> {
debug_assert!(curr_is_newline(ls), "inc_line_number: not at a newline");
let old = ls.current;
advance(ls);
if curr_is_newline(ls) && ls.current != old {
advance(ls);
}
ls.linenumber += 1;
if ls.linenumber >= i32::MAX {
return Err(lex_error(ls, b"chunk has too many lines", 0));
}
Ok(())
}
fn read_numeral(
state: &mut LuaState,
ls: &mut LexState,
seminfo: &mut TokenValue,
) -> Result<i32, LuaError> {
let mut expo: &[u8; 2] = b"Ee";
let first = ls.current;
debug_assert!(is_digit(ls.current), "read_numeral: not at a digit");
save_and_next(ls, state)?;
if first == b'0' as i32 && check_next2(ls, state, b"xX")? {
expo = b"Pp";
}
loop {
if check_next2(ls, state, expo)? {
check_next2(ls, state, b"-+")?;
} else if is_xdigit(ls.current) || ls.current == b'.' as i32 {
save_and_next(ls, state)?;
} else {
break;
}
}
if is_lalpha(ls.current) {
save_and_next(ls, state)?;
}
save(ls, state, 0)?;
let buf = ls.buff.as_slice();
let num_bytes = if buf.last() == Some(&0) { &buf[..buf.len() - 1] } else { buf };
let mut obj = lua_types::LuaValue::Nil;
if lua_vm::object::str2num(num_bytes, &mut obj) == 0 {
return Err(lex_error(ls, b"malformed number", TK_FLT));
}
match obj {
lua_types::LuaValue::Int(i) => {
*seminfo = TokenValue::Int(i);
Ok(TK_INT)
}
lua_types::LuaValue::Float(f) => {
*seminfo = TokenValue::Float(f);
Ok(TK_FLT)
}
_ => unreachable!("str2num returned non-numeric LuaValue"),
}
}
fn skip_sep(
state: &mut LuaState,
ls: &mut LexState,
) -> Result<usize, LuaError> {
let mut count: usize = 0;
let s = ls.current;
debug_assert!(s == b'[' as i32 || s == b']' as i32, "skip_sep: not at bracket");
save_and_next(ls, state)?;
while ls.current == b'=' as i32 {
save_and_next(ls, state)?;
count += 1;
}
if ls.current == s {
Ok(count + 2)
} else if count == 0 {
Ok(1)
} else {
Ok(0)
}
}
fn read_long_string(
state: &mut LuaState,
ls: &mut LexState,
seminfo: Option<&mut TokenValue>,
sep: usize,
) -> Result<(), LuaError> {
let line = ls.linenumber;
save_and_next(ls, state)?;
if curr_is_newline(ls) {
inc_line_number(ls, state)?;
}
let is_string = seminfo.is_some();
loop {
match ls.current {
c if c == EOZ => {
let what: &[u8] = if is_string { b"string" } else { b"comment" };
let mut msg: Vec<u8> = Vec::new();
msg.extend_from_slice(b"unfinished long ");
msg.extend_from_slice(what);
msg.extend_from_slice(b" (starting at line ");
let _ = write!(&mut msg, "{}", line);
msg.push(b')');
return Err(lex_error(ls, &msg, TK_EOS));
}
c if c == b']' as i32 => {
let s = skip_sep(state, ls)?;
if s == sep {
save_and_next(ls, state)?;
break;
}
}
c if c == b'\n' as i32 || c == b'\r' as i32 => {
save(ls, state, b'\n' as i32)?;
inc_line_number(ls, state)?;
if !is_string {
ls.buff.clear();
}
}
_ => {
if is_string {
save_and_next(ls, state)?;
} else {
advance(ls);
}
}
}
}
if let Some(out) = seminfo {
let buf = ls.buff.as_slice();
let content: Vec<u8> = buf[sep..buf.len() - sep].to_vec();
let ts = new_string(state, ls, &content)?;
*out = TokenValue::Str(ts);
}
Ok(())
}
fn esc_check(
state: &mut LuaState,
ls: &mut LexState,
ok: bool,
msg: &[u8],
) -> Result<(), LuaError> {
if !ok {
if ls.current != EOZ {
save_and_next(ls, state)?;
}
return Err(lex_error(ls, msg, TK_STRING));
}
Ok(())
}
fn get_hexa(
state: &mut LuaState,
ls: &mut LexState,
) -> Result<u32, LuaError> {
save_and_next(ls, state)?;
esc_check(state, ls, is_xdigit(ls.current), b"hexadecimal digit expected")?;
Ok(hex_value_stub(ls.current))
}
fn read_hex_esc(
state: &mut LuaState,
ls: &mut LexState,
) -> Result<u32, LuaError> {
let r = get_hexa(state, ls)?;
let r = (r << 4) + get_hexa(state, ls)?;
ls.buff.truncate_by(2);
Ok(r)
}
fn read_utf8_esc(
state: &mut LuaState,
ls: &mut LexState,
) -> Result<u32, LuaError> {
let mut i: usize = 4;
save_and_next(ls, state)?;
esc_check(state, ls, ls.current == b'{' as i32, b"missing '{'")?;
let mut r = get_hexa(state, ls)?;
loop {
save_and_next(ls, state)?;
if !is_xdigit(ls.current) {
break;
}
i += 1;
esc_check(state, ls, r <= (0x7FFF_FFFFu32 >> 4), b"UTF-8 value too large")?;
r = (r << 4) + hex_value_stub(ls.current);
}
esc_check(state, ls, ls.current == b'}' as i32, b"missing '}'")?;
advance(ls);
ls.buff.truncate_by(i);
Ok(r)
}
fn utf8_esc(
state: &mut LuaState,
ls: &mut LexState,
) -> Result<(), LuaError> {
let codepoint = read_utf8_esc(state, ls)?;
let encoded = utf8_encode_stub(codepoint);
for &b in &encoded {
save(ls, state, b as i32)?;
}
Ok(())
}
fn read_dec_esc(
state: &mut LuaState,
ls: &mut LexState,
) -> Result<u32, LuaError> {
let mut i: usize = 0;
let mut r: u32 = 0;
while i < 3 && is_digit(ls.current) {
r = 10 * r + (ls.current as u32 - b'0' as u32);
save_and_next(ls, state)?;
i += 1;
}
esc_check(state, ls, r <= u8::MAX as u32, b"decimal escape too large")?;
ls.buff.truncate_by(i);
Ok(r)
}
fn read_string(
state: &mut LuaState,
ls: &mut LexState,
del: i32,
seminfo: &mut TokenValue,
) -> Result<(), LuaError> {
enum EscapeResult {
ReadSave(i32),
OnlySave(i32),
NoSave,
}
save_and_next(ls, state)?;
while ls.current != del {
match ls.current {
c if c == EOZ => {
return Err(lex_error(ls, b"unfinished string", TK_EOS));
}
c if c == b'\n' as i32 || c == b'\r' as i32 => {
return Err(lex_error(ls, b"unfinished string", TK_STRING));
}
c if c == b'\\' as i32 => {
save_and_next(ls, state)?;
let esc = match ls.current {
c if c == b'a' as i32 => EscapeResult::ReadSave(b'\x07' as i32),
c if c == b'b' as i32 => EscapeResult::ReadSave(b'\x08' as i32),
c if c == b'f' as i32 => EscapeResult::ReadSave(b'\x0C' as i32),
c if c == b'n' as i32 => EscapeResult::ReadSave(b'\n' as i32),
c if c == b'r' as i32 => EscapeResult::ReadSave(b'\r' as i32),
c if c == b't' as i32 => EscapeResult::ReadSave(b'\t' as i32),
c if c == b'v' as i32 => EscapeResult::ReadSave(b'\x0B' as i32),
c if c == b'x' as i32 => {
let decoded = read_hex_esc(state, ls)?;
EscapeResult::ReadSave(decoded as i32)
}
c if c == b'u' as i32 => {
utf8_esc(state, ls)?;
EscapeResult::NoSave
}
c if c == b'\n' as i32 || c == b'\r' as i32 => {
inc_line_number(ls, state)?;
EscapeResult::OnlySave(b'\n' as i32)
}
c if c == b'\\' as i32 || c == b'"' as i32 || c == b'\'' as i32 => {
EscapeResult::ReadSave(c)
}
c if c == EOZ => EscapeResult::NoSave,
c if c == b'z' as i32 => {
ls.buff.truncate_by(1);
advance(ls);
while is_space(ls.current) {
if curr_is_newline(ls) {
inc_line_number(ls, state)?;
} else {
advance(ls);
}
}
EscapeResult::NoSave
}
_ => {
esc_check(
state, ls,
is_digit(ls.current),
b"invalid escape sequence",
)?;
let decoded = read_dec_esc(state, ls)?;
EscapeResult::OnlySave(decoded as i32)
}
};
match esc {
EscapeResult::ReadSave(c) => {
advance(ls);
ls.buff.truncate_by(1);
save(ls, state, c)?;
}
EscapeResult::OnlySave(c) => {
ls.buff.truncate_by(1);
save(ls, state, c)?;
}
EscapeResult::NoSave => {}
}
}
_ => {
save_and_next(ls, state)?;
}
}
}
save_and_next(ls, state)?;
let buf = ls.buff.as_slice();
let content: Vec<u8> = if buf.len() >= 2 {
buf[1..buf.len() - 1].to_vec()
} else {
Vec::new()
};
let ts = new_string(state, ls, &content)?;
*seminfo = TokenValue::Str(ts);
Ok(())
}
fn llex(
state: &mut LuaState,
ls: &mut LexState,
seminfo: &mut TokenValue,
) -> Result<i32, LuaError> {
ls.buff.clear();
loop {
match ls.current {
c if c == b'\n' as i32 || c == b'\r' as i32 => {
inc_line_number(ls, state)?;
if ls.current == b'#' as i32 {
while !curr_is_newline(ls) && ls.current != EOZ {
advance(ls);
}
}
}
c if c == b' ' as i32
|| c == b'\x0C' as i32
|| c == b'\t' as i32
|| c == b'\x0B' as i32 =>
{
advance(ls);
}
c if c == b'-' as i32 => {
advance(ls);
if ls.current != b'-' as i32 {
return Ok(b'-' as i32);
}
advance(ls);
if ls.current == b'[' as i32 {
let sep = skip_sep(state, ls)?;
ls.buff.clear();
if sep >= 2 {
read_long_string(state, ls, None, sep)?;
ls.buff.clear();
continue;
}
}
while !curr_is_newline(ls) && ls.current != EOZ {
advance(ls);
}
}
c if c == b'[' as i32 => {
let sep = skip_sep(state, ls)?;
if sep >= 2 {
read_long_string(state, ls, Some(seminfo), sep)?;
return Ok(TK_STRING);
} else if sep == 0 {
return Err(lex_error(ls, b"invalid long string delimiter", TK_STRING));
}
return Ok(b'[' as i32);
}
c if c == b'=' as i32 => {
advance(ls);
if check_next1(ls, b'=' as i32) {
return Ok(TK_EQ);
}
return Ok(b'=' as i32);
}
c if c == b'<' as i32 => {
advance(ls);
if check_next1(ls, b'=' as i32) {
return Ok(TK_LE);
} else if check_next1(ls, b'<' as i32) {
return Ok(TK_SHL);
}
return Ok(b'<' as i32);
}
c if c == b'>' as i32 => {
advance(ls);
if check_next1(ls, b'=' as i32) {
return Ok(TK_GE);
} else if check_next1(ls, b'>' as i32) {
return Ok(TK_SHR);
}
return Ok(b'>' as i32);
}
c if c == b'/' as i32 => {
advance(ls);
if check_next1(ls, b'/' as i32) {
return Ok(TK_IDIV);
}
return Ok(b'/' as i32);
}
c if c == b'~' as i32 => {
advance(ls);
if check_next1(ls, b'=' as i32) {
return Ok(TK_NE);
}
return Ok(b'~' as i32);
}
c if c == b':' as i32 => {
advance(ls);
if check_next1(ls, b':' as i32) {
return Ok(TK_DBCOLON);
}
return Ok(b':' as i32);
}
c if c == b'"' as i32 || c == b'\'' as i32 => {
let del = ls.current;
read_string(state, ls, del, seminfo)?;
return Ok(TK_STRING);
}
c if c == b'.' as i32 => {
save_and_next(ls, state)?;
if check_next1(ls, b'.' as i32) {
if check_next1(ls, b'.' as i32) {
return Ok(TK_DOTS);
}
return Ok(TK_CONCAT);
} else if !is_digit(ls.current) {
return Ok(b'.' as i32);
} else {
return read_numeral(state, ls, seminfo);
}
}
c if is_digit(c) => {
return read_numeral(state, ls, seminfo);
}
c if c == EOZ => {
return Ok(TK_EOS);
}
c => {
if is_lalpha(c) {
loop {
save_and_next(ls, state)?;
if !is_lalnum(ls.current) {
break;
}
}
let content: Vec<u8> = ls.buff.as_slice().to_vec();
let ts = new_string(state, ls, &content)?;
let reserved_token: Option<i32> = LUAX_TOKENS[..NUM_RESERVED]
.iter()
.position(|kw| *kw == content.as_slice())
.map(|i| FIRST_RESERVED + i as i32);
*seminfo = TokenValue::Str(ts);
if let Some(tk) = reserved_token {
return Ok(tk);
} else {
return Ok(TK_NAME);
}
} else {
let tok = ls.current;
advance(ls);
return Ok(tok);
}
}
}
}
}
fn intern_str_stub(
state: &mut LuaState,
bytes: &[u8],
) -> Result<GcRef<LuaString>, LuaError> {
state.intern_str(bytes)
}
fn hex_value_stub(c: i32) -> u32 {
match c {
c if c >= b'0' as i32 && c <= b'9' as i32 => (c - b'0' as i32) as u32,
c if c >= b'a' as i32 && c <= b'f' as i32 => (c - b'a' as i32 + 10) as u32,
c if c >= b'A' as i32 && c <= b'F' as i32 => (c - b'A' as i32 + 10) as u32,
_ => 0,
}
}
fn utf8_encode_stub(codepoint: u32) -> Vec<u8> {
debug_assert!(codepoint <= 0x7FFF_FFFF);
if codepoint < 0x80 {
return vec![codepoint as u8];
}
let mut x = codepoint;
let mut mfb: u32 = 0x3f;
let mut buf: Vec<u8> = Vec::with_capacity(8);
loop {
buf.push(0x80 | ((x & 0x3f) as u8));
x >>= 6;
mfb >>= 1;
if x <= mfb {
break;
}
}
buf.push(((!mfb << 1) | x) as u8);
buf.reverse();
buf
}