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    matches!(object.untag(), ObjectType::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    matches!(object.untag(), ObjectType::SubrFn(_))
179}
180
181#[defun]
182pub(crate) fn stringp(object: Object) -> bool {
183    matches!(object.untag(), ObjectType::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    matches!(object.untag(), ObjectType::Vec(_))
200}
201
202#[defun]
203pub(crate) fn recordp(object: Object) -> bool {
204    matches!(object.untag(), ObjectType::Record(_))
205}
206
207#[defun]
208pub(crate) fn consp(object: Object) -> bool {
209    matches!(object.untag(), ObjectType::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    matches!(object.untag(), ObjectType::Int(_))
223}
224
225#[defun]
226pub(crate) fn floatp(object: Object) -> bool {
227    matches!(object.untag(), ObjectType::Float(_))
228}
229
230#[defun]
231pub(crate) fn atom(object: Object) -> bool {
232    !consp(object)
233}
234
235#[defun]
236fn byte_code_function_p(object: Object) -> bool {
237    matches!(object.untag(), ObjectType::ByteFn(_))
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    matches!(object.untag(), ObjectType::String(_))
254}
255
256#[defun]
257fn string_to_number<'ob>(string: &str, base: Option<i64>, cx: &'ob Context) -> Number<'ob> {
258    // TODO: Handle trailing characters, which should be ignored
259    let base = base.unwrap_or(10);
260    let string = string.trim();
261    match i64::from_str_radix(string, base as u32) {
262        Ok(x) => x.into(),
263        Err(_) => match string.parse::<f64>() {
264            Ok(x) => cx.add_as(x),
265            Err(_) => 0.into(),
266        },
267    }
268}
269
270#[defun]
271pub(crate) fn defvar<'ob>(
272    symbol: Symbol,
273    initvalue: Option<Object<'ob>>,
274    _docstring: Option<&str>,
275    env: &mut Rt<Env>,
276) -> Result<Object<'ob>> {
277    let value = initvalue.unwrap_or_default();
278    set(symbol, value, env)
279}
280
281#[defun]
282pub(crate) fn make_variable_buffer_local(variable: Symbol) -> Symbol {
283    // TODO: Implement
284    variable
285}
286
287#[defun]
288fn subr_arity<'ob>(subr: &SubrFn, cx: &'ob Context) -> Object<'ob> {
289    let min = subr.args.required as usize;
290    let max: Object = {
291        if subr.args.rest {
292            sym::MANY.into()
293        } else {
294            (min + subr.args.optional as usize).into()
295        }
296    };
297    Cons::new(min, max, cx).into()
298}
299
300#[defun]
301fn ash(value: i64, count: i64) -> i64 {
302    let shift = if count >= 0 { std::ops::Shl::shl } else { std::ops::Shr::shr };
303    let result = shift(value.abs(), count.abs());
304    if value >= 0 { result } else { -result }
305}
306
307#[defun]
308pub(crate) fn aset<'ob>(
309    array: Object<'ob>,
310    idx: usize,
311    newlet: Object<'ob>,
312) -> Result<Object<'ob>> {
313    match array.untag() {
314        ObjectType::Vec(vec) => {
315            let vec = vec.try_mut()?;
316            if idx < vec.len() {
317                vec[idx].set(newlet);
318                Ok(newlet)
319            } else {
320                let len = vec.len();
321                Err(anyhow!("index {idx} is out of bounds. Length was {len}"))
322            }
323        }
324        ObjectType::Record(vec) => {
325            let vec = vec.try_mut()?;
326            if idx < vec.len() {
327                vec[idx].set(newlet);
328                Ok(newlet)
329            } else {
330                let len = vec.len();
331                Err(anyhow!("index {idx} is out of bounds. Length was {len}"))
332            }
333        }
334        ObjectType::CharTable(table) => {
335            table.set(idx, newlet);
336            Ok(newlet)
337        }
338        x => Err(TypeError::new(Type::Sequence, x).into()),
339    }
340}
341
342#[defun]
343pub(crate) fn aref<'ob>(array: Object<'ob>, idx: usize, cx: &'ob Context) -> Result<Object<'ob>> {
344    match array.untag() {
345        ObjectType::Vec(vec) => match vec.get(idx) {
346            Some(x) => Ok(x.get()),
347            None => {
348                let len = vec.len();
349                Err(anyhow!("index {idx} is out of bounds. Length was {len}"))
350            }
351        },
352        ObjectType::Record(vec) => match vec.get(idx) {
353            Some(x) => Ok(x.get()),
354            None => {
355                let len = vec.len();
356                Err(anyhow!("index {idx} is out of bounds. Length was {len}"))
357            }
358        },
359        ObjectType::String(string) => match string.chars().nth(idx) {
360            Some(x) => Ok((i64::from(x as u32)).into()),
361            None => {
362                let len = string.len();
363                Err(anyhow!("index {idx} is out of bounds. Length was {len}"))
364            }
365        },
366        ObjectType::ByteString(string) => match string.get(idx) {
367            Some(x) => Ok((i64::from(*x)).into()),
368            None => {
369                let len = string.len();
370                Err(anyhow!("index {idx} is out of bounds. Length was {len}"))
371            }
372        },
373        ObjectType::ByteFn(fun) => match fun.index(idx, cx) {
374            Some(x) => Ok(x),
375            None => Err(anyhow!("index {idx} is out of bounds")),
376        },
377        ObjectType::CharTable(chartable) => Ok(chartable.get(idx)),
378        x => Err(TypeError::new(Type::Sequence, x).into()),
379    }
380}
381
382#[defun]
383fn type_of(object: Object) -> Object {
384    match object.untag() {
385        ObjectType::Int(_) => sym::INTEGER.into(),
386        ObjectType::Float(_) => sym::FLOATP.into(),
387        ObjectType::Symbol(_) => sym::SYMBOL.into(),
388        ObjectType::Cons(_) => sym::CONS.into(),
389        ObjectType::Vec(_) => sym::VECTOR.into(),
390        ObjectType::Record(x) => x.first().expect("record was missing type").get(),
391        ObjectType::ByteFn(_) => sym::COMPILED_FUNCTION.into(),
392        ObjectType::HashTable(_) => sym::HASH_TABLE.into(),
393        ObjectType::String(_) | ObjectType::ByteString(_) => sym::STRING.into(),
394        ObjectType::SubrFn(_) => sym::SUBR.into(),
395        ObjectType::Buffer(_) => sym::BUFFER.into(),
396        ObjectType::CharTable(_) => sym::CHAR_TABLE.into(),
397        ObjectType::BigInt(_) => sym::BIG_INT.into(),
398    }
399}
400
401#[defun]
402pub(crate) fn indirect_function<'ob>(object: Object<'ob>, cx: &'ob Context) -> Object<'ob> {
403    match object.untag() {
404        ObjectType::Symbol(sym) => match sym.follow_indirect(cx) {
405            Some(func) => func.into(),
406            None => NIL,
407        },
408        _ => object,
409    }
410}
411
412#[defun]
413pub(crate) fn provide<'ob>(feature: Symbol<'ob>, _subfeatures: Option<&Cons>) -> Symbol<'ob> {
414    let mut features = FEATURES.lock().unwrap();
415    // TODO: SYMBOL - need to trace this
416    let feat = unsafe { feature.with_lifetime() };
417    features.insert(feat);
418    feature
419}
420
421#[defun]
422pub(crate) fn car(list: List) -> Object {
423    match list.untag() {
424        ListType::Cons(cons) => cons.car(),
425        ListType::Nil => NIL,
426    }
427}
428
429#[defun]
430pub(crate) fn cdr(list: List) -> Object {
431    match list.untag() {
432        ListType::Cons(cons) => cons.cdr(),
433        ListType::Nil => NIL,
434    }
435}
436
437#[defun]
438pub(crate) fn car_safe(object: Object) -> Object {
439    match object.untag() {
440        ObjectType::Cons(cons) => cons.car(),
441        _ => NIL,
442    }
443}
444
445#[defun]
446pub(crate) fn cdr_safe(object: Object) -> Object {
447    match object.untag() {
448        ObjectType::Cons(cons) => cons.cdr(),
449        _ => NIL,
450    }
451}
452
453#[defun]
454pub(crate) fn setcar<'ob>(cell: &Cons, newcar: Object<'ob>) -> Result<Object<'ob>> {
455    cell.set_car(newcar)?;
456    Ok(newcar)
457}
458
459#[defun]
460pub(crate) fn setcdr<'ob>(cell: &Cons, newcdr: Object<'ob>) -> Result<Object<'ob>> {
461    cell.set_cdr(newcdr)?;
462    Ok(newcdr)
463}
464
465#[defun]
466pub(crate) fn cons<'ob>(car: Object, cdr: Object, cx: &'ob Context) -> Object<'ob> {
467    Cons::new(car, cdr, cx).into()
468}
469
470// Symbol with position
471#[defun]
472fn bare_symbol(sym: Symbol) -> Symbol {
473    // TODO: implement
474    sym
475}
476
477#[defun]
478fn symbol_with_pos_p(_sym: Object) -> bool {
479    // TODO: implement
480    false
481}
482
483#[derive(Debug, PartialEq)]
484pub(crate) struct LispError {
485    message: &'static Cons,
486}
487
488impl std::error::Error for LispError {}
489
490impl std::fmt::Display for LispError {
491    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
492        let Self { message } = self;
493        write!(f, "Error: {message}")
494    }
495}
496
497defsym!(WRONG_NUMBER_OF_ARGUMENTS);
498impl LispError {
499    pub(crate) fn new(message: &Cons) -> Self {
500        Self { message: unsafe { message.with_lifetime() } }
501    }
502
503    pub(crate) fn bind<'ob>(&self, cx: &'ob Context) -> &'ob Cons {
504        cx.bind(self.message)
505    }
506
507    pub(crate) fn arg_cnt<'ob, T>(
508        func: impl IntoObject<Out<'ob> = T>,
509        expected: u16,
510        actual: u16,
511        cx: &'ob Context,
512    ) -> Self {
513        let func = func.into_obj(cx);
514        let list = list![sym::WRONG_NUMBER_OF_ARGUMENTS, func, expected, actual; cx];
515        Self::new(list.try_into().unwrap())
516    }
517}
518
519unsafe impl Send for LispError {}
520unsafe impl Sync for LispError {}
521
522#[cfg(test)]
523mod test {
524    use super::*;
525    use crate::interpreter::assert_lisp;
526
527    #[test]
528    fn test_ash() {
529        assert_eq!(ash(4, 1), 8);
530        assert_eq!(ash(4, -1), 2);
531        assert_eq!(ash(-8, -1), -4);
532        assert_eq!(ash(256, -8), 1);
533        assert_eq!(ash(-8, 1), -16);
534    }
535
536    #[test]
537    fn test_functionp() {
538        assert_lisp("(functionp '(lambda nil))", "t");
539    }
540}
541
542defsym!(MANY);
543defsym!(INTEGER);
544defsym!(SYMBOL);
545defsym!(COMPILED_FUNCTION);
546defsym!(HASH_TABLE);
547defsym!(BUFFER);
548defsym!(SUBR);
549defsym!(CHAR_TABLE);
550defsym!(BIG_INT);