1use 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
17pub(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 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 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 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 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 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 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 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#[defun]
472fn bare_symbol(sym: Symbol) -> Symbol {
473 sym
475}
476
477#[defun]
478fn symbol_with_pos_p(_sym: Object) -> bool {
479 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);