use std::{
cell::RefCell,
fmt,
hash::{Hash, Hasher},
io::Write,
rc::Rc,
};
mod function;
mod numeric;
mod table;
mod thread;
mod userdata;
pub use function::{CallResult, FuncBuiltin, FuncBuiltinRaw, FuncClosure, FuncDef};
pub use numeric::{from_str_radix_wrapping, parse_f64_hex, LuaFloat, LuaInt, Numeric};
pub use table::Table;
pub use thread::{
ProtectedHandler, Thread, ThreadState, HOOK_CALL, HOOK_COUNT, HOOK_LINE, HOOK_RET,
};
pub use userdata::{ChildMode, FileBufMode, FileDesc, FileHandle, FileMode, UserData};
use crate::{vm::Literal, LuaError, Result};
#[derive(Clone)]
pub enum Value {
Nil,
Bool(bool),
Number(Numeric),
String(Rc<Vec<u8>>),
Func(Rc<FuncDef>),
Table(Rc<RefCell<Table>>),
Thread(Rc<RefCell<Thread>>),
UserData(UserData),
Mult(Vec<Value>), }
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ValueType {
Nil,
Bool,
Number,
String,
Func,
Table,
UData,
Thread,
Custom(String),
}
impl Default for Value {
fn default() -> Self {
Value::Nil
}
}
impl Value {
pub fn empty() -> Self {
Self::Mult(Vec::new())
}
pub fn empty_cap(cap: usize) -> Self {
Self::Mult(Vec::with_capacity(cap))
}
pub fn int(n: i64) -> Self {
Self::Number(Numeric::Integer(n))
}
pub fn float(f: f64) -> Self {
Self::Number(Numeric::Float(f))
}
pub fn string(s: String) -> Self {
Self::String(Rc::new(s.into_bytes()))
}
pub fn str(s: &str) -> Self {
Self::String(Rc::new(s.to_string().into_bytes()))
}
pub fn str_bytes(s: Vec<u8>) -> Self {
Self::String(Rc::new(s))
}
pub fn is_falsy(&self) -> bool {
match self {
Value::Nil | Value::Bool(false) => true,
Value::Mult(vec) => vec.first().map(Value::is_falsy).unwrap_or(true),
_ => false,
}
}
pub fn is_truthy(&self) -> bool {
!self.is_falsy()
}
pub fn value_type_basic(&self) -> ValueType {
match self {
Value::Nil => ValueType::Nil,
Value::Bool(..) => ValueType::Bool,
Value::Number(..) => ValueType::Number,
Value::String(..) => ValueType::String,
Value::Func(..) => ValueType::Func,
Value::Table(..) => ValueType::Table,
Value::Thread(..) => ValueType::Thread,
Value::UserData(..) => ValueType::UData,
Value::Mult(vec) => match vec.first() {
Some(val) => val.value_type(),
None => panic!("empty multi-value"),
},
}
}
pub fn value_type(&self) -> ValueType {
match self {
Value::Nil => ValueType::Nil,
Value::Bool(..) => ValueType::Bool,
Value::Number(..) => ValueType::Number,
Value::String(..) => ValueType::String,
Value::Func(..) => ValueType::Func,
Value::Table(tbl) => match tbl
.borrow()
.get_meta()
.clone()
.map(|meta| meta.borrow().get(&Value::str("__name")))
{
Some(Value::String(str)) => {
ValueType::Custom(String::from_utf8_lossy(&str).into_owned())
}
_ => ValueType::Table,
},
Value::Thread(..) => ValueType::Thread,
Value::UserData(ud) => match ud
.get_meta()
.map(|meta| meta.borrow().get(&Value::str("__name")))
{
Some(Value::String(str)) => {
ValueType::Custom(String::from_utf8_lossy(&str).into_owned())
}
_ => ValueType::UData,
},
Value::Mult(vec) => match vec.first() {
Some(val) => val.value_type(),
None => panic!("empty multi-value"),
},
}
}
pub fn to_bool(&self) -> Result<bool> {
match self.to_single() {
Value::Bool(b) => Ok(*b),
v => err!(LuaError::ExpectedType(ValueType::Bool, v.value_type())),
}
}
pub fn to_number(&self) -> Result<&Numeric> {
match self.to_single() {
Value::Number(n) => Ok(n),
v => err!(LuaError::ExpectedType(ValueType::Number, v.value_type())),
}
}
pub fn to_number_coerce(&self) -> Result<Numeric> {
match self.to_single() {
Value::Number(n) => Ok(n.clone()),
Value::String(s) => match Numeric::from_str(s) {
Ok(n) => Ok(n),
Err(..) => err!(LuaError::ExpectedType(ValueType::Number, ValueType::String)),
},
v => err!(LuaError::ExpectedType(ValueType::Number, v.value_type())),
}
}
pub fn to_int_coerce(&self) -> Result<i64> {
self.to_number_coerce().and_then(|n| n.coerce_int())
}
pub fn to_float_coerce(&self) -> Result<f64> {
self.to_number_coerce().map(|n| n.to_float())
}
pub fn to_string(&self) -> Result<&[u8]> {
match self.to_single() {
Value::String(s) => Ok(s.as_ref()),
v => err!(LuaError::ExpectedType(ValueType::String, v.value_type())),
}
}
pub fn into_string(self) -> Result<Vec<u8>> {
match self.into_single() {
Value::String(s) => Ok(s.to_vec()),
v => err!(LuaError::ExpectedType(ValueType::String, v.value_type())),
}
}
pub fn to_string_coerce(&self) -> Result<Vec<u8>> {
match self.to_single() {
Value::String(s) => Ok(s.to_vec()),
Value::Number(n) => Ok(format!("{}", n).into_bytes()),
v => err!(LuaError::ExpectedType(ValueType::String, v.value_type())),
}
}
pub fn to_func(&self) -> Result<Rc<FuncDef>> {
match self.to_single() {
Value::Func(f) => Ok(f.clone()),
v => err!(LuaError::ExpectedType(ValueType::Func, v.value_type())),
}
}
pub fn to_table(&self) -> Result<Rc<RefCell<Table>>> {
match self.to_single() {
Value::Table(t) => Ok(t.clone()),
v => err!(LuaError::ExpectedType(ValueType::Table, v.value_type())),
}
}
pub fn to_thread(&self) -> Result<Rc<RefCell<Thread>>> {
match self.to_single() {
Value::Thread(t) => Ok(t.clone()),
v => err!(LuaError::ExpectedType(ValueType::Thread, v.value_type())),
}
}
pub fn to_file(&self) -> Result<Rc<RefCell<FileHandle>>> {
match self.to_single() {
Value::UserData(UserData::File(file)) => {
if file.borrow().is_closed() {
err!(LuaError::FileUseClosed)
} else {
Ok(file.clone())
}
}
v => err!(LuaError::ExpectedType(
ValueType::Custom("FILE*".to_string()),
v.value_type()
)),
}
}
pub fn to_single(&self) -> &Self {
match self {
Value::Mult(vec) => vec.first().unwrap_or(&Value::Nil),
v => v,
}
}
pub fn into_single(self) -> Self {
match self {
Value::Mult(vec) => vec.into_iter().next().unwrap_or(Value::Nil),
v => v,
}
}
pub fn into_vec(self) -> Vec<Value> {
match self {
Value::Mult(vec) => vec,
v => vec![v],
}
}
pub fn write<W: Write + ?Sized>(&self, dst: &mut W) -> Result<()> {
Ok(match self {
Value::Nil => write!(dst, "nil")?,
Value::Bool(bool) => write!(dst, "{}", bool)?,
Value::Number(num) => write!(dst, "{}", num)?,
Value::String(str) => dst.write_all(str)?,
Value::Func(func) => write!(dst, "function: {:?}", Rc::as_ptr(func))?,
Value::Table(tbl) => write!(dst, "table: {:?}", Rc::as_ptr(tbl))?,
Value::Thread(thrd) => write!(dst, "thread: {:?}", Rc::as_ptr(thrd))?,
Value::UserData(data) => write!(dst, "{}", data)?,
Value::Mult(..) => unreachable!(),
})
}
#[inline]
pub fn write_as_string(&self) -> Result<Vec<u8>> {
let mut str = Vec::new();
self.write(&mut str)?;
Ok(str)
}
pub fn write_as_value(&self) -> Result<Value> {
Ok(Value::str_bytes(self.write_as_string()?))
}
}
impl From<&Literal> for Value {
fn from(lit: &Literal) -> Self {
match lit {
Literal::Nil => Self::Nil,
Literal::Empty => Self::empty(),
Literal::EmptyCap(c) => Self::empty_cap(*c),
Literal::Bool(b) => Self::Bool(*b),
Literal::Int(i) => Self::int(*i),
Literal::Float(f) => Self::float(*f),
Literal::String(s) => Self::String(s.clone()),
}
}
}
impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Nil => write!(f, "nil"),
Value::Bool(bool) => write!(f, "{}", bool),
Value::Number(num) => write!(f, "{}", num),
Value::String(str) => write!(f, "{}", String::from_utf8_lossy(str)),
Value::Func(func) => write!(f, "function: {:?}", Rc::as_ptr(func)),
Value::Table(tbl) => write!(f, "table: {:?}", Rc::as_ptr(tbl)),
Value::Thread(thrd) => write!(f, "thread: {:?}", Rc::as_ptr(thrd)),
Value::UserData(data) => write!(f, "{:?}", data),
Value::Mult(vec) => {
let mut vec = vec.iter();
write!(f, "[")?;
if let Some(next) = vec.next() {
write!(f, "{:?}", next)?;
}
for val in vec {
write!(f, ", {:?}", val)?;
}
write!(f, "]")
}
}
}
}
impl Hash for Value {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Value::Nil => state.write_u8(0),
Value::Bool(bool) => {
state.write_u8(1);
bool.hash(state);
}
Value::Number(num) => {
state.write_u8(2);
num.hash(state);
}
Value::String(str) => {
state.write_u8(3);
str.hash(state);
}
Value::Func(func) => {
state.write_u8(4);
Rc::as_ptr(func).hash(state);
}
Value::Table(tbl) => {
state.write_u8(5);
Rc::as_ptr(tbl).hash(state);
}
Value::Thread(thrd) => {
state.write_u8(6);
Rc::as_ptr(thrd).hash(state);
}
Value::UserData(data) => {
state.write_u8(7);
data.hash(state);
}
Value::Mult(..) => unreachable!(),
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (&self, &other) {
(&Value::Nil, &Value::Nil) => true,
(&Value::Bool(b1), &Value::Bool(b2)) => b1 == b2,
(&Value::Number(n1), &Value::Number(n2)) => n1 == n2,
(&Value::String(s1), &Value::String(s2)) => s1 == s2,
(&Value::Func(f1), &Value::Func(f2)) => Rc::as_ptr(f1) == Rc::as_ptr(f2),
(&Value::Table(v1), &Value::Table(v2)) => Rc::as_ptr(v1) == Rc::as_ptr(v2),
(&Value::Thread(t1), &Value::Thread(t2)) => Rc::as_ptr(t1) == Rc::as_ptr(t2),
(&Value::UserData(d1), &Value::UserData(d2)) => d1 == d2,
_ => false,
}
}
}
impl Eq for Value {}
impl From<std::io::Error> for Value {
fn from(value: std::io::Error) -> Self {
Value::Mult(vec![
Value::Nil,
Value::string(value.kind().to_string()),
Value::int(value.raw_os_error().unwrap_or(1) as i64),
])
}
}
impl fmt::Display for ValueType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ValueType::Nil => write!(f, "nil"),
ValueType::Bool => write!(f, "boolean"),
ValueType::Number => write!(f, "number"),
ValueType::String => write!(f, "string"),
ValueType::Func => write!(f, "function"),
ValueType::Table => write!(f, "table"),
ValueType::UData => write!(f, "userdata"),
ValueType::Thread => write!(f, "thread"),
ValueType::Custom(str) => write!(f, "{}", str),
}
}
}