Skip to main content

runmat_vm/call/
builtins.rs

1use 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}