1use super::*;
2
3impl RunMatSession {
4 pub async fn execute(&mut self, input: &str) -> std::result::Result<ExecutionResult, RunError> {
6 self.run(input).await
7 }
8
9 pub async fn run(&mut self, input: &str) -> std::result::Result<ExecutionResult, RunError> {
11 let _active = ActiveExecutionGuard::new(self).map_err(|err| {
12 RunError::Runtime(
13 build_runtime_error(err.to_string())
14 .with_identifier("RunMat:ExecutionAlreadyActive")
15 .build(),
16 )
17 })?;
18 runmat_vm::set_call_stack_limit(self.callstack_limit);
19 runmat_vm::set_error_namespace(&self.error_namespace);
20 runmat_hir::set_error_namespace(&self.error_namespace);
21 let exec_span = info_span!(
22 "runtime.execute",
23 input_len = input.len(),
24 verbose = self.verbose
25 );
26 let _exec_guard = exec_span.enter();
27 runmat_runtime::console::reset_thread_buffer();
28 runmat_runtime::plotting_hooks::reset_recent_figures();
29 runmat_runtime::warning_store::reset();
30 reset_provider_telemetry();
31 self.interrupt_flag.store(false, Ordering::Relaxed);
32 let _interrupt_guard =
33 runmat_runtime::interrupt::replace_interrupt(Some(self.interrupt_flag.clone()));
34 let start_time = Instant::now();
35 self.stats.total_executions += 1;
36 let debug_trace = std::env::var("RUNMAT_DEBUG_REPL").is_ok();
37 let stdin_events: Arc<Mutex<Vec<StdinEvent>>> = Arc::new(Mutex::new(Vec::new()));
38 let host_async_handler = self.async_input_handler.clone();
39 let stdin_events_async = Arc::clone(&stdin_events);
40 let runtime_async_handler: Arc<runmat_runtime::interaction::AsyncInteractionHandler> =
41 Arc::new(
42 move |prompt: runmat_runtime::interaction::InteractionPromptOwned| {
43 let request_kind = match prompt.kind {
44 runmat_runtime::interaction::InteractionKind::Line { echo } => {
45 InputRequestKind::Line { echo }
46 }
47 runmat_runtime::interaction::InteractionKind::KeyPress => {
48 InputRequestKind::KeyPress
49 }
50 };
51 let request = InputRequest {
52 prompt: prompt.prompt,
53 kind: request_kind,
54 };
55 let (event_kind, echo_flag) = match &request.kind {
56 InputRequestKind::Line { echo } => (StdinEventKind::Line, *echo),
57 InputRequestKind::KeyPress => (StdinEventKind::KeyPress, false),
58 };
59 let mut event = StdinEvent {
60 prompt: request.prompt.clone(),
61 kind: event_kind,
62 echo: echo_flag,
63 value: None,
64 error: None,
65 };
66
67 let stdin_events_async = Arc::clone(&stdin_events_async);
68 let host_async_handler = host_async_handler.clone();
69 Box::pin(async move {
70 let resp: Result<InputResponse, String> =
71 if let Some(handler) = host_async_handler {
72 handler(request).await
73 } else {
74 match &request.kind {
75 InputRequestKind::Line { echo } => {
76 runmat_runtime::interaction::default_read_line(
77 &request.prompt,
78 *echo,
79 )
80 .map(InputResponse::Line)
81 }
82 InputRequestKind::KeyPress => {
83 runmat_runtime::interaction::default_wait_for_key(
84 &request.prompt,
85 )
86 .map(|_| InputResponse::KeyPress)
87 }
88 }
89 };
90
91 let resp = resp.inspect_err(|err| {
92 event.error = Some(err.clone());
93 if let Ok(mut guard) = stdin_events_async.lock() {
94 guard.push(event.clone());
95 }
96 })?;
97
98 let interaction_resp = match resp {
99 InputResponse::Line(value) => {
100 event.value = Some(value.clone());
101 if let Ok(mut guard) = stdin_events_async.lock() {
102 guard.push(event);
103 }
104 runmat_runtime::interaction::InteractionResponse::Line(value)
105 }
106 InputResponse::KeyPress => {
107 if let Ok(mut guard) = stdin_events_async.lock() {
108 guard.push(event);
109 }
110 runmat_runtime::interaction::InteractionResponse::KeyPress
111 }
112 };
113 Ok(interaction_resp)
114 })
115 },
116 );
117 let _async_input_guard =
118 runmat_runtime::interaction::replace_async_handler(Some(runtime_async_handler));
119
120 let compat = self.compat_mode;
135 let _eval_hook_guard =
136 runmat_runtime::interaction::replace_eval_hook(Some(std::sync::Arc::new(
137 move |expr: String| -> runmat_runtime::interaction::EvalHookFuture {
138 async fn eval_expr(
141 expr: String,
142 compat: runmat_parser::CompatMode,
143 ) -> Result<Value, RuntimeError> {
144 let wrapped = format!("__runmat_input_result__ = ({expr});");
145 let ast = parse_with_options(&wrapped, ParserOptions::new(compat))
146 .map_err(|e| {
147 build_runtime_error(format!("input: parse error: {e}"))
148 .with_identifier("RunMat:input:ParseError")
149 .build()
150 })?;
151 let lowering = runmat_hir::lower(
152 &ast,
153 &LoweringContext::new(&HashMap::new(), &HashMap::new()),
154 )
155 .map_err(|e| {
156 build_runtime_error(format!("input: lowering error: {e}"))
157 .with_identifier("RunMat:input:LowerError")
158 .build()
159 })?;
160 let result_idx = lowering.variables.get("__runmat_input_result__").copied();
161 let bc = runmat_vm::compile(&lowering.hir, &HashMap::new())
162 .map_err(RuntimeError::from)?;
163 let vars = runmat_vm::interpret(&bc).await?;
164 result_idx
165 .and_then(|idx| vars.get(idx).cloned())
166 .ok_or_else(|| {
167 build_runtime_error("input: expression produced no value")
168 .with_identifier("RunMat:input:NoValue")
169 .build()
170 })
171 }
172
173 #[cfg(target_arch = "wasm32")]
174 {
175 Box::pin(eval_expr(expr, compat))
179 }
180
181 #[cfg(not(target_arch = "wasm32"))]
182 {
183 let (tx, rx) = tokio::sync::oneshot::channel();
189 let spawn_result = std::thread::Builder::new()
190 .stack_size(16 * 1024 * 1024)
191 .spawn(move || {
192 let result = futures::executor::block_on(eval_expr(expr, compat));
193 let _ = tx.send(result);
194 });
195 Box::pin(async move {
196 spawn_result.map_err(|err| {
197 build_runtime_error(format!(
198 "input: failed to spawn eval thread: {err}"
199 ))
200 .with_identifier("RunMat:input:EvalThreadSpawnFailed")
201 .build()
202 })?;
203 rx.await.unwrap_or_else(|_| {
204 Err(build_runtime_error("input: eval thread panicked")
205 .with_identifier("RunMat:input:EvalThreadPanic")
206 .build())
207 })
208 })
209 }
210 },
211 )));
212
213 if self.verbose {
214 debug!("Executing: {}", input.trim());
215 }
216
217 let _source_guard = runmat_runtime::source_context::replace_current_source(Some(input));
218
219 let PreparedExecution {
220 ast,
221 lowering,
222 mut bytecode,
223 } = self.compile_input(input)?;
224 if self.verbose {
225 debug!("AST: {ast:?}");
226 }
227 let (hir, updated_vars, updated_functions, var_names_map) = (
228 lowering.hir,
229 lowering.variables,
230 lowering.functions,
231 lowering.var_names,
232 );
233 let max_var_id = updated_vars.values().copied().max().unwrap_or(0);
234 if debug_trace {
235 debug!(?updated_vars, "[repl] updated_vars");
236 }
237 if debug_trace {
238 debug!(workspace_values_before = ?self.workspace_values, "[repl] workspace snapshot before execution");
239 }
240 let id_to_name: HashMap<usize, String> = var_names_map
241 .iter()
242 .map(|(var_id, name)| (var_id.0, name.clone()))
243 .collect();
244 let mut assigned_this_execution: HashSet<String> = HashSet::new();
245 let assigned_snapshot: HashSet<String> = updated_vars
246 .keys()
247 .filter(|name| self.workspace_values.contains_key(name.as_str()))
248 .cloned()
249 .collect();
250 let prev_assigned_snapshot = assigned_snapshot.clone();
251 if debug_trace {
252 debug!(?assigned_snapshot, "[repl] assigned snapshot");
253 }
254 let _pending_workspace_guard =
255 runmat_vm::push_pending_workspace(updated_vars.clone(), assigned_snapshot.clone());
256 if self.verbose {
257 debug!("HIR generated successfully");
258 }
259
260 let (single_assign_var, single_stmt_non_assign) = if hir.body.len() == 1 {
261 match &hir.body[0] {
262 runmat_hir::HirStmt::Assign(var_id, _, _, _) => (Some(var_id.0), false),
263 _ => (None, true),
264 }
265 } else {
266 (None, false)
267 };
268
269 bytecode.var_names = id_to_name.clone();
270 if self.verbose {
271 debug!(
272 "Bytecode compiled: {} instructions",
273 bytecode.instructions.len()
274 );
275 }
276
277 #[cfg(not(target_arch = "wasm32"))]
278 let fusion_snapshot = if self.emit_fusion_plan {
279 build_fusion_snapshot(bytecode.accel_graph.as_ref(), &bytecode.fusion_groups)
280 } else {
281 None
282 };
283 #[cfg(target_arch = "wasm32")]
284 let fusion_snapshot: Option<FusionPlanSnapshot> = None;
285
286 self.prepare_variable_array_for_execution(&bytecode, &updated_vars, debug_trace);
288
289 if self.verbose {
290 debug!(
291 "Variable array after preparation: {:?}",
292 self.variable_array
293 );
294 debug!("Updated variable mapping: {updated_vars:?}");
295 debug!("Bytecode instructions: {:?}", bytecode.instructions);
296 }
297
298 #[cfg(feature = "jit")]
299 let mut used_jit = false;
300 #[cfg(not(feature = "jit"))]
301 let used_jit = false;
302 #[cfg(feature = "jit")]
303 let mut execution_completed = false;
304 #[cfg(not(feature = "jit"))]
305 let execution_completed = false;
306 let mut result_value: Option<Value> = None; let mut suppressed_value: Option<Value> = None; let mut error = None;
309 let mut workspace_updates: Vec<WorkspaceEntry> = Vec::new();
310 let mut workspace_snapshot_force_full = false;
311 let mut ans_update: Option<(usize, Value)> = None;
312
313 let is_expression_stmt = bytecode
315 .instructions
316 .last()
317 .map(|instr| matches!(instr, runmat_vm::Instr::Pop))
318 .unwrap_or(false);
319
320 let is_semicolon_suppressed = {
322 let toks = tokenize_detailed(input);
323 toks.into_iter()
324 .rev()
325 .map(|t| t.token)
326 .find(|token| {
327 !matches!(
328 token,
329 LexToken::Newline
330 | LexToken::LineComment
331 | LexToken::BlockComment
332 | LexToken::Section
333 )
334 })
335 .map(|t| matches!(t, LexToken::Semicolon))
336 .unwrap_or(false)
337 };
338 let final_stmt_emit = last_displayable_statement_emit_disposition(&hir.body);
339
340 if self.verbose {
341 debug!("HIR body len: {}", hir.body.len());
342 if !hir.body.is_empty() {
343 debug!("HIR statement: {:?}", &hir.body[0]);
344 }
345 debug!("is_semicolon_suppressed: {is_semicolon_suppressed}");
346 }
347
348 #[cfg(feature = "jit")]
350 {
351 if let Some(ref mut jit_engine) = &mut self.jit_engine {
352 if !is_expression_stmt {
353 if self.variable_array.len() < bytecode.var_count {
355 self.variable_array
356 .resize(bytecode.var_count, Value::Num(0.0));
357 }
358
359 if self.verbose {
360 debug!(
361 "JIT path for assignment: variable_array size: {}, bytecode.var_count: {}",
362 self.variable_array.len(),
363 bytecode.var_count
364 );
365 }
366
367 match jit_engine.execute_or_compile(&bytecode, &mut self.variable_array) {
369 Ok((_, actual_used_jit)) => {
370 used_jit = actual_used_jit;
371 execution_completed = true;
372 if actual_used_jit {
373 self.stats.jit_compiled += 1;
374 } else {
375 self.stats.interpreter_fallback += 1;
376 }
377 if let Some(runmat_hir::HirStmt::Assign(var_id, _, _, _)) =
378 hir.body.first()
379 {
380 if let Some(name) = id_to_name.get(&var_id.0) {
381 assigned_this_execution.insert(name.clone());
382 }
383 if var_id.0 < self.variable_array.len() {
384 let assignment_value = self.variable_array[var_id.0].clone();
385 if !is_semicolon_suppressed {
386 result_value = Some(assignment_value);
387 if self.verbose {
388 debug!("JIT assignment result: {result_value:?}");
389 }
390 } else {
391 suppressed_value = Some(assignment_value);
392 if self.verbose {
393 debug!("JIT assignment suppressed due to semicolon, captured for type info");
394 }
395 }
396 }
397 }
398
399 if self.verbose {
400 debug!(
401 "{} assignment successful, variable_array: {:?}",
402 if actual_used_jit {
403 "JIT"
404 } else {
405 "Interpreter"
406 },
407 self.variable_array
408 );
409 }
410 }
411 Err(e) => {
412 if self.verbose {
413 debug!("JIT execution failed: {e}, using interpreter");
414 }
415 }
417 }
418 }
419 }
420 }
421
422 if !execution_completed {
424 if self.verbose {
425 debug!(
426 "Interpreter path: variable_array size: {}, bytecode.var_count: {}",
427 self.variable_array.len(),
428 bytecode.var_count
429 );
430 }
431
432 let mut execution_bytecode = bytecode.clone();
434 if is_expression_stmt
435 && matches!(final_stmt_emit, FinalStmtEmitDisposition::Inline)
436 && !execution_bytecode.instructions.is_empty()
437 {
438 execution_bytecode.instructions.pop(); let temp_var_id = std::cmp::max(execution_bytecode.var_count, max_var_id + 1);
442 execution_bytecode
443 .instructions
444 .push(runmat_vm::Instr::StoreVar(temp_var_id));
445 execution_bytecode.var_count = temp_var_id + 1; if self.variable_array.len() <= temp_var_id {
449 self.variable_array.resize(temp_var_id + 1, Value::Num(0.0));
450 }
451
452 if self.verbose {
453 debug!(
454 "Modified expression bytecode, new instructions: {:?}",
455 execution_bytecode.instructions
456 );
457 }
458 }
459
460 match self.interpret_with_context(&execution_bytecode).await {
461 Ok(runmat_vm::InterpreterOutcome::Completed(results)) => {
462 if !self.has_jit() || is_expression_stmt {
464 self.stats.interpreter_fallback += 1;
465 }
466 if self.verbose {
467 debug!("Interpreter results: {results:?}");
468 }
469
470 if hir.body.len() == 1 {
472 if let runmat_hir::HirStmt::Assign(var_id, _, _, _) = &hir.body[0] {
473 if let Some(name) = id_to_name.get(&var_id.0) {
474 assigned_this_execution.insert(name.clone());
475 }
476 if var_id.0 < self.variable_array.len() {
478 let assignment_value = self.variable_array[var_id.0].clone();
479 if !is_semicolon_suppressed {
480 result_value = Some(assignment_value);
481 if self.verbose {
482 debug!("Interpreter assignment result: {result_value:?}");
483 }
484 } else {
485 suppressed_value = Some(assignment_value);
486 if self.verbose {
487 debug!("Interpreter assignment suppressed due to semicolon, captured for type info");
488 }
489 }
490 }
491 } else if !is_expression_stmt
492 && !results.is_empty()
493 && !is_semicolon_suppressed
494 && matches!(final_stmt_emit, FinalStmtEmitDisposition::NeedsFallback)
495 {
496 result_value = Some(results[0].clone());
497 }
498 }
499
500 if is_expression_stmt
502 && matches!(final_stmt_emit, FinalStmtEmitDisposition::Inline)
503 && !execution_bytecode.instructions.is_empty()
504 && result_value.is_none()
505 && suppressed_value.is_none()
506 {
507 let temp_var_id = execution_bytecode.var_count - 1; if temp_var_id < self.variable_array.len() {
509 let expression_value = self.variable_array[temp_var_id].clone();
510 if !is_semicolon_suppressed {
511 ans_update = Some((temp_var_id, expression_value.clone()));
513 result_value = Some(expression_value);
514 if self.verbose {
515 debug!("Expression result from temp var {temp_var_id}: {result_value:?}");
516 }
517 } else {
518 suppressed_value = Some(expression_value);
519 if self.verbose {
520 debug!("Expression suppressed, captured for type info from temp var {temp_var_id}: {suppressed_value:?}");
521 }
522 }
523 }
524 } else if !is_semicolon_suppressed
525 && matches!(final_stmt_emit, FinalStmtEmitDisposition::NeedsFallback)
526 && result_value.is_none()
527 {
528 result_value = results.into_iter().last();
529 if self.verbose {
530 debug!("Fallback result from interpreter: {result_value:?}");
531 }
532 }
533
534 if self.verbose {
535 debug!("Final result_value: {result_value:?}");
536 }
537 debug!("Interpreter execution successful");
538 }
539
540 Err(e) => {
541 debug!("Interpreter execution failed: {e}");
542 error = Some(e);
543 }
544 }
545 }
546
547 let last_assign_var = last_unsuppressed_assign_var(&hir.body);
548 let last_expr_emits = last_expr_emits_value(&hir.body);
549 if !is_semicolon_suppressed && result_value.is_none() {
550 if last_assign_var.is_some() || last_expr_emits {
551 if let Some(value) = runmat_runtime::console::take_last_value_output() {
552 result_value = Some(value);
553 }
554 }
555 if result_value.is_none() {
556 if last_assign_var.is_some() {
557 if let Some(var_id) = last_emit_var_index(&bytecode) {
558 if var_id < self.variable_array.len() {
559 result_value = Some(self.variable_array[var_id].clone());
560 }
561 }
562 }
563 if result_value.is_none() {
564 if let Some(var_id) = last_assign_var {
565 if var_id < self.variable_array.len() {
566 result_value = Some(self.variable_array[var_id].clone());
567 }
568 }
569 }
570 }
571 }
572
573 let execution_time = start_time.elapsed();
574 let execution_time_ms = execution_time.as_millis() as u64;
575
576 self.stats.total_execution_time_ms += execution_time_ms;
577 self.stats.average_execution_time_ms =
578 self.stats.total_execution_time_ms as f64 / self.stats.total_executions as f64;
579
580 if error.is_none() {
582 if let Some((mutated_names, assigned)) = runmat_vm::take_updated_workspace_state() {
583 if debug_trace {
584 debug!(
585 ?mutated_names,
586 ?assigned,
587 "[repl] mutated names and assigned return values"
588 );
589 }
590 self.variable_names = mutated_names.clone();
591 let previous_workspace = self.workspace_values.clone();
592 let current_names: HashSet<String> = assigned
593 .iter()
594 .filter(|name| {
595 mutated_names
596 .get(*name)
597 .map(|var_id| *var_id < self.variable_array.len())
598 .unwrap_or(false)
599 })
600 .cloned()
601 .collect();
602 let removed_names: HashSet<String> = previous_workspace
603 .keys()
604 .filter(|name| !current_names.contains(*name))
605 .cloned()
606 .collect();
607 let mut rebuilt_workspace = HashMap::new();
608 let mut changed_names: HashSet<String> = assigned
609 .difference(&prev_assigned_snapshot)
610 .cloned()
611 .collect();
612 changed_names.extend(assigned_this_execution.iter().cloned());
613
614 for name in ¤t_names {
615 let Some(var_id) = mutated_names.get(name).copied() else {
616 continue;
617 };
618 if var_id >= self.variable_array.len() {
619 continue;
620 }
621 let value_clone = self.variable_array[var_id].clone();
622 if previous_workspace.get(name) != Some(&value_clone) {
623 changed_names.insert(name.clone());
624 }
625 rebuilt_workspace.insert(name.clone(), value_clone);
626 }
627
628 if debug_trace {
629 debug!(?changed_names, ?removed_names, "[repl] workspace changes");
630 }
631
632 self.workspace_values = rebuilt_workspace;
633 if !removed_names.is_empty() {
634 workspace_snapshot_force_full = true;
635 } else {
636 for name in changed_names {
637 if let Some(value_clone) = self.workspace_values.get(&name).cloned() {
638 workspace_updates.push(workspace_entry(&name, &value_clone));
639 if debug_trace {
640 debug!(name, ?value_clone, "[repl] workspace update");
641 }
642 }
643 }
644 }
645 } else {
646 for name in &assigned_this_execution {
647 if let Some(var_id) =
648 id_to_name
649 .iter()
650 .find_map(|(vid, n)| if n == name { Some(*vid) } else { None })
651 {
652 if var_id < self.variable_array.len() {
653 let value_clone = self.variable_array[var_id].clone();
654 self.workspace_values
655 .insert(name.clone(), value_clone.clone());
656 workspace_updates.push(workspace_entry(name, &value_clone));
657 }
658 }
659 }
660 }
661 let mut repl_source_id: Option<SourceId> = None;
662 for (name, stmt) in &updated_functions {
663 if matches!(stmt, runmat_hir::HirStmt::Function { .. }) {
664 let source_id = *repl_source_id
665 .get_or_insert_with(|| self.source_pool.intern("<repl>", input));
666 self.function_source_ids.insert(name.clone(), source_id);
667 }
668 }
669 self.function_definitions = updated_functions;
670 if let Some((var_id, value)) = ans_update {
672 self.variable_names.insert("ans".to_string(), var_id);
673 self.workspace_values.insert("ans".to_string(), value);
674 if debug_trace {
675 println!("Updated 'ans' to var_id {}", var_id);
676 }
677 }
678 }
679
680 if self.verbose {
681 debug!("Execution completed in {execution_time_ms}ms (JIT: {used_jit})");
682 }
683
684 if !is_expression_stmt
685 && !is_semicolon_suppressed
686 && matches!(final_stmt_emit, FinalStmtEmitDisposition::NeedsFallback)
687 && result_value.is_none()
688 {
689 if let Some(v) = self
690 .variable_array
691 .iter()
692 .rev()
693 .find(|v| !matches!(v, Value::Num(0.0)))
694 .cloned()
695 {
696 result_value = Some(v);
697 }
698 }
699
700 if !is_semicolon_suppressed
701 && matches!(final_stmt_emit, FinalStmtEmitDisposition::NeedsFallback)
702 {
703 if let Some(value) = result_value.as_ref() {
704 let label = determine_display_label_from_context(
705 single_assign_var,
706 &id_to_name,
707 is_expression_stmt,
708 single_stmt_non_assign,
709 );
710 runmat_runtime::console::record_value_output(label.as_deref(), value);
711 }
712 }
713
714 let type_info = suppressed_value.as_ref().map(format_type_info);
716
717 let streams = runmat_runtime::console::take_thread_buffer()
718 .into_iter()
719 .map(|entry| ExecutionStreamEntry {
720 stream: match entry.stream {
721 runmat_runtime::console::ConsoleStream::Stdout => ExecutionStreamKind::Stdout,
722 runmat_runtime::console::ConsoleStream::Stderr => ExecutionStreamKind::Stderr,
723 runmat_runtime::console::ConsoleStream::ClearScreen => {
724 ExecutionStreamKind::ClearScreen
725 }
726 },
727 text: entry.text,
728 timestamp_ms: entry.timestamp_ms,
729 })
730 .collect();
731 let (workspace_entries, snapshot_full) = if workspace_snapshot_force_full {
732 let mut entries: Vec<WorkspaceEntry> = self
733 .workspace_values
734 .iter()
735 .map(|(name, value)| workspace_entry(name, value))
736 .collect();
737 entries.sort_by(|a, b| a.name.cmp(&b.name));
738 (entries, true)
739 } else if workspace_updates.is_empty() {
740 let source_map = if self.workspace_values.is_empty() {
741 &self.variables
742 } else {
743 &self.workspace_values
744 };
745 if source_map.is_empty() {
746 (workspace_updates, false)
747 } else {
748 let mut entries: Vec<WorkspaceEntry> = source_map
749 .iter()
750 .map(|(name, value)| workspace_entry(name, value))
751 .collect();
752 entries.sort_by(|a, b| a.name.cmp(&b.name));
753 (entries, true)
754 }
755 } else {
756 (workspace_updates, false)
757 };
758 let workspace_snapshot = self.build_workspace_snapshot(workspace_entries, snapshot_full);
759 let figures_touched = runmat_runtime::plotting_hooks::take_recent_figures();
760 let stdin_events = stdin_events
761 .lock()
762 .map(|guard| guard.clone())
763 .unwrap_or_default();
764
765 let warnings = runmat_runtime::warning_store::take_all();
766
767 if let Some(runtime_error) = &mut error {
768 self.normalize_error_namespace(runtime_error);
769 self.populate_callstack(runtime_error);
770 }
771
772 let suppress_public_value =
773 is_expression_stmt && matches!(final_stmt_emit, FinalStmtEmitDisposition::Suppressed);
774 let public_value = if is_semicolon_suppressed || suppress_public_value {
775 None
776 } else {
777 result_value
778 };
779
780 Ok(ExecutionResult {
781 value: public_value,
782 execution_time_ms,
783 used_jit,
784 error,
785 type_info,
786 streams,
787 workspace: workspace_snapshot,
788 figures_touched,
789 warnings,
790 profiling: gather_profiling(execution_time_ms),
791 fusion_plan: fusion_snapshot,
792 stdin_events,
793 })
794 }
795
796 async fn interpret_with_context(
798 &mut self,
799 bytecode: &runmat_vm::Bytecode,
800 ) -> Result<runmat_vm::InterpreterOutcome, RuntimeError> {
801 let source_name = self.current_source_name().to_string();
802 runmat_vm::interpret_with_vars(
803 bytecode,
804 &mut self.variable_array,
805 Some(source_name.as_str()),
806 )
807 .await
808 }
809}