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}