use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
use crate::error::{LuaError, Result};
use crate::num::{from_str_radix_wrapping, parse_f64_dec, parse_f64_hex};
use crate::vm::{StackFrame, VM};
#[derive(Clone, Debug)]
pub enum Value {
Nil,
Bool(bool),
Number(Numeric),
String(String),
Func(Rc<FuncDef>),
Table(Rc<RefCell<Table>>),
LUData(*const u8),
Thread(Rc<RefCell<Thread>>),
Mult(Vec<Value>), }
#[derive(Clone, Copy, Debug)]
pub enum ValueType {
Nil,
Bool,
Number,
String,
Func,
Table,
UData,
Thread,
}
#[derive(Clone, Debug)]
pub enum Numeric {
Integer(i64),
Float(f64),
}
#[derive(Clone)]
pub enum FuncDef {
Builtin(FuncBuiltin),
Defined(FuncClosure),
Protected(bool),
Yield,
}
#[derive(Clone, Debug)]
pub struct FuncObject {
pub regs: usize,
pub locals_cap: usize,
pub params: Vec<String>,
pub ups: Vec<VarType>,
pub varargs: bool,
pub chunk: usize,
}
#[derive(Clone)]
pub struct FuncClosure {
pub regs: usize,
pub locals_cap: usize,
pub params: Vec<String>,
pub ups: Vec<(Rc<RefCell<Value>>, String)>,
pub varargs: bool,
pub chunk: usize,
}
#[derive(Clone)]
pub struct FuncBuiltin {
pub name: &'static str,
pub func: Rc<dyn Fn(&mut VM, Vec<Value>) -> Result<Value>>,
}
#[derive(Clone, Copy, Debug)]
pub enum LocalType {
Local,
Param,
}
#[derive(Clone, Copy, Debug)]
pub enum VarType {
Local(usize),
Up(usize),
}
#[derive(Clone, Debug, Default)]
pub struct Table {
map: HashMap<Value, Value>,
meta: Option<Rc<RefCell<Table>>>,
next_key: Option<HashMap<Value, Value>>,
}
#[derive(Debug)]
pub struct Thread {
pub state: ThreadState,
pub func: Option<Rc<FuncDef>>,
pub frames: Vec<StackFrame>,
pub pc: Option<(usize, usize)>,
pub protected: Option<ProtectedHandler>,
pub yld_reg: Option<usize>,
pub error: Option<LuaError>,
}
#[derive(Clone, Copy, Debug)]
pub enum ThreadState {
Created,
Suspended,
Running,
Normal,
Dead,
}
#[derive(Clone, Debug)]
pub enum ProtectedHandler {
PCall,
XPCall(Rc<FuncDef>),
}
#[derive(Clone, Debug)]
pub enum Source {
None,
Field(Value),
Local(String),
Global(String),
Up(String),
}
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 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(&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::LUData(..) => ValueType::UData,
Value::Thread(..) => ValueType::Thread,
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_string(&self) -> Result<&str> {
match self.to_single() {
Value::String(s) => Ok(s.as_str()),
v => err!(LuaError::ExpectedType(ValueType::String, v.value_type())),
}
}
pub fn to_string_coerce(&self) -> Result<String> {
match self.to_single() {
Value::String(s) => Ok(s.clone()),
Value::Number(n) => Ok(format!("{}", n)),
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_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],
}
}
}
impl fmt::Display 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, "{}", str),
Value::Func(func) => write!(f, "function: {:?}", Rc::as_ptr(func)),
Value::Table(tbl) => write!(f, "table: {:?}", Rc::as_ptr(tbl)),
Value::LUData(ptr) => write!(f, "userdata: {:?}", ptr),
Value::Thread(thrd) => write!(f, "thread: {:?}", Rc::as_ptr(thrd)),
Value::Mult(..) => unreachable!(),
}
}
}
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::LUData(ptr) => {
state.write_u8(6);
ptr.hash(state);
}
Value::Thread(thrd) => {
state.write_u8(7);
Rc::as_ptr(thrd).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::LUData(p1), &Value::LUData(p2)) => p1 == p2,
(&Value::Thread(t1), &Value::Thread(t2)) => Rc::as_ptr(t1) == Rc::as_ptr(t2),
_ => false,
}
}
}
impl Eq for Value {}
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"),
}
}
}
impl Numeric {
pub fn from_str(str: &str) -> Result<Self> {
Self::from_str_radix(str, None)
}
pub fn from_str_radix(str: &str, base: Option<u32>) -> Result<Self> {
let str = str.trim();
Ok(match base {
Some(base) => Numeric::Integer(i64::from_str_radix(str, base)?),
None => {
let hex = if !str.is_empty() && matches!(&str[0..1], "+" | "-") {
str.len() >= 3 && matches!(&str[1..3], "0x" | "0X")
} else {
str.len() >= 2 && matches!(&str[0..2], "0x" | "0X")
};
if hex {
match from_str_radix_wrapping(str, 16) {
Some(n) => Numeric::Integer(n),
None => match parse_f64_hex(str) {
Some(f) => Numeric::Float(f),
None => {
return err!(LuaError::CustomString(
"invalid hex float".to_string()
))
}
},
}
} else {
match str.parse() {
Ok(n) => Numeric::Integer(n),
Err(..) => Numeric::Float(parse_f64_dec(str)?),
}
}
}
})
}
pub(super) fn from_usize_try_int(u: usize) -> Numeric {
if u <= i64::MAX as usize {
Numeric::Integer(u as i64)
} else {
Numeric::Float(u as f64)
}
}
pub(super) fn from_f64_try_int(f: f64) -> Numeric {
if f > i128::MIN as f64
&& f < i128::MAX as f64
&& f as i128 >= i64::MIN as i128
&& f as i128 <= i64::MAX as i128
{
Numeric::Integer(f as i64)
} else {
Numeric::Float(f)
}
}
pub(super) fn to_int(&self) -> Result<i64> {
match self {
Numeric::Integer(n) => Ok(*n),
Numeric::Float(..) => err!(LuaError::FloatToInt(Source::None)),
}
}
pub(super) fn coerce_int(&self) -> Result<i64> {
match self {
Numeric::Integer(n) => Ok(*n),
Numeric::Float(n) => {
if n.fract().abs() <= f64::EPSILON
&& *n > i128::MIN as f64
&& *n < i128::MAX as f64
&& *n as i128 >= i64::MIN as i128
&& *n as i128 <= i64::MAX as i128
{
Ok(*n as i64)
} else {
err!(LuaError::FloatToInt(Source::None))
}
}
}
}
pub(super) fn to_float(&self) -> f64 {
match self {
Numeric::Integer(n) => *n as f64,
Numeric::Float(n) => *n,
}
}
fn coerce_int_failover(&self) -> Numeric {
match self {
Numeric::Integer(n) => Numeric::Integer(*n),
Numeric::Float(n) => {
if n.fract().abs() <= f64::EPSILON {
Numeric::Integer(*n as i64)
} else {
Numeric::Float(*n)
}
}
}
}
}
impl fmt::Display for Numeric {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Numeric::Integer(n) => write!(f, "{}", n),
Numeric::Float(n) => write!(f, "{}", n),
}
}
}
impl Hash for Numeric {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Numeric::Integer(n) => {
state.write_u8(0);
state.write_i64(*n);
}
Numeric::Float(f) => {
state.write_u8(1);
for byte in f.to_be_bytes() {
state.write_u8(byte);
}
}
}
}
}
impl PartialEq for Numeric {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(&Numeric::Integer(n1), &Numeric::Integer(n2)) => n1 == n2,
(&Numeric::Float(f1), &Numeric::Float(f2)) => f1 == f2,
_ => false,
}
}
}
impl Eq for Numeric {}
impl fmt::Debug for FuncDef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FuncDef::Builtin(func) => {
write!(f, "builtin:{}", func.name)
}
FuncDef::Defined(func) => write!(f, "defined:{}", func.chunk),
FuncDef::Protected(typ) => {
write!(f, "builtin:{}", if *typ { "xpcall" } else { "pcall" })
}
FuncDef::Yield => write!(f, "builtin:yield"),
}
}
}
impl Table {
pub(super) fn get_meta(&self) -> &Option<Rc<RefCell<Table>>> {
&self.meta
}
pub(super) fn set_meta(&mut self, meta: Rc<RefCell<Table>>) {
self.meta = Some(meta);
}
pub(super) fn init_next_keys(&mut self) -> Value {
let keys: Vec<Value> = self.map.keys().cloned().collect();
self.next_key = Some(
keys.iter()
.cloned()
.zip(keys.iter().skip(1).cloned())
.collect(),
);
keys.first().cloned().unwrap_or(Value::Nil)
}
pub(super) fn next_key(&mut self, key: Value) -> Value {
self.next_key
.as_ref()
.unwrap()
.get(&key)
.cloned()
.unwrap_or(Value::Nil)
}
pub(super) fn len(&self) -> usize {
self.map.len()
}
pub(super) fn border(&self) -> usize {
let mut border = 1;
while let Some(..) = self.map.get(&Value::int(border as i64)) {
border += 1;
}
border - 1
}
pub(super) fn get(&self, key: &Value) -> Value {
match key {
Value::Number(n) => self.map.get(&Value::Number(n.coerce_int_failover())),
v => self.map.get(v),
}
.cloned()
.unwrap_or(Value::Nil)
}
pub(super) fn set(&mut self, key: Value, value: Value) -> Result<()> {
if matches!(key, Value::Nil)
|| matches!(key, Value::Number(Numeric::Float(f)) if f.is_nan())
{
return err!(LuaError::TableIndex);
}
let key = match key {
Value::Number(n) => Value::Number(n.coerce_int_failover()),
v => v,
};
if let Value::Nil = value {
self.map.remove(&key);
} else {
self.map.insert(key, value);
}
Ok(())
}
}
impl fmt::Display for Source {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Source::None => write!(f, ""),
Source::Field(name) => write!(f, " (field '{}')", name),
Source::Local(name) => write!(f, " (local '{}')", name),
Source::Global(name) => write!(f, " (global '{}')", name),
Source::Up(name) => write!(f, " (upvalue '{}')", name),
}
}
}