runmat_vm/call/
builtins.rs1use crate::bytecode::Instr;
2use crate::interpreter::errors::mex;
3use crate::interpreter::stack::pop_args;
4use runmat_builtins::Value;
5use runmat_runtime::RuntimeError;
6
7#[cfg(feature = "native-accel")]
8pub async fn prepare_builtin_args(name: &str, args: &[Value]) -> Result<Vec<Value>, RuntimeError> {
9 Ok(runmat_accelerate::prepare_builtin_args(name, args)
10 .await
11 .map_err(|e| e.to_string())?)
12}
13
14#[cfg(not(feature = "native-accel"))]
15pub async fn prepare_builtin_args(_name: &str, args: &[Value]) -> Result<Vec<Value>, RuntimeError> {
16 Ok(args.to_vec())
17}
18
19pub fn collect_call_args(
20 stack: &mut Vec<Value>,
21 arg_count: usize,
22) -> Result<Vec<Value>, RuntimeError> {
23 pop_args(stack, arg_count)
24}
25
26pub fn special_counter_builtin(
27 name: &str,
28 arg_count: usize,
29 call_counts: &[(usize, usize)],
30) -> Result<Option<Value>, RuntimeError> {
31 if name == "nargin" {
32 if arg_count != 0 {
33 return Err(mex("TooManyInputs", "nargin takes no arguments"));
34 }
35 let (nin, _) = call_counts.last().cloned().unwrap_or((0, 0));
36 return Ok(Some(Value::Num(nin as f64)));
37 }
38 if name == "nargout" {
39 if arg_count != 0 {
40 return Err(mex("TooManyInputs", "nargout takes no arguments"));
41 }
42 let (_, nout) = call_counts.last().cloned().unwrap_or((0, 0));
43 return Ok(Some(Value::Num(nout as f64)));
44 }
45 Ok(None)
46}
47
48pub fn requested_output_count(instructions: &[Instr], pc: usize) -> Option<usize> {
49 match instructions.get(pc + 1) {
50 Some(Instr::Unpack(count)) => Some(*count),
51 _ => None,
52 }
53}
54
55pub fn single_result_output_list(result: Value, out_count: usize) -> Value {
56 let mut outputs = Vec::with_capacity(out_count);
57 if out_count > 0 {
58 outputs.push(result);
59 for _ in 1..out_count {
60 outputs.push(Value::Num(0.0));
61 }
62 }
63 Value::OutputList(outputs)
64}
65
66pub enum ImportedBuiltinResolution {
67 Resolved(Value),
68 Ambiguous(String),
69 NotFound,
70}
71
72pub async fn resolve_imported_builtin(
73 name: &str,
74 imports: &[(Vec<String>, bool)],
75 prepared_primary: &[Value],
76 requested_outputs: Option<usize>,
77) -> Result<ImportedBuiltinResolution, RuntimeError> {
78 let mut specific_matches: Vec<(String, Value)> = Vec::new();
79 for (path, wildcard) in imports {
80 if *wildcard {
81 continue;
82 }
83 if path.last().map(|s| s.as_str()) == Some(name) {
84 let qual = path.join(".");
85 let qual_args = prepare_builtin_args(&qual, prepared_primary).await?;
86 let result = match requested_outputs {
87 Some(count) => {
88 runmat_runtime::call_builtin_async_with_outputs(&qual, &qual_args, count).await
89 }
90 None => runmat_runtime::call_builtin_async(&qual, &qual_args).await,
91 };
92 if let Ok(value) = result {
93 specific_matches.push((qual, value));
94 }
95 }
96 }
97 if specific_matches.len() > 1 {
98 let msg = specific_matches
99 .iter()
100 .map(|(q, _)| q.clone())
101 .collect::<Vec<_>>()
102 .join(", ");
103 return Ok(ImportedBuiltinResolution::Ambiguous(format!(
104 "ambiguous builtin '{}' via imports: {}",
105 name, msg
106 )));
107 }
108 if let Some((_, value)) = specific_matches.pop() {
109 return Ok(ImportedBuiltinResolution::Resolved(value));
110 }
111
112 let mut wildcard_matches: Vec<(String, Value)> = Vec::new();
113 for (path, wildcard) in imports {
114 if !*wildcard || path.is_empty() {
115 continue;
116 }
117 let qual = format!("{}.{}", path.join("."), name);
118 let qual_args = prepare_builtin_args(&qual, prepared_primary).await?;
119 let result = match requested_outputs {
120 Some(count) => {
121 runmat_runtime::call_builtin_async_with_outputs(&qual, &qual_args, count).await
122 }
123 None => runmat_runtime::call_builtin_async(&qual, &qual_args).await,
124 };
125 if let Ok(value) = result {
126 wildcard_matches.push((qual, value));
127 }
128 }
129 if wildcard_matches.len() > 1 {
130 let msg = wildcard_matches
131 .iter()
132 .map(|(q, _)| q.clone())
133 .collect::<Vec<_>>()
134 .join(", ");
135 return Ok(ImportedBuiltinResolution::Ambiguous(format!(
136 "ambiguous builtin '{}' via wildcard imports: {}",
137 name, msg
138 )));
139 }
140 if let Some((_, value)) = wildcard_matches.pop() {
141 return Ok(ImportedBuiltinResolution::Resolved(value));
142 }
143
144 Ok(ImportedBuiltinResolution::NotFound)
145}
146
147pub fn rethrow_without_explicit_exception(
148 name: &str,
149 args: &[Value],
150 last_identifier: Option<&str>,
151 last_message: Option<&str>,
152) -> Option<RuntimeError> {
153 if name == "rethrow" && args.is_empty() {
154 if let (Some(identifier), Some(message)) = (last_identifier, last_message) {
155 return Some(format!("{}: {}", identifier, message).to_string().into());
156 }
157 }
158 None
159}