use super::Result;
use super::State;
use super::object::{Closure, GcHeap, ObjectPtr, StringPtr};
use std::borrow::Cow;
use std::fmt;
use std::hash::{Hash, Hasher};
pub type RustFunc = fn(&mut State) -> Result<u8>;
#[derive(Clone, Copy, Default)]
pub(crate) enum Val {
#[default]
Nil,
Bool(bool),
Num(f64),
Str(StringPtr),
RustFn(RustFunc),
Obj(ObjectPtr),
}
use Val::*;
impl Val {
pub(super) fn as_lua_function(&self, heap: &GcHeap) -> Option<Closure> {
if let Obj(o) = self {
heap.as_lua_function(*o)
} else {
None
}
}
pub(super) fn as_num(&self) -> Option<f64> {
match self {
Num(f) => Some(*f),
_ => None,
}
}
pub(super) fn as_string<'a>(&self, heap: &'a GcHeap) -> Option<&'a [u8]> {
if let Str(s) = self {
Some(heap.get_string(*s))
} else {
None
}
}
pub(super) fn as_string_ptr(&self) -> Option<StringPtr> {
if let Str(s) = self { Some(*s) } else { None }
}
pub(super) fn as_object_ptr(&self) -> Option<ObjectPtr> {
if let Obj(o) = self { Some(*o) } else { None }
}
pub(super) fn truthy(&self) -> bool {
!matches!(self, Nil | Bool(false))
}
pub(super) fn typ(&self, heap: &GcHeap) -> LuaType {
match self {
Nil => LuaType::Nil,
Bool(_) => LuaType::Boolean,
Num(_) => LuaType::Number,
RustFn(_) => LuaType::Function,
Str(_) => LuaType::String,
Obj(o) => o.typ(heap),
}
}
pub(super) fn typ_simple(&self) -> LuaType {
match self {
Nil => LuaType::Nil,
Bool(_) => LuaType::Boolean,
Num(_) => LuaType::Number,
RustFn(_) => LuaType::Function,
Str(_) => LuaType::String,
Obj(_) => LuaType::Table, }
}
pub(super) fn to_string_with_heap(self, heap: &GcHeap) -> String {
match self {
Nil => "nil".to_string(),
Bool(b) => b.to_string(),
Num(n) => n.to_string(),
RustFn(func) => format!("<function: {func:p}>"),
Obj(o) => format!("{o}"),
Str(s) => String::from_utf8_lossy(heap.get_string(s)).into_owned(),
}
}
pub(super) fn to_bytes_with_heap(self, heap: &GcHeap) -> Cow<'_, [u8]> {
match self {
Nil => Cow::Borrowed(b"nil"),
Bool(false) => Cow::Borrowed(b"false"),
Bool(true) => Cow::Borrowed(b"true"),
Num(n) => Cow::Owned(n.to_string().into_bytes()),
RustFn(func) => Cow::Owned(format!("<function: {func:p}>").into_bytes()),
Obj(o) => Cow::Owned(format!("{o}").into_bytes()),
Str(s) => Cow::Borrowed(heap.get_string(s)),
}
}
}
impl fmt::Debug for Val {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Nil => write!(f, "nil"),
Bool(b) => b.fmt(f),
Num(n) => n.fmt(f),
RustFn(func) => write!(f, "<function: {func:p}>"),
Obj(o) => o.fmt(f),
Str(s) => s.fmt(f),
}
}
}
impl fmt::Display for Val {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Nil => write!(f, "nil"),
Bool(b) => b.fmt(f),
Num(n) => n.fmt(f),
Obj(o) => o.fmt(f),
Str(s) => s.fmt(f),
RustFn(func) => write!(f, "<function: {func:p}>"),
}
}
}
impl Eq for Val {}
impl Hash for Val {
fn hash<H: Hasher>(&self, hasher: &mut H) {
match self {
Nil => (),
Bool(b) => b.hash(hasher),
Obj(o) => o.hash(hasher),
Num(n) => {
assert!(!n.is_nan(), "Cannot use NaN as table key");
let mut bits = n.to_bits();
if bits == 1 << 63 {
bits = 0;
}
bits.hash(hasher);
}
RustFn(func) => {
let f: *const RustFunc = func;
f.hash(hasher);
}
Str(s) => s.hash(hasher),
}
}
}
impl PartialEq for Val {
fn eq(&self, other: &Val) -> bool {
match (self, other) {
(Nil, Nil) => true,
(Bool(a), Bool(b)) => a == b,
(Num(a), Num(b)) => a == b,
(RustFn(a), RustFn(b)) => {
let x: *const RustFunc = a;
let y: *const RustFunc = b;
x == y
}
(Obj(a), Obj(b)) => a == b,
(Str(a), Str(b)) => a == b,
_ => false,
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum LuaType {
Nil,
Boolean,
Number,
String,
Table,
Function,
}
impl LuaType {
pub fn as_str(&self) -> &'static str {
use LuaType::*;
match self {
Nil => "nil",
Boolean => "boolean",
Number => "number",
String => "string",
Table => "table",
Function => "function",
}
}
}
impl fmt::Display for LuaType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(f)
}
}