Skip to main content

runmat_vm/ops/
stack.rs

1use crate::bytecode::EmitLabel;
2use crate::bytecode::ExecutionContext;
3use crate::interpreter::errors::mex;
4use crate::interpreter::stack::{pop2, pop_value};
5use crate::runtime::workspace::{
6    ensure_workspace_slot_name, mark_workspace_assigned, refresh_workspace_state,
7};
8use runmat_builtins::{CharArray, Value};
9use runmat_runtime::{dispatcher::gather_if_needed_async, RuntimeError};
10use std::collections::HashMap;
11
12fn resolve_emit_label_text(
13    label: &EmitLabel,
14    var_names: &HashMap<usize, String>,
15) -> Option<String> {
16    match label {
17        EmitLabel::Ans => Some("ans".to_string()),
18        EmitLabel::Var(idx) => var_names
19            .get(idx)
20            .cloned()
21            .or_else(|| Some(format!("var{idx}"))),
22    }
23}
24
25pub async fn emit_stack_top(
26    stack: &[Value],
27    label: &EmitLabel,
28    var_names: &HashMap<usize, String>,
29) -> Result<(), RuntimeError> {
30    if let Some(value) = stack.last() {
31        let label_text = resolve_emit_label_text(label, var_names);
32        let host_value = gather_if_needed_async(value).await?;
33        runmat_runtime::console::record_value_output(label_text.as_deref(), &host_value);
34    }
35    Ok(())
36}
37
38pub async fn emit_var(
39    vars: &[Value],
40    var_index: usize,
41    label: &EmitLabel,
42    var_names: &HashMap<usize, String>,
43) -> Result<(), RuntimeError> {
44    if let Some(value) = vars.get(var_index) {
45        let label_text = resolve_emit_label_text(label, var_names);
46        let host_value = gather_if_needed_async(value).await?;
47        runmat_runtime::console::record_value_output(label_text.as_deref(), &host_value);
48    }
49    Ok(())
50}
51
52#[inline]
53pub fn load_const(stack: &mut Vec<Value>, value: f64) {
54    stack.push(Value::Num(value));
55}
56
57#[inline]
58pub fn load_complex(stack: &mut Vec<Value>, re: f64, im: f64) {
59    stack.push(Value::Complex(re, im));
60}
61
62#[inline]
63pub fn load_bool(stack: &mut Vec<Value>, value: bool) {
64    stack.push(Value::Bool(value));
65}
66
67#[inline]
68pub fn load_string(stack: &mut Vec<Value>, value: String) {
69    stack.push(Value::String(value));
70}
71
72pub fn load_char_row(stack: &mut Vec<Value>, value: String) -> Result<(), RuntimeError> {
73    let ca = CharArray::new(value.chars().collect(), 1, value.chars().count())
74        .map_err(|e| mex("CharError", &e))?;
75    stack.push(Value::CharArray(ca));
76    Ok(())
77}
78
79pub fn load_local(
80    stack: &mut Vec<Value>,
81    context: &ExecutionContext,
82    vars: &[Value],
83    offset: usize,
84) -> Result<(), RuntimeError> {
85    if let Some(current_frame) = context.call_stack.last() {
86        let local_index = current_frame.locals_start + offset;
87        if local_index >= context.locals.len() {
88            return Err("Local variable index out of bounds".to_string().into());
89        }
90        stack.push(context.locals[local_index].clone());
91    } else if offset < vars.len() {
92        stack.push(vars[offset].clone());
93    } else {
94        stack.push(Value::Num(0.0));
95    }
96    Ok(())
97}
98
99#[inline]
100pub fn load_var(stack: &mut Vec<Value>, vars: &[Value], index: usize) {
101    stack.push(vars[index].clone());
102}
103
104pub fn store_var<BeforeOverwrite, AfterStore>(
105    stack: &mut Vec<Value>,
106    vars: &mut Vec<Value>,
107    index: usize,
108    var_names: &HashMap<usize, String>,
109    mut before_overwrite: BeforeOverwrite,
110    mut after_store: AfterStore,
111) -> Result<(), RuntimeError>
112where
113    BeforeOverwrite: FnMut(&Value, &Value),
114    AfterStore: FnMut(usize, &Value),
115{
116    let value = pop_value(stack)?;
117    if index < vars.len() {
118        before_overwrite(&vars[index], &value);
119    }
120    if index >= vars.len() {
121        vars.resize(index + 1, Value::Num(0.0));
122        refresh_workspace_state(vars);
123    }
124    vars[index] = value;
125    if let Some(name) = var_names.get(&index) {
126        ensure_workspace_slot_name(index, name);
127    }
128    mark_workspace_assigned(index);
129    after_store(index, &vars[index]);
130    Ok(())
131}
132
133pub fn store_local<BeforeLocalOverwrite, BeforeVarOverwrite, AfterFallbackStore>(
134    stack: &mut Vec<Value>,
135    context: &mut ExecutionContext,
136    vars: &mut Vec<Value>,
137    offset: usize,
138    mut before_local_overwrite: BeforeLocalOverwrite,
139    mut before_var_overwrite: BeforeVarOverwrite,
140    mut after_fallback_store: AfterFallbackStore,
141) -> Result<(), RuntimeError>
142where
143    BeforeLocalOverwrite: FnMut(&Value, &Value),
144    BeforeVarOverwrite: FnMut(&Value, &Value),
145    AfterFallbackStore: FnMut(&str, usize, &Value),
146{
147    let value = pop_value(stack)?;
148    if let Some(current_frame) = context.call_stack.last() {
149        let local_index = current_frame.locals_start + offset;
150        while context.locals.len() <= local_index {
151            context.locals.push(Value::Num(0.0));
152        }
153        before_local_overwrite(&context.locals[local_index], &value);
154        context.locals[local_index] = value;
155    } else {
156        if offset >= vars.len() {
157            vars.resize(offset + 1, Value::Num(0.0));
158            refresh_workspace_state(vars);
159        }
160        before_var_overwrite(&vars[offset], &value);
161        vars[offset] = value;
162        let func_name = context
163            .call_stack
164            .last()
165            .map(|f| f.function_name.as_str())
166            .unwrap_or("<main>");
167        after_fallback_store(func_name, offset, &vars[offset]);
168    }
169    Ok(())
170}
171
172#[inline]
173pub fn pop(stack: &mut Vec<Value>) {
174    let _ = stack.pop();
175}
176
177#[inline]
178pub fn swap(stack: &mut Vec<Value>) -> Result<(), RuntimeError> {
179    let (b, a) = pop2(stack)?;
180    stack.push(a);
181    stack.push(b);
182    Ok(())
183}