rune/
data.rs

1//! Utilities for variables and values.
2use crate::core::{
3    cons::Cons,
4    env::{Env, INTERNED_SYMBOLS, sym},
5    error::{Type, TypeError},
6    gc::{Context, Rt},
7    object::{
8        IntoObject, List, ListType, NIL, Number, Object, ObjectType, SubrFn, Symbol, WithLifetime,
9    },
10};
11use anyhow::{Result, anyhow};
12use rune_core::{hashmap::HashSet, macros::list};
13use rune_macros::defun;
14use std::sync::LazyLock;
15use std::sync::Mutex;
16
17/// Rust translation of the `features` variable: A list of symbols are the features
18/// of the executing Emacs. Used by [`featurep`](`crate::fns::featurep`) and [`require`](`crate::fns::require`),
19/// altered by [`provide`].
20pub(crate) static FEATURES: LazyLock<Mutex<HashSet<Symbol<'static>>>> =
21    LazyLock::new(Mutex::default);
22
23#[defun]
24pub(crate) fn fset<'ob>(symbol: Symbol<'ob>, definition: Object) -> Result<Symbol<'ob>> {
25    if definition.is_nil() {
26        symbol.unbind_func();
27    } else {
28        let func = definition.try_into()?;
29        let map = INTERNED_SYMBOLS.lock().unwrap();
30        map.set_func(symbol, func)?;
31    }
32    Ok(symbol)
33}
34
35#[defun]
36pub(crate) fn defalias<'ob>(
37    symbol: Symbol<'ob>,
38    definition: Object,
39    _docstring: Option<&str>,
40) -> Result<Symbol<'ob>> {
41    fset(symbol, definition)
42}
43
44#[defun]
45pub(crate) fn set<'ob>(
46    place: Symbol,
47    newlet: Object<'ob>,
48    env: &mut Rt<Env>,
49) -> Result<Object<'ob>> {
50    env.set_var(place, newlet)?;
51    Ok(newlet)
52}
53
54#[defun]
55pub(crate) fn put<'ob>(
56    symbol: Symbol,
57    propname: Symbol,
58    value: Object<'ob>,
59    env: &mut Rt<Env>,
60) -> Object<'ob> {
61    env.set_prop(symbol, propname, value);
62    value
63}
64
65#[defun]
66pub(crate) fn get<'ob>(
67    symbol: Symbol,
68    propname: Symbol,
69    env: &Rt<Env>,
70    cx: &'ob Context,
71) -> Object<'ob> {
72    match env.props.get(symbol) {
73        Some(plist) => match plist.iter().find(|x| x.0 == propname) {
74            Some(element) => cx.bind(element.1.bind(cx)),
75            None => NIL,
76        },
77        None => NIL,
78    }
79}
80
81#[defun]
82pub(crate) fn local_variable_if_set_p(_sym: Symbol) -> bool {
83    // TODO: Implement buffer locals
84    false
85}
86
87#[defun]
88pub(crate) fn default_value<'ob>(
89    symbol: Symbol,
90    env: &Rt<Env>,
91    cx: &'ob Context,
92) -> Result<Object<'ob>> {
93    // TODO: Implement buffer locals
94    symbol_value(symbol, env, cx).ok_or_else(|| anyhow!("Void variable: {symbol}"))
95}
96
97#[defun]
98pub(crate) fn symbol_function<'ob>(symbol: Symbol, cx: &'ob Context) -> Object<'ob> {
99    match symbol.func(cx) {
100        Some(f) => f.into(),
101        None => NIL,
102    }
103}
104
105#[defun]
106pub(crate) fn symbol_value<'ob>(
107    symbol: Symbol,
108    env: &Rt<Env>,
109    cx: &'ob Context,
110) -> Option<Object<'ob>> {
111    env.vars.get(symbol).map(|x| x.bind(cx))
112}
113
114#[defun]
115pub(crate) fn symbol_name(symbol: Symbol<'_>) -> &str {
116    symbol.get().name()
117}
118
119#[defun]
120pub(crate) fn null(obj: Object) -> bool {
121    obj.is_nil()
122}
123
124#[defun]
125pub(crate) fn fboundp(symbol: Symbol) -> bool {
126    symbol.has_func()
127}
128
129#[defun]
130pub(crate) fn fmakunbound(symbol: Symbol) -> Symbol {
131    symbol.unbind_func();
132    symbol
133}
134
135#[defun]
136pub(crate) fn boundp(symbol: Symbol, env: &Rt<Env>) -> bool {
137    env.vars.get(symbol).is_some()
138}
139
140#[defun]
141pub(crate) fn makunbound<'ob>(symbol: Symbol<'ob>, env: &mut Rt<Env>) -> Symbol<'ob> {
142    env.vars.remove(symbol);
143    symbol
144}
145
146#[defun]
147pub(crate) fn default_boundp(symbol: Symbol, env: &Rt<Env>) -> bool {
148    env.vars.get(symbol).is_some()
149}
150
151#[defun]
152pub(crate) fn listp(object: Object) -> bool {
153    matches!(object.untag(), ObjectType::NIL | ObjectType::Cons(_))
154}
155
156#[defun]
157pub(crate) fn nlistp(object: Object) -> bool {
158    !listp(object)
159}
160
161#[defun]
162pub(crate) fn symbolp(object: Object) -> bool {
163    object.is_symbol()
164}
165
166#[defun]
167pub(crate) fn functionp(object: Object) -> bool {
168    match object.untag() {
169        ObjectType::ByteFn(_) | ObjectType::SubrFn(_) => true,
170        ObjectType::Cons(cons) => cons.car() == sym::CLOSURE || cons.car() == sym::LAMBDA,
171        ObjectType::Symbol(sym) => sym.has_func(),
172        _ => false,
173    }
174}
175
176#[defun]
177pub(crate) fn subrp(object: Object) -> bool {
178    object.is_subr_fn()
179}
180
181#[defun]
182pub(crate) fn stringp(object: Object) -> bool {
183    object.is_string()
184}
185
186#[defun]
187pub(crate) fn numberp(object: Object) -> bool {
188    matches!(object.untag(), ObjectType::Int(_) | ObjectType::Float(_))
189}
190
191#[defun]
192pub(crate) fn markerp(_: Object) -> bool {
193    // TODO: implement
194    false
195}
196
197#[defun]
198pub(crate) fn vectorp(object: Object) -> bool {
199    object.is_vec()
200}
201
202#[defun]
203pub(crate) fn recordp(object: Object) -> bool {
204    object.is_record()
205}
206
207#[defun]
208pub(crate) fn consp(object: Object) -> bool {
209    object.is_cons()
210}
211
212#[defun]
213pub(crate) fn keywordp(object: Object) -> bool {
214    match object.untag() {
215        ObjectType::Symbol(s) => s.name().starts_with(':'),
216        _ => false,
217    }
218}
219
220#[defun]
221pub(crate) fn integerp(object: Object) -> bool {
222    object.is_int()
223}
224
225#[defun]
226pub(crate) fn floatp(object: Object) -> bool {
227    object.is_float()
228}
229
230#[defun]
231pub(crate) fn atom(object: Object) -> bool {
232    object.is_not_cons()
233}
234
235#[defun]
236fn byte_code_function_p(object: Object) -> bool {
237    object.is_byte_fn()
238}
239
240#[defun]
241fn subr_native_elisp_p(_: Object) -> bool {
242    false
243}
244
245#[defun]
246fn bufferp(_object: Object) -> bool {
247    // TODO: Implement once buffers are added
248    false
249}
250
251#[defun]
252pub(crate) fn multibyte_string_p(object: Object) -> bool {
253    // TODO: handle multi-line strings
254    matches!(object.untag(), ObjectType::String(_))
255}
256
257#[defun]
258fn string_to_number<'ob>(string: &str, base: Option<i64>, cx: &'ob Context) -> Number<'ob> {
259    // TODO: Handle trailing characters, which should be ignored
260    let base = base.unwrap_or(10);
261    let string = string.trim();
262    match i64::from_str_radix(string, base as u32) {
263        Ok(x) => x.into(),
264        Err(_) => match string.parse::<f64>() {
265            Ok(x) => cx.add_as(x),
266            Err(_) => 0.into(),
267        },
268    }
269}
270
271#[defun]
272pub(crate) fn defvar<'ob>(
273    symbol: Symbol,
274    initvalue: Option<Object<'ob>>,
275    _docstring: Option<&str>,
276    env: &mut Rt<Env>,
277) -> Result<Object<'ob>> {
278    let value = initvalue.unwrap_or_default();
279    set(symbol, value, env)
280}
281
282#[defun]
283pub(crate) fn make_variable_buffer_local(variable: Symbol) -> Symbol {
284    // TODO: Implement
285    variable
286}
287
288#[defun]
289fn subr_arity<'ob>(subr: &SubrFn, cx: &'ob Context) -> Object<'ob> {
290    let min = subr.args.required as usize;
291    let max: Object = {
292        if subr.args.rest {
293            sym::MANY.into()
294        } else {
295            (min + subr.args.optional as usize).into()
296        }
297    };
298    Cons::new(min, max, cx).into()
299}
300
301#[defun]
302fn ash(value: i64, count: i64) -> i64 {
303    let shift = if count >= 0 { std::ops::Shl::shl } else { std::ops::Shr::shr };
304    let result = shift(value.abs(), count.abs());
305    if value >= 0 { result } else { -result }
306}
307
308#[defun]
309pub(crate) fn aset<'ob>(
310    array: Object<'ob>,
311    idx: usize,
312    newlet: Object<'ob>,
313) -> Result<Object<'ob>> {
314    match array.untag() {
315        ObjectType::Vec(vec) => {
316            let vec = vec.try_mut()?;
317            if idx < vec.len() {
318                vec[idx].set(newlet);
319                Ok(newlet)
320            } else {
321                let len = vec.len();
322                Err(anyhow!("index {idx} is out of bounds. Length was {len}"))
323            }
324        }
325        ObjectType::Record(vec) => {
326            let vec = vec.try_mut()?;
327            if idx < vec.len() {
328                vec[idx].set(newlet);
329                Ok(newlet)
330            } else {
331                let len = vec.len();
332                Err(anyhow!("index {idx} is out of bounds. Length was {len}"))
333            }
334        }
335        ObjectType::CharTable(table) => {
336            table.set(idx, newlet);
337            Ok(newlet)
338        }
339        x => Err(TypeError::new(Type::Sequence, x).into()),
340    }
341}
342
343#[defun]
344pub(crate) fn aref<'ob>(array: Object<'ob>, idx: usize, cx: &'ob Context) -> Result<Object<'ob>> {
345    match array.untag() {
346        ObjectType::Vec(vec) => match vec.get(idx) {
347            Some(x) => Ok(x.get()),
348            None => {
349                let len = vec.len();
350                Err(anyhow!("index {idx} is out of bounds. Length was {len}"))
351            }
352        },
353        ObjectType::Record(vec) => match vec.get(idx) {
354            Some(x) => Ok(x.get()),
355            None => {
356                let len = vec.len();
357                Err(anyhow!("index {idx} is out of bounds. Length was {len}"))
358            }
359        },
360        ObjectType::String(string) => match string.chars().nth(idx) {
361            Some(x) => Ok((i64::from(x as u32)).into()),
362            None => {
363                let len = string.len();
364                Err(anyhow!("index {idx} is out of bounds. Length was {len}"))
365            }
366        },
367        ObjectType::ByteString(string) => match string.get(idx) {
368            Some(x) => Ok((i64::from(*x)).into()),
369            None => {
370                let len = string.len();
371                Err(anyhow!("index {idx} is out of bounds. Length was {len}"))
372            }
373        },
374        ObjectType::ByteFn(fun) => match fun.index(idx, cx) {
375            Some(x) => Ok(x),
376            None => Err(anyhow!("index {idx} is out of bounds")),
377        },
378        ObjectType::CharTable(chartable) => Ok(chartable.get(idx)),
379        x => Err(TypeError::new(Type::Sequence, x).into()),
380    }
381}
382
383#[defun]
384fn type_of(object: Object) -> Object {
385    match object.untag() {
386        ObjectType::Int(_) => sym::INTEGER.into(),
387        ObjectType::Float(_) => sym::FLOATP.into(),
388        ObjectType::Symbol(_) => sym::SYMBOL.into(),
389        ObjectType::Cons(_) => sym::CONS.into(),
390        ObjectType::Vec(_) => sym::VECTOR.into(),
391        ObjectType::Record(x) => x.first().expect("record was missing type").get(),
392        ObjectType::ByteFn(_) => sym::COMPILED_FUNCTION.into(),
393        ObjectType::HashTable(_) => sym::HASH_TABLE.into(),
394        ObjectType::String(_) | ObjectType::ByteString(_) => sym::STRING.into(),
395        ObjectType::SubrFn(_) => sym::SUBR.into(),
396        ObjectType::Buffer(_) => sym::BUFFER.into(),
397        ObjectType::CharTable(_) => sym::CHAR_TABLE.into(),
398        ObjectType::BigInt(_) => sym::BIG_INT.into(),
399    }
400}
401
402#[defun]
403pub(crate) fn indirect_function<'ob>(object: Object<'ob>, cx: &'ob Context) -> Object<'ob> {
404    match object.untag() {
405        ObjectType::Symbol(sym) => match sym.follow_indirect(cx) {
406            Some(func) => func.into(),
407            None => NIL,
408        },
409        _ => object,
410    }
411}
412
413#[defun]
414pub(crate) fn provide<'ob>(feature: Symbol<'ob>, _subfeatures: Option<&Cons>) -> Symbol<'ob> {
415    let mut features = FEATURES.lock().unwrap();
416    // TODO: SYMBOL - need to trace this
417    let feat = unsafe { feature.with_lifetime() };
418    features.insert(feat);
419    feature
420}
421
422#[defun]
423pub(crate) fn car(list: List) -> Object {
424    match list.untag() {
425        ListType::Cons(cons) => cons.car(),
426        ListType::Nil => NIL,
427    }
428}
429
430#[defun]
431pub(crate) fn cdr(list: List) -> Object {
432    match list.untag() {
433        ListType::Cons(cons) => cons.cdr(),
434        ListType::Nil => NIL,
435    }
436}
437
438#[defun]
439pub(crate) fn car_safe(object: Object) -> Object {
440    match object.untag() {
441        ObjectType::Cons(cons) => cons.car(),
442        _ => NIL,
443    }
444}
445
446#[defun]
447pub(crate) fn cdr_safe(object: Object) -> Object {
448    match object.untag() {
449        ObjectType::Cons(cons) => cons.cdr(),
450        _ => NIL,
451    }
452}
453
454#[defun]
455pub(crate) fn setcar<'ob>(cell: &Cons, newcar: Object<'ob>) -> Result<Object<'ob>> {
456    cell.set_car(newcar)?;
457    Ok(newcar)
458}
459
460#[defun]
461pub(crate) fn setcdr<'ob>(cell: &Cons, newcdr: Object<'ob>) -> Result<Object<'ob>> {
462    cell.set_cdr(newcdr)?;
463    Ok(newcdr)
464}
465
466#[defun]
467pub(crate) fn cons<'ob>(car: Object, cdr: Object, cx: &'ob Context) -> Object<'ob> {
468    Cons::new(car, cdr, cx).into()
469}
470
471// Symbol with position
472#[defun]
473fn bare_symbol(sym: Symbol) -> Symbol {
474    // TODO: implement
475    sym
476}
477
478#[defun]
479fn symbol_with_pos_p(_sym: Object) -> bool {
480    // TODO: implement
481    false
482}
483
484#[derive(Debug, PartialEq)]
485pub(crate) struct LispError {
486    message: &'static Cons,
487}
488
489impl std::error::Error for LispError {}
490
491impl std::fmt::Display for LispError {
492    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
493        let Self { message } = self;
494        write!(f, "Error: {message}")
495    }
496}
497
498defsym!(WRONG_NUMBER_OF_ARGUMENTS);
499impl LispError {
500    pub(crate) fn new(message: &Cons) -> Self {
501        Self { message: unsafe { message.with_lifetime() } }
502    }
503
504    pub(crate) fn bind<'ob>(&self, cx: &'ob Context) -> &'ob Cons {
505        cx.bind(self.message)
506    }
507
508    pub(crate) fn arg_cnt<'ob, T>(
509        func: impl IntoObject<Out<'ob> = T>,
510        expected: u16,
511        actual: u16,
512        cx: &'ob Context,
513    ) -> Self {
514        let func = func.into_obj(cx);
515        let list = list![sym::WRONG_NUMBER_OF_ARGUMENTS, func, expected, actual; cx];
516        Self::new(list.try_into().unwrap())
517    }
518}
519
520unsafe impl Send for LispError {}
521unsafe impl Sync for LispError {}
522
523#[cfg(test)]
524mod test {
525    use super::*;
526    use crate::interpreter::assert_lisp;
527
528    #[test]
529    fn test_ash() {
530        assert_eq!(ash(4, 1), 8);
531        assert_eq!(ash(4, -1), 2);
532        assert_eq!(ash(-8, -1), -4);
533        assert_eq!(ash(256, -8), 1);
534        assert_eq!(ash(-8, 1), -16);
535    }
536
537    #[test]
538    fn test_functionp() {
539        assert_lisp("(functionp '(lambda nil))", "t");
540    }
541}
542
543defsym!(MANY);
544defsym!(INTEGER);
545defsym!(SYMBOL);
546defsym!(COMPILED_FUNCTION);
547defsym!(HASH_TABLE);
548defsym!(BUFFER);
549defsym!(SUBR);
550defsym!(CHAR_TABLE);
551defsym!(BIG_INT);