1use anyhow::Result;
2use log::{debug, info, warn};
3use runmat_builtins::Value;
4use runmat_gc::{gc_configure, gc_stats, GcConfig};
5
6use runmat_lexer::{tokenize, tokenize_detailed, Token as LexToken};
7use runmat_parser::parse;
8use runmat_snapshot::{Snapshot, SnapshotConfig, SnapshotLoader};
9use runmat_turbine::TurbineEngine;
10use std::collections::HashMap;
11use std::path::Path;
12use std::sync::Arc;
13use std::time::Instant;
14
15pub struct ReplEngine {
17 jit_engine: Option<TurbineEngine>,
19 verbose: bool,
21 stats: ExecutionStats,
23 variables: HashMap<String, Value>,
25 variable_array: Vec<Value>,
27 variable_names: HashMap<String, usize>,
29 function_definitions: HashMap<String, runmat_hir::HirStmt>,
31 snapshot: Option<Arc<Snapshot>>,
33}
34
35#[derive(Debug, Default)]
36pub struct ExecutionStats {
37 pub total_executions: usize,
38 pub jit_compiled: usize,
39 pub interpreter_fallback: usize,
40 pub total_execution_time_ms: u64,
41 pub average_execution_time_ms: f64,
42}
43
44#[derive(Debug)]
45pub struct ExecutionResult {
46 pub value: Option<Value>,
47 pub execution_time_ms: u64,
48 pub used_jit: bool,
49 pub error: Option<String>,
50 pub type_info: Option<String>,
52}
53
54fn format_type_info(value: &Value) -> String {
56 match value {
57 Value::Int(_) => "scalar".to_string(),
58 Value::Num(_) => "scalar".to_string(),
59 Value::Bool(_) => "logical scalar".to_string(),
60 Value::String(_) => "string".to_string(),
61 Value::StringArray(sa) => {
62 if sa.shape == vec![1, 1] {
64 "string".to_string()
65 } else {
66 format!("{}x{} string array", sa.rows(), sa.cols())
67 }
68 }
69 Value::CharArray(ca) => {
70 if ca.rows == 1 && ca.cols == 1 {
71 "char".to_string()
72 } else {
73 format!("{}x{} char array", ca.rows, ca.cols)
74 }
75 }
76 Value::Tensor(m) => {
77 if m.rows() == 1 && m.cols() == 1 {
78 "scalar".to_string()
79 } else if m.rows() == 1 || m.cols() == 1 {
80 format!("{}x{} vector", m.rows(), m.cols())
81 } else {
82 format!("{}x{} matrix", m.rows(), m.cols())
83 }
84 }
85 Value::Cell(cells) => {
86 if cells.data.len() == 1 {
87 "1x1 cell".to_string()
88 } else {
89 format!("{}x1 cell array", cells.data.len())
90 }
91 }
92 Value::GpuTensor(h) => {
93 if h.shape.len() == 2 {
94 let r = h.shape[0];
95 let c = h.shape[1];
96 if r == 1 && c == 1 {
97 "scalar (gpu)".to_string()
98 } else if r == 1 || c == 1 {
99 format!("{r}x{c} vector (gpu)")
100 } else {
101 format!("{r}x{c} matrix (gpu)")
102 }
103 } else {
104 format!("Tensor{:?} (gpu)", h.shape)
105 }
106 }
107 _ => "value".to_string(),
108 }
109}
110
111impl ReplEngine {
112 pub fn new() -> Result<Self> {
114 Self::with_options(true, false) }
116
117 pub fn with_options(enable_jit: bool, verbose: bool) -> Result<Self> {
119 Self::with_snapshot(enable_jit, verbose, None::<&str>)
120 }
121
122 pub fn with_snapshot<P: AsRef<Path>>(
124 enable_jit: bool,
125 verbose: bool,
126 snapshot_path: Option<P>,
127 ) -> Result<Self> {
128 let snapshot = if let Some(path) = snapshot_path {
130 match Self::load_snapshot(path.as_ref()) {
131 Ok(snapshot) => {
132 info!(
133 "Snapshot loaded successfully from {}",
134 path.as_ref().display()
135 );
136 Some(Arc::new(snapshot))
137 }
138 Err(e) => {
139 warn!(
140 "Failed to load snapshot from {}: {}, continuing without snapshot",
141 path.as_ref().display(),
142 e
143 );
144 None
145 }
146 }
147 } else {
148 None
149 };
150
151 let jit_engine = if enable_jit {
152 match TurbineEngine::new() {
153 Ok(engine) => {
154 info!("JIT compiler initialized successfully");
155 Some(engine)
156 }
157 Err(e) => {
158 warn!("JIT compiler initialization failed: {e}, falling back to interpreter");
159 None
160 }
161 }
162 } else {
163 info!("JIT compiler disabled, using interpreter only");
164 None
165 };
166
167 Ok(Self {
168 jit_engine,
169 verbose,
170 stats: ExecutionStats::default(),
171 variables: HashMap::new(),
172 variable_array: Vec::new(),
173 variable_names: HashMap::new(),
174 function_definitions: HashMap::new(),
175 snapshot,
176 })
177 }
178
179 fn load_snapshot(path: &Path) -> Result<Snapshot> {
181 let mut loader = SnapshotLoader::new(SnapshotConfig::default());
182 let (snapshot, _stats) = loader
183 .load(path)
184 .map_err(|e| anyhow::anyhow!("Failed to load snapshot: {}", e))?;
185 Ok(snapshot)
186 }
187
188 pub fn snapshot_info(&self) -> Option<String> {
190 self.snapshot.as_ref().map(|snapshot| {
191 format!(
192 "Snapshot loaded: {} builtins, {} HIR functions, {} bytecode entries",
193 snapshot.builtins.functions.len(),
194 snapshot.hir_cache.functions.len(),
195 snapshot.bytecode_cache.stdlib_bytecode.len()
196 )
197 })
198 }
199
200 pub fn has_snapshot(&self) -> bool {
202 self.snapshot.is_some()
203 }
204
205 pub fn execute(&mut self, input: &str) -> Result<ExecutionResult> {
207 let start_time = Instant::now();
208 self.stats.total_executions += 1;
209
210 if self.verbose {
211 debug!("Executing: {}", input.trim());
212 }
213
214 let ast = parse(input)
216 .map_err(|e| anyhow::anyhow!("Failed to parse input '{}': {}", input, e))?;
217 if self.verbose {
218 debug!("AST: {ast:?}");
219 }
220
221 let lowering_result = runmat_hir::lower_with_full_context(
223 &ast,
224 &self.variable_names,
225 &self.function_definitions,
226 )
227 .map_err(|e| anyhow::anyhow!("Failed to lower to HIR: {}", e))?;
228 let (hir, updated_vars, updated_functions) = (
229 lowering_result.hir,
230 lowering_result.variables,
231 lowering_result.functions,
232 );
233 if self.verbose {
234 debug!("HIR generated successfully");
235 }
236
237 let existing_functions = self.convert_hir_functions_to_user_functions();
239 let bytecode = runmat_ignition::compile_with_functions(&hir, &existing_functions)
240 .map_err(|e| anyhow::anyhow!("Failed to compile to bytecode: {}", e))?;
241 if self.verbose {
242 debug!(
243 "Bytecode compiled: {} instructions",
244 bytecode.instructions.len()
245 );
246 }
247
248 self.prepare_variable_array_for_execution(&bytecode, &updated_vars);
250
251 if self.verbose {
252 debug!(
253 "Variable array after preparation: {:?}",
254 self.variable_array
255 );
256 debug!("Updated variable mapping: {updated_vars:?}");
257 debug!("Bytecode instructions: {:?}", bytecode.instructions);
258 }
259
260 let mut used_jit = false;
261 let mut result_value: Option<Value> = None; let mut suppressed_value: Option<Value> = None; let mut error = None;
264
265 let is_expression_stmt = bytecode
267 .instructions
268 .last()
269 .map(|instr| matches!(instr, runmat_ignition::Instr::Pop))
270 .unwrap_or(false);
271
272 let ends_with_semicolon = {
274 let toks = tokenize_detailed(input);
275 toks.into_iter()
276 .rev()
277 .map(|t| t.token)
278 .find(|_| true)
279 .map(|t| matches!(t, LexToken::Semicolon))
280 .unwrap_or(false)
281 };
282
283 let is_semicolon_suppressed = if hir.body.len() == 1 {
286 match &hir.body[0] {
287 runmat_hir::HirStmt::ExprStmt(_, _) => ends_with_semicolon,
288 runmat_hir::HirStmt::Assign(_, _, _) => ends_with_semicolon,
289 runmat_hir::HirStmt::If { .. }
290 | runmat_hir::HirStmt::While { .. }
291 | runmat_hir::HirStmt::For { .. }
292 | runmat_hir::HirStmt::Break
293 | runmat_hir::HirStmt::Continue
294 | runmat_hir::HirStmt::Return
295 | runmat_hir::HirStmt::Function { .. }
296 | runmat_hir::HirStmt::MultiAssign(_, _, _)
297 | runmat_hir::HirStmt::AssignLValue(_, _, _)
298 | runmat_hir::HirStmt::Switch { .. }
299 | runmat_hir::HirStmt::TryCatch { .. }
300 | runmat_hir::HirStmt::Global(_)
301 | runmat_hir::HirStmt::Persistent(_)
302 | runmat_hir::HirStmt::Import {
303 path: _,
304 wildcard: _,
305 }
306 | runmat_hir::HirStmt::ClassDef { .. } => true,
307 }
308 } else {
309 false
310 };
311
312 if self.verbose {
313 debug!("HIR body len: {}", hir.body.len());
314 if !hir.body.is_empty() {
315 debug!("HIR statement: {:?}", &hir.body[0]);
316 }
317 debug!("is_semicolon_suppressed: {is_semicolon_suppressed}");
318 }
319
320 if let Some(ref mut jit_engine) = &mut self.jit_engine {
322 if !is_expression_stmt {
323 if self.variable_array.len() < bytecode.var_count {
325 self.variable_array
326 .resize(bytecode.var_count, Value::Num(0.0));
327 }
328
329 if self.verbose {
330 debug!(
331 "JIT path for assignment: variable_array size: {}, bytecode.var_count: {}",
332 self.variable_array.len(),
333 bytecode.var_count
334 );
335 }
336
337 match jit_engine.execute_or_compile(&bytecode, &mut self.variable_array) {
339 Ok((_, actual_used_jit)) => {
340 used_jit = actual_used_jit;
341 if actual_used_jit {
342 self.stats.jit_compiled += 1;
343 } else {
344 self.stats.interpreter_fallback += 1;
345 }
346 let assignment_value =
349 if let Some(runmat_hir::HirStmt::Assign(var_id, _, _)) =
350 hir.body.first()
351 {
352 if var_id.0 < self.variable_array.len() {
353 Some(self.variable_array[var_id.0].clone())
354 } else {
355 None
356 }
357 } else {
358 self.variable_array
359 .iter()
360 .rev()
361 .find(|v| !matches!(v, Value::Num(0.0)))
362 .cloned()
363 };
364
365 if !is_semicolon_suppressed {
366 result_value = assignment_value.clone();
367 if self.verbose {
368 debug!("JIT assignment result: {result_value:?}");
369 }
370 } else {
371 suppressed_value = assignment_value;
372 if self.verbose {
373 debug!("JIT assignment suppressed due to semicolon, captured for type info");
374 }
375 }
376
377 if self.verbose {
378 debug!(
379 "{} assignment successful, variable_array: {:?}",
380 if actual_used_jit {
381 "JIT"
382 } else {
383 "Interpreter"
384 },
385 self.variable_array
386 );
387 }
388 }
389 Err(e) => {
390 if self.verbose {
391 debug!("JIT execution failed: {e}, using interpreter");
392 }
393 }
395 }
396 }
397 }
398
399 if !used_jit {
401 if self.verbose {
402 debug!(
403 "Interpreter path: variable_array size: {}, bytecode.var_count: {}",
404 self.variable_array.len(),
405 bytecode.var_count
406 );
407 }
408
409 let mut execution_bytecode = bytecode.clone();
411 if is_expression_stmt && !execution_bytecode.instructions.is_empty() {
412 execution_bytecode.instructions.pop(); let temp_var_id = execution_bytecode.var_count;
416 execution_bytecode
417 .instructions
418 .push(runmat_ignition::Instr::StoreVar(temp_var_id));
419 execution_bytecode.var_count += 1; if self.variable_array.len() <= temp_var_id {
423 self.variable_array.resize(temp_var_id + 1, Value::Num(0.0));
424 }
425
426 if self.verbose {
427 debug!(
428 "Modified expression bytecode, new instructions: {:?}",
429 execution_bytecode.instructions
430 );
431 }
432 }
433
434 match self.interpret_with_context(&execution_bytecode) {
435 Ok(results) => {
436 if self.jit_engine.is_none() || is_expression_stmt {
438 self.stats.interpreter_fallback += 1;
439 }
440 if self.verbose {
441 debug!("Interpreter results: {results:?}");
442 }
443
444 if hir.body.len() == 1 {
446 if let runmat_hir::HirStmt::Assign(var_id, _, _) = &hir.body[0] {
447 if self.verbose {
448 debug!(
449 "Assignment detected, var_id: {}, ends_with_semicolon: {}",
450 var_id.0, ends_with_semicolon
451 );
452 }
453 if var_id.0 < self.variable_array.len() {
455 let assignment_value = self.variable_array[var_id.0].clone();
456 if !is_semicolon_suppressed {
457 result_value = Some(assignment_value);
458 if self.verbose {
459 debug!("Setting assignment result_value: {result_value:?}");
460 }
461 } else {
462 suppressed_value = Some(assignment_value);
463 if self.verbose {
464 debug!("Assignment suppressed, captured for type info: {suppressed_value:?}");
465 }
466 }
467 }
468 }
469 }
470
471 if is_expression_stmt
473 && !execution_bytecode.instructions.is_empty()
474 && result_value.is_none()
475 && suppressed_value.is_none()
476 {
477 let temp_var_id = execution_bytecode.var_count - 1; if temp_var_id < self.variable_array.len() {
479 let expression_value = self.variable_array[temp_var_id].clone();
480 if !is_semicolon_suppressed {
481 result_value = Some(expression_value);
482 if self.verbose {
483 debug!("Expression result from temp var {temp_var_id}: {result_value:?}");
484 }
485 } else {
486 suppressed_value = Some(expression_value);
487 if self.verbose {
488 debug!("Expression suppressed, captured for type info from temp var {temp_var_id}: {suppressed_value:?}");
489 }
490 }
491 }
492 } else if !is_semicolon_suppressed && result_value.is_none() {
493 result_value = results.into_iter().last();
494 if self.verbose {
495 debug!("Fallback result from interpreter: {result_value:?}");
496 }
497 }
498
499 if self.verbose {
500 debug!("Final result_value: {result_value:?}");
501 }
502 debug!(
503 "Interpreter execution successful, variable_array: {:?}",
504 self.variable_array
505 );
506 }
507 Err(e) => {
508 debug!("Interpreter execution failed: {e}");
509 error = Some(format!("Execution failed: {e}"));
510 }
511 }
512 }
513
514 let execution_time = start_time.elapsed();
515 let execution_time_ms = execution_time.as_millis() as u64;
516
517 self.stats.total_execution_time_ms += execution_time_ms;
518 self.stats.average_execution_time_ms =
519 self.stats.total_execution_time_ms as f64 / self.stats.total_executions as f64;
520
521 if error.is_none() {
523 self.variable_names = updated_vars;
524 self.function_definitions = updated_functions;
525 }
526
527 if self.verbose {
528 debug!("Execution completed in {execution_time_ms}ms (JIT: {used_jit})");
529 }
530
531 let type_info = suppressed_value.as_ref().map(format_type_info);
533
534 if !is_semicolon_suppressed && result_value.is_none() {
536 if let Some(v) = self
537 .variable_array
538 .iter()
539 .rev()
540 .find(|v| !matches!(v, Value::Num(0.0)))
541 .cloned()
542 {
543 result_value = Some(v);
544 }
545 }
546
547 Ok(ExecutionResult {
548 value: result_value,
549 execution_time_ms,
550 used_jit,
551 error,
552 type_info,
553 })
554 }
555
556 pub fn stats(&self) -> &ExecutionStats {
558 &self.stats
559 }
560
561 pub fn reset_stats(&mut self) {
563 self.stats = ExecutionStats::default();
564 }
565
566 pub fn clear_variables(&mut self) {
568 self.variables.clear();
569 self.variable_array.clear();
570 self.variable_names.clear();
571 }
572
573 pub fn get_variables(&self) -> &HashMap<String, Value> {
575 &self.variables
576 }
577
578 fn interpret_with_context(
580 &mut self,
581 bytecode: &runmat_ignition::Bytecode,
582 ) -> Result<Vec<Value>, String> {
583 match runmat_ignition::interpret_with_vars(
587 bytecode,
588 &mut self.variable_array,
589 Some("<repl>"),
590 ) {
591 Ok(result) => {
592 self.variables.clear();
594 for (i, value) in self.variable_array.iter().enumerate() {
595 if !matches!(value, Value::Num(0.0)) {
596 self.variables.insert(format!("var_{i}"), value.clone());
598 }
599 }
600
601 Ok(result)
602 }
603 Err(e) => Err(e),
604 }
605 }
606
607 fn prepare_variable_array_for_execution(
609 &mut self,
610 bytecode: &runmat_ignition::Bytecode,
611 updated_var_mapping: &HashMap<String, usize>,
612 ) {
613 let mut new_variable_array = vec![Value::Num(0.0); bytecode.var_count];
615
616 for (var_name, &new_var_id) in updated_var_mapping {
618 if let Some(&old_var_id) = self.variable_names.get(var_name) {
619 if old_var_id < self.variable_array.len() && new_var_id < new_variable_array.len() {
621 new_variable_array[new_var_id] = self.variable_array[old_var_id].clone();
622 }
623 }
624 }
625
626 self.variable_array = new_variable_array;
628 }
629
630 fn convert_hir_functions_to_user_functions(
632 &self,
633 ) -> HashMap<String, runmat_ignition::UserFunction> {
634 let mut user_functions = HashMap::new();
635
636 for (name, hir_stmt) in &self.function_definitions {
637 if let runmat_hir::HirStmt::Function {
638 name: func_name,
639 params,
640 outputs,
641 body,
642 has_varargin: _,
643 has_varargout: _,
644 } = hir_stmt
645 {
646 let var_map =
648 runmat_hir::remapping::create_complete_function_var_map(params, outputs, body);
649 let max_local_var = var_map.len();
650
651 let user_func = runmat_ignition::UserFunction {
652 name: func_name.clone(),
653 params: params.clone(),
654 outputs: outputs.clone(),
655 body: body.clone(),
656 local_var_count: max_local_var,
657 has_varargin: false,
658 has_varargout: false,
659 };
660 user_functions.insert(name.clone(), user_func);
661 }
662 }
663
664 user_functions
665 }
666
667 pub fn configure_gc(&self, config: GcConfig) -> Result<()> {
669 gc_configure(config)
670 .map_err(|e| anyhow::anyhow!("Failed to configure garbage collector: {}", e))
671 }
672
673 pub fn gc_stats(&self) -> runmat_gc::GcStats {
675 gc_stats()
676 }
677
678 pub fn show_system_info(&self) {
680 println!("RunMat REPL Engine Status");
681 println!("==========================");
682 println!();
683
684 println!(
685 "JIT Compiler: {}",
686 if self.jit_engine.is_some() {
687 "Available"
688 } else {
689 "Disabled/Failed"
690 }
691 );
692 println!("Verbose Mode: {}", self.verbose);
693 println!();
694
695 println!("Execution Statistics:");
696 println!(" Total Executions: {}", self.stats.total_executions);
697 println!(" JIT Compiled: {}", self.stats.jit_compiled);
698 println!(" Interpreter Used: {}", self.stats.interpreter_fallback);
699 println!(
700 " Average Time: {:.2}ms",
701 self.stats.average_execution_time_ms
702 );
703 println!();
704
705 let gc_stats = self.gc_stats();
706 println!("Garbage Collector:");
707 println!(
708 " Total Allocations: {}",
709 gc_stats
710 .total_allocations
711 .load(std::sync::atomic::Ordering::Relaxed)
712 );
713 println!(
714 " Minor Collections: {}",
715 gc_stats
716 .minor_collections
717 .load(std::sync::atomic::Ordering::Relaxed)
718 );
719 println!(
720 " Major Collections: {}",
721 gc_stats
722 .major_collections
723 .load(std::sync::atomic::Ordering::Relaxed)
724 );
725 println!(
726 " Current Memory: {:.2} MB",
727 gc_stats
728 .current_memory_usage
729 .load(std::sync::atomic::Ordering::Relaxed) as f64
730 / 1024.0
731 / 1024.0
732 );
733 println!();
734 }
735}
736
737impl Default for ReplEngine {
738 fn default() -> Self {
739 Self::new().expect("Failed to create default REPL engine")
740 }
741}
742
743pub fn format_tokens(input: &str) -> String {
746 let tokens = tokenize(input);
747 tokens
748 .into_iter()
749 .map(|t| format!("{t:?}"))
750 .collect::<Vec<_>>()
751 .join(" ")
752}
753
754pub fn execute_and_format(input: &str) -> String {
756 match ReplEngine::new() {
757 Ok(mut engine) => match engine.execute(input) {
758 Ok(result) => {
759 if let Some(error) = result.error {
760 format!("Error: {error}")
761 } else if let Some(value) = result.value {
762 format!("{value:?}")
763 } else {
764 "".to_string()
765 }
766 }
767 Err(e) => format!("Error: {e}"),
768 },
769 Err(e) => format!("Engine Error: {e}"),
770 }
771}