lox_lang/lox_core/
mod.rs

1use std::{any::Any, cmp, collections::HashMap, error, fmt};
2
3mod chunk;
4mod obj;
5mod ops;
6
7pub(crate) use {chunk::Chunk, obj::Gc, ops::Op};
8
9use super::RuntimeError;
10
11/// Underlying representation of runtime values in Lox.
12#[derive(Clone)]
13#[non_exhaustive]
14pub enum Value {
15    Nil,
16    Boolean(bool),
17    Number(f64),
18    r#String(Box<str>),
19    NativeFun(Gc<NativeFun>),
20
21    #[doc(hidden)]
22    Fun(Gc<Fun>),
23    #[doc(hidden)]
24    Closure(Gc<Fun>, Box<[Gc<UpvalueRef>]>),
25    #[doc(hidden)]
26    BoundMethod {
27        recv: Gc<Instance>,
28        fun: Gc<Fun>,
29        upvalues: Box<[Gc<UpvalueRef>]>,
30    },
31    #[doc(hidden)]
32    Class(Gc<Class>),
33    #[doc(hidden)]
34    Instance(Gc<Instance>),
35}
36
37impl fmt::Display for Value {
38    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39        match self {
40            Self::Nil => write!(f, "nil"),
41            Self::Boolean(b) => write!(f, "{}", b),
42            Self::Number(v) => write!(f, "{}", v),
43            Self::r#String(s) => write!(f, "{}", s),
44            Self::Fun(fun) | Self::Closure(fun, _) | Self::BoundMethod { fun, .. } => {
45                write!(f, "{}", **fun)
46            }
47            Self::Class(c) => write!(f, "{}", **c),
48            Self::Instance(i) => write!(f, "{} instance", *i.class),
49            Self::NativeFun(_) => write!(f, "#<native fun>"),
50        }
51    }
52}
53
54impl fmt::Debug for Value {
55    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56        match self {
57            Self::Nil => write!(f, "Nil"),
58            Self::Boolean(b) => write!(f, "Boolean({})", b),
59            Self::Number(v) => write!(f, "Number({})", v),
60            Self::r#String(s) => write!(f, "String({})", s),
61            Self::Fun(fun) => write!(f, "Fun({})", **fun),
62            Self::Closure(fun, upvals) => write!(f, "Closure({}, {:#?})", **fun, upvals),
63            Self::Class(c) => write!(f, "Class({})", **c),
64            Self::Instance(i) => write!(f, "Instance({:?})", i),
65            Self::BoundMethod {
66                recv,
67                fun,
68                upvalues,
69            } => write!(
70                f,
71                "BoundMethod {{ {:?}#{} {:#?} }}",
72                **recv, **fun, upvalues
73            ),
74            Self::NativeFun(ptr) => write!(f, "NativeFun({:p})", ptr),
75        }
76    }
77}
78
79impl cmp::PartialEq for Value {
80    fn eq(&self, other: &Self) -> bool {
81        match (self, other) {
82            (Self::Nil, Self::Nil) => true,
83            (Self::Boolean(a), Self::Boolean(b)) => a == b,
84            (Self::Number(a), Self::Number(b)) => a == b,
85            (Self::r#String(a), Self::r#String(b)) => a == b,
86            _ => false,
87        }
88    }
89}
90
91impl From<bool> for Value {
92    fn from(b: bool) -> Self {
93        Self::Boolean(b)
94    }
95}
96
97impl From<f64> for Value {
98    fn from(f: f64) -> Self {
99        Self::Number(f)
100    }
101}
102
103impl From<&str> for Value {
104    fn from(s: &str) -> Self {
105        s.to_string().into()
106    }
107}
108
109impl From<String> for Value {
110    fn from(s: String) -> Self {
111        Self::r#String(s.into_boxed_str())
112    }
113}
114
115impl Value {
116    pub(crate) fn is_falsey(&self) -> bool {
117        match self {
118            Self::Nil | Self::Boolean(false) => true,
119            _ => false,
120        }
121    }
122
123    pub(crate) fn negate(self) -> Result<Value, RuntimeError> {
124        if let Self::Number(a) = self {
125            Ok(Self::Number(-a))
126        } else {
127            Err(RuntimeError::ArgumentTypes)
128        }
129    }
130
131    pub(crate) fn mark(&self, grays: &mut Vec<Gc<dyn Any>>) {
132        match self {
133            Self::Fun(f) => {
134                f.mark();
135                grays.push(f.as_any());
136            }
137            Self::Closure(f, u) => {
138                f.mark();
139                grays.push(f.as_any());
140
141                for u_val in u.iter() {
142                    u_val.mark();
143                    grays.push(u_val.as_any());
144                }
145            }
146            Self::Class(c) => {
147                c.mark();
148                grays.push(c.as_any());
149            }
150            Self::Instance(i) => {
151                i.mark();
152                grays.push(i.as_any());
153            }
154            Self::BoundMethod {
155                recv,
156                fun,
157                upvalues,
158            } => {
159                recv.mark();
160                grays.push(recv.as_any());
161
162                fun.mark();
163                grays.push(fun.as_any());
164
165                for u_val in upvalues.iter() {
166                    u_val.mark();
167                    grays.push(u_val.as_any());
168                }
169            }
170            Self::NativeFun(nf) => {
171                nf.mark();
172                grays.push(nf.as_any());
173            }
174            _ => (),
175        }
176    }
177}
178
179impl Gc<dyn Any> {
180    pub(crate) fn blacken(self, gray_stack: &mut Vec<Gc<dyn Any>>) {
181        if self.is_marked() {
182            return;
183        }
184
185        #[cfg(feature = "trace-gc")]
186        log::debug!("{0:p} blacken\t{0:?}", self);
187
188        if let Some(f) = self.downcast_ref::<Fun>() {
189            for c in &f.chunk.constants {
190                c.mark(gray_stack);
191            }
192        } else if let Some(u) = self.downcast_ref::<UpvalueRef>() {
193            if let UpvalueRef::Captured(ref v) = *u {
194                v.mark(gray_stack);
195            }
196        } else if let Some(Class { methods, .. }) = self.downcast_ref::<Class>() {
197            for method in methods.values() {
198                method.mark(gray_stack);
199            }
200        } else if let Some(Instance { class, fields }) = self.downcast_ref::<Instance>() {
201            class.mark();
202            gray_stack.push(class.as_any());
203
204            for value in fields.values() {
205                value.mark(gray_stack);
206            }
207        }
208    }
209}
210
211#[derive(Clone, Debug)]
212pub struct Fun {
213    pub(crate) name: Box<str>,
214    pub(crate) arity: u8,
215    pub(crate) chunk: Chunk,
216}
217
218impl fmt::Display for Fun {
219    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
220        write!(f, "#<fun {}/{}>", self.name, self.arity)
221    }
222}
223
224impl Fun {
225    pub(crate) fn new<T: ToString>(name: &T, arity: u8) -> Self {
226        Self {
227            arity,
228            name: name.to_string().into_boxed_str(),
229            chunk: Chunk::new(name),
230        }
231    }
232}
233
234#[derive(Clone, Debug)]
235pub enum UpvalueRef {
236    Live(usize),
237    Captured(Box<Value>),
238}
239
240impl UpvalueRef {
241    pub(crate) fn new(slot: usize) -> Self {
242        Self::Live(slot)
243    }
244
245    pub(crate) fn close(&mut self, ptr: Value) {
246        *self = Self::Captured(Box::new(ptr));
247    }
248}
249
250impl fmt::Display for UpvalueRef {
251    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
252        match self {
253            Self::Live(idx) => write!(f, "at stack index {}", idx),
254            Self::Captured(ptr) => write!(f, "at address {:p}", ptr),
255        }
256    }
257}
258
259#[derive(Clone, Debug)]
260pub struct Class {
261    pub(crate) name: Box<str>,
262    pub(crate) methods: HashMap<Box<str>, Value>,
263}
264
265impl Class {
266    pub(crate) fn new<T: ToString>(name: &T) -> Self {
267        Self {
268            name: name.to_string().into_boxed_str(),
269            methods: HashMap::new(),
270        }
271    }
272}
273
274impl fmt::Display for Class {
275    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
276        write!(f, "{}", self.name)
277    }
278}
279
280#[derive(Clone, Debug)]
281pub struct Instance {
282    pub(crate) class: Gc<Class>,
283    pub(crate) fields: HashMap<Box<str>, Value>,
284}
285
286impl Instance {
287    pub(crate) fn new(class: Gc<Class>) -> Self {
288        Self {
289            class,
290            fields: HashMap::new(),
291        }
292    }
293}
294
295/// A natively-implemented function that can be called from Lox code.
296///
297/// ## Example
298///
299/// ```
300/// # use {std::{error::Error, fmt, io::Read}, lox_lang::{Value, VM}};
301/// # #[derive(Debug)]
302/// # struct MyError;
303/// # impl fmt::Display for MyError {
304/// #     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
305/// #         write!(f, "wrong types")
306/// #     }
307/// # }
308/// # impl Error for MyError {}
309/// # let mut vm = VM::default();
310/// # vm.buffer_output(true);
311/// /// This function wraps `str::replace` for use in Lox
312/// let replace = vm.alloc(Box::new(|args: &[Value]| {
313///     match args {
314///         [Value::r#String(text), Value::r#String(pat), Value::r#String(rep)] =>
315///             Ok(text.replace(pat.as_ref(), rep.as_ref()).into()),
316///         _ => Err(Box::new(MyError) as Box<dyn Error>),
317///     }
318/// }) as lox_lang::NativeFun);
319///
320/// vm.define_global("replace", Value::NativeFun(replace));
321///
322/// vm.interpret(r#"
323///     var proverb = "what is old becomes new again";
324///     print replace(proverb, "new", "old");
325/// "#);
326///
327/// # let mut output = String::new();
328/// # vm.read_to_string(&mut output).unwrap();
329/// assert_eq!(output, "what is old becomes old again\n");
330/// ```
331pub type NativeFun = Box<dyn FnMut(&[Value]) -> Result<Value, Box<dyn error::Error>>>;