1#[cfg(not(feature = "native"))]
6use tracing::debug;
7
8use crate::interpreter::value::*;
9use crate::interpreter::BuiltinContext;
10use minir_macros::{builtin, interpreter_builtin, stub_builtin};
11
12stub_builtin!("installed.packages");
15stub_builtin!("install.packages");
16
17#[cfg(not(feature = "native"))]
22#[builtin(name = ".Call")]
30fn builtin_dot_call(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
31 let name = args
32 .first()
33 .and_then(|v| v.as_vector()?.as_character_scalar())
34 .unwrap_or_else(|| "<native>".to_string());
35 debug!(
36 symbol = name.as_str(),
37 nargs = args.len().saturating_sub(1),
38 "native .Call"
39 );
40 Err(RError::new(
41 RErrorKind::Other,
42 format!(".Call(\"{name}\") is not available — miniR cannot call compiled C/C++ code"),
43 ))
44}
45
46#[cfg(not(feature = "native"))]
47#[builtin(name = ".C")]
54fn builtin_dot_c(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
55 let name = args
56 .first()
57 .and_then(|v| v.as_vector()?.as_character_scalar())
58 .unwrap_or_else(|| "<native>".to_string());
59 debug!(
60 symbol = name.as_str(),
61 nargs = args.len().saturating_sub(1),
62 "native .C"
63 );
64 Err(RError::new(
65 RErrorKind::Other,
66 format!(".C(\"{name}\") is not available — miniR cannot call compiled C/C++ code"),
67 ))
68}
69
70#[builtin(name = ".Internal")]
76fn builtin_dot_internal(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
77 Err(RError::new(
78 RErrorKind::Other,
79 ".Internal() is not available in miniR".to_string(),
80 ))
81}
82
83#[cfg(not(feature = "native"))]
84#[builtin(name = ".External")]
91fn builtin_dot_external(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
92 Err(RError::new(
93 RErrorKind::Other,
94 ".External() is not available — miniR was built without the 'native' feature".to_string(),
95 ))
96}
97
98#[cfg(not(feature = "native"))]
101#[builtin(name = ".External2")]
103fn builtin_dot_external2(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
104 Err(RError::new(
105 RErrorKind::Other,
106 ".External2() is not available — miniR was built without the 'native' feature".to_string(),
107 ))
108}
109
110#[builtin(name = "removeSource", min_args = 1)]
123fn builtin_remove_source(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
124 Ok(args[0].clone())
125}
126
127#[builtin(name = "vignetteEngine")]
136fn builtin_vignette_engine(_args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
137 if named.iter().any(|(n, _)| n == "package") {
139 return Ok(RValue::List(RList::new(vec![])));
140 }
141 Ok(RValue::Null)
142}
143
144#[builtin(name = "getClassDef", namespace = "methods")]
155fn builtin_get_class_def(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
156 let _name = args
157 .first()
158 .and_then(|v| v.as_vector()?.as_character_scalar())
159 .unwrap_or_default();
160 Ok(RValue::Null)
164}
165
166#[interpreter_builtin(name = "hasMethod", namespace = "methods")]
173fn interp_has_method(
174 _args: &[RValue],
175 _named: &[(String, RValue)],
176 context: &BuiltinContext,
177) -> Result<RValue, RError> {
178 context
179 .interpreter()
180 .write_stderr("[miniR stub] hasMethod() is a no-op in miniR — always returns FALSE\n");
181 Ok(RValue::vec(Vector::Logical(vec![Some(false)].into())))
182}
183
184#[interpreter_builtin(name = "setOldClass", namespace = "methods")]
190fn interp_set_old_class(
191 _args: &[RValue],
192 _named: &[(String, RValue)],
193 _context: &BuiltinContext,
194) -> Result<RValue, RError> {
195 Ok(RValue::Null)
196}
197
198#[builtin(name = ".Deprecated")]
209fn builtin_deprecated(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
210 let new = args
211 .first()
212 .and_then(|v| v.as_vector()?.as_character_scalar())
213 .unwrap_or_default();
214 if !new.is_empty() {
215 return Ok(RValue::Null);
217 }
218 Ok(RValue::Null)
219}
220
221#[builtin(name = ".Defunct")]
228fn builtin_defunct(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
229 let msg = args
230 .first()
231 .and_then(|v| v.as_vector()?.as_character_scalar())
232 .unwrap_or_else(|| "this function is defunct".to_string());
233 Err(RError::other(msg))
234}
235
236#[builtin(name = "packageStartupMessage")]
241fn builtin_package_startup_message(
242 _args: &[RValue],
243 _: &[(String, RValue)],
244) -> Result<RValue, RError> {
245 Ok(RValue::Null)
247}
248
249#[builtin(name = "suppressPackageStartupMessages", min_args = 1)]
255fn builtin_suppress_pkg_startup(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
256 Ok(args.first().cloned().unwrap_or(RValue::Null))
257}
258
259#[builtin(name = "is.R")]
264fn builtin_is_r(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
265 Ok(RValue::vec(Vector::Logical(vec![Some(true)].into())))
266}
267
268#[builtin(name = "getRversion")]
273fn builtin_get_rversion(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
274 Ok(RValue::vec(Vector::Character(
275 vec![Some("4.4.0".to_string())].into(),
276 )))
277}
278
279#[builtin(name = "numeric_version", min_args = 1)]
285fn builtin_numeric_version(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
286 Ok(args.first().cloned().unwrap_or(RValue::Null))
288}
289
290#[builtin(name = "package_version", min_args = 1)]
296fn builtin_package_version(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
297 Ok(args.first().cloned().unwrap_or(RValue::Null))
298}
299
300#[builtin(name = "OlsonNames")]
305fn builtin_olson_names(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
306 Ok(RValue::vec(Vector::Character(
307 vec![
308 Some("UTC".to_string()),
309 Some("GMT".to_string()),
310 Some("US/Eastern".to_string()),
311 Some("US/Central".to_string()),
312 Some("US/Mountain".to_string()),
313 Some("US/Pacific".to_string()),
314 Some("Europe/London".to_string()),
315 Some("Europe/Berlin".to_string()),
316 Some("Europe/Paris".to_string()),
317 Some("Asia/Tokyo".to_string()),
318 Some("Australia/Sydney".to_string()),
319 ]
320 .into(),
321 )))
322}
323
324#[interpreter_builtin(name = "asNamespace", min_args = 1)]
338fn interp_as_namespace(
339 args: &[RValue],
340 _named: &[(String, RValue)],
341 context: &BuiltinContext,
342) -> Result<RValue, RError> {
343 let ns = args
344 .first()
345 .and_then(|v| v.as_vector()?.as_character_scalar())
346 .ok_or_else(|| RError::new(RErrorKind::Argument, "invalid namespace name".to_string()))?;
347
348 let loaded_ns = context.with_interpreter(|interp| {
350 interp
351 .loaded_namespaces
352 .borrow()
353 .get(&ns)
354 .map(|loaded| loaded.namespace_env.clone())
355 });
356
357 if let Some(env) = loaded_ns {
358 return Ok(RValue::Environment(env));
359 }
360
361 let loaded_env = context.with_interpreter(|interp| interp.load_namespace(&ns).ok());
363
364 if let Some(env) = loaded_env {
365 return Ok(RValue::Environment(env));
366 }
367
368 let env = context.with_interpreter(|interp| interp.base_env());
370 Ok(RValue::Environment(env))
371}
372
373#[builtin(name = "getNamespaceName", min_args = 1)]
379fn builtin_get_namespace_name(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
380 match args.first() {
381 Some(RValue::Environment(env)) => {
382 let name = env.name().unwrap_or_default();
383 Ok(RValue::vec(Vector::Character(vec![Some(name)].into())))
384 }
385 _ => Ok(RValue::vec(Vector::Character(
386 vec![Some(String::new())].into(),
387 ))),
388 }
389}
390
391#[interpreter_builtin(name = "isNamespace", min_args = 1)]
402fn interp_is_namespace(
403 args: &[RValue],
404 _named: &[(String, RValue)],
405 context: &BuiltinContext,
406) -> Result<RValue, RError> {
407 let is_ns = match args.first() {
408 Some(RValue::Environment(env)) => {
409 let has_ns_name = env
411 .name()
412 .map(|n| n.starts_with("namespace:"))
413 .unwrap_or(false);
414
415 if has_ns_name {
416 true
417 } else {
418 context.with_interpreter(|interp| {
420 interp
421 .loaded_namespaces
422 .borrow()
423 .values()
424 .any(|loaded| loaded.namespace_env.ptr_eq(env))
425 })
426 }
427 }
428 _ => false,
429 };
430 Ok(RValue::vec(Vector::Logical(vec![Some(is_ns)].into())))
431}
432
433#[builtin(name = "topenv", min_args = 0)]
439fn builtin_topenv(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
440 match args.first() {
441 Some(RValue::Environment(env)) => {
442 let mut current = env.clone();
444 loop {
445 if let Some(name) = current.name() {
446 if name.starts_with("namespace:")
447 || name == "R_GlobalEnv"
448 || name == "base"
449 || name.starts_with("package:")
450 {
451 return Ok(RValue::Environment(current));
452 }
453 }
454 match current.parent() {
455 Some(parent) => current = parent,
456 None => return Ok(RValue::Environment(current)),
457 }
458 }
459 }
460 _ => Ok(RValue::Null),
461 }
462}
463
464#[builtin(name = ".POSIXct", min_args = 1)]
475fn builtin_dot_posixct(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
476 let val = args.first().cloned().unwrap_or(RValue::Null);
477 match val {
478 RValue::Vector(mut rv) => {
479 rv.set_attr(
480 "class".to_string(),
481 RValue::vec(Vector::Character(
482 vec![Some("POSIXct".to_string()), Some("POSIXt".to_string())].into(),
483 )),
484 );
485 Ok(RValue::Vector(rv))
486 }
487 _ => Ok(val),
488 }
489}
490
491#[builtin(name = ".POSIXlt", min_args = 1)]
498fn builtin_dot_posixlt(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
499 let val = args.first().cloned().unwrap_or(RValue::Null);
500 match val {
502 RValue::List(mut list) => {
503 let mut attrs = *list.attrs.take().unwrap_or_default();
504 attrs.insert(
505 "class".to_string(),
506 RValue::vec(Vector::Character(
507 vec![Some("POSIXlt".to_string()), Some("POSIXt".to_string())].into(),
508 )),
509 );
510 list.attrs = Some(Box::new(attrs));
511 Ok(RValue::List(list))
512 }
513 _ => Ok(val),
514 }
515}
516
517#[builtin(name = ".Date", min_args = 1)]
523fn builtin_dot_date(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
524 let val = args.first().cloned().unwrap_or(RValue::Null);
525 match val {
526 RValue::Vector(mut rv) => {
527 rv.set_attr(
528 "class".to_string(),
529 RValue::vec(Vector::Character(vec![Some("Date".to_string())].into())),
530 );
531 Ok(RValue::Vector(rv))
532 }
533 _ => Ok(val),
534 }
535}
536
537#[builtin(name = ".difftime", min_args = 1)]
544fn builtin_dot_difftime(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
545 let val = args.first().cloned().unwrap_or(RValue::Null);
546 let units = named
547 .iter()
548 .find(|(n, _)| n == "units")
549 .and_then(|(_, v)| v.as_vector()?.as_character_scalar())
550 .or_else(|| {
551 args.get(1)
552 .and_then(|v| v.as_vector()?.as_character_scalar())
553 })
554 .unwrap_or_else(|| "secs".to_string());
555 match val {
556 RValue::Vector(mut rv) => {
557 rv.set_attr(
558 "class".to_string(),
559 RValue::vec(Vector::Character(vec![Some("difftime".to_string())].into())),
560 );
561 rv.set_attr(
562 "units".to_string(),
563 RValue::vec(Vector::Character(vec![Some(units)].into())),
564 );
565 Ok(RValue::Vector(rv))
566 }
567 _ => Ok(val),
568 }
569}
570
571#[builtin(name = ".subset", min_args = 1)]
582fn builtin_dot_subset(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
583 if args.len() < 2 {
584 return Ok(args.first().cloned().unwrap_or(RValue::Null));
585 }
586 let obj = &args[0];
588 let idx = &args[1];
589 match (obj, idx) {
590 (RValue::List(list), RValue::Vector(iv)) => {
591 if let Some(name) = iv.as_character_scalar() {
592 for (n, v) in &list.values {
593 if n.as_deref() == Some(&name) {
594 return Ok(v.clone());
595 }
596 }
597 Ok(RValue::Null)
598 } else {
599 let i = iv.as_integer_scalar().unwrap_or(0) as usize;
600 if i > 0 && i <= list.values.len() {
601 Ok(RValue::List(RList::new(vec![list.values[i - 1].clone()])))
602 } else {
603 Ok(RValue::Null)
604 }
605 }
606 }
607 (RValue::Vector(v), RValue::Vector(iv)) => {
608 let i = iv.as_integer_scalar().unwrap_or(0) as usize;
609 if i > 0 && i <= v.len() {
610 Ok(crate::interpreter::indexing::extract_vector_element(
611 v,
612 i - 1,
613 ))
614 } else {
615 Ok(RValue::Null)
616 }
617 }
618 _ => Ok(RValue::Null),
619 }
620}
621
622#[builtin(name = ".subset2", min_args = 2)]
629fn builtin_dot_subset2(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
630 let obj = &args[0];
632 let idx = &args[1];
633 match (obj, idx) {
634 (RValue::List(list), RValue::Vector(iv)) => {
635 if let Some(name) = iv.as_character_scalar() {
636 for (n, v) in &list.values {
637 if n.as_deref() == Some(&name) {
638 return Ok(v.clone());
639 }
640 }
641 Ok(RValue::Null)
642 } else {
643 let i = iv.as_integer_scalar().unwrap_or(0) as usize;
644 if i > 0 && i <= list.values.len() {
645 Ok(list.values[i - 1].1.clone())
646 } else {
647 Ok(RValue::Null)
648 }
649 }
650 }
651 (RValue::Vector(v), RValue::Vector(iv)) => {
652 if let Vector::Character(idx_names) = &iv.inner {
653 if let Some(Some(name)) = idx_names.first() {
654 if let Some(names_attr) = v.get_attr("names") {
655 if let Some(names_vec) = names_attr.as_vector() {
656 let name_strs = names_vec.to_characters();
657 for (j, n) in name_strs.iter().enumerate() {
658 if n.as_deref() == Some(name.as_str()) && j < v.len() {
659 return Ok(
660 crate::interpreter::indexing::extract_vector_element(v, j),
661 );
662 }
663 }
664 }
665 }
666 return Ok(RValue::Null);
667 }
668 }
669 let i = iv.as_integer_scalar().unwrap_or(0) as usize;
670 if i > 0 && i <= v.len() {
671 Ok(crate::interpreter::indexing::extract_vector_element(
672 v,
673 i - 1,
674 ))
675 } else {
676 Ok(RValue::Null)
677 }
678 }
679 _ => Ok(RValue::Null),
680 }
681}
682
683#[builtin(name = "standardGeneric", namespace = "methods", min_args = 1)]
693fn builtin_standard_generic(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
694 Err(RError::new(
695 RErrorKind::Other,
696 "standardGeneric() dispatch not yet implemented — use S3 methods instead".to_string(),
697 ))
698}
699
700#[interpreter_builtin(name = "setIs", namespace = "methods")]
703fn interp_set_is(
704 _args: &[RValue],
705 _named: &[(String, RValue)],
706 context: &BuiltinContext,
707) -> Result<RValue, RError> {
708 context
709 .interpreter()
710 .write_stderr("[miniR stub] setIs() is a no-op in miniR\n");
711 Ok(RValue::Null)
712}
713
714#[interpreter_builtin(name = "removeClass", namespace = "methods")]
717fn interp_remove_class(
718 _args: &[RValue],
719 _named: &[(String, RValue)],
720 context: &BuiltinContext,
721) -> Result<RValue, RError> {
722 context
723 .interpreter()
724 .write_stderr("[miniR stub] removeClass() is a no-op in miniR\n");
725 Ok(RValue::Null)
726}
727
728#[interpreter_builtin(name = "resetGeneric", namespace = "methods")]
731fn interp_reset_generic(
732 _args: &[RValue],
733 _named: &[(String, RValue)],
734 context: &BuiltinContext,
735) -> Result<RValue, RError> {
736 context
737 .interpreter()
738 .write_stderr("[miniR stub] resetGeneric() is a no-op in miniR\n");
739 Ok(RValue::Null)
740}
741
742#[builtin(name = "Encoding<-", min_args = 2)]
745fn builtin_encoding_set(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
746 Ok(args.first().cloned().unwrap_or(RValue::Null))
748}
749
750#[interpreter_builtin(name = "bindtextdomain")]
753fn interp_bindtextdomain(
754 _args: &[RValue],
755 _named: &[(String, RValue)],
756 context: &BuiltinContext,
757) -> Result<RValue, RError> {
758 context
759 .interpreter()
760 .write_stderr("[miniR stub] bindtextdomain() is a no-op in miniR — no i18n support\n");
761 Ok(RValue::Null)
762}
763
764#[interpreter_builtin(name = "eapply", min_args = 2)]
767fn interp_eapply(
768 _args: &[RValue],
769 _named: &[(String, RValue)],
770 context: &BuiltinContext,
771) -> Result<RValue, RError> {
772 context
773 .interpreter()
774 .write_stderr("[miniR stub] eapply() is a stub in miniR — returns empty list\n");
775 Ok(RValue::List(RList::new(vec![])))
776}
777
778#[builtin(name = "unlockBinding", min_args = 2)]
781fn builtin_unlock_binding(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
782 Ok(RValue::Null)
784}
785
786#[builtin(name = "sys.status")]
789fn builtin_sys_status(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
790 Ok(RValue::List(RList::new(vec![
791 (
792 Some("sys.calls".to_string()),
793 RValue::List(RList::new(vec![])),
794 ),
795 (
796 Some("sys.parents".to_string()),
797 RValue::vec(Vector::Integer(vec![].into())),
798 ),
799 (
800 Some("sys.frames".to_string()),
801 RValue::List(RList::new(vec![])),
802 ),
803 ])))
804}
805
806#[builtin(name = "file.access", min_args = 1)]
809fn builtin_file_access(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
810 let path = args
811 .first()
812 .and_then(|v| v.as_vector()?.as_character_scalar())
813 .unwrap_or_default();
814 let exists = std::path::Path::new(&path).exists();
815 Ok(RValue::vec(Vector::Integer(
816 vec![Some(if exists { 0 } else { -1 })].into(),
817 )))
818}
819
820#[builtin(name = "serialize", min_args = 2)]
823fn builtin_serialize(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
824 Err(RError::new(
825 RErrorKind::Other,
826 "serialize() to connection not yet implemented — use saveRDS() instead".to_string(),
827 ))
828}
829
830#[builtin(name = "unserialize", min_args = 1)]
832fn builtin_unserialize(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
833 Err(RError::new(
834 RErrorKind::Other,
835 "unserialize() from connection not yet implemented — use readRDS() instead".to_string(),
836 ))
837}
838
839#[builtin(name = "tracemem", min_args = 1)]
842fn builtin_tracemem(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
843 Ok(RValue::vec(Vector::Character(
844 vec![Some(format!("<{:p}>", &args[0]))].into(),
845 )))
846}
847#[builtin(name = "untracemem", min_args = 1)]
848fn builtin_untracemem(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
849 Ok(RValue::Null)
850}
851#[builtin(name = "retracemem")]
852fn builtin_retracemem(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
853 Ok(RValue::Null)
854}
855
856stub_builtin!("rawConnection", 1, "rawConnection() not yet implemented");
861stub_builtin!("textConnection", 1, "textConnection() not yet implemented");
862stub_builtin!("pipe", 1, "pipe() not yet implemented");
863stub_builtin!("fifo", 1, "fifo() not yet implemented");
864stub_builtin!(
865 "socketConnection",
866 1,
867 "socketConnection() not yet implemented — use make.socket() instead"
868);
869stub_builtin!("gzcon", 1, "gzcon() not yet implemented");
870stub_builtin!("readBin", 1, "readBin() not yet implemented");
871stub_builtin!("writeBin", 1, "writeBin() not yet implemented");
872stub_builtin!("readChar", 1, "readChar() not yet implemented");
873stub_builtin!("writeChar", 1, "writeChar() not yet implemented");
874stub_builtin!("memCompress", 1, "memCompress() not yet implemented");
875stub_builtin!("memDecompress", 1, "memDecompress() not yet implemented");
876
877#[builtin(name = "interactive")]
886fn builtin_interactive(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
887 Ok(RValue::vec(Vector::Logical(vec![Some(false)].into())))
890}
891
892#[builtin(name = "eval.parent", min_args = 1)]
899fn builtin_eval_parent(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
900 Ok(args.first().cloned().unwrap_or(RValue::Null))
902}
903
904#[builtin(name = "length<-", min_args = 2)]
911fn builtin_length_set(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
912 let new_len = args
913 .get(1)
914 .and_then(|v| v.as_vector()?.as_integer_scalar())
915 .unwrap_or(0) as usize;
916
917 match args.first() {
918 Some(RValue::Vector(v)) => {
919 let mut doubles = v.to_doubles();
920 doubles.resize(new_len, None);
921 Ok(RValue::vec(Vector::Double(doubles.into())))
922 }
923 Some(RValue::List(list)) => {
924 let mut values = list.values.clone();
925 values.resize(new_len, (None, RValue::Null));
926 Ok(RValue::List(RList::new(values)))
927 }
928 _ => Ok(RValue::Null),
929 }
930}
931
932#[builtin(name = "levels<-", min_args = 2)]
939fn builtin_levels_set(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
940 match args.first() {
941 Some(RValue::Vector(v)) => {
942 let mut rv = v.clone();
943 let new_levels = args.get(1).cloned().unwrap_or(RValue::Null);
944 rv.set_attr("levels".to_string(), new_levels);
945 Ok(RValue::Vector(rv))
946 }
947 _ => Ok(args.first().cloned().unwrap_or(RValue::Null)),
948 }
949}
950
951#[builtin(name = "file.append", min_args = 2)]
958fn builtin_file_append(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
959 let file1 = args
960 .first()
961 .and_then(|v| v.as_vector()?.as_character_scalar())
962 .unwrap_or_default();
963 let file2 = args
964 .get(1)
965 .and_then(|v| v.as_vector()?.as_character_scalar())
966 .unwrap_or_default();
967 let result = (|| {
968 let content = std::fs::read(&file2).ok()?;
969 use std::io::Write;
970 let mut f = std::fs::OpenOptions::new().append(true).open(&file1).ok()?;
971 f.write_all(&content).ok()
972 })();
973 Ok(RValue::vec(Vector::Logical(
974 vec![Some(result.is_some())].into(),
975 )))
976}
977
978#[builtin(name = "Cstack_info")]
981fn builtin_cstack_info(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
982 let mut rv = RVector::from(Vector::Integer(
983 vec![Some(8388608), Some(16384), Some(0)].into(),
984 ));
985 rv.set_attr(
986 "names".to_string(),
987 RValue::vec(Vector::Character(
988 vec![
989 Some("size".to_string()),
990 Some("current".to_string()),
991 Some("direction".to_string()),
992 ]
993 .into(),
994 )),
995 );
996 Ok(RValue::Vector(rv))
997}
998
999#[builtin(name = "extSoftVersion")]
1002fn builtin_ext_soft_version(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1003 let mut rv = RVector::from(Vector::Character(
1004 vec![
1005 Some("".to_string()),
1006 Some("1.1.1".to_string()),
1007 Some("".to_string()),
1008 Some("".to_string()),
1009 ]
1010 .into(),
1011 ));
1012 rv.set_attr(
1013 "names".to_string(),
1014 RValue::vec(Vector::Character(
1015 vec![
1016 Some("zlib".to_string()),
1017 Some("bzlib".to_string()),
1018 Some("xz".to_string()),
1019 Some("PCRE".to_string()),
1020 ]
1021 .into(),
1022 )),
1023 );
1024 Ok(RValue::Vector(rv))
1025}
1026
1027#[cfg(not(feature = "native"))]
1032#[builtin(name = "dyn.load", min_args = 1)]
1035fn builtin_dyn_load(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1036 let path = args
1037 .first()
1038 .and_then(|v| v.as_vector()?.as_character_scalar())
1039 .unwrap_or_default();
1040 debug!(path = path.as_str(), "dyn.load");
1041 Err(RError::other(
1042 "dyn.load() not available — miniR cannot load compiled shared libraries",
1043 ))
1044}
1045
1046#[cfg(not(feature = "native"))]
1047#[builtin(name = "dyn.unload", min_args = 1)]
1050fn builtin_dyn_unload(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1051 let name = args
1052 .first()
1053 .and_then(|v| v.as_vector()?.as_character_scalar())
1054 .unwrap_or_default();
1055 debug!(name = name.as_str(), "dyn.unload");
1056 Err(RError::other("dyn.unload() not available"))
1057}
1058#[cfg(not(feature = "native"))]
1059stub_builtin!(
1060 "library.dynam",
1061 1,
1062 "library.dynam() not available — miniR cannot load compiled code"
1063);
1064#[cfg(not(feature = "native"))]
1065stub_builtin!(
1066 "library.dynam.unload",
1067 1,
1068 "library.dynam.unload() not available"
1069);
1070
1071#[cfg(not(feature = "native"))]
1072#[builtin(name = "is.loaded", min_args = 1)]
1075fn builtin_is_loaded(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1076 Ok(RValue::vec(Vector::Logical(vec![Some(false)].into())))
1077}
1078
1079#[cfg(not(feature = "native"))]
1080#[builtin(name = "getNativeSymbolInfo", min_args = 1)]
1083fn builtin_get_native_symbol_info(
1084 args: &[RValue],
1085 _: &[(String, RValue)],
1086) -> Result<RValue, RError> {
1087 let name = args
1088 .first()
1089 .and_then(|v| v.as_vector()?.as_character_scalar())
1090 .unwrap_or_default();
1091 Err(RError::new(
1092 RErrorKind::Other,
1093 format!("no such symbol '{name}' — miniR cannot load native code"),
1094 ))
1095}
1096
1097#[interpreter_builtin(name = "debugonce")]
1104fn interp_debugonce(
1105 _args: &[RValue],
1106 _named: &[(String, RValue)],
1107 context: &BuiltinContext,
1108) -> Result<RValue, RError> {
1109 context
1110 .interpreter()
1111 .write_stderr("[miniR stub] debugonce() is a no-op in miniR — no debugger\n");
1112 Ok(RValue::Null)
1113}
1114
1115#[interpreter_builtin(name = "trace")]
1118fn interp_trace(
1119 _args: &[RValue],
1120 _named: &[(String, RValue)],
1121 context: &BuiltinContext,
1122) -> Result<RValue, RError> {
1123 context
1124 .interpreter()
1125 .write_stderr("[miniR stub] trace() is a no-op in miniR — no debugger\n");
1126 Ok(RValue::Null)
1127}
1128
1129#[interpreter_builtin(name = "untrace")]
1132fn interp_untrace(
1133 _args: &[RValue],
1134 _named: &[(String, RValue)],
1135 context: &BuiltinContext,
1136) -> Result<RValue, RError> {
1137 context
1138 .interpreter()
1139 .write_stderr("[miniR stub] untrace() is a no-op in miniR — no debugger\n");
1140 Ok(RValue::Null)
1141}
1142
1143#[interpreter_builtin(name = "browseEnv", namespace = "utils")]
1146fn interp_browse_env(
1147 _args: &[RValue],
1148 _named: &[(String, RValue)],
1149 context: &BuiltinContext,
1150) -> Result<RValue, RError> {
1151 context
1152 .interpreter()
1153 .write_stderr("[miniR stub] browseEnv() is a no-op in miniR\n");
1154 Ok(RValue::Null)
1155}
1156
1157stub_builtin!("sink", 0, "sink() not yet implemented");
1162stub_builtin!("flush", 1, "flush() not yet implemented for connections");
1163stub_builtin!(
1164 "showConnections",
1165 0,
1166 "showConnections() not yet implemented"
1167);
1168stub_builtin!(
1169 "getAllConnections",
1170 0,
1171 "getAllConnections() not yet implemented"
1172);
1173stub_builtin!("pushBack", 1, "pushBack() not yet implemented");
1174stub_builtin!("pushBackLength", 1);
1175stub_builtin!("clearPushBack", 1);
1176stub_builtin!("seek", 1, "seek() not yet implemented");
1177stub_builtin!("truncate", 1, "truncate() not yet implemented");
1178stub_builtin!("isSeekable", 1);
1179stub_builtin!("isIncomplete", 1);
1180stub_builtin!("summary.connection", 1);
1181
1182#[builtin(name = "Sys.readlink", min_args = 1)]
1189fn builtin_sys_readlink(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1190 let path = args
1191 .first()
1192 .and_then(|v| v.as_vector()?.as_character_scalar())
1193 .unwrap_or_default();
1194 let target = std::fs::read_link(&path)
1195 .ok()
1196 .map(|p| p.to_string_lossy().to_string())
1197 .unwrap_or_default();
1198 Ok(RValue::vec(Vector::Character(vec![Some(target)].into())))
1199}
1200
1201#[builtin(name = "file.link", min_args = 2)]
1204fn builtin_file_link(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1205 let from = args
1206 .first()
1207 .and_then(|v| v.as_vector()?.as_character_scalar())
1208 .unwrap_or_default();
1209 let to = args
1210 .get(1)
1211 .and_then(|v| v.as_vector()?.as_character_scalar())
1212 .unwrap_or_default();
1213 let ok = std::fs::hard_link(&from, &to).is_ok();
1214 Ok(RValue::vec(Vector::Logical(vec![Some(ok)].into())))
1215}
1216
1217#[builtin(name = "file.symlink", min_args = 2)]
1220fn builtin_file_symlink(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1221 let from = args
1222 .first()
1223 .and_then(|v| v.as_vector()?.as_character_scalar())
1224 .unwrap_or_default();
1225 let to = args
1226 .get(1)
1227 .and_then(|v| v.as_vector()?.as_character_scalar())
1228 .unwrap_or_default();
1229 #[cfg(unix)]
1230 let ok = std::os::unix::fs::symlink(&from, &to).is_ok();
1231 #[cfg(not(unix))]
1232 let ok = false;
1233 Ok(RValue::vec(Vector::Logical(vec![Some(ok)].into())))
1234}
1235
1236#[interpreter_builtin(name = "Sys.chmod", min_args = 1)]
1239fn interp_sys_chmod(
1240 _args: &[RValue],
1241 _named: &[(String, RValue)],
1242 context: &BuiltinContext,
1243) -> Result<RValue, RError> {
1244 context
1245 .interpreter()
1246 .write_stderr("[miniR stub] Sys.chmod() is a no-op in miniR\n");
1247 Ok(RValue::Null)
1248}
1249
1250#[builtin(name = "Sys.umask")]
1253fn builtin_sys_umask(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1254 Ok(RValue::vec(Vector::Integer(vec![Some(0o022)].into())))
1255}
1256
1257#[builtin(name = "Sys.setFileTime", min_args = 2)]
1260fn builtin_sys_set_file_time(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1261 Ok(RValue::vec(Vector::Logical(vec![Some(false)].into())))
1263}
1264
1265#[builtin(name = "getTaskCallbackNames")]
1272fn builtin_get_task_callback_names(
1273 _args: &[RValue],
1274 _: &[(String, RValue)],
1275) -> Result<RValue, RError> {
1276 Ok(RValue::vec(Vector::Character(vec![].into())))
1277}
1278
1279#[builtin(name = "addTaskCallback", min_args = 1)]
1282fn builtin_add_task_callback(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1283 Ok(RValue::vec(Vector::Logical(vec![Some(true)].into())))
1284}
1285
1286#[builtin(name = "removeTaskCallback", min_args = 1)]
1289fn builtin_remove_task_callback(
1290 _args: &[RValue],
1291 _: &[(String, RValue)],
1292) -> Result<RValue, RError> {
1293 Ok(RValue::vec(Vector::Logical(vec![Some(true)].into())))
1294}
1295
1296#[builtin(name = "gc.time")]
1303fn builtin_gc_time(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1304 Ok(RValue::vec(Vector::Double(
1305 vec![Some(0.0), Some(0.0), Some(0.0)].into(),
1306 )))
1307}
1308
1309#[builtin(name = "mem.limits")]
1312fn builtin_mem_limits(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1313 Ok(RValue::vec(Vector::Double(
1314 vec![Some(f64::INFINITY), Some(f64::INFINITY)].into(),
1315 )))
1316}
1317
1318#[builtin(name = "memory.profile")]
1321fn builtin_memory_profile(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1322 Ok(RValue::vec(Vector::Integer(vec![].into())))
1323}
1324
1325#[builtin(name = "pos.to.env", min_args = 1)]
1328fn builtin_pos_to_env(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1329 let _pos = args
1330 .first()
1331 .and_then(|v| v.as_vector()?.as_integer_scalar())
1332 .unwrap_or(1);
1333 Ok(RValue::Null)
1335}
1336
1337#[builtin(name = "setNames", namespace = "stats", min_args = 2)]
1345fn builtin_set_names(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1346 let obj = args.first().cloned().unwrap_or(RValue::Null);
1347 let names = args.get(1).cloned().unwrap_or(RValue::Null);
1348 match obj {
1349 RValue::Vector(mut rv) => {
1350 rv.set_attr("names".to_string(), names);
1351 Ok(RValue::Vector(rv))
1352 }
1353 RValue::List(mut list) => {
1354 if let Some(names_vec) = names.as_vector() {
1355 let name_strs = names_vec.to_characters();
1356 for (i, (n, _)) in list.values.iter_mut().enumerate() {
1357 if let Some(new_name) = name_strs.get(i) {
1358 *n = new_name.clone();
1359 }
1360 }
1361 }
1362 Ok(RValue::List(list))
1363 }
1364 _ => Ok(obj),
1365 }
1366}
1367
1368#[builtin(name = "globalVariables", namespace = "utils")]
1377fn builtin_global_variables(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1378 Ok(RValue::Null)
1379}
1380
1381#[builtin(name = "withAutoprint")]
1384fn builtin_with_autoprint(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1385 Ok(args.first().cloned().unwrap_or(RValue::Null))
1386}
1387
1388#[builtin(name = "signature", namespace = "methods")]
1395fn builtin_signature(_args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1396 let names: Vec<Option<String>> = named.iter().map(|(n, _)| Some(n.clone())).collect();
1397 let values: Vec<Option<String>> = named
1398 .iter()
1399 .map(|(_, v)| v.as_vector().and_then(|vec| vec.as_character_scalar()))
1400 .collect();
1401 let mut rv = RVector::from(Vector::Character(values.into()));
1402 rv.set_attr(
1403 "names".to_string(),
1404 RValue::vec(Vector::Character(names.into())),
1405 );
1406 Ok(RValue::Vector(rv))
1407}
1408
1409#[builtin(name = "prototype", namespace = "methods")]
1416fn builtin_prototype(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1417 let mut values: Vec<(Option<String>, RValue)> = named
1418 .iter()
1419 .map(|(n, v)| (Some(n.clone()), v.clone()))
1420 .collect();
1421 for arg in args {
1423 values.push((None, arg.clone()));
1424 }
1425 Ok(RValue::List(RList::new(values)))
1426}
1427
1428#[builtin(min_args = 1)]
1435fn builtin_lengths(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1436 match args.first() {
1437 Some(RValue::List(list)) => {
1438 let lens: Vec<Option<i64>> = list
1439 .values
1440 .iter()
1441 .map(|(_, v)| {
1442 Some(match v {
1443 RValue::Vector(rv) => rv.len() as i64,
1444 RValue::List(l) => l.values.len() as i64,
1445 RValue::Null => 0,
1446 _ => 1,
1447 })
1448 })
1449 .collect();
1450 Ok(RValue::vec(Vector::Integer(lens.into())))
1451 }
1452 Some(RValue::Vector(v)) => {
1453 let lens: Vec<Option<i64>> = (0..v.len()).map(|_| Some(1)).collect();
1455 Ok(RValue::vec(Vector::Integer(lens.into())))
1456 }
1457 _ => Ok(RValue::vec(Vector::Integer(vec![].into()))),
1458 }
1459}
1460
1461#[builtin(name = "commandArgs")]
1467fn builtin_command_args(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1468 let trailing_only = args
1469 .first()
1470 .and_then(|v| v.as_vector()?.as_logical_scalar())
1471 .unwrap_or(false);
1472 let cli_args: Vec<String> = std::env::args().collect();
1473 let result = if trailing_only {
1474 if let Some(pos) = cli_args.iter().position(|a| a == "--args") {
1476 cli_args[pos + 1..].to_vec()
1477 } else {
1478 vec![]
1479 }
1480 } else {
1481 cli_args
1482 };
1483 Ok(RValue::vec(Vector::Character(
1484 result.into_iter().map(Some).collect::<Vec<_>>().into(),
1485 )))
1486}
1487
1488#[cfg(not(feature = "tls"))]
1493stub_builtin!(
1494 "url",
1495 1,
1496 "url() requires the 'tls' feature — rebuild miniR with --features tls"
1497);
1498
1499stub_builtin!("arity", 1);
1502#[builtin(name = "is.object", min_args = 1)]
1507fn builtin_is_object(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1508 let has_class = match args.first() {
1509 Some(RValue::Vector(rv)) => rv.get_attr("class").is_some(),
1510 Some(RValue::List(l)) => l.get_attr("class").is_some(),
1511 _ => false,
1512 };
1513 Ok(RValue::vec(Vector::Logical(vec![Some(has_class)].into())))
1514}
1515
1516#[builtin(name = "isS4", min_args = 1)]
1521fn builtin_is_s4(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1522 Ok(RValue::vec(Vector::Logical(vec![Some(false)].into())))
1524}
1525
1526#[interpreter_builtin(name = "registerS3method", min_args = 3)]
1533fn builtin_register_s3_method(
1534 args: &[RValue],
1535 _named: &[(String, RValue)],
1536 ctx: &BuiltinContext,
1537) -> Result<RValue, RError> {
1538 let generic = args
1539 .first()
1540 .and_then(|v| v.as_vector()?.as_character_scalar())
1541 .unwrap_or_default();
1542 let class = args
1543 .get(1)
1544 .and_then(|v| v.as_vector()?.as_character_scalar())
1545 .unwrap_or_default();
1546 let method = args.get(2).cloned().unwrap_or(RValue::Null);
1547
1548 if !generic.is_empty() && !class.is_empty() {
1549 ctx.interpreter()
1550 .s3_method_registry
1551 .borrow_mut()
1552 .insert((generic, class), method);
1553 }
1554 Ok(RValue::Null)
1555}
1556
1557#[builtin(name = "isatty", min_args = 1)]
1562fn builtin_isatty(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1563 let con = args
1564 .first()
1565 .and_then(|v| v.as_vector()?.as_integer_scalar())
1566 .unwrap_or(0);
1567 let result = match con {
1569 0 => std::io::IsTerminal::is_terminal(&std::io::stdin()),
1570 1 => std::io::IsTerminal::is_terminal(&std::io::stdout()),
1571 2 => std::io::IsTerminal::is_terminal(&std::io::stderr()),
1572 _ => false,
1573 };
1574 Ok(RValue::vec(Vector::Logical(vec![Some(result)].into())))
1575}
1576
1577#[builtin(name = "conflictRules", min_args = 1)]
1580fn builtin_conflict_rules(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1581 Ok(RValue::Null)
1582}
1583
1584#[builtin(name = "setHook")]
1587fn builtin_set_hook(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1588 Ok(RValue::Null)
1589}
1590
1591#[builtin(name = "packageEvent", min_args = 1)]
1594fn builtin_package_event(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1595 let pkg = args
1596 .first()
1597 .and_then(|v| v.as_vector()?.as_character_scalar())
1598 .unwrap_or_default();
1599 let event = args
1600 .get(1)
1601 .and_then(|v| v.as_vector()?.as_character_scalar())
1602 .unwrap_or_else(|| "onLoad".to_string());
1603 Ok(RValue::vec(Vector::Character(
1604 vec![Some(format!("{event}:{pkg}"))].into(),
1605 )))
1606}
1607
1608#[builtin(name = "getHook", min_args = 1)]
1611fn builtin_get_hook(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1612 Ok(RValue::Null)
1613}
1614
1615#[cfg(not(feature = "linalg"))]
1630#[builtin(name = "svd", min_args = 1)]
1631fn builtin_svd(_args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1632 Err(RError::other(
1633 "svd() is not yet implemented in miniR. \
1634 Singular value decomposition requires a linear algebra backend (e.g. nalgebra). \
1635 This will be available when the 'linalg' feature is implemented.",
1636 ))
1637}
1638
1639#[builtin(name = "asRboolean", min_args = 1)]
1651fn builtin_as_r_boolean(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1652 match args.first() {
1653 Some(RValue::Vector(v)) => {
1654 let logicals = v.to_logicals();
1655 Ok(RValue::vec(Vector::Logical(
1656 vec![logicals.first().copied().flatten()].into(),
1657 )))
1658 }
1659 Some(RValue::Null) => Ok(RValue::vec(Vector::Logical(vec![None].into()))),
1660 _ => Ok(RValue::vec(Vector::Logical(vec![None].into()))),
1661 }
1662}