1use crate::Result;
7use runmat_builtins::Value;
8use runmat_repl::ReplEngine;
9use std::path::Path;
10use std::time::{Duration, Instant};
11
12pub struct ExecutionEngine {
14 execution_count: u64,
16 timeout: Option<Duration>,
18 debug: bool,
20 repl_engine: ReplEngine,
22}
23
24#[derive(Debug, Clone)]
26pub struct ExecutionResult {
27 pub status: ExecutionStatus,
29 pub stdout: String,
31 pub stderr: String,
33 pub result: Option<Value>,
35 pub execution_time_ms: u64,
37 pub error: Option<ExecutionError>,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum ExecutionStatus {
44 Success,
46 Error,
48 Interrupted,
50 Timeout,
52}
53
54#[derive(Debug, Clone)]
56pub struct ExecutionError {
57 pub error_type: String,
59 pub message: String,
61 pub traceback: Vec<String>,
63}
64
65impl ExecutionEngine {
66 pub fn new() -> Self {
68 let repl_engine =
69 ReplEngine::with_options(true, false).expect("Failed to create ReplEngine");
70 Self {
71 execution_count: 0,
72 timeout: Some(Duration::from_secs(300)), debug: false,
74 repl_engine,
75 }
76 }
77
78 pub fn with_timeout(timeout: Option<Duration>) -> Self {
80 let repl_engine =
81 ReplEngine::with_options(true, false).expect("Failed to create ReplEngine");
82 Self {
83 execution_count: 0,
84 timeout,
85 debug: false,
86 repl_engine,
87 }
88 }
89
90 pub fn with_options(enable_jit: bool, debug: bool, timeout: Option<Duration>) -> Result<Self> {
92 Self::with_snapshot(enable_jit, debug, timeout, None::<&str>)
93 }
94
95 pub fn with_snapshot<P: AsRef<Path>>(
97 enable_jit: bool,
98 debug: bool,
99 timeout: Option<Duration>,
100 snapshot_path: Option<P>,
101 ) -> Result<Self> {
102 let repl_engine =
103 ReplEngine::with_snapshot(enable_jit, debug, snapshot_path).map_err(|e| {
104 crate::KernelError::Internal(format!("Failed to create ReplEngine: {e}"))
105 })?;
106 Ok(Self {
107 execution_count: 0,
108 timeout,
109 debug,
110 repl_engine,
111 })
112 }
113
114 pub fn set_debug(&mut self, debug: bool) {
116 self.debug = debug;
117 }
118
119 pub fn execution_count(&self) -> u64 {
121 self.execution_count
122 }
123
124 pub fn execute(&mut self, code: &str) -> Result<ExecutionResult> {
126 let start_time = Instant::now();
127 self.execution_count += 1;
128
129 if self.debug {
130 log::debug!("Executing code ({}): {}", self.execution_count, code);
131 }
132
133 match self.repl_engine.execute(code) {
135 Ok(repl_result) => {
136 let execution_time_ms = start_time.elapsed().as_millis() as u64;
137
138 if let Some(error_msg) = repl_result.error {
139 let error_type = if error_msg.contains("parse") || error_msg.contains("Parse") {
141 "ParseError"
142 } else if error_msg.contains("undefined") || error_msg.contains("variable") {
143 "RuntimeError"
144 } else if error_msg.contains("lower") || error_msg.contains("HIR") {
145 "CompileError"
146 } else {
147 "ExecutionError"
148 };
149
150 Ok(ExecutionResult {
151 status: ExecutionStatus::Error,
152 stdout: self.capture_stdout(),
153 stderr: self.capture_stderr(&error_msg),
154 result: None,
155 execution_time_ms,
156 error: Some(ExecutionError {
157 error_type: error_type.to_string(),
158 message: error_msg,
159 traceback: vec!["Error during code execution".to_string()],
160 }),
161 })
162 } else {
163 Ok(ExecutionResult {
164 status: ExecutionStatus::Success,
165 stdout: self.capture_stdout(),
166 stderr: String::new(), result: repl_result.value,
168 execution_time_ms,
169 error: None,
170 })
171 }
172 }
173 Err(e) => {
174 let execution_time_ms = start_time.elapsed().as_millis() as u64;
175 let error_msg = e.to_string();
176
177 let error_type = if error_msg.contains("parse") || error_msg.contains("Parse") {
179 "ParseError"
180 } else if error_msg.contains("undefined") || error_msg.contains("variable") {
181 "RuntimeError"
182 } else if error_msg.contains("lower") || error_msg.contains("HIR") {
183 "CompileError"
184 } else {
185 "ExecutionError"
186 };
187
188 Ok(ExecutionResult {
189 status: ExecutionStatus::Error,
190 stdout: String::new(),
191 stderr: String::new(),
192 result: None,
193 execution_time_ms,
194 error: Some(ExecutionError {
195 error_type: error_type.to_string(),
196 message: error_msg,
197 traceback: vec!["Error during code execution".to_string()],
198 }),
199 })
200 }
201 }
202 }
203
204 pub fn execute_with_timeout(
206 &mut self,
207 code: &str,
208 timeout: Duration,
209 ) -> Result<ExecutionResult> {
210 let original_timeout = self.timeout;
211 self.timeout = Some(timeout);
212 let result = self.execute(code);
213 self.timeout = original_timeout;
214 result
215 }
216
217 pub fn reset(&mut self) {
219 self.execution_count = 0;
220 if self.debug {
221 log::debug!("Execution engine reset");
222 }
223 }
224
225 pub fn stats(&self) -> ExecutionStats {
227 let repl_stats = self.repl_engine.stats();
228 ExecutionStats {
229 execution_count: self.execution_count,
230 timeout_seconds: self.timeout.map(|d| d.as_secs()),
231 debug_enabled: self.debug,
232 repl_total_executions: repl_stats.total_executions,
233 repl_jit_compiled: repl_stats.jit_compiled,
234 repl_interpreter_fallback: repl_stats.interpreter_fallback,
235 repl_average_time_ms: repl_stats.average_execution_time_ms,
236 }
237 }
238
239 pub fn snapshot_info(&self) -> Option<String> {
241 self.repl_engine.snapshot_info()
242 }
243
244 pub fn has_snapshot(&self) -> bool {
246 self.repl_engine.has_snapshot()
247 }
248
249 fn capture_stdout(&self) -> String {
251 String::new()
256 }
257
258 fn capture_stderr(&self, error_msg: &str) -> String {
260 format!("Error: {error_msg}")
262 }
263}
264
265impl Default for ExecutionEngine {
266 fn default() -> Self {
267 Self::new()
268 }
269}
270
271#[derive(Debug, Clone)]
273pub struct ExecutionStats {
274 pub execution_count: u64,
276 pub timeout_seconds: Option<u64>,
278 pub debug_enabled: bool,
280 pub repl_total_executions: usize,
282 pub repl_jit_compiled: usize,
284 pub repl_interpreter_fallback: usize,
286 pub repl_average_time_ms: f64,
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 #[test]
295 fn test_execution_engine_creation() {
296 let engine = ExecutionEngine::new();
297 assert_eq!(engine.execution_count(), 0);
298 assert!(!engine.debug);
299 }
300
301 #[test]
302 fn test_execution_engine_with_timeout() {
303 let timeout = Duration::from_secs(60);
304 let engine = ExecutionEngine::with_timeout(Some(timeout));
305 assert_eq!(engine.timeout, Some(timeout));
306 }
307
308 #[test]
309 fn test_simple_execution() {
310 let mut engine = ExecutionEngine::new();
311 let result = engine.execute("x = 1 + 2").unwrap();
312
313 assert_eq!(result.status, ExecutionStatus::Success);
314 assert_eq!(engine.execution_count(), 1);
315 let _time = result.execution_time_ms;
317 assert!(result.error.is_none());
318 }
319
320 #[test]
321 fn test_parse_error_handling() {
322 let mut engine = ExecutionEngine::new();
323 let result = engine.execute("x = 1 +").unwrap();
324
325 assert_eq!(result.status, ExecutionStatus::Error);
326 assert!(result.error.is_some());
327
328 let error = result.error.unwrap();
329 assert_eq!(error.error_type, "ParseError");
330 assert!(!error.message.is_empty());
331 }
332
333 #[test]
334 fn test_runtime_error_handling() {
335 let mut engine = ExecutionEngine::new();
336 let result = engine.execute("x = undefined_var").unwrap();
337
338 assert_eq!(result.status, ExecutionStatus::Error);
339 assert!(result.error.is_some());
340
341 let error = result.error.unwrap();
342 assert!(error.error_type == "RuntimeError" || error.error_type == "CompileError");
343 }
344
345 #[test]
346 fn test_execution_count_increment() {
347 let mut engine = ExecutionEngine::new();
348
349 engine.execute("x = 1").unwrap();
350 assert_eq!(engine.execution_count(), 1);
351
352 engine.execute("y = 2").unwrap();
353 assert_eq!(engine.execution_count(), 2);
354
355 engine.execute("invalid syntax").unwrap();
357 assert_eq!(engine.execution_count(), 3);
358 }
359
360 #[test]
361 fn test_engine_reset() {
362 let mut engine = ExecutionEngine::new();
363 engine.execute("x = 1").unwrap();
364 assert_eq!(engine.execution_count(), 1);
365
366 engine.reset();
367 assert_eq!(engine.execution_count(), 0);
368 }
369
370 #[test]
371 fn test_debug_mode() {
372 let mut engine = ExecutionEngine::new();
373 assert!(!engine.debug);
374
375 engine.set_debug(true);
376 assert!(engine.debug);
377
378 engine.set_debug(false);
379 assert!(!engine.debug);
380 }
381
382 #[test]
383 fn test_stats() {
384 let mut engine = ExecutionEngine::new();
385 engine.set_debug(true);
386 engine.execute("x = 1").unwrap();
387
388 let stats = engine.stats();
389 assert_eq!(stats.execution_count, 1);
390 assert!(stats.debug_enabled);
391 assert!(stats.timeout_seconds.is_some());
392 }
393}