1use anyhow::Result;
2use log::{debug, info, warn};
3use runmat_builtins::{Type, Value};
4use runmat_gc::{gc_configure, gc_stats, GcConfig};
5
6use runmat_lexer::{tokenize_detailed, Token as LexToken};
7use runmat_parser::parse;
8use runmat_snapshot::{Snapshot, SnapshotConfig, SnapshotLoader};
9use runmat_turbine::TurbineEngine;
10use std::collections::{HashMap, HashSet};
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 workspace_values: HashMap<String, Value>,
31 function_definitions: HashMap<String, runmat_hir::HirStmt>,
33 snapshot: Option<Arc<Snapshot>>,
35}
36
37#[derive(Debug, Default)]
38pub struct ExecutionStats {
39 pub total_executions: usize,
40 pub jit_compiled: usize,
41 pub interpreter_fallback: usize,
42 pub total_execution_time_ms: u64,
43 pub average_execution_time_ms: f64,
44}
45
46#[derive(Debug)]
47pub struct ExecutionResult {
48 pub value: Option<Value>,
49 pub execution_time_ms: u64,
50 pub used_jit: bool,
51 pub error: Option<String>,
52 pub type_info: Option<String>,
54}
55
56fn format_type_info(value: &Value) -> String {
58 match value {
59 Value::Int(_) => "scalar".to_string(),
60 Value::Num(_) => "scalar".to_string(),
61 Value::Bool(_) => "logical scalar".to_string(),
62 Value::String(_) => "string".to_string(),
63 Value::StringArray(sa) => {
64 if sa.shape == vec![1, 1] {
66 "string".to_string()
67 } else {
68 format!("{}x{} string array", sa.rows(), sa.cols())
69 }
70 }
71 Value::CharArray(ca) => {
72 if ca.rows == 1 && ca.cols == 1 {
73 "char".to_string()
74 } else {
75 format!("{}x{} char array", ca.rows, ca.cols)
76 }
77 }
78 Value::Tensor(m) => {
79 if m.rows() == 1 && m.cols() == 1 {
80 "scalar".to_string()
81 } else if m.rows() == 1 || m.cols() == 1 {
82 format!("{}x{} vector", m.rows(), m.cols())
83 } else {
84 format!("{}x{} matrix", m.rows(), m.cols())
85 }
86 }
87 Value::Cell(cells) => {
88 if cells.data.len() == 1 {
89 "1x1 cell".to_string()
90 } else {
91 format!("{}x1 cell array", cells.data.len())
92 }
93 }
94 Value::GpuTensor(h) => {
95 if h.shape.len() == 2 {
96 let r = h.shape[0];
97 let c = h.shape[1];
98 if r == 1 && c == 1 {
99 "scalar (gpu)".to_string()
100 } else if r == 1 || c == 1 {
101 format!("{r}x{c} vector (gpu)")
102 } else {
103 format!("{r}x{c} matrix (gpu)")
104 }
105 } else {
106 format!("Tensor{:?} (gpu)", h.shape)
107 }
108 }
109 _ => "value".to_string(),
110 }
111}
112
113impl ReplEngine {
114 pub fn new() -> Result<Self> {
116 Self::with_options(true, false) }
118
119 pub fn with_options(enable_jit: bool, verbose: bool) -> Result<Self> {
121 Self::with_snapshot(enable_jit, verbose, None::<&str>)
122 }
123
124 pub fn with_snapshot<P: AsRef<Path>>(
126 enable_jit: bool,
127 verbose: bool,
128 snapshot_path: Option<P>,
129 ) -> Result<Self> {
130 let snapshot = if let Some(path) = snapshot_path {
132 match Self::load_snapshot(path.as_ref()) {
133 Ok(snapshot) => {
134 info!(
135 "Snapshot loaded successfully from {}",
136 path.as_ref().display()
137 );
138 Some(Arc::new(snapshot))
139 }
140 Err(e) => {
141 warn!(
142 "Failed to load snapshot from {}: {}, continuing without snapshot",
143 path.as_ref().display(),
144 e
145 );
146 None
147 }
148 }
149 } else {
150 None
151 };
152
153 let jit_engine = if enable_jit {
154 match TurbineEngine::new() {
155 Ok(engine) => {
156 info!("JIT compiler initialized successfully");
157 Some(engine)
158 }
159 Err(e) => {
160 warn!("JIT compiler initialization failed: {e}, falling back to interpreter");
161 None
162 }
163 }
164 } else {
165 info!("JIT compiler disabled, using interpreter only");
166 None
167 };
168
169 Ok(Self {
170 jit_engine,
171 verbose,
172 stats: ExecutionStats::default(),
173 variables: HashMap::new(),
174 variable_array: Vec::new(),
175 variable_names: HashMap::new(),
176 workspace_values: HashMap::new(),
177 function_definitions: HashMap::new(),
178 snapshot,
179 })
180 }
181
182 fn load_snapshot(path: &Path) -> Result<Snapshot> {
184 let mut loader = SnapshotLoader::new(SnapshotConfig::default());
185 let (snapshot, _stats) = loader
186 .load(path)
187 .map_err(|e| anyhow::anyhow!("Failed to load snapshot: {}", e))?;
188 Ok(snapshot)
189 }
190
191 pub fn snapshot_info(&self) -> Option<String> {
193 self.snapshot.as_ref().map(|snapshot| {
194 format!(
195 "Snapshot loaded: {} builtins, {} HIR functions, {} bytecode entries",
196 snapshot.builtins.functions.len(),
197 snapshot.hir_cache.functions.len(),
198 snapshot.bytecode_cache.stdlib_bytecode.len()
199 )
200 })
201 }
202
203 pub fn has_snapshot(&self) -> bool {
205 self.snapshot.is_some()
206 }
207
208 pub fn execute(&mut self, input: &str) -> Result<ExecutionResult> {
210 let start_time = Instant::now();
211 self.stats.total_executions += 1;
212 let debug_trace = std::env::var("RUNMAT_DEBUG_REPL").is_ok();
213
214 if self.verbose {
215 debug!("Executing: {}", input.trim());
216 }
217
218 let ast = parse(input)
220 .map_err(|e| anyhow::anyhow!("Failed to parse input '{}': {}", input, e))?;
221 if self.verbose {
222 debug!("AST: {ast:?}");
223 }
224
225 let lowering_result = runmat_hir::lower_with_full_context(
227 &ast,
228 &self.variable_names,
229 &self.function_definitions,
230 )
231 .map_err(|e| anyhow::anyhow!("Failed to lower to HIR: {}", e))?;
232 let (hir, updated_vars, updated_functions, var_names_map) = (
233 lowering_result.hir,
234 lowering_result.variables,
235 lowering_result.functions,
236 lowering_result.var_names,
237 );
238 let max_var_id = updated_vars.values().copied().max().unwrap_or(0);
239 if debug_trace {
240 println!("updated_vars: {:?}", updated_vars);
241 }
242 if debug_trace {
243 println!("workspace_values_before: {:?}", self.workspace_values);
244 }
245 let id_to_name: HashMap<usize, String> = var_names_map
246 .iter()
247 .map(|(var_id, name)| (var_id.0, name.clone()))
248 .collect();
249 let mut assigned_this_execution: HashSet<String> = HashSet::new();
250 let assigned_snapshot: HashSet<String> = updated_vars
251 .keys()
252 .filter(|name| self.workspace_values.contains_key(name.as_str()))
253 .cloned()
254 .collect();
255 let prev_assigned_snapshot = assigned_snapshot.clone();
256 if debug_trace {
257 println!("assigned_snapshot: {:?}", assigned_snapshot);
258 }
259 let _pending_workspace_guard =
260 runmat_ignition::push_pending_workspace(updated_vars.clone(), assigned_snapshot);
261 if self.verbose {
262 debug!("HIR generated successfully");
263 }
264
265 let existing_functions = self.convert_hir_functions_to_user_functions();
267 let bytecode = runmat_ignition::compile_with_functions(&hir, &existing_functions)
268 .map_err(|e| anyhow::anyhow!("Failed to compile to bytecode: {}", e))?;
269 if self.verbose {
270 debug!(
271 "Bytecode compiled: {} instructions",
272 bytecode.instructions.len()
273 );
274 }
275
276 self.prepare_variable_array_for_execution(&bytecode, &updated_vars, debug_trace);
278
279 if self.verbose {
280 debug!(
281 "Variable array after preparation: {:?}",
282 self.variable_array
283 );
284 debug!("Updated variable mapping: {updated_vars:?}");
285 debug!("Bytecode instructions: {:?}", bytecode.instructions);
286 }
287
288 let mut used_jit = false;
289 let mut result_value: Option<Value> = None; let mut suppressed_value: Option<Value> = None; let mut error = None;
292 let mut ans_update: Option<(usize, Value)> = None;
293
294 let is_expression_stmt = bytecode
296 .instructions
297 .last()
298 .map(|instr| matches!(instr, runmat_ignition::Instr::Pop))
299 .unwrap_or(false);
300
301 let ends_with_semicolon = {
303 let toks = tokenize_detailed(input);
304 toks.into_iter()
305 .rev()
306 .map(|t| t.token)
307 .find(|_| true)
308 .map(|t| matches!(t, LexToken::Semicolon))
309 .unwrap_or(false)
310 };
311
312 let is_semicolon_suppressed = if hir.body.len() == 1 {
315 match &hir.body[0] {
316 runmat_hir::HirStmt::ExprStmt(_, _) => ends_with_semicolon,
317 runmat_hir::HirStmt::Assign(_, _, _) => ends_with_semicolon,
318 runmat_hir::HirStmt::If { .. }
319 | runmat_hir::HirStmt::While { .. }
320 | runmat_hir::HirStmt::For { .. }
321 | runmat_hir::HirStmt::Break
322 | runmat_hir::HirStmt::Continue
323 | runmat_hir::HirStmt::Return
324 | runmat_hir::HirStmt::Function { .. }
325 | runmat_hir::HirStmt::MultiAssign(_, _, _)
326 | runmat_hir::HirStmt::AssignLValue(_, _, _)
327 | runmat_hir::HirStmt::Switch { .. }
328 | runmat_hir::HirStmt::TryCatch { .. }
329 | runmat_hir::HirStmt::Global(_)
330 | runmat_hir::HirStmt::Persistent(_)
331 | runmat_hir::HirStmt::Import {
332 path: _,
333 wildcard: _,
334 }
335 | runmat_hir::HirStmt::ClassDef { .. } => true,
336 }
337 } else {
338 false
339 };
340
341 if self.verbose {
342 debug!("HIR body len: {}", hir.body.len());
343 if !hir.body.is_empty() {
344 debug!("HIR statement: {:?}", &hir.body[0]);
345 }
346 debug!("is_semicolon_suppressed: {is_semicolon_suppressed}");
347 }
348
349 if let Some(ref mut jit_engine) = &mut self.jit_engine {
351 if !is_expression_stmt {
352 if self.variable_array.len() < bytecode.var_count {
354 self.variable_array
355 .resize(bytecode.var_count, Value::Num(0.0));
356 }
357
358 if self.verbose {
359 debug!(
360 "JIT path for assignment: variable_array size: {}, bytecode.var_count: {}",
361 self.variable_array.len(),
362 bytecode.var_count
363 );
364 }
365
366 match jit_engine.execute_or_compile(&bytecode, &mut self.variable_array) {
368 Ok((_, actual_used_jit)) => {
369 used_jit = actual_used_jit;
370 if actual_used_jit {
371 self.stats.jit_compiled += 1;
372 } else {
373 self.stats.interpreter_fallback += 1;
374 }
375 let assignment_value =
378 if let Some(runmat_hir::HirStmt::Assign(var_id, _, _)) =
379 hir.body.first()
380 {
381 if let Some(name) = id_to_name.get(&var_id.0) {
382 assigned_this_execution.insert(name.clone());
383 }
384 if var_id.0 < self.variable_array.len() {
385 Some(self.variable_array[var_id.0].clone())
386 } else {
387 None
388 }
389 } else {
390 self.variable_array
391 .iter()
392 .rev()
393 .find(|v| !matches!(v, Value::Num(0.0)))
394 .cloned()
395 };
396
397 if !is_semicolon_suppressed {
398 result_value = assignment_value.clone();
399 if self.verbose {
400 debug!("JIT assignment result: {result_value:?}");
401 }
402 } else {
403 suppressed_value = assignment_value;
404 if self.verbose {
405 debug!("JIT assignment suppressed due to semicolon, captured for type info");
406 }
407 }
408
409 if self.verbose {
410 debug!(
411 "{} assignment successful, variable_array: {:?}",
412 if actual_used_jit {
413 "JIT"
414 } else {
415 "Interpreter"
416 },
417 self.variable_array
418 );
419 }
420 }
421 Err(e) => {
422 if self.verbose {
423 debug!("JIT execution failed: {e}, using interpreter");
424 }
425 }
427 }
428 }
429 }
430
431 if !used_jit {
433 if self.verbose {
434 debug!(
435 "Interpreter path: variable_array size: {}, bytecode.var_count: {}",
436 self.variable_array.len(),
437 bytecode.var_count
438 );
439 }
440
441 let mut execution_bytecode = bytecode.clone();
443 if is_expression_stmt && !execution_bytecode.instructions.is_empty() {
444 execution_bytecode.instructions.pop(); let temp_var_id = std::cmp::max(execution_bytecode.var_count, max_var_id + 1);
448 execution_bytecode
449 .instructions
450 .push(runmat_ignition::Instr::StoreVar(temp_var_id));
451 execution_bytecode.var_count = temp_var_id + 1; if self.variable_array.len() <= temp_var_id {
455 self.variable_array.resize(temp_var_id + 1, Value::Num(0.0));
456 }
457
458 if self.verbose {
459 debug!(
460 "Modified expression bytecode, new instructions: {:?}",
461 execution_bytecode.instructions
462 );
463 }
464 }
465
466 match self.interpret_with_context(&execution_bytecode) {
467 Ok(results) => {
468 if self.jit_engine.is_none() || is_expression_stmt {
470 self.stats.interpreter_fallback += 1;
471 }
472 if self.verbose {
473 debug!("Interpreter results: {results:?}");
474 }
475
476 if hir.body.len() == 1 {
478 if let runmat_hir::HirStmt::Assign(var_id, _, _) = &hir.body[0] {
479 if self.verbose {
480 debug!(
481 "Assignment detected, var_id: {}, ends_with_semicolon: {}",
482 var_id.0, ends_with_semicolon
483 );
484 }
485 if let Some(name) = id_to_name.get(&var_id.0) {
486 assigned_this_execution.insert(name.clone());
487 }
488 if var_id.0 < self.variable_array.len() {
490 let assignment_value = self.variable_array[var_id.0].clone();
491 if !is_semicolon_suppressed {
492 result_value = Some(assignment_value);
493 if self.verbose {
494 debug!("Interpreter assignment result: {result_value:?}");
495 }
496 } else {
497 suppressed_value = Some(assignment_value);
498 if self.verbose {
499 debug!("Interpreter assignment suppressed due to semicolon, captured for type info");
500 }
501 }
502 }
503 } else if !is_expression_stmt
504 && !results.is_empty()
505 && !is_semicolon_suppressed
506 {
507 result_value = Some(results[0].clone());
508 }
509 }
510
511 if is_expression_stmt
513 && !execution_bytecode.instructions.is_empty()
514 && result_value.is_none()
515 && suppressed_value.is_none()
516 {
517 let temp_var_id = execution_bytecode.var_count - 1; if temp_var_id < self.variable_array.len() {
519 let expression_value = self.variable_array[temp_var_id].clone();
520 ans_update = Some((temp_var_id, expression_value.clone()));
522
523 if !is_semicolon_suppressed {
524 result_value = Some(expression_value);
525 if self.verbose {
526 debug!("Expression result from temp var {temp_var_id}: {result_value:?}");
527 }
528 } else {
529 suppressed_value = Some(expression_value);
530 if self.verbose {
531 debug!("Expression suppressed, captured for type info from temp var {temp_var_id}: {suppressed_value:?}");
532 }
533 }
534 }
535 } else if !is_semicolon_suppressed && result_value.is_none() {
536 result_value = results.into_iter().last();
537 if self.verbose {
538 debug!("Fallback result from interpreter: {result_value:?}");
539 }
540 }
541
542 if self.verbose {
543 debug!("Final result_value: {result_value:?}");
544 }
545 debug!(
546 "Interpreter execution successful, variable_array: {:?}",
547 self.variable_array
548 );
549 }
550 Err(e) => {
551 debug!("Interpreter execution failed: {e}");
552 error = Some(format!("Execution failed: {e}"));
553 }
554 }
555 }
556
557 let execution_time = start_time.elapsed();
558 let execution_time_ms = execution_time.as_millis() as u64;
559
560 self.stats.total_execution_time_ms += execution_time_ms;
561 self.stats.average_execution_time_ms =
562 self.stats.total_execution_time_ms as f64 / self.stats.total_executions as f64;
563
564 if error.is_none() {
566 if let Some((mutated_names, assigned)) = runmat_ignition::take_updated_workspace_state()
567 {
568 if debug_trace {
569 println!("mutated_names: {:?}", mutated_names);
570 println!("assigned_returned: {:?}", assigned);
571 }
572 self.variable_names = mutated_names.clone();
573 let mut new_assigned: HashSet<String> = assigned
574 .difference(&prev_assigned_snapshot)
575 .cloned()
576 .collect();
577 new_assigned.extend(assigned_this_execution.iter().cloned());
578 for (name, var_id) in &mutated_names {
579 if *var_id >= self.variable_array.len() {
580 continue;
581 }
582 let new_value = &self.variable_array[*var_id];
583 let changed = match self.workspace_values.get(name) {
584 Some(old_value) => old_value != new_value,
585 None => true,
586 };
587 if changed {
588 new_assigned.insert(name.clone());
589 }
590 }
591 if debug_trace {
592 println!("new_assigned: {:?}", new_assigned);
593 }
594 for name in new_assigned {
595 let var_id = mutated_names.get(&name).copied().or_else(|| {
596 id_to_name
597 .iter()
598 .find_map(|(vid, n)| if n == &name { Some(*vid) } else { None })
599 });
600 if let Some(var_id) = var_id {
601 if var_id < self.variable_array.len() {
602 self.workspace_values
603 .insert(name.clone(), self.variable_array[var_id].clone());
604 if debug_trace {
605 println!(
606 "workspace_update: {} -> {:?}",
607 name, self.variable_array[var_id]
608 );
609 }
610 }
611 }
612 }
613 } else {
614 for name in &assigned_this_execution {
615 if let Some(var_id) =
616 id_to_name
617 .iter()
618 .find_map(|(vid, n)| if n == name { Some(*vid) } else { None })
619 {
620 if var_id < self.variable_array.len() {
621 self.workspace_values
622 .insert(name.clone(), self.variable_array[var_id].clone());
623 }
624 }
625 }
626 }
627 self.function_definitions = updated_functions;
628
629 if let Some((var_id, value)) = ans_update {
631 self.variable_names.insert("ans".to_string(), var_id);
632 self.workspace_values.insert("ans".to_string(), value);
633 if debug_trace {
634 println!("Updated 'ans' to var_id {}", var_id);
635 }
636 }
637 }
638
639 if self.verbose {
640 debug!("Execution completed in {execution_time_ms}ms (JIT: {used_jit})");
641 }
642
643 let type_info = suppressed_value.as_ref().map(format_type_info);
645
646 if !is_semicolon_suppressed && result_value.is_none() {
648 if let Some(v) = self
649 .variable_array
650 .iter()
651 .rev()
652 .find(|v| !matches!(v, Value::Num(0.0)))
653 .cloned()
654 {
655 result_value = Some(v);
656 }
657 }
658
659 Ok(ExecutionResult {
660 value: result_value,
661 execution_time_ms,
662 used_jit,
663 error,
664 type_info,
665 })
666 }
667
668 pub fn stats(&self) -> &ExecutionStats {
670 &self.stats
671 }
672
673 pub fn reset_stats(&mut self) {
675 self.stats = ExecutionStats::default();
676 }
677
678 pub fn clear_variables(&mut self) {
680 self.variables.clear();
681 self.variable_array.clear();
682 self.variable_names.clear();
683 self.workspace_values.clear();
684 }
685
686 pub fn get_variables(&self) -> &HashMap<String, Value> {
688 &self.variables
689 }
690
691 fn interpret_with_context(
693 &mut self,
694 bytecode: &runmat_ignition::Bytecode,
695 ) -> Result<Vec<Value>, String> {
696 match runmat_ignition::interpret_with_vars(
700 bytecode,
701 &mut self.variable_array,
702 Some("<repl>"),
703 ) {
704 Ok(result) => {
705 self.variables.clear();
707 for (i, value) in self.variable_array.iter().enumerate() {
708 if !matches!(value, Value::Num(0.0)) {
709 self.variables.insert(format!("var_{i}"), value.clone());
711 }
712 }
713
714 Ok(result)
715 }
716 Err(e) => Err(e),
717 }
718 }
719
720 fn prepare_variable_array_for_execution(
722 &mut self,
723 bytecode: &runmat_ignition::Bytecode,
724 updated_var_mapping: &HashMap<String, usize>,
725 debug_trace: bool,
726 ) {
727 let max_var_id = updated_var_mapping.values().copied().max().unwrap_or(0);
729 let required_len = std::cmp::max(bytecode.var_count, max_var_id + 1);
730 let mut new_variable_array = vec![Value::Num(0.0); required_len];
731 if debug_trace {
732 println!(
733 "prepare: bytecode.var_count={} required_len={} max_var_id={}",
734 bytecode.var_count, required_len, max_var_id
735 );
736 }
737
738 for (var_name, &new_var_id) in updated_var_mapping {
740 if new_var_id < new_variable_array.len() {
741 if let Some(value) = self.workspace_values.get(var_name) {
742 if debug_trace {
743 println!(
744 "prepare: setting {} (var_id={}) -> {:?}",
745 var_name, new_var_id, value
746 );
747 }
748 new_variable_array[new_var_id] = value.clone();
749 }
750 } else if debug_trace {
751 println!(
752 "prepare: skipping {} (var_id={}) because len={}",
753 var_name,
754 new_var_id,
755 new_variable_array.len()
756 );
757 }
758 }
759
760 self.variable_array = new_variable_array;
762 }
763
764 fn convert_hir_functions_to_user_functions(
766 &self,
767 ) -> HashMap<String, runmat_ignition::UserFunction> {
768 let mut user_functions = HashMap::new();
769
770 for (name, hir_stmt) in &self.function_definitions {
771 if let runmat_hir::HirStmt::Function {
772 name: func_name,
773 params,
774 outputs,
775 body,
776 has_varargin: _,
777 has_varargout: _,
778 } = hir_stmt
779 {
780 let var_map =
782 runmat_hir::remapping::create_complete_function_var_map(params, outputs, body);
783 let max_local_var = var_map.len();
784
785 let user_func = runmat_ignition::UserFunction {
786 name: func_name.clone(),
787 params: params.clone(),
788 outputs: outputs.clone(),
789 body: body.clone(),
790 local_var_count: max_local_var,
791 has_varargin: false,
792 has_varargout: false,
793 var_types: vec![Type::Unknown; max_local_var],
794 };
795 user_functions.insert(name.clone(), user_func);
796 }
797 }
798
799 user_functions
800 }
801
802 pub fn configure_gc(&self, config: GcConfig) -> Result<()> {
804 gc_configure(config)
805 .map_err(|e| anyhow::anyhow!("Failed to configure garbage collector: {}", e))
806 }
807
808 pub fn gc_stats(&self) -> runmat_gc::GcStats {
810 gc_stats()
811 }
812
813 pub fn show_system_info(&self) {
815 println!("RunMat REPL Engine Status");
816 println!("==========================");
817 println!();
818
819 println!(
820 "JIT Compiler: {}",
821 if self.jit_engine.is_some() {
822 "Available"
823 } else {
824 "Disabled/Failed"
825 }
826 );
827 println!("Verbose Mode: {}", self.verbose);
828 println!();
829
830 println!("Execution Statistics:");
831 println!(" Total Executions: {}", self.stats.total_executions);
832 println!(" JIT Compiled: {}", self.stats.jit_compiled);
833 println!(" Interpreter Used: {}", self.stats.interpreter_fallback);
834 println!(
835 " Average Time: {:.2}ms",
836 self.stats.average_execution_time_ms
837 );
838 println!();
839
840 let gc_stats = self.gc_stats();
841 println!("Garbage Collector:");
842 println!(
843 " Total Allocations: {}",
844 gc_stats
845 .total_allocations
846 .load(std::sync::atomic::Ordering::Relaxed)
847 );
848 println!(
849 " Minor Collections: {}",
850 gc_stats
851 .minor_collections
852 .load(std::sync::atomic::Ordering::Relaxed)
853 );
854 println!(
855 " Major Collections: {}",
856 gc_stats
857 .major_collections
858 .load(std::sync::atomic::Ordering::Relaxed)
859 );
860 println!(
861 " Current Memory: {:.2} MB",
862 gc_stats
863 .current_memory_usage
864 .load(std::sync::atomic::Ordering::Relaxed) as f64
865 / 1024.0
866 / 1024.0
867 );
868 println!();
869 }
870}
871
872impl Default for ReplEngine {
873 fn default() -> Self {
874 Self::new().expect("Failed to create default REPL engine")
875 }
876}
877
878pub fn format_tokens(input: &str) -> String {
881 tokenize_detailed(input)
882 .into_iter()
883 .map(|t| format!("{:?}", t.token))
884 .collect::<Vec<_>>()
885 .join(" ")
886}
887
888pub fn execute_and_format(input: &str) -> String {
890 match ReplEngine::new() {
891 Ok(mut engine) => match engine.execute(input) {
892 Ok(result) => {
893 if let Some(error) = result.error {
894 format!("Error: {error}")
895 } else if let Some(value) = result.value {
896 format!("{value:?}")
897 } else {
898 "".to_string()
899 }
900 }
901 Err(e) => format!("Error: {e}"),
902 },
903 Err(e) => format!("Engine Error: {e}"),
904 }
905}