Skip to main content

repl_core/
eval.rs

1//! REPL Evaluation Engine
2//!
3//! Handles evaluation of DSL expressions and commands in the REPL.
4
5use crate::dsl::{evaluator::DslEvaluator, lexer::Lexer, parser::Parser, DslValue};
6use crate::error::{ReplError, Result};
7use crate::runtime_bridge::RuntimeBridge;
8use std::sync::Arc;
9use uuid::Uuid;
10
11/// REPL Engine that coordinates DSL evaluation
12pub struct ReplEngine {
13    /// DSL evaluator
14    evaluator: DslEvaluator,
15}
16
17impl ReplEngine {
18    /// Create a new REPL engine
19    pub fn new(runtime_bridge: Arc<RuntimeBridge>) -> Self {
20        let evaluator = DslEvaluator::new(runtime_bridge);
21
22        Self { evaluator }
23    }
24
25    /// Evaluate an expression or command
26    pub async fn evaluate(&self, input: &str) -> Result<String> {
27        let trimmed = input.trim();
28
29        if trimmed.is_empty() {
30            return Err(ReplError::Evaluation("Empty expression".to_string()));
31        }
32
33        // Check for special commands first
34        if let Some(result) = self.handle_repl_command(trimmed).await? {
35            return Ok(result);
36        }
37
38        // Parse and evaluate as DSL
39        match self.evaluate_dsl(trimmed).await {
40            Ok(value) => Ok(self.format_value(value)),
41            Err(e) => {
42                // Try to evaluate as a simple expression for better UX
43                if trimmed.contains('=') || trimmed.contains('+') || trimmed.contains('-') {
44                    self.evaluate_simple_expression(trimmed)
45                } else {
46                    Err(e)
47                }
48            }
49        }
50    }
51
52    /// Handle REPL-specific commands
53    async fn handle_repl_command(&self, input: &str) -> Result<Option<String>> {
54        let parts: Vec<&str> = input.split_whitespace().collect();
55        if parts.is_empty() {
56            return Ok(None);
57        }
58
59        match parts[0] {
60            ":help" | ":h" => Ok(Some(self.show_help())),
61            ":agents" => Ok(Some(self.list_agents().await)),
62            ":agent" => {
63                if parts.len() > 1 {
64                    match parts[1] {
65                        "list" => Ok(Some(self.list_agents().await)),
66                        "start" => {
67                            if parts.len() > 2 {
68                                let agent_id = parts[2].parse::<Uuid>().map_err(|_| {
69                                    ReplError::Evaluation("Invalid agent ID".to_string())
70                                })?;
71                                self.start_agent(agent_id).await
72                            } else {
73                                Err(ReplError::Evaluation(
74                                    "Usage: :agent start <agent_id>".to_string(),
75                                ))
76                            }
77                        }
78                        "stop" => {
79                            if parts.len() > 2 {
80                                let agent_id = parts[2].parse::<Uuid>().map_err(|_| {
81                                    ReplError::Evaluation("Invalid agent ID".to_string())
82                                })?;
83                                self.stop_agent(agent_id).await
84                            } else {
85                                Err(ReplError::Evaluation(
86                                    "Usage: :agent stop <agent_id>".to_string(),
87                                ))
88                            }
89                        }
90                        "pause" => {
91                            if parts.len() > 2 {
92                                let agent_id = parts[2].parse::<Uuid>().map_err(|_| {
93                                    ReplError::Evaluation("Invalid agent ID".to_string())
94                                })?;
95                                self.pause_agent(agent_id).await
96                            } else {
97                                Err(ReplError::Evaluation(
98                                    "Usage: :agent pause <agent_id>".to_string(),
99                                ))
100                            }
101                        }
102                        "resume" => {
103                            if parts.len() > 2 {
104                                let agent_id = parts[2].parse::<Uuid>().map_err(|_| {
105                                    ReplError::Evaluation("Invalid agent ID".to_string())
106                                })?;
107                                self.resume_agent(agent_id).await
108                            } else {
109                                Err(ReplError::Evaluation(
110                                    "Usage: :agent resume <agent_id>".to_string(),
111                                ))
112                            }
113                        }
114                        "destroy" => {
115                            if parts.len() > 2 {
116                                let agent_id = parts[2].parse::<Uuid>().map_err(|_| {
117                                    ReplError::Evaluation("Invalid agent ID".to_string())
118                                })?;
119                                self.destroy_agent(agent_id).await
120                            } else {
121                                Err(ReplError::Evaluation(
122                                    "Usage: :agent destroy <agent_id>".to_string(),
123                                ))
124                            }
125                        }
126                        "execute" => {
127                            if parts.len() > 3 {
128                                let agent_id = parts[2].parse::<Uuid>().map_err(|_| {
129                                    ReplError::Evaluation("Invalid agent ID".to_string())
130                                })?;
131                                let behavior_name = parts[3];
132                                // Parse remaining parts as arguments
133                                let args = parts[4..].join(" ");
134                                self.execute_agent_behavior(agent_id, behavior_name, &args)
135                                    .await
136                            } else {
137                                Err(ReplError::Evaluation(
138                                    "Usage: :agent execute <agent_id> <behavior> [args...]"
139                                        .to_string(),
140                                ))
141                            }
142                        }
143                        "debug" => {
144                            if parts.len() > 2 {
145                                let agent_id = parts[2].parse::<Uuid>().map_err(|_| {
146                                    ReplError::Evaluation("Invalid agent ID".to_string())
147                                })?;
148                                self.debug_agent(agent_id).await
149                            } else {
150                                Err(ReplError::Evaluation(
151                                    "Usage: :agent debug <agent_id>".to_string(),
152                                ))
153                            }
154                        }
155                        _ => Err(ReplError::Evaluation(
156                            "Unknown agent command. Use :help for available commands".to_string(),
157                        )),
158                    }
159                } else {
160                    Ok(Some(self.list_agents().await))
161                }
162            }
163            ":snapshot" => {
164                let snapshot = self.evaluator.create_snapshot().await;
165                Ok(Some(format!("Created snapshot: {}", snapshot.id)))
166            }
167            ":monitor" => {
168                if parts.len() > 1 {
169                    match parts[1] {
170                        "stats" => self.show_monitor_stats().await,
171                        "traces" => {
172                            let limit = if parts.len() > 2 {
173                                parts[2].parse().unwrap_or(20)
174                            } else {
175                                20
176                            };
177                            self.show_traces(limit).await
178                        }
179                        "report" => self.show_monitor_report().await,
180                        "clear" => self.clear_monitor().await,
181                        _ => Err(ReplError::Evaluation(
182                            "Unknown monitor command. Use :help for available commands".to_string(),
183                        )),
184                    }
185                } else {
186                    self.show_monitor_stats().await
187                }
188            }
189            ":clear" => Ok(Some("Session cleared".to_string())),
190            ":version" => Ok(Some("Symbiont REPL v0.3.0".to_string())),
191            _ => Ok(None), // Not a REPL command
192        }
193    }
194
195    /// Evaluate DSL code
196    async fn evaluate_dsl(&self, input: &str) -> Result<DslValue> {
197        let mut lexer = Lexer::new(input);
198        let tokens = lexer.tokenize()?;
199
200        let mut parser = Parser::new(tokens);
201        let program = parser.parse()?;
202
203        self.evaluator.execute_program(program).await
204    }
205
206    /// Simple expression evaluation for basic arithmetic
207    fn evaluate_simple_expression(&self, input: &str) -> Result<String> {
208        // Very basic arithmetic parser for immediate feedback
209        if let Some(result) = self.try_basic_arithmetic(input) {
210            Ok(result.to_string())
211        } else {
212            Err(ReplError::Evaluation(format!(
213                "Unable to evaluate: {}",
214                input
215            )))
216        }
217    }
218
219    /// Try to evaluate basic arithmetic expressions
220    fn try_basic_arithmetic(&self, input: &str) -> Option<f64> {
221        // Very simple arithmetic - just for demo purposes
222        if let Ok(num) = input.parse::<f64>() {
223            return Some(num);
224        }
225
226        // Handle simple addition
227        if let Some(pos) = input.find('+') {
228            let left = input[..pos].trim().parse::<f64>().ok()?;
229            let right = input[pos + 1..].trim().parse::<f64>().ok()?;
230            return Some(left + right);
231        }
232
233        // Handle simple subtraction
234        if let Some(pos) = input.rfind('-') {
235            if pos > 0 {
236                // Not a negative number
237                let left = input[..pos].trim().parse::<f64>().ok()?;
238                let right = input[pos + 1..].trim().parse::<f64>().ok()?;
239                return Some(left - right);
240            }
241        }
242
243        // Handle simple multiplication
244        if let Some(pos) = input.find('*') {
245            let left = input[..pos].trim().parse::<f64>().ok()?;
246            let right = input[pos + 1..].trim().parse::<f64>().ok()?;
247            return Some(left * right);
248        }
249
250        // Handle simple division
251        if let Some(pos) = input.find('/') {
252            let left = input[..pos].trim().parse::<f64>().ok()?;
253            let right = input[pos + 1..].trim().parse::<f64>().ok()?;
254            if right != 0.0 {
255                return Some(left / right);
256            }
257        }
258
259        None
260    }
261
262    /// Format a DSL value for display
263    fn format_value(&self, value: DslValue) -> String {
264        Self::format_value_impl(value)
265    }
266
267    /// Internal implementation for formatting DSL values
268    fn format_value_impl(value: DslValue) -> String {
269        match value {
270            DslValue::String(s) => format!("\"{}\"", s),
271            DslValue::Number(n) => n.to_string(),
272            DslValue::Integer(i) => i.to_string(),
273            DslValue::Boolean(b) => b.to_string(),
274            DslValue::Duration { value, unit } => {
275                let unit_str = match unit {
276                    crate::dsl::ast::DurationUnit::Milliseconds => "ms",
277                    crate::dsl::ast::DurationUnit::Seconds => "s",
278                    crate::dsl::ast::DurationUnit::Minutes => "m",
279                    crate::dsl::ast::DurationUnit::Hours => "h",
280                    crate::dsl::ast::DurationUnit::Days => "d",
281                };
282                format!("{}{}", value, unit_str)
283            }
284            DslValue::Size { value, unit } => {
285                let unit_str = match unit {
286                    crate::dsl::ast::SizeUnit::Bytes => "B",
287                    crate::dsl::ast::SizeUnit::KB => "KB",
288                    crate::dsl::ast::SizeUnit::MB => "MB",
289                    crate::dsl::ast::SizeUnit::GB => "GB",
290                    crate::dsl::ast::SizeUnit::TB => "TB",
291                };
292                format!("{}{}", value, unit_str)
293            }
294            DslValue::List(items) => {
295                let formatted_items: Vec<String> =
296                    items.into_iter().map(Self::format_value_impl).collect();
297                format!("[{}]", formatted_items.join(", "))
298            }
299            DslValue::Map(entries) => {
300                let formatted_entries: Vec<String> = entries
301                    .into_iter()
302                    .map(|(k, v)| format!("{}: {}", k, Self::format_value_impl(v)))
303                    .collect();
304                format!("{{{}}}", formatted_entries.join(", "))
305            }
306            DslValue::Null => "null".to_string(),
307            DslValue::Agent(agent) => {
308                format!("Agent(id: {}, state: {:?})", agent.id, agent.state)
309            }
310            DslValue::Function(name) => format!("Function({})", name),
311            DslValue::Lambda(lambda) => format!("Lambda({} params)", lambda.parameters.len()),
312        }
313    }
314
315    /// Show help message
316    fn show_help(&self) -> String {
317        r#"Symbiont REPL Commands:
318
319DSL Expressions:
320  agent MyAgent { ... }     - Define an agent
321  behavior MyBehavior { ... } - Define a behavior
322  function myFunc(...) { ... } - Define a function
323  let x = 42               - Variable assignment
324  x + y                    - Arithmetic expressions
325
326REPL Commands:
327  :help, :h               - Show this help
328  :agents                 - List all agents
329  :agent list             - List all agents
330  :agent start <id>       - Start an agent
331  :agent stop <id>        - Stop an agent
332  :agent pause <id>       - Pause an agent
333  :agent resume <id>      - Resume a paused agent
334  :agent destroy <id>     - Destroy an agent
335  :agent execute <id> <behavior> [args] - Execute agent behavior
336  :agent debug <id>       - Show debug info for an agent
337  :snapshot               - Create a session snapshot
338  :monitor stats          - Show execution statistics
339  :monitor traces [limit] - Show execution traces
340  :monitor report         - Show detailed execution report
341  :monitor clear          - Clear monitoring data
342  :clear                  - Clear the session
343  :version                - Show version information
344
345Examples:
346  agent TestAgent {
347    name: "My Test Agent"
348    version: "1.0.0"
349  }
350  
351  behavior Greet {
352    input { name: string }
353    output { greeting: string }
354    steps {
355      let greeting = format("Hello, {}!", name)
356      return greeting
357    }
358  }"#
359        .to_string()
360    }
361
362    /// List all agents
363    async fn list_agents(&self) -> String {
364        let agents = self.evaluator.list_agents().await;
365
366        if agents.is_empty() {
367            "No agents created.".to_string()
368        } else {
369            let mut output = String::from("Agents:\n");
370            for agent in agents {
371                let state_str = format!("{:?}", agent.state);
372                output.push_str(&format!(
373                    "  {} - {} ({})\n",
374                    agent.id, agent.definition.name, state_str
375                ));
376            }
377            output
378        }
379    }
380
381    /// Start an agent
382    async fn start_agent(&self, agent_id: Uuid) -> Result<Option<String>> {
383        match self.evaluator.start_agent(agent_id).await {
384            Ok(()) => Ok(Some(format!("Started agent {}", agent_id))),
385            Err(e) => Err(e),
386        }
387    }
388
389    /// Stop an agent
390    async fn stop_agent(&self, agent_id: Uuid) -> Result<Option<String>> {
391        match self.evaluator.stop_agent(agent_id).await {
392            Ok(()) => Ok(Some(format!("Stopped agent {}", agent_id))),
393            Err(e) => Err(e),
394        }
395    }
396
397    /// Pause an agent
398    async fn pause_agent(&self, agent_id: Uuid) -> Result<Option<String>> {
399        match self.evaluator.pause_agent(agent_id).await {
400            Ok(()) => Ok(Some(format!("Paused agent {}", agent_id))),
401            Err(e) => Err(e),
402        }
403    }
404
405    /// Resume an agent
406    async fn resume_agent(&self, agent_id: Uuid) -> Result<Option<String>> {
407        match self.evaluator.resume_agent(agent_id).await {
408            Ok(()) => Ok(Some(format!("Resumed agent {}", agent_id))),
409            Err(e) => Err(e),
410        }
411    }
412
413    /// Destroy an agent
414    async fn destroy_agent(&self, agent_id: Uuid) -> Result<Option<String>> {
415        match self.evaluator.destroy_agent(agent_id).await {
416            Ok(()) => Ok(Some(format!("Destroyed agent {}", agent_id))),
417            Err(e) => Err(e),
418        }
419    }
420
421    /// Execute agent behavior
422    async fn execute_agent_behavior(
423        &self,
424        agent_id: Uuid,
425        behavior_name: &str,
426        args: &str,
427    ) -> Result<Option<String>> {
428        match self
429            .evaluator
430            .execute_agent_behavior(agent_id, behavior_name, args)
431            .await
432        {
433            Ok(result) => Ok(Some(format!(
434                "Executed behavior '{}' on agent {}: {}",
435                behavior_name,
436                agent_id,
437                self.format_value(result)
438            ))),
439            Err(e) => Err(e),
440        }
441    }
442
443    /// Debug agent
444    async fn debug_agent(&self, agent_id: Uuid) -> Result<Option<String>> {
445        match self.evaluator.debug_agent(agent_id).await {
446            Ok(debug_info) => Ok(Some(debug_info)),
447            Err(e) => Err(e),
448        }
449    }
450
451    /// Get the DSL evaluator (for advanced operations)
452    pub fn evaluator(&self) -> &DslEvaluator {
453        &self.evaluator
454    }
455
456    /// Show monitoring statistics
457    async fn show_monitor_stats(&self) -> Result<Option<String>> {
458        let stats = self.evaluator.monitor().get_stats();
459        let mut output = String::from("Execution Monitor Statistics:\n");
460        output.push_str(&format!("  Total Executions: {}\n", stats.total_executions));
461        output.push_str(&format!("  Successful: {}\n", stats.successful_executions));
462        output.push_str(&format!("  Failed: {}\n", stats.failed_executions));
463
464        if stats.total_executions > 0 {
465            let success_rate =
466                (stats.successful_executions as f64 / stats.total_executions as f64) * 100.0;
467            output.push_str(&format!("  Success Rate: {:.1}%\n", success_rate));
468            output.push_str(&format!(
469                "  Average Duration: {:?}\n",
470                stats.average_duration
471            ));
472            output.push_str(&format!("  Total Duration: {:?}\n", stats.total_duration));
473        }
474
475        let active = self.evaluator.monitor().get_active_executions();
476        output.push_str(&format!("  Active Executions: {}\n", active.len()));
477
478        Ok(Some(output))
479    }
480
481    /// Show execution traces
482    async fn show_traces(&self, limit: usize) -> Result<Option<String>> {
483        let traces = self.evaluator.monitor().get_traces(Some(limit));
484
485        if traces.is_empty() {
486            return Ok(Some("No execution traces available.".to_string()));
487        }
488
489        let mut output = String::from("Recent Execution Traces:\n");
490        for trace in traces.iter().rev() {
491            let agent_info = if let Some(agent_id) = trace.agent_id {
492                format!(" [Agent: {}]", agent_id)
493            } else {
494                String::new()
495            };
496
497            let duration_info = if let Some(duration) = trace.duration {
498                format!(" ({:?})", duration)
499            } else {
500                String::new()
501            };
502
503            output.push_str(&format!(
504                "  {} - {:?}{}{}\n",
505                trace.timestamp.format("%H:%M:%S%.3f"),
506                trace.event_type,
507                agent_info,
508                duration_info
509            ));
510        }
511
512        Ok(Some(output))
513    }
514
515    /// Show detailed monitoring report
516    async fn show_monitor_report(&self) -> Result<Option<String>> {
517        let report = self.evaluator.monitor().generate_report();
518        Ok(Some(report))
519    }
520
521    /// Clear monitoring data
522    async fn clear_monitor(&self) -> Result<Option<String>> {
523        self.evaluator.monitor().clear_traces();
524        Ok(Some("Monitoring data cleared.".to_string()))
525    }
526}
527
528/// Legacy function for backward compatibility
529pub fn evaluate(expression: &str) -> Result<String> {
530    // For simple expressions without async context
531    if expression.is_empty() {
532        return Err(ReplError::Evaluation("Empty expression".to_string()));
533    }
534
535    // Try basic arithmetic
536    if let Ok(num) = expression.parse::<f64>() {
537        return Ok(num.to_string());
538    }
539
540    // Handle simple addition
541    if let Some(pos) = expression.find('+') {
542        let left = expression[..pos]
543            .trim()
544            .parse::<f64>()
545            .map_err(|_| ReplError::Evaluation("Invalid number".to_string()))?;
546        let right = expression[pos + 1..]
547            .trim()
548            .parse::<f64>()
549            .map_err(|_| ReplError::Evaluation("Invalid number".to_string()))?;
550        return Ok((left + right).to_string());
551    }
552
553    // For now, just echo back complex expressions
554    Ok(format!("ECHO: {}", expression))
555}
556
557#[cfg(test)]
558mod tests {
559    use super::*;
560    use crate::runtime_bridge::RuntimeBridge;
561
562    async fn create_test_engine() -> ReplEngine {
563        let runtime_bridge = Arc::new(RuntimeBridge::new());
564        ReplEngine::new(runtime_bridge)
565    }
566
567    #[tokio::test]
568    async fn test_basic_arithmetic() {
569        let engine = create_test_engine().await;
570
571        let result = engine.evaluate("2 + 3").await.unwrap();
572        assert_eq!(result, "5");
573
574        let result = engine.evaluate("10 - 4").await.unwrap();
575        assert_eq!(result, "6");
576    }
577
578    #[tokio::test]
579    async fn test_help_command() {
580        let engine = create_test_engine().await;
581
582        let result = engine.evaluate(":help").await.unwrap();
583        assert!(result.contains("Symbiont REPL Commands"));
584    }
585
586    #[tokio::test]
587    async fn test_version_command() {
588        let engine = create_test_engine().await;
589
590        let result = engine.evaluate(":version").await.unwrap();
591        assert!(result.contains("Symbiont REPL"));
592    }
593
594    #[tokio::test]
595    async fn test_agents_command() {
596        let engine = create_test_engine().await;
597
598        let result = engine.evaluate(":agents").await.unwrap();
599        assert_eq!(result, "No agents created.");
600    }
601
602    #[test]
603    fn test_legacy_evaluate() {
604        let result = evaluate("42").unwrap();
605        assert_eq!(result, "42");
606
607        let result = evaluate("5 + 3").unwrap();
608        assert_eq!(result, "8");
609    }
610}