1use crate::interpreter::value::*;
11use crate::interpreter::{BuiltinContext, S4ClassDef, S4GenericDef};
12use minir_macros::{builtin, interpreter_builtin};
13
14fn extract_slots(val: &RValue) -> Vec<(String, String)> {
20 match val {
21 RValue::List(list) => list
22 .values
23 .iter()
24 .filter_map(|(name, v)| {
25 let slot_name = name.as_ref()?;
26 let type_name = match v {
27 RValue::Vector(rv) => rv.as_character_scalar(),
28 _ => None,
29 }
30 .unwrap_or_else(|| "ANY".to_string());
31 Some((slot_name.clone(), type_name))
32 })
33 .collect(),
34 RValue::Vector(rv) => {
35 let names = rv
37 .attrs
38 .as_ref()
39 .and_then(|a| a.get("names"))
40 .and_then(|v| {
41 if let RValue::Vector(nv) = v {
42 if let Vector::Character(c) = &nv.inner {
43 Some(c.clone())
44 } else {
45 None
46 }
47 } else {
48 None
49 }
50 });
51 if let (Some(names), Vector::Character(types)) = (names, &rv.inner) {
52 names
53 .iter()
54 .zip(types.iter())
55 .filter_map(|(n, t)| {
56 let slot_name = n.as_ref()?;
57 let type_name = t.clone().unwrap_or_else(|| "ANY".to_string());
58 Some((slot_name.clone(), type_name))
59 })
60 .collect()
61 } else {
62 Vec::new()
63 }
64 }
65 _ => Vec::new(),
66 }
67}
68
69fn extract_character_vec(val: &RValue) -> Vec<String> {
71 match val {
72 RValue::Vector(rv) => rv.to_characters().into_iter().flatten().collect(),
73 _ => Vec::new(),
74 }
75}
76
77fn extract_prototype(val: &RValue) -> Vec<(String, RValue)> {
79 match val {
80 RValue::List(list) => list
81 .values
82 .iter()
83 .filter_map(|(name, v)| Some((name.as_ref()?.clone(), v.clone())))
84 .collect(),
85 _ => Vec::new(),
86 }
87}
88
89fn inheritance_chain(
92 class_name: &str,
93 registry: &std::collections::HashMap<String, S4ClassDef>,
94) -> Vec<String> {
95 let mut chain = vec![class_name.to_string()];
96 let mut visited = std::collections::HashSet::new();
97 visited.insert(class_name.to_string());
98 let mut queue = std::collections::VecDeque::new();
99 queue.push_back(class_name.to_string());
100
101 while let Some(current) = queue.pop_front() {
102 if let Some(def) = registry.get(¤t) {
103 for parent in &def.contains {
104 if visited.insert(parent.clone()) {
105 chain.push(parent.clone());
106 queue.push_back(parent.clone());
107 }
108 }
109 }
110 }
111
112 chain
113}
114
115fn all_slots_for_class(
117 class_name: &str,
118 registry: &std::collections::HashMap<String, S4ClassDef>,
119) -> Vec<(String, String)> {
120 let chain = inheritance_chain(class_name, registry);
121 let mut slots = Vec::new();
122 let mut seen = std::collections::HashSet::new();
123 for class in chain.iter().rev() {
125 if let Some(def) = registry.get(class) {
126 for (name, typ) in &def.slots {
127 if seen.insert(name.clone()) {
128 slots.push((name.clone(), typ.clone()));
129 }
130 }
131 }
132 }
133 slots
134}
135
136fn all_prototypes_for_class(
138 class_name: &str,
139 registry: &std::collections::HashMap<String, S4ClassDef>,
140) -> Vec<(String, RValue)> {
141 let chain = inheritance_chain(class_name, registry);
142 let mut proto = Vec::new();
143 let mut seen = std::collections::HashSet::new();
144 for class in chain.iter().rev() {
146 if let Some(def) = registry.get(class) {
147 for (name, val) in &def.prototype {
148 if seen.insert(name.clone()) {
149 proto.push((name.clone(), val.clone()));
150 }
151 }
152 }
153 }
154 proto
155}
156
157#[interpreter_builtin(name = "setClass", min_args = 1, namespace = "methods")]
175fn interp_set_class_s4(
176 args: &[RValue],
177 named: &[(String, RValue)],
178 context: &BuiltinContext,
179) -> Result<RValue, RError> {
180 let class_name = args
181 .first()
182 .and_then(|v| v.as_vector()?.as_character_scalar())
183 .ok_or_else(|| {
184 RError::new(
185 RErrorKind::Argument,
186 "setClass() requires a character string for the class name",
187 )
188 })?;
189
190 let slots_val = named
194 .iter()
195 .find(|(n, _)| n == "representation" || n == "slots")
196 .map(|(_, v)| v)
197 .or_else(|| args.get(1));
198
199 let slots = slots_val.map(extract_slots).unwrap_or_default();
200
201 let contains = named
203 .iter()
204 .find(|(n, _)| n == "contains")
205 .map(|(_, v)| v)
206 .or_else(|| args.get(2))
207 .map(extract_character_vec)
208 .unwrap_or_default();
209
210 let prototype = named
212 .iter()
213 .find(|(n, _)| n == "prototype")
214 .map(|(_, v)| extract_prototype(v))
215 .unwrap_or_default();
216
217 let is_virtual = named
219 .iter()
220 .find(|(n, _)| n == "virtual")
221 .and_then(|(_, v)| v.as_vector()?.as_logical_scalar())
222 .unwrap_or(false);
223
224 let validity = named
226 .iter()
227 .find(|(n, _)| n == "validity")
228 .map(|(_, v)| v.clone())
229 .filter(|v| matches!(v, RValue::Function(_)));
230
231 let class_def = S4ClassDef {
232 name: class_name.clone(),
233 slots,
234 contains,
235 prototype,
236 is_virtual,
237 validity,
238 };
239
240 context.with_interpreter(|interp| {
242 interp
243 .s4_classes
244 .borrow_mut()
245 .insert(class_name.clone(), class_def);
246 });
247
248 Ok(RValue::vec(Vector::Character(
249 vec![Some(class_name)].into(),
250 )))
251}
252
253#[interpreter_builtin(name = "new", min_args = 1, namespace = "methods")]
266fn interp_new(
267 args: &[RValue],
268 named: &[(String, RValue)],
269 context: &BuiltinContext,
270) -> Result<RValue, RError> {
271 let class_name = args
272 .first()
273 .and_then(|v| v.as_vector()?.as_character_scalar())
274 .ok_or_else(|| {
275 RError::new(
276 RErrorKind::Argument,
277 "new() requires a character string for the class name",
278 )
279 })?;
280
281 context.with_interpreter(|interp| {
282 let registry = interp.s4_classes.borrow();
283
284 if let Some(class_def) = registry.get(&class_name) {
285 if class_def.is_virtual {
287 return Err(RError::new(
288 RErrorKind::Other,
289 format!(
290 "trying to generate an object from a virtual class (\"{}\")",
291 class_name
292 ),
293 ));
294 }
295
296 let all_slots = all_slots_for_class(&class_name, ®istry);
298 let all_protos = all_prototypes_for_class(&class_name, ®istry);
299 let slot_names: std::collections::HashSet<&str> =
300 all_slots.iter().map(|(n, _)| n.as_str()).collect();
301
302 let mut errors = Vec::new();
304 for (name, _) in named {
305 if !slot_names.contains(name.as_str()) {
306 errors.push(format!("\"{}\"", name));
307 }
308 }
309 if !errors.is_empty() {
310 return Err(RError::new(
311 RErrorKind::Argument,
312 format!(
313 "invalid name{} for slot{} of class \"{}\": {}.\n \
314 Valid slots are: {}",
315 if errors.len() > 1 { "s" } else { "" },
316 if errors.len() > 1 { "s" } else { "" },
317 class_name,
318 errors.join(", "),
319 all_slots
320 .iter()
321 .map(|(n, _)| format!("\"{}\"", n))
322 .collect::<Vec<_>>()
323 .join(", "),
324 ),
325 ));
326 }
327
328 let named_map: std::collections::HashMap<&str, &RValue> =
331 named.iter().map(|(n, v)| (n.as_str(), v)).collect();
332
333 let proto_map: std::collections::HashMap<&str, &RValue> =
334 all_protos.iter().map(|(n, v)| (n.as_str(), v)).collect();
335
336 let mut values: Vec<(Option<String>, RValue)> = Vec::new();
337 for (slot_name, _slot_type) in &all_slots {
338 let val = if let Some(v) = named_map.get(slot_name.as_str()) {
339 (*v).clone()
340 } else if let Some(v) = proto_map.get(slot_name.as_str()) {
341 (*v).clone()
342 } else {
343 RValue::Null
345 };
346 values.push((Some(slot_name.clone()), val));
347 }
348
349 let chain = inheritance_chain(&class_name, ®istry);
351 let class_vec: Vec<Option<String>> = chain.into_iter().map(Some).collect();
352
353 let mut list = RList::new(values);
354 list.set_attr(
355 "class".to_string(),
356 RValue::vec(Vector::Character(class_vec.into())),
357 );
358
359 if let Some(ref validity_fn) = class_def.validity {
363 let obj = RValue::List(list.clone());
364 let result = interp
365 .call_function(validity_fn, &[obj], &[], &interp.global_env)
366 .map_err(RError::from)?;
367 if let Some(vec) = result.as_vector() {
368 match vec {
369 Vector::Logical(vals) if vals.first() == Some(&Some(true)) => {}
371 Vector::Character(vals) => {
373 if let Some(Some(msg)) = vals.first() {
374 return Err(RError::new(
375 RErrorKind::Other,
376 format!("invalid class \"{}\" object: {}", class_name, msg),
377 ));
378 }
379 }
380 _ => {}
381 }
382 }
383 }
384
385 Ok(RValue::List(list))
386 } else {
387 let mut values: Vec<(Option<String>, RValue)> = Vec::new();
389 for arg in args.iter().skip(1) {
390 values.push((None, arg.clone()));
391 }
392 for (name, val) in named {
393 values.push((Some(name.clone()), val.clone()));
394 }
395
396 let mut list = RList::new(values);
397 list.set_attr(
398 "class".to_string(),
399 RValue::vec(Vector::Character(vec![Some(class_name)].into())),
400 );
401
402 Ok(RValue::List(list))
403 }
404 })
405}
406
407#[interpreter_builtin(min_args = 1, namespace = "methods")]
421fn interp_is(
422 args: &[RValue],
423 _named: &[(String, RValue)],
424 context: &BuiltinContext,
425) -> Result<RValue, RError> {
426 let object = args
427 .first()
428 .ok_or_else(|| RError::new(RErrorKind::Argument, "is() requires at least one argument"))?;
429
430 let class2 = match args.get(1) {
431 Some(v) => v
432 .as_vector()
433 .and_then(|v| v.as_character_scalar())
434 .ok_or_else(|| {
435 RError::new(
436 RErrorKind::Argument,
437 "is() requires a character string for class2",
438 )
439 })?,
440 None => {
442 let classes = get_class(object);
443 if classes.is_empty() {
444 return Ok(RValue::vec(Vector::Character(vec![None].into())));
445 }
446 return Ok(RValue::vec(Vector::Character(
447 classes.into_iter().map(Some).collect::<Vec<_>>().into(),
448 )));
449 }
450 };
451
452 let classes = get_class(object);
453
454 if classes.iter().any(|c| c == &class2) {
456 return Ok(RValue::vec(Vector::Logical(vec![Some(true)].into())));
457 }
458
459 let result = context.with_interpreter(|interp| {
461 let registry = interp.s4_classes.borrow();
462 for obj_class in &classes {
463 let chain = inheritance_chain(obj_class, ®istry);
464 if chain.iter().any(|c| c == &class2) {
465 return true;
466 }
467 }
468 false
469 });
470
471 Ok(RValue::vec(Vector::Logical(vec![Some(result)].into())))
472}
473
474#[interpreter_builtin(name = "setGeneric", min_args = 1, namespace = "methods")]
487fn interp_set_generic(
488 args: &[RValue],
489 named: &[(String, RValue)],
490 context: &BuiltinContext,
491) -> Result<RValue, RError> {
492 let name = args
493 .first()
494 .and_then(|v| v.as_vector()?.as_character_scalar())
495 .ok_or_else(|| {
496 RError::new(
497 RErrorKind::Argument,
498 "setGeneric() requires a character string for the generic name",
499 )
500 })?;
501
502 let def = args.get(1).cloned().or_else(|| {
504 named
505 .iter()
506 .find(|(n, _)| n == "def")
507 .map(|(_, v)| v.clone())
508 });
509
510 let default_fn = def.filter(|v| matches!(v, RValue::Function(_)));
511
512 context.with_interpreter(|interp| {
514 interp.s4_generics.borrow_mut().insert(
515 name.clone(),
516 S4GenericDef {
517 name: name.clone(),
518 default: default_fn.clone(),
519 },
520 );
521 });
522
523 if let Some(func) = default_fn {
527 context.env().set(name.clone(), func);
528 }
529
530 Ok(RValue::vec(Vector::Character(vec![Some(name)].into())))
531}
532
533#[interpreter_builtin(name = "setMethod", min_args = 1, namespace = "methods")]
544fn interp_set_method(
545 args: &[RValue],
546 named: &[(String, RValue)],
547 context: &BuiltinContext,
548) -> Result<RValue, RError> {
549 let f = args
550 .first()
551 .and_then(|v| v.as_vector()?.as_character_scalar())
552 .ok_or_else(|| {
553 RError::new(
554 RErrorKind::Argument,
555 "setMethod() requires a character string for the function name",
556 )
557 })?;
558
559 let sig_val = args.get(1).cloned().or_else(|| {
561 named
562 .iter()
563 .find(|(n, _)| n == "signature")
564 .map(|(_, v)| v.clone())
565 });
566
567 let signature: Vec<String> = sig_val
568 .as_ref()
569 .map(extract_character_vec)
570 .unwrap_or_default();
571
572 let def = args.get(2).cloned().or_else(|| {
574 named
575 .iter()
576 .find(|(n, _)| n == "def")
577 .map(|(_, v)| v.clone())
578 });
579
580 if let Some(func) = def.filter(|v| matches!(v, RValue::Function(_))) {
581 context.with_interpreter(|interp| {
582 interp
584 .s4_methods
585 .borrow_mut()
586 .insert((f.clone(), signature), func.clone());
587
588 let generics = interp.s4_generics.borrow();
591 if !generics.contains_key(&f) {
592 drop(generics);
593 context.env().set(f.clone(), func);
594 }
595 });
596 }
597
598 Ok(RValue::vec(Vector::Character(vec![Some(f)].into())))
599}
600
601#[interpreter_builtin(name = "isVirtualClass", min_args = 1, namespace = "methods")]
612fn interp_is_virtual_class(
613 args: &[RValue],
614 _named: &[(String, RValue)],
615 context: &BuiltinContext,
616) -> Result<RValue, RError> {
617 let class_name = args
618 .first()
619 .and_then(|v| v.as_vector()?.as_character_scalar())
620 .unwrap_or_default();
621
622 let is_virtual = context.with_interpreter(|interp| {
623 interp
624 .s4_classes
625 .borrow()
626 .get(&class_name)
627 .is_some_and(|def| def.is_virtual)
628 });
629
630 Ok(RValue::vec(Vector::Logical(vec![Some(is_virtual)].into())))
631}
632
633#[interpreter_builtin(name = "validObject", min_args = 1, namespace = "methods")]
640fn interp_valid_object(
641 args: &[RValue],
642 _named: &[(String, RValue)],
643 context: &BuiltinContext,
644) -> Result<RValue, RError> {
645 let object = args.first().cloned().ok_or_else(|| {
646 RError::new(
647 RErrorKind::Argument,
648 "validObject() requires an object argument",
649 )
650 })?;
651
652 let classes = get_class(&object);
653 if let Some(class_name) = classes.first() {
654 context.with_interpreter(|interp| {
655 let registry = interp.s4_classes.borrow();
656 if let Some(def) = registry.get(class_name) {
657 if let Some(ref validity_fn) = def.validity {
658 let validity_fn = validity_fn.clone();
659 drop(registry);
660 let result = interp
661 .call_function(
662 &validity_fn,
663 std::slice::from_ref(&object),
664 &[],
665 &interp.global_env,
666 )
667 .map_err(RError::from)?;
668 if let Some(vec) = result.as_vector() {
669 match vec {
670 Vector::Logical(vals) if vals.first() == Some(&Some(true)) => {}
672 Vector::Character(vals) => {
674 if let Some(Some(msg)) = vals.first() {
675 return Err(RError::new(
676 RErrorKind::Other,
677 format!("invalid class \"{}\" object: {}", class_name, msg),
678 ));
679 }
680 }
681 _ => {}
682 }
683 }
684 }
685 }
686 Ok(())
687 })?;
688 }
689
690 Ok(object)
691}
692
693#[interpreter_builtin(name = "setValidity", min_args = 1, namespace = "methods")]
701fn interp_set_validity(
702 args: &[RValue],
703 named: &[(String, RValue)],
704 context: &BuiltinContext,
705) -> Result<RValue, RError> {
706 let class_name = args
707 .first()
708 .and_then(|v| v.as_vector()?.as_character_scalar())
709 .ok_or_else(|| {
710 RError::new(
711 RErrorKind::Argument,
712 "setValidity() requires a character string for the class name",
713 )
714 })?;
715
716 let method = args.get(1).cloned().or_else(|| {
717 named
718 .iter()
719 .find(|(n, _)| n == "method")
720 .map(|(_, v)| v.clone())
721 });
722
723 if let Some(func) = method.filter(|v| matches!(v, RValue::Function(_))) {
724 context.with_interpreter(|interp| {
725 let mut registry = interp.s4_classes.borrow_mut();
726 if let Some(def) = registry.get_mut(&class_name) {
727 def.validity = Some(func);
728 }
729 });
730 }
731
732 Ok(RValue::vec(Vector::Character(
733 vec![Some(class_name)].into(),
734 )))
735}
736
737#[interpreter_builtin(name = "showClass", min_args = 1, namespace = "methods")]
744fn interp_show_class(
745 args: &[RValue],
746 _named: &[(String, RValue)],
747 context: &BuiltinContext,
748) -> Result<RValue, RError> {
749 let class_name = args
750 .first()
751 .and_then(|v| v.as_vector()?.as_character_scalar())
752 .ok_or_else(|| {
753 RError::new(
754 RErrorKind::Argument,
755 "showClass() requires a character string for the class name",
756 )
757 })?;
758
759 context.with_interpreter(|interp| {
760 let registry = interp.s4_classes.borrow();
761 if let Some(def) = registry.get(&class_name) {
762 interp.write_stderr(&format!("Class \"{}\"\n", class_name));
763 if !def.slots.is_empty() {
764 interp.write_stderr("Slots:\n");
765 for (name, typ) in &def.slots {
766 interp.write_stderr(&format!(" Name: {} Class: {}\n", name, typ));
767 }
768 }
769 if !def.contains.is_empty() {
770 interp.write_stderr(&format!("Extends: {}\n", def.contains.join(", ")));
771 }
772 if def.is_virtual {
773 interp.write_stderr("(virtual class)\n");
774 }
775 } else {
776 interp.write_stderr(&format!(
777 "Class \"{}\" (not registered in S4 class registry)\n",
778 class_name
779 ));
780 }
781 });
782
783 Ok(RValue::Null)
784}
785
786#[interpreter_builtin(name = "existsMethod", min_args = 1, namespace = "methods")]
794fn interp_exists_method(
795 args: &[RValue],
796 _named: &[(String, RValue)],
797 context: &BuiltinContext,
798) -> Result<RValue, RError> {
799 let f = args
800 .first()
801 .and_then(|v| v.as_vector()?.as_character_scalar())
802 .unwrap_or_default();
803
804 let sig_val = args.get(1);
805 let signature: Vec<String> = sig_val.map(extract_character_vec).unwrap_or_default();
806
807 let exists = context.with_interpreter(|interp| {
808 let methods = interp.s4_methods.borrow();
809 methods.contains_key(&(f, signature))
810 });
811
812 Ok(RValue::vec(Vector::Logical(vec![Some(exists)].into())))
813}
814
815#[builtin(min_args = 2, namespace = "methods")]
828fn builtin_slot(args: &[RValue], _named: &[(String, RValue)]) -> Result<RValue, RError> {
829 let object = args
830 .first()
831 .ok_or_else(|| RError::new(RErrorKind::Argument, "slot() requires an object argument"))?;
832
833 let slot_name = args
834 .get(1)
835 .and_then(|v| v.as_vector()?.as_character_scalar())
836 .ok_or_else(|| {
837 RError::new(
838 RErrorKind::Argument,
839 "slot() requires a character string for the slot name",
840 )
841 })?;
842
843 match object {
844 RValue::List(list) => {
845 for (name, val) in &list.values {
846 if name.as_deref() == Some(&slot_name) {
847 return Ok(val.clone());
848 }
849 }
850 Err(RError::new(
851 RErrorKind::Name,
852 format!(
853 "no slot of name \"{}\" for this object of class \"{}\"",
854 slot_name,
855 get_class(object).first().unwrap_or(&"unknown".to_string())
856 ),
857 ))
858 }
859 _ => Err(RError::new(
860 RErrorKind::Type,
861 "slot() requires an S4 object (list with class attribute)",
862 )),
863 }
864}
865
866#[builtin(name = "slot<-", min_args = 3, namespace = "methods")]
875fn builtin_slot_set(args: &[RValue], _named: &[(String, RValue)]) -> Result<RValue, RError> {
876 let object = args
877 .first()
878 .ok_or_else(|| RError::new(RErrorKind::Argument, "slot<-() requires an object argument"))?;
879
880 let slot_name = args
881 .get(1)
882 .and_then(|v| v.as_vector()?.as_character_scalar())
883 .ok_or_else(|| {
884 RError::new(
885 RErrorKind::Argument,
886 "slot<-() requires a character string for the slot name",
887 )
888 })?;
889
890 let value = args.get(2).cloned().unwrap_or(RValue::Null);
891
892 match object {
893 RValue::List(list) => {
894 let mut new_list = list.clone();
895 let mut found = false;
896 for entry in &mut new_list.values {
897 if entry.0.as_deref() == Some(&slot_name) {
898 entry.1 = value.clone();
899 found = true;
900 break;
901 }
902 }
903 if !found {
904 new_list.values.push((Some(slot_name.to_string()), value));
905 }
906 Ok(RValue::List(new_list))
907 }
908 _ => Err(RError::new(
909 RErrorKind::Type,
910 "slot<-() requires an S4 object (list with class attribute)",
911 )),
912 }
913}
914
915#[builtin(min_args = 0, namespace = "methods")]
927fn builtin_representation(_args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
928 let names: Vec<Option<String>> = named.iter().map(|(n, _)| Some(n.clone())).collect();
929 let values: Vec<Option<String>> = named
930 .iter()
931 .map(|(_, v)| {
932 v.as_vector()
933 .and_then(|rv| rv.as_character_scalar())
934 .or_else(|| Some(format!("{}", v)))
935 })
936 .collect();
937
938 let mut rv = RVector::from(Vector::Character(values.into()));
939 rv.set_attr(
940 "names".to_string(),
941 RValue::vec(Vector::Character(names.into())),
942 );
943 Ok(RValue::Vector(rv))
944}
945
946