use crate::LuaType;
use crate::Result;
use crate::State;
use crate::error::{ArgError, ErrorKind};
pub(crate) fn base_ipairs(state: &mut State) -> Result<u8> {
state.check_type(1, LuaType::Table)?;
state.set_top(1);
state.push_rust_fn(base_ipairs_iter);
state.push_value(1)?;
state.remove(1)?;
state.push_number(0.0);
Ok(3)
}
pub(crate) fn base_ipairs_iter(state: &mut State) -> Result<u8> {
state.check_type(1, LuaType::Table)?;
state.check_type(2, LuaType::Number)?;
state.set_top(2);
let old_index = state.to_number(2)?;
let new_index = old_index + 1.0;
state.pop(1); state.push_number(new_index);
state.get_table(1)?;
if state.typ(-1) != LuaType::Nil {
state.push_number(new_index);
state.replace(1)?; Ok(2)
} else {
state.set_top(0);
state.push_nil();
Ok(1)
}
}
pub(crate) fn base_next(state: &mut State) -> Result<u8> {
state.check_type(1, LuaType::Table)?;
if state.get_top() < 2 {
state.push_nil();
}
state.set_top(2);
let has_more = state.table_next(1)?;
if has_more {
state.remove(1)?;
Ok(2)
} else {
state.remove(1)?;
Ok(1)
}
}
pub(crate) fn base_pairs(state: &mut State) -> Result<u8> {
state.check_type(1, LuaType::Table)?;
state.set_top(1);
state.get_global("next");
state.push_value(1)?; state.push_nil(); state.remove(1)?; Ok(3)
}
pub(crate) fn open_base(state: &mut State) {
let mut add = |name, func| {
state.push_rust_fn(func);
state.set_global(name);
};
add("ipairs", base_ipairs);
add("next", base_next);
add("pairs", base_pairs);
add("print", |state| {
let top = state.get_top();
let mut parts = Vec::with_capacity(top);
for i in 1..=top {
parts.push(state.to_string_with_meta(i as isize)?);
}
let message = parts.join("\t");
state.host_print(&message);
Ok(0)
});
add("error", |state| {
let message = if state.get_top() >= 1 {
state.to_string_with_meta(1)?
} else {
"(error raised with no message)".to_string()
};
Err(state.error(ErrorKind::InternalError(message)))
});
add("type", |state| {
state.check_any(1)?;
let typ = state.typ(1);
let type_str = typ.to_string();
state.pop(state.get_top() as isize);
state.push_string(type_str);
Ok(1)
});
add("tonumber", |state| {
state.check_any(1)?;
let typ = state.typ(1);
match typ {
LuaType::Number => {
let num = state.to_number(1)?;
state.pop(state.get_top() as isize);
state.push_number(num);
Ok(1)
}
LuaType::String => {
let s = state.to_string(1)?;
state.pop(state.get_top() as isize);
if let Ok(num) = s.parse::<f64>() {
state.push_number(num);
} else {
state.push_nil();
}
Ok(1)
}
_ => {
state.pop(state.get_top() as isize);
state.push_nil();
Ok(1)
}
}
});
add("tostring", |state| {
state.check_any(1)?;
if state.typ(1) == LuaType::String {
let bytes = state.to_bytes(1)?.to_vec();
state.pop(state.get_top() as isize);
state.push_bytes(bytes);
} else {
let s = state.to_string_with_meta(1)?;
state.pop(state.get_top() as isize);
state.push_string(s);
}
Ok(1)
});
add("unpack", |state| {
state.check_type(1, LuaType::Table)?;
let mut i = 1.0;
loop {
state.push_number(i);
state.get_table(1)?;
if let LuaType::Nil = state.typ(-1) {
state.pop(1);
break;
}
i += 1.0;
}
Ok(i as u8 - 1)
});
add("getmetatable", |state| {
state.check_any(1)?;
state.set_top(1);
state.get_metatable_of(1)?;
state.remove(1)?;
Ok(1)
});
add("setmetatable", |state| {
state.check_type(1, LuaType::Table)?;
state.set_top(2);
state.set_metatable_of(1)?;
Ok(1)
});
add("rawget", |state| {
state.check_type(1, LuaType::Table)?;
state.check_any(2)?;
state.set_top(2);
state.push_value(2)?; state.get_table_raw(1)?;
state.remove(1)?;
state.remove(1)?;
Ok(1)
});
add("rawset", |state| {
state.check_type(1, LuaType::Table)?;
state.check_any(2)?;
state.check_any(3)?;
state.set_top(3);
state.push_value(3)?; state.push_value(2)?; state.set_table_raw(1)?;
state.set_top(1);
Ok(1)
});
add("rawequal", |state| {
state.check_any(1)?;
state.check_any(2)?;
let equal = state.raw_equal(1, 2);
state.set_top(0);
state.push_boolean(equal);
Ok(1)
});
add("rawlen", |state| {
state.check_any(1)?;
let typ = state.typ(1);
let len = match typ {
LuaType::String => state.to_bytes(1)?.len(),
LuaType::Table => state.table_len(1),
_ => {
let e = ArgError {
arg_number: 1,
func_name: Some("rawlen".to_string()),
expected: Some(LuaType::Table),
received: Some(typ),
};
return Err(state.error(ErrorKind::ArgError(e)));
}
};
state.set_top(0);
state.push_number(len as f64);
Ok(1)
});
add("select", |state| {
state.check_any(1)?;
let num_args = state.get_top();
if state.typ(1) == LuaType::String && state.to_bytes(1)? == b"#" {
state.set_top(0);
state.push_number((num_args - 1) as f64);
return Ok(1);
}
state.check_type(1, LuaType::Number)?;
let index = state.to_number(1)? as isize;
if index <= 0 {
state.remove(1)?;
return Ok((num_args - 1) as u8);
}
let index = index as usize;
if index > num_args - 1 {
state.set_top(0);
return Ok(0);
}
let start_pos = 1 + index; let count = num_args - index;
for _ in 1..start_pos {
state.remove(1)?;
}
Ok(count as u8)
});
state.new_table(); state.new_table();
state.push_rust_fn(|state| {
state.check_any(2)?;
let key = state.to_string(2)?;
state.set_top(0);
state.get_global(&key);
Ok(1)
});
state.push_string("__index");
state
.set_table_raw(-3)
.expect("_G metatable __index assignment cannot fail");
state.push_rust_fn(|state| {
state.check_any(2)?;
state.check_any(3)?;
let key = state.to_string(2)?;
state.push_value(3)?;
state.set_global(&key);
state.set_top(0);
Ok(0)
});
state.push_string("__newindex");
state
.set_table_raw(-3)
.expect("_G metatable __newindex assignment cannot fail");
state
.set_metatable_of(1)
.expect("_G metatable installation cannot fail");
state.set_global("_G");
}