rust_rule_engine/backward/
grl_query.rs

1//! GRL Query Syntax Implementation
2//!
3//! This module provides parsing and execution of backward chaining queries defined in GRL
4//! (Goal-driven Rule Language) syntax. GRL queries allow you to define goal-driven reasoning
5//! tasks with configurable search strategies and action handlers.
6//!
7//! # Features
8//!
9//! - **Declarative query syntax** - Define queries in a readable, structured format
10//! - **Multiple search strategies** - Choose between depth-first, breadth-first, or iterative deepening
11//! - **Action handlers** - Execute actions on query success, failure, or missing facts
12//! - **Conditional execution** - Use `when` clauses to conditionally execute queries
13//! - **Parameterized queries** - Support for query parameters (future enhancement)
14//!
15//! # GRL Query Syntax
16//!
17//! ```grl
18//! query "QueryName" {
19//!     goal: <expression>                    // Required: Goal to prove
20//!     strategy: <depth-first|breadth-first|iterative>  // Optional: Search strategy
21//!     max-depth: <number>                   // Optional: Maximum search depth
22//!     max-solutions: <number>               // Optional: Maximum solutions to find
23//!     enable-memoization: <true|false>      // Optional: Enable result caching
24//!
25//!     when: <condition>                     // Optional: Only execute if condition is true
26//!
27//!     on-success: {                         // Optional: Actions on successful proof
28//!         <variable> = <value>;
29//!         <FunctionName>(<args>);
30//!     }
31//!
32//!     on-failure: {                         // Optional: Actions on proof failure
33//!         <actions>
34//!     }
35//!
36//!     on-missing: {                         // Optional: Actions when facts are missing
37//!         <actions>
38//!     }
39//! }
40//! ```
41//!
42//! # Example
43//!
44//! ```rust
45//! use rust_rule_engine::backward::grl_query::{GRLQueryParser, GRLQueryExecutor};
46//! use rust_rule_engine::backward::BackwardEngine;
47//! use rust_rule_engine::{KnowledgeBase, Facts, Value};
48//!
49//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
50//! let query_str = r#"
51//! query "CheckVIPStatus" {
52//!     goal: User.IsVIP == true
53//!     strategy: depth-first
54//!     max-depth: 10
55//!     on-success: {
56//!         User.DiscountRate = 0.2;
57//!         LogMessage("VIP confirmed");
58//!     }
59//!     on-failure: {
60//!         LogMessage("Not a VIP user");
61//!     }
62//! }
63//! "#;
64//!
65//! let query = GRLQueryParser::parse(query_str)?;
66//! let mut bc_engine = BackwardEngine::new(KnowledgeBase::new("kb"));
67//! let mut facts = Facts::new();
68//! facts.set("User.LoyaltyPoints", Value::Number(1500.0));
69//!
70//! let result = GRLQueryExecutor::execute(&query, &mut bc_engine, &mut facts)?;
71//!
72//! if result.provable {
73//!     println!("Goal proven!");
74//! }
75//! # Ok(())
76//! # }
77//! ```
78//!
79//! # Supported Functions in Actions
80//!
81//! - `LogMessage(message)` - Print a log message
82//! - `Request(message)` - Send a request message
83//! - `Print(message)` - Print output
84//! - `Debug(message)` - Print debug output to stderr
85
86use crate::errors::RuleEngineError;
87use crate::{Facts, Value};
88use super::backward_engine::{BackwardEngine, BackwardConfig};
89use super::search::SearchStrategy;
90use super::query::{QueryResult, QueryStats, ProofTrace};
91use super::optimizer::QueryOptimizer;
92
93use std::collections::HashMap;
94
95/// Search strategy option for queries
96#[derive(Debug, Clone, PartialEq)]
97pub enum GRLSearchStrategy {
98    DepthFirst,
99    BreadthFirst,
100    Iterative,
101}
102
103impl Default for GRLSearchStrategy {
104    fn default() -> Self {
105        GRLSearchStrategy::DepthFirst
106    }
107}
108
109/// Action to execute based on query result
110#[derive(Debug, Clone)]
111pub struct QueryAction {
112    /// Assignment: Variable = Value (as string to be parsed)
113    pub assignments: Vec<(String, String)>,
114    /// Function/method calls
115    pub calls: Vec<String>,
116}
117
118impl QueryAction {
119    pub fn new() -> Self {
120        QueryAction {
121            assignments: Vec::new(),
122            calls: Vec::new(),
123        }
124    }
125
126    /// Execute the action on the given facts
127    pub fn execute(&self, facts: &mut Facts) -> Result<(), RuleEngineError> {
128        // Execute assignments - for now just log them
129        for (var_name, value_str) in &self.assignments {
130            // Simple value parsing
131            let value = if value_str == "true" {
132                Value::Boolean(true)
133            } else if value_str == "false" {
134                Value::Boolean(false)
135            } else if let Ok(n) = value_str.parse::<f64>() {
136                Value::Number(n)
137            } else {
138                // Remove quotes if present
139                let cleaned = value_str.trim_matches('"');
140                Value::String(cleaned.to_string())
141            };
142            
143            facts.set(var_name, value);
144        }
145
146        // Execute function calls
147        for call in &self.calls {
148            self.execute_function_call(call)?;
149        }
150
151        Ok(())
152    }
153
154    /// Execute a single function call
155    fn execute_function_call(&self, call: &str) -> Result<(), RuleEngineError> {
156        let call = call.trim();
157
158        // Parse function name and arguments
159        if let Some(open_paren) = call.find('(') {
160            let func_name = call[..open_paren].trim();
161
162            // Extract arguments (everything between first ( and last ))
163            if let Some(close_paren) = call.rfind(')') {
164                let args_str = &call[open_paren + 1..close_paren];
165
166                match func_name {
167                    "LogMessage" => {
168                        // Parse string argument (remove quotes if present)
169                        let message = args_str.trim().trim_matches('"').trim_matches('\'');
170                        println!("[LOG] {}", message);
171                    }
172                    "Request" => {
173                        // Parse request call
174                        let message = args_str.trim().trim_matches('"').trim_matches('\'');
175                        println!("[REQUEST] {}", message);
176                    }
177                    "Print" => {
178                        // Generic print function
179                        let message = args_str.trim().trim_matches('"').trim_matches('\'');
180                        println!("{}", message);
181                    }
182                    "Debug" => {
183                        // Debug output
184                        let message = args_str.trim().trim_matches('"').trim_matches('\'');
185                        eprintln!("[DEBUG] {}", message);
186                    }
187                    other => {
188                        // Unknown function - log warning but don't fail
189                        eprintln!("[WARNING] Unknown function call in query action: {}({})", other, args_str);
190                    }
191                }
192            } else {
193                return Err(RuleEngineError::ParseError {
194                    message: format!("Malformed function call (missing closing paren): {}", call),
195                });
196            }
197        } else {
198            return Err(RuleEngineError::ParseError {
199                message: format!("Malformed function call (missing opening paren): {}", call),
200            });
201        }
202
203        Ok(())
204    }
205}
206
207/// A GRL Query definition
208#[derive(Debug, Clone)]
209pub struct GRLQuery {
210    /// Query name
211    pub name: String,
212
213    /// Goal pattern to prove (as string expression)
214    pub goal: String,
215
216    /// Search strategy
217    pub strategy: GRLSearchStrategy,
218
219    /// Maximum search depth
220    pub max_depth: usize,
221
222    /// Maximum number of solutions
223    pub max_solutions: usize,
224
225    /// Enable memoization
226    pub enable_memoization: bool,
227
228    /// Enable query optimization (goal reordering, etc.)
229    pub enable_optimization: bool,
230
231    /// Action on success
232    pub on_success: Option<QueryAction>,
233
234    /// Action on failure
235    pub on_failure: Option<QueryAction>,
236
237    /// Action on missing facts
238    pub on_missing: Option<QueryAction>,
239
240    /// Parameters for parameterized queries
241    pub params: HashMap<String, String>, // param_name -> type
242
243    /// Conditional execution (as string condition)
244    pub when_condition: Option<String>,
245}
246
247impl GRLQuery {
248    /// Create a new query with defaults
249    pub fn new(name: String, goal: String) -> Self {
250        GRLQuery {
251            name,
252            goal,
253            strategy: GRLSearchStrategy::default(),
254            max_depth: 10,
255            max_solutions: 1,
256            enable_memoization: true,
257            enable_optimization: true,
258            on_success: None,
259            on_failure: None,
260            on_missing: None,
261            params: HashMap::new(),
262            when_condition: None,
263        }
264    }
265
266    /// Set search strategy
267    pub fn with_strategy(mut self, strategy: GRLSearchStrategy) -> Self {
268        self.strategy = strategy;
269        self
270    }
271
272    /// Set max depth
273    pub fn with_max_depth(mut self, max_depth: usize) -> Self {
274        self.max_depth = max_depth;
275        self
276    }
277
278    /// Set max solutions
279    pub fn with_max_solutions(mut self, max_solutions: usize) -> Self {
280        self.max_solutions = max_solutions;
281        self
282    }
283
284    /// Set memoization
285    pub fn with_memoization(mut self, enable: bool) -> Self {
286        self.enable_memoization = enable;
287        self
288    }
289
290    /// Set query optimization
291    pub fn with_optimization(mut self, enable: bool) -> Self {
292        self.enable_optimization = enable;
293        self
294    }
295
296    /// Add success action
297    pub fn with_on_success(mut self, action: QueryAction) -> Self {
298        self.on_success = Some(action);
299        self
300    }
301
302    /// Add failure action
303    pub fn with_on_failure(mut self, action: QueryAction) -> Self {
304        self.on_failure = Some(action);
305        self
306    }
307
308    /// Add missing facts action
309    pub fn with_on_missing(mut self, action: QueryAction) -> Self {
310        self.on_missing = Some(action);
311        self
312    }
313
314    /// Add parameter
315    pub fn with_param(mut self, name: String, type_name: String) -> Self {
316        self.params.insert(name, type_name);
317        self
318    }
319
320    /// Set conditional execution
321    pub fn with_when(mut self, condition: String) -> Self {
322        self.when_condition = Some(condition);
323        self
324    }
325
326    /// Check if query should execute based on when condition
327    pub fn should_execute(&self, _facts: &Facts) -> Result<bool, RuleEngineError> {
328        // If there's no when condition, execute by default
329        if self.when_condition.is_none() {
330            return Ok(true);
331        }
332
333        // Parse and evaluate the when condition expression against the current facts
334        if let Some(ref cond_str) = self.when_condition {
335            use crate::backward::expression::ExpressionParser;
336
337            match ExpressionParser::parse(cond_str) {
338                Ok(expr) => Ok(expr.is_satisfied(_facts)),
339                Err(e) => Err(e),
340            }
341        } else {
342            Ok(true)
343        }
344    }
345
346    /// Execute success actions
347    pub fn execute_success_actions(&self, facts: &mut Facts) -> Result<(), RuleEngineError> {
348        if let Some(ref action) = self.on_success {
349            action.execute(facts)?;
350        }
351        Ok(())
352    }
353
354    /// Execute failure actions
355    pub fn execute_failure_actions(&self, facts: &mut Facts) -> Result<(), RuleEngineError> {
356        if let Some(ref action) = self.on_failure {
357            action.execute(facts)?;
358        }
359        Ok(())
360    }
361
362    /// Execute missing facts actions
363    pub fn execute_missing_actions(&self, facts: &mut Facts) -> Result<(), RuleEngineError> {
364        if let Some(ref action) = self.on_missing {
365            action.execute(facts)?;
366        }
367        Ok(())
368    }
369
370    /// Convert to BackwardConfig
371    pub fn to_config(&self) -> BackwardConfig {
372        let search_strategy = match self.strategy {
373            GRLSearchStrategy::DepthFirst => SearchStrategy::DepthFirst,
374            GRLSearchStrategy::BreadthFirst => SearchStrategy::BreadthFirst,
375            GRLSearchStrategy::Iterative => SearchStrategy::Iterative,
376        };
377
378        BackwardConfig {
379            strategy: search_strategy,
380            max_depth: self.max_depth,
381            enable_memoization: self.enable_memoization,
382            max_solutions: self.max_solutions,
383        }
384    }
385}
386
387/// Parser for GRL Query syntax
388pub struct GRLQueryParser;
389
390impl GRLQueryParser {
391    /// Parse a query from string
392    /// 
393    /// # Example
394    /// ```
395    /// let query_str = r#"
396    /// query "CheckVIP" {
397    ///     goal: User.IsVIP == true
398    ///     strategy: depth-first
399    /// }
400    /// "#;
401    /// let query = rust_rule_engine::backward::GRLQueryParser::parse(query_str).unwrap();
402    /// ```
403    pub fn parse(input: &str) -> Result<GRLQuery, RuleEngineError> {
404        let input = input.trim();
405
406        // Extract query name
407        let name = Self::extract_query_name(input)?;
408
409        // Extract goal
410        let goal = Self::extract_goal(input)?;
411
412        // Create base query
413        let mut query = GRLQuery::new(name, goal);
414
415        // Parse optional attributes
416        if let Some(strategy) = Self::extract_strategy(input) {
417            query.strategy = strategy;
418        }
419
420        if let Some(max_depth) = Self::extract_max_depth(input) {
421            query.max_depth = max_depth;
422        }
423
424        if let Some(max_solutions) = Self::extract_max_solutions(input) {
425            query.max_solutions = max_solutions;
426        }
427
428        if let Some(enable_memo) = Self::extract_memoization(input) {
429            query.enable_memoization = enable_memo;
430        }
431
432        if let Some(enable_opt) = Self::extract_optimization(input) {
433            query.enable_optimization = enable_opt;
434        }
435
436        // Parse actions
437        if let Some(action) = Self::extract_on_success(input)? {
438            query.on_success = Some(action);
439        }
440
441        if let Some(action) = Self::extract_on_failure(input)? {
442            query.on_failure = Some(action);
443        }
444
445        if let Some(action) = Self::extract_on_missing(input)? {
446            query.on_missing = Some(action);
447        }
448
449        // Parse when condition
450        if let Some(condition) = Self::extract_when_condition(input)? {
451            query.when_condition = Some(condition);
452        }
453
454        Ok(query)
455    }
456
457    fn extract_query_name(input: &str) -> Result<String, RuleEngineError> {
458        let re = regex::Regex::new(r#"query\s+"([^"]+)"\s*\{"#).unwrap();
459        if let Some(caps) = re.captures(input) {
460            Ok(caps[1].to_string())
461        } else {
462            Err(RuleEngineError::ParseError {
463                message: "Invalid query syntax: missing query name".to_string(),
464            })
465        }
466    }
467
468    fn extract_goal(input: &str) -> Result<String, RuleEngineError> {
469        // Find goal: line
470        if let Some(goal_start) = input.find("goal:") {
471            let after_goal = &input[goal_start + 5..]; // Skip "goal:"
472
473            // Find the end of the goal line (newline or end of attributes section)
474            // Goal ends at newline, but we need to handle parentheses carefully
475            let goal_end = Self::find_goal_end(after_goal)?;
476            let goal_str = after_goal[..goal_end].trim().to_string();
477
478            if goal_str.is_empty() {
479                return Err(RuleEngineError::ParseError {
480                    message: "Invalid query syntax: empty goal".to_string(),
481                });
482            }
483
484            Ok(goal_str)
485        } else {
486            Err(RuleEngineError::ParseError {
487                message: "Invalid query syntax: missing goal".to_string(),
488            })
489        }
490    }
491
492    fn find_goal_end(input: &str) -> Result<usize, RuleEngineError> {
493        let mut paren_depth = 0;
494        let mut in_string = false;
495        let mut escape_next = false;
496
497        for (i, ch) in input.chars().enumerate() {
498            if escape_next {
499                escape_next = false;
500                continue;
501            }
502
503            match ch {
504                '\\' if in_string => escape_next = true,
505                '"' => in_string = !in_string,
506                '(' if !in_string => paren_depth += 1,
507                ')' if !in_string => {
508                    if paren_depth == 0 {
509                        return Err(RuleEngineError::ParseError {
510                            message: format!("Parse error: Unexpected closing parenthesis at position {}", i),
511                        });
512                    }
513                    paren_depth -= 1;
514                }
515                '\n' if !in_string && paren_depth == 0 => return Ok(i),
516                _ => {}
517            }
518        }
519
520        if in_string {
521            return Err(RuleEngineError::ParseError {
522                message: "Parse error: Unclosed string in goal".to_string(),
523            });
524        }
525
526        if paren_depth > 0 {
527            return Err(RuleEngineError::ParseError {
528                message: format!("Parse error: {} unclosed parentheses in goal", paren_depth),
529            });
530        }
531
532        // If we reach here, goal extends to end of input
533        Ok(input.len())
534    }
535
536    fn extract_strategy(input: &str) -> Option<GRLSearchStrategy> {
537        let re = regex::Regex::new(r"strategy:\s*([a-z-]+)").unwrap();
538        re.captures(input).and_then(|caps| {
539            match caps[1].trim() {
540                "depth-first" => Some(GRLSearchStrategy::DepthFirst),
541                "breadth-first" => Some(GRLSearchStrategy::BreadthFirst),
542                "iterative" => Some(GRLSearchStrategy::Iterative),
543                _ => None,
544            }
545        })
546    }
547
548    fn extract_max_depth(input: &str) -> Option<usize> {
549        let re = regex::Regex::new(r"max-depth:\s*(\d+)").unwrap();
550        re.captures(input)
551            .and_then(|caps| caps[1].parse().ok())
552    }
553
554    fn extract_max_solutions(input: &str) -> Option<usize> {
555        let re = regex::Regex::new(r"max-solutions:\s*(\d+)").unwrap();
556        re.captures(input)
557            .and_then(|caps| caps[1].parse().ok())
558    }
559
560    fn extract_memoization(input: &str) -> Option<bool> {
561        let re = regex::Regex::new(r"enable-memoization:\s*(true|false)").unwrap();
562        re.captures(input).and_then(|caps| {
563            match caps[1].trim() {
564                "true" => Some(true),
565                "false" => Some(false),
566                _ => None,
567            }
568        })
569    }
570
571    fn extract_optimization(input: &str) -> Option<bool> {
572        let re = regex::Regex::new(r"enable-optimization:\s*(true|false)").unwrap();
573        re.captures(input).and_then(|caps| {
574            match caps[1].trim() {
575                "true" => Some(true),
576                "false" => Some(false),
577                _ => None,
578            }
579        })
580    }
581
582    fn extract_on_success(input: &str) -> Result<Option<QueryAction>, RuleEngineError> {
583        Self::extract_action_block(input, "on-success")
584    }
585
586    fn extract_on_failure(input: &str) -> Result<Option<QueryAction>, RuleEngineError> {
587        Self::extract_action_block(input, "on-failure")
588    }
589
590    fn extract_on_missing(input: &str) -> Result<Option<QueryAction>, RuleEngineError> {
591        Self::extract_action_block(input, "on-missing")
592    }
593
594    fn extract_action_block(input: &str, action_name: &str) -> Result<Option<QueryAction>, RuleEngineError> {
595        let pattern = format!(r"{}:\s*\{{([^}}]+)\}}", action_name);
596        let re = regex::Regex::new(&pattern).unwrap();
597        
598        if let Some(caps) = re.captures(input) {
599            let block = caps[1].trim();
600            let mut action = QueryAction::new();
601
602            // Parse assignments: Variable = Value
603            let assign_re = regex::Regex::new(r"([A-Za-z_][A-Za-z0-9_.]*)\s*=\s*([^;]+);").unwrap();
604            for caps in assign_re.captures_iter(block) {
605                let var_name = caps[1].trim().to_string();
606                let value_str = caps[2].trim().to_string();
607                action.assignments.push((var_name, value_str));
608            }
609
610            // Parse function calls: Function(...)
611            let call_re = regex::Regex::new(r"([A-Za-z_][A-Za-z0-9_]*\([^)]*\));").unwrap();
612            for caps in call_re.captures_iter(block) {
613                action.calls.push(caps[1].trim().to_string());
614            }
615
616            Ok(Some(action))
617        } else {
618            Ok(None)
619        }
620    }
621
622    fn extract_when_condition(input: &str) -> Result<Option<String>, RuleEngineError> {
623        let re = regex::Regex::new(r"when:\s*([^\n}]+)").unwrap();
624        if let Some(caps) = re.captures(input) {
625            let condition_str = caps[1].trim().to_string();
626            Ok(Some(condition_str))
627        } else {
628            Ok(None)
629        }
630    }
631
632    /// Parse multiple queries from a file
633    pub fn parse_queries(input: &str) -> Result<Vec<GRLQuery>, RuleEngineError> {
634        let mut queries = Vec::new();
635        
636        // Find all query blocks - use simpler approach
637        // Split by "query" keyword and process each block
638        let parts: Vec<&str> = input.split("query").collect();
639        
640        for part in parts.iter().skip(1) { // Skip first empty part
641            let query_str = format!("query{}", part);
642            // Find the matching closing brace
643            if let Some(end_idx) = find_matching_brace(&query_str) {
644                let complete_query = &query_str[..end_idx];
645                if let Ok(query) = Self::parse(complete_query) {
646                    queries.push(query);
647                }
648            }
649        }
650
651        Ok(queries)
652    }
653}
654
655// Helper function to find matching closing brace
656fn find_matching_brace(input: &str) -> Option<usize> {
657    let mut depth = 0;
658    let mut in_string = false;
659    let mut escape_next = false;
660    
661    for (i, ch) in input.chars().enumerate() {
662        if escape_next {
663            escape_next = false;
664            continue;
665        }
666        
667        match ch {
668            '\\' => escape_next = true,
669            '"' => in_string = !in_string,
670            '{' if !in_string => depth += 1,
671            '}' if !in_string => {
672                depth -= 1;
673                if depth == 0 {
674                    return Some(i + 1);
675                }
676            }
677            _ => {}
678        }
679    }
680    
681    None
682}
683
684/// Executor for GRL queries
685pub struct GRLQueryExecutor;
686
687impl GRLQueryExecutor {
688    /// Execute a single query
689    pub fn execute(
690        query: &GRLQuery,
691        bc_engine: &mut BackwardEngine,
692        facts: &mut Facts,
693    ) -> Result<QueryResult, RuleEngineError> {
694        // Check when condition
695        if !query.should_execute(facts)? {
696            return Ok(QueryResult {
697                provable: false,
698                bindings: HashMap::new(),
699                proof_trace: ProofTrace { goal: String::new(), steps: Vec::new() },
700                missing_facts: Vec::new(),
701                stats: QueryStats::default(),
702                solutions: Vec::new(),
703            });
704        }
705
706        // Apply config
707        bc_engine.set_config(query.to_config());
708
709        // Parse compound goals (support &&, ||, and !=)
710        let result = if query.goal.contains("&&") && query.goal.contains("||") {
711            // Complex expression with both AND and OR - need proper parsing
712            // For now, evaluate left-to-right with precedence: AND before OR
713            Self::execute_complex_goal(&query.goal, bc_engine, facts)?
714        } else if query.goal.contains("||") {
715            // Split on || and check any goal (OR logic)
716            Self::execute_compound_or_goal(&query.goal, bc_engine, facts)?
717        } else if query.goal.contains("&&") {
718            // Split on && and check all goals (AND logic)
719            Self::execute_compound_and_goal(&query.goal, bc_engine, facts)?
720        } else {
721            // Single goal
722            bc_engine.query(&query.goal, facts)?
723        };
724
725        // Execute appropriate actions
726        if result.provable {
727            query.execute_success_actions(facts)?;
728        } else if !result.missing_facts.is_empty() {
729            query.execute_missing_actions(facts)?;
730        } else {
731            query.execute_failure_actions(facts)?;
732        }
733
734        Ok(result)
735    }
736
737    /// Execute compound AND goal (all must be true)
738    fn execute_compound_and_goal(
739        goal_expr: &str,
740        bc_engine: &mut BackwardEngine,
741        facts: &mut Facts,
742    ) -> Result<QueryResult, RuleEngineError> {
743        let sub_goals: Vec<&str> = goal_expr.split("&&").map(|s| s.trim()).collect();
744
745        let mut all_provable = true;
746        let mut combined_bindings = HashMap::new();
747        let mut all_missing = Vec::new();
748        let mut combined_stats = QueryStats::default();
749
750        for (i, sub_goal) in sub_goals.iter().enumerate() {
751            // Handle != by using expression parser directly
752            let goal_satisfied = if sub_goal.contains("!=") {
753                // Parse and evaluate the expression directly
754                use crate::backward::expression::ExpressionParser;
755
756                match ExpressionParser::parse(sub_goal) {
757                    Ok(expr) => expr.is_satisfied(facts),
758                    Err(_) => false,
759                }
760            } else {
761                // Normal == comparison, use backward chaining
762                let result = bc_engine.query(sub_goal, facts)?;
763                result.provable
764            };
765
766            if !goal_satisfied {
767                all_provable = false;
768            }
769
770            // Note: For compound goals with !=, we don't track missing facts well yet
771            // This is a simplification for now
772        }
773
774        Ok(QueryResult {
775            provable: all_provable,
776            bindings: combined_bindings,
777            proof_trace: ProofTrace {
778                goal: goal_expr.to_string(),
779                steps: Vec::new()
780            },
781            missing_facts: all_missing,
782            stats: combined_stats,
783            solutions: Vec::new(),
784        })
785    }
786
787    /// Execute compound OR goal (any must be true)
788    fn execute_compound_or_goal(
789        goal_expr: &str,
790        bc_engine: &mut BackwardEngine,
791        facts: &mut Facts,
792    ) -> Result<QueryResult, RuleEngineError> {
793        let sub_goals: Vec<&str> = goal_expr.split("||").map(|s| s.trim()).collect();
794
795        let mut any_provable = false;
796        let mut combined_bindings = HashMap::new();
797        let mut all_missing = Vec::new();
798        let mut combined_stats = QueryStats::default();
799        let mut all_solutions = Vec::new();
800
801        for sub_goal in sub_goals.iter() {
802            // Handle != by using expression parser directly
803            let (goal_satisfied, result_opt) = if sub_goal.contains("!=") {
804                // Parse and evaluate the expression directly
805                use crate::backward::expression::ExpressionParser;
806
807                match ExpressionParser::parse(sub_goal) {
808                    Ok(expr) => (expr.is_satisfied(facts), None),
809                    Err(_) => (false, None),
810                }
811            } else {
812                // Normal == comparison, use backward chaining
813                let result = bc_engine.query(sub_goal, facts)?;
814                let provable = result.provable;
815                (provable, Some(result))
816            };
817
818            if goal_satisfied {
819                any_provable = true;
820
821                // Merge results from successful branch
822                if let Some(result) = result_opt {
823                    combined_bindings.extend(result.bindings);
824                    all_missing.extend(result.missing_facts);
825                    combined_stats.goals_explored += result.stats.goals_explored;
826                    combined_stats.rules_evaluated += result.stats.rules_evaluated;
827                    if let Some(dur) = result.stats.duration_ms {
828                        combined_stats.duration_ms = Some(combined_stats.duration_ms.unwrap_or(0) + dur);
829                    }
830                    all_solutions.extend(result.solutions);
831                }
832            }
833        }
834
835        Ok(QueryResult {
836            provable: any_provable,
837            bindings: combined_bindings,
838            proof_trace: ProofTrace {
839                goal: goal_expr.to_string(),
840                steps: Vec::new()
841            },
842            missing_facts: all_missing,
843            stats: combined_stats,
844            solutions: all_solutions,
845        })
846    }
847
848    /// Strip outer parentheses from expression
849    fn strip_outer_parens(expr: &str) -> &str {
850        let trimmed = expr.trim();
851        if trimmed.starts_with('(') && trimmed.ends_with(')') {
852            // Check if these are matching outer parens
853            let inner = &trimmed[1..trimmed.len()-1];
854            let mut depth = 0;
855            for ch in inner.chars() {
856                match ch {
857                    '(' => depth += 1,
858                    ')' => {
859                        depth -= 1;
860                        if depth < 0 {
861                            // Closing paren in middle, so outer parens don't match
862                            return trimmed;
863                        }
864                    }
865                    _ => {}
866                }
867            }
868            if depth == 0 {
869                // Outer parens match, return inner
870                return inner.trim();
871            }
872        }
873        trimmed
874    }
875
876    /// Execute complex goal with both AND and OR operators
877    /// Precedence: AND is evaluated before OR (like multiplication before addition)
878    /// Example: "A || B && C" is evaluated as "A || (B && C)"
879    fn execute_complex_goal(
880        goal_expr: &str,
881        bc_engine: &mut BackwardEngine,
882        facts: &mut Facts,
883    ) -> Result<QueryResult, RuleEngineError> {
884        // Strip outer parentheses first
885        let cleaned_expr = Self::strip_outer_parens(goal_expr);
886
887        // Split by || first (lowest precedence)
888        let or_parts: Vec<&str> = cleaned_expr.split("||").map(|s| s.trim()).collect();
889
890        let mut any_provable = false;
891        let mut combined_bindings = HashMap::new();
892        let mut all_missing = Vec::new();
893        let mut combined_stats = QueryStats::default();
894        let mut all_solutions = Vec::new();
895
896        for or_part in or_parts.iter() {
897            // Strip parentheses from each part
898            let cleaned_part = Self::strip_outer_parens(or_part);
899
900            // Each OR part might contain AND clauses
901            let result = if cleaned_part.contains("&&") {
902                Self::execute_compound_and_goal(cleaned_part, bc_engine, facts)?
903            } else {
904                bc_engine.query(cleaned_part, facts)?
905            };
906
907            if result.provable {
908                any_provable = true;
909                combined_bindings.extend(result.bindings);
910                all_missing.extend(result.missing_facts);
911                combined_stats.goals_explored += result.stats.goals_explored;
912                combined_stats.rules_evaluated += result.stats.rules_evaluated;
913                if let Some(dur) = result.stats.duration_ms {
914                    combined_stats.duration_ms = Some(combined_stats.duration_ms.unwrap_or(0) + dur);
915                }
916                all_solutions.extend(result.solutions);
917            }
918        }
919
920        Ok(QueryResult {
921            provable: any_provable,
922            bindings: combined_bindings,
923            proof_trace: ProofTrace {
924                goal: goal_expr.to_string(),
925                steps: Vec::new()
926            },
927            missing_facts: all_missing,
928            stats: combined_stats,
929            solutions: all_solutions,
930        })
931    }
932
933    /// Execute multiple queries
934    pub fn execute_queries(
935        queries: &[GRLQuery],
936        bc_engine: &mut BackwardEngine,
937        facts: &mut Facts,
938    ) -> Result<Vec<QueryResult>, RuleEngineError> {
939        let mut results = Vec::new();
940
941        for query in queries {
942            let result = Self::execute(query, bc_engine, facts)?;
943            results.push(result);
944        }
945
946        Ok(results)
947    }
948}
949
950#[cfg(test)]
951mod tests {
952    use super::*;
953
954    #[test]
955    fn test_parse_simple_query() {
956        let input = r#"
957        query "TestQuery" {
958            goal: User.IsVIP == true
959        }
960        "#;
961
962        let query = GRLQueryParser::parse(input).unwrap();
963        assert_eq!(query.name, "TestQuery");
964        assert_eq!(query.strategy, GRLSearchStrategy::DepthFirst);
965        assert_eq!(query.max_depth, 10);
966    }
967
968    #[test]
969    fn test_parse_query_with_strategy() {
970        let input = r#"
971        query "TestQuery" {
972            goal: User.IsVIP == true
973            strategy: breadth-first
974            max-depth: 5
975        }
976        "#;
977
978        let query = GRLQueryParser::parse(input).unwrap();
979        assert_eq!(query.strategy, GRLSearchStrategy::BreadthFirst);
980        assert_eq!(query.max_depth, 5);
981    }
982
983    #[test]
984    fn test_parse_query_with_actions() {
985        let input = r#"
986        query "TestQuery" {
987            goal: User.IsVIP == true
988            on-success: {
989                User.DiscountRate = 0.2;
990                LogMessage("VIP confirmed");
991            }
992        }
993        "#;
994
995        let query = GRLQueryParser::parse(input).unwrap();
996        assert!(query.on_success.is_some());
997        
998        let action = query.on_success.unwrap();
999        assert_eq!(action.assignments.len(), 1);
1000        assert_eq!(action.calls.len(), 1);
1001    }
1002
1003    #[test]
1004    fn test_parse_query_with_when_condition() {
1005        let input = r#"
1006        query "TestQuery" {
1007            goal: User.IsVIP == true
1008            when: Environment.Mode == "Production"
1009        }
1010        "#;
1011
1012        let query = GRLQueryParser::parse(input).unwrap();
1013        assert!(query.when_condition.is_some());
1014    }
1015
1016    #[test]
1017    fn test_parse_multiple_queries() {
1018        let input = r#"
1019        query "Query1" {
1020            goal: A == true
1021        }
1022        
1023        query "Query2" {
1024            goal: B == true
1025            strategy: breadth-first
1026        }
1027        "#;
1028
1029        let queries = GRLQueryParser::parse_queries(input).unwrap();
1030        assert_eq!(queries.len(), 2);
1031        assert_eq!(queries[0].name, "Query1");
1032        assert_eq!(queries[1].name, "Query2");
1033    }
1034
1035    #[test]
1036    fn test_query_config_conversion() {
1037        let query = GRLQuery::new(
1038            "Test".to_string(),
1039            "X == true".to_string(),
1040        )
1041        .with_strategy(GRLSearchStrategy::BreadthFirst)
1042        .with_max_depth(15)
1043        .with_memoization(false);
1044
1045        let config = query.to_config();
1046        assert_eq!(config.max_depth, 15);
1047        assert_eq!(config.enable_memoization, false);
1048    }
1049
1050    #[test]
1051    fn test_action_execution() {
1052        let mut facts = Facts::new();
1053
1054        let mut action = QueryAction::new();
1055        action.assignments.push((
1056            "User.DiscountRate".to_string(),
1057            "0.2".to_string(),
1058        ));
1059
1060        action.execute(&mut facts).unwrap();
1061
1062        // Check that assignment was executed
1063        let value = facts.get("User.DiscountRate");
1064        assert!(value.is_some());
1065    }
1066
1067    #[test]
1068    fn test_should_execute_no_condition() {
1069        let query = GRLQuery::new("Q".to_string(), "X == true".to_string());
1070        let facts = Facts::new();
1071        // No when condition -> should execute
1072        let res = query.should_execute(&facts).unwrap();
1073        assert!(res);
1074    }
1075
1076    #[test]
1077    fn test_should_execute_condition_true() {
1078        let mut facts = Facts::new();
1079        facts.set("Environment.Mode", Value::String("Production".to_string()));
1080
1081        let query = GRLQuery::new("Q".to_string(), "X == true".to_string())
1082            .with_when("Environment.Mode == \"Production\"".to_string());
1083
1084        let res = query.should_execute(&facts).unwrap();
1085        assert!(res, "expected when condition to be satisfied");
1086    }
1087
1088    #[test]
1089    fn test_should_execute_condition_false() {
1090        let mut facts = Facts::new();
1091        facts.set("Environment.Mode", Value::String("Development".to_string()));
1092
1093        let query = GRLQuery::new("Q".to_string(), "X == true".to_string())
1094            .with_when("Environment.Mode == \"Production\"".to_string());
1095
1096        let res = query.should_execute(&facts).unwrap();
1097        assert!(!res, "expected when condition to be unsatisfied");
1098    }
1099
1100    #[test]
1101    fn test_should_execute_parse_error_propagates() {
1102        let facts = Facts::new();
1103        // Use an unterminated string literal to force a parse error from the expression parser
1104        let query = GRLQuery::new("Q".to_string(), "X == true".to_string())
1105            .with_when("Environment.Mode == \"Production".to_string());
1106
1107        let res = query.should_execute(&facts);
1108        assert!(res.is_err(), "expected parse error to propagate");
1109    }
1110
1111    #[test]
1112    fn test_parse_query_with_or_goal() {
1113        let input = r#"
1114        query "TestOR" {
1115            goal: User.IsVIP == true || User.TotalSpent > 10000
1116        }
1117        "#;
1118
1119        let query = GRLQueryParser::parse(input).unwrap();
1120        assert_eq!(query.name, "TestOR");
1121        assert!(query.goal.contains("||"));
1122    }
1123
1124    #[test]
1125    fn test_parse_query_with_complex_goal() {
1126        let input = r#"
1127        query "ComplexQuery" {
1128            goal: (User.IsVIP == true && User.Active == true) || User.TotalSpent > 10000
1129        }
1130        "#;
1131
1132        let query = GRLQueryParser::parse(input).unwrap();
1133        assert!(query.goal.contains("||"));
1134        assert!(query.goal.contains("&&"));
1135    }
1136
1137    #[test]
1138    fn test_parse_query_with_multiple_or_branches() {
1139        let input = r#"
1140        query "MultiOR" {
1141            goal: Employee.IsManager == true || Employee.IsSenior == true || Employee.IsDirector == true
1142        }
1143        "#;
1144
1145        let query = GRLQueryParser::parse(input).unwrap();
1146        let branches: Vec<&str> = query.goal.split("||").collect();
1147        assert_eq!(branches.len(), 3);
1148    }
1149
1150    #[test]
1151    fn test_parse_query_with_parentheses() {
1152        let input = r#"
1153        query "ParenQuery" {
1154            goal: (User.IsVIP == true && User.Active == true) || User.TotalSpent > 10000
1155        }
1156        "#;
1157
1158        let query = GRLQueryParser::parse(input).unwrap();
1159        assert!(query.goal.contains("("));
1160        assert!(query.goal.contains(")"));
1161        assert!(query.goal.contains("||"));
1162        assert!(query.goal.contains("&&"));
1163    }
1164
1165    #[test]
1166    fn test_parse_query_with_nested_parentheses() {
1167        let input = r#"
1168        query "NestedParen" {
1169            goal: ((A == true && B == true) || C == true) && D == true
1170        }
1171        "#;
1172
1173        let query = GRLQueryParser::parse(input).unwrap();
1174        assert_eq!(query.name, "NestedParen");
1175        // Check that parentheses are preserved
1176        assert!(query.goal.starts_with("(("));
1177    }
1178
1179    #[test]
1180    fn test_parse_query_unclosed_parenthesis() {
1181        let input = r#"
1182        query "BadParen" {
1183            goal: (User.IsVIP == true && User.Active == true
1184        }
1185        "#;
1186
1187        let result = GRLQueryParser::parse(input);
1188        assert!(result.is_err());
1189        if let Err(e) = result {
1190            let msg = format!("{:?}", e);
1191            assert!(msg.contains("unclosed parentheses") || msg.contains("parenthesis"));
1192        }
1193    }
1194}