use std::{any::Any, cmp, collections::HashMap, error, fmt};
mod chunk;
mod obj;
mod ops;
pub(crate) use {chunk::Chunk, obj::Gc, ops::Op};
use super::RuntimeError;
#[derive(Clone)]
#[non_exhaustive]
pub enum Value {
Nil,
Boolean(bool),
Number(f64),
r#String(Box<str>),
NativeFun(Gc<NativeFun>),
#[doc(hidden)]
Fun(Gc<Fun>),
#[doc(hidden)]
Closure(Gc<Fun>, Box<[Gc<UpvalueRef>]>),
#[doc(hidden)]
BoundMethod {
recv: Gc<Instance>,
fun: Gc<Fun>,
upvalues: Box<[Gc<UpvalueRef>]>,
},
#[doc(hidden)]
Class(Gc<Class>),
#[doc(hidden)]
Instance(Gc<Instance>),
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Nil => write!(f, "nil"),
Self::Boolean(b) => write!(f, "{}", b),
Self::Number(v) => write!(f, "{}", v),
Self::r#String(s) => write!(f, "{}", s),
Self::Fun(fun) | Self::Closure(fun, _) | Self::BoundMethod { fun, .. } => {
write!(f, "{}", **fun)
}
Self::Class(c) => write!(f, "{}", **c),
Self::Instance(i) => write!(f, "{} instance", *i.class),
Self::NativeFun(_) => write!(f, "#<native fun>"),
}
}
}
impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Nil => write!(f, "Nil"),
Self::Boolean(b) => write!(f, "Boolean({})", b),
Self::Number(v) => write!(f, "Number({})", v),
Self::r#String(s) => write!(f, "String({})", s),
Self::Fun(fun) => write!(f, "Fun({})", **fun),
Self::Closure(fun, upvals) => write!(f, "Closure({}, {:#?})", **fun, upvals),
Self::Class(c) => write!(f, "Class({})", **c),
Self::Instance(i) => write!(f, "Instance({:?})", i),
Self::BoundMethod {
recv,
fun,
upvalues,
} => write!(
f,
"BoundMethod {{ {:?}#{} {:#?} }}",
**recv, **fun, upvalues
),
Self::NativeFun(ptr) => write!(f, "NativeFun({:p})", ptr),
}
}
}
impl cmp::PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Nil, Self::Nil) => true,
(Self::Boolean(a), Self::Boolean(b)) => a == b,
(Self::Number(a), Self::Number(b)) => a == b,
(Self::r#String(a), Self::r#String(b)) => a == b,
_ => false,
}
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Self::Boolean(b)
}
}
impl From<f64> for Value {
fn from(f: f64) -> Self {
Self::Number(f)
}
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
s.to_string().into()
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Self::r#String(s.into_boxed_str())
}
}
impl Value {
pub(crate) fn is_falsey(&self) -> bool {
match self {
Self::Nil | Self::Boolean(false) => true,
_ => false,
}
}
pub(crate) fn negate(self) -> Result<Value, RuntimeError> {
if let Self::Number(a) = self {
Ok(Self::Number(-a))
} else {
Err(RuntimeError::ArgumentTypes)
}
}
pub(crate) fn mark(&self, grays: &mut Vec<Gc<dyn Any>>) {
match self {
Self::Fun(f) => {
f.mark();
grays.push(f.as_any());
}
Self::Closure(f, u) => {
f.mark();
grays.push(f.as_any());
for u_val in u.iter() {
u_val.mark();
grays.push(u_val.as_any());
}
}
Self::Class(c) => {
c.mark();
grays.push(c.as_any());
}
Self::Instance(i) => {
i.mark();
grays.push(i.as_any());
}
Self::BoundMethod {
recv,
fun,
upvalues,
} => {
recv.mark();
grays.push(recv.as_any());
fun.mark();
grays.push(fun.as_any());
for u_val in upvalues.iter() {
u_val.mark();
grays.push(u_val.as_any());
}
}
Self::NativeFun(nf) => {
nf.mark();
grays.push(nf.as_any());
}
_ => (),
}
}
}
impl Gc<dyn Any> {
pub(crate) fn blacken(self, gray_stack: &mut Vec<Gc<dyn Any>>) {
if self.is_marked() {
return;
}
#[cfg(feature = "trace-gc")]
log::debug!("{0:p} blacken\t{0:?}", self);
if let Some(f) = self.downcast_ref::<Fun>() {
for c in &f.chunk.constants {
c.mark(gray_stack);
}
} else if let Some(u) = self.downcast_ref::<UpvalueRef>() {
if let UpvalueRef::Captured(ref v) = *u {
v.mark(gray_stack);
}
} else if let Some(Class { methods, .. }) = self.downcast_ref::<Class>() {
for method in methods.values() {
method.mark(gray_stack);
}
} else if let Some(Instance { class, fields }) = self.downcast_ref::<Instance>() {
class.mark();
gray_stack.push(class.as_any());
for value in fields.values() {
value.mark(gray_stack);
}
}
}
}
#[derive(Clone, Debug)]
pub struct Fun {
pub(crate) name: Box<str>,
pub(crate) arity: u8,
pub(crate) chunk: Chunk,
}
impl fmt::Display for Fun {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "#<fun {}/{}>", self.name, self.arity)
}
}
impl Fun {
pub(crate) fn new<T: ToString>(name: &T, arity: u8) -> Self {
Self {
arity,
name: name.to_string().into_boxed_str(),
chunk: Chunk::new(name),
}
}
}
#[derive(Clone, Debug)]
pub enum UpvalueRef {
Live(usize),
Captured(Box<Value>),
}
impl UpvalueRef {
pub(crate) fn new(slot: usize) -> Self {
Self::Live(slot)
}
pub(crate) fn close(&mut self, ptr: Value) {
*self = Self::Captured(Box::new(ptr));
}
}
impl fmt::Display for UpvalueRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Live(idx) => write!(f, "at stack index {}", idx),
Self::Captured(ptr) => write!(f, "at address {:p}", ptr),
}
}
}
#[derive(Clone, Debug)]
pub struct Class {
pub(crate) name: Box<str>,
pub(crate) methods: HashMap<Box<str>, Value>,
}
impl Class {
pub(crate) fn new<T: ToString>(name: &T) -> Self {
Self {
name: name.to_string().into_boxed_str(),
methods: HashMap::new(),
}
}
}
impl fmt::Display for Class {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
#[derive(Clone, Debug)]
pub struct Instance {
pub(crate) class: Gc<Class>,
pub(crate) fields: HashMap<Box<str>, Value>,
}
impl Instance {
pub(crate) fn new(class: Gc<Class>) -> Self {
Self {
class,
fields: HashMap::new(),
}
}
}
pub type NativeFun = Box<dyn FnMut(&[Value]) -> Result<Value, Box<dyn error::Error>>>;