rust_rule_engine/parser/
grl_parser.rs

1use crate::engine::rule::{Condition, ConditionGroup, Rule};
2use crate::errors::{Result, RuleEngineError};
3use crate::types::{ActionType, Operator, Value};
4
5/// Simple GRL Parser for parsing rule files
6pub struct GRLParser;
7
8impl GRLParser {
9    /// Parse a complete GRL file content into rules
10    pub fn parse_rules(content: &str) -> Result<Vec<Rule>> {
11        let mut rules = Vec::new();
12
13        // Split content by rule blocks
14        let rule_blocks = Self::extract_rule_blocks(content)?;
15
16        for block in rule_blocks {
17            let rule = Self::parse_single_rule(&block)?;
18            rules.push(rule);
19        }
20
21        Ok(rules)
22    }
23
24    /// Extract individual rule blocks from content
25    fn extract_rule_blocks(content: &str) -> Result<Vec<String>> {
26        let mut blocks = Vec::new();
27        let mut current_block = String::new();
28        let mut brace_count = 0;
29        let mut in_rule = false;
30
31        for line in content.lines() {
32            let line = line.trim();
33
34            if line.starts_with("rule ") {
35                in_rule = true;
36                current_block.clear();
37                current_block.push_str(line);
38                current_block.push('\n');
39
40                // Count braces in the rule declaration line
41                for ch in line.chars() {
42                    match ch {
43                        '{' => brace_count += 1,
44                        '}' => {
45                            brace_count -= 1;
46                            if brace_count == 0 {
47                                blocks.push(current_block.clone());
48                                current_block.clear();
49                                in_rule = false;
50                                break;
51                            }
52                        }
53                        _ => {}
54                    }
55                }
56                continue;
57            }
58
59            if in_rule {
60                current_block.push_str(line);
61                current_block.push('\n');
62
63                for ch in line.chars() {
64                    match ch {
65                        '{' => brace_count += 1,
66                        '}' => {
67                            brace_count -= 1;
68                            if brace_count == 0 {
69                                blocks.push(current_block.clone());
70                                current_block.clear();
71                                in_rule = false;
72                                break;
73                            }
74                        }
75                        _ => {}
76                    }
77                }
78            }
79        }
80
81        Ok(blocks)
82    }
83
84    /// Parse a single rule block
85    fn parse_single_rule(block: &str) -> Result<Rule> {
86        let lines: Vec<&str> = block.lines().collect();
87
88        // Parse rule header
89        let (name, salience) = Self::parse_rule_header(lines[0])?;
90
91        // Find when and then sections
92        let mut when_section = Vec::new();
93        let mut then_section = Vec::new();
94        let mut current_section = "";
95
96        for line in lines.iter().skip(1) {
97            let line = line.trim();
98
99            if line == "when" {
100                current_section = "when";
101                continue;
102            } else if line == "then" {
103                current_section = "then";
104                continue;
105            } else if line == "}" || line.is_empty() {
106                continue;
107            }
108
109            match current_section {
110                "when" => when_section.push(line),
111                "then" => then_section.push(line),
112                _ => {}
113            }
114        }
115
116        // Parse conditions
117        let conditions = Self::parse_conditions(&when_section)?;
118
119        // Parse actions
120        let actions = Self::parse_actions(&then_section)?;
121
122        Ok(Rule::new(name, conditions, actions).with_salience(salience))
123    }
124
125    /// Parse rule header to extract name and salience
126    fn parse_rule_header(header: &str) -> Result<(String, i32)> {
127        // Example: rule "SpeedLimitCheck" salience 20 {
128        let parts: Vec<&str> = header.split_whitespace().collect();
129
130        let name = if parts.len() >= 2 {
131            parts[1].trim_matches('"').to_string()
132        } else {
133            return Err(RuleEngineError::ParseError {
134                message: "Invalid rule header".to_string(),
135            });
136        };
137
138        let salience = if let Some(sal_idx) = parts.iter().position(|&x| x == "salience") {
139            if sal_idx + 1 < parts.len() {
140                parts[sal_idx + 1].parse::<i32>().unwrap_or(0)
141            } else {
142                0
143            }
144        } else {
145            0
146        };
147
148        Ok((name, salience))
149    }
150
151    /// Parse when conditions
152    fn parse_conditions(when_lines: &[&str]) -> Result<ConditionGroup> {
153        if when_lines.is_empty() {
154            return Err(RuleEngineError::ParseError {
155                message: "No conditions found".to_string(),
156            });
157        }
158
159        // Simple parsing - just take first condition for now
160        let condition_line = when_lines.join(" ");
161
162        // Parse simple conditions like: Car.Speed > 80.0
163        if let Some((field, op, value)) = Self::parse_simple_condition(&condition_line) {
164            let condition = Condition::new(field, op, value);
165            return Ok(ConditionGroup::single(condition));
166        }
167
168        // Default fallback
169        Ok(ConditionGroup::single(Condition::new(
170            "Default.Field".to_string(),
171            Operator::Equal,
172            Value::Boolean(true),
173        )))
174    }
175
176    /// Parse a simple condition
177    fn parse_simple_condition(condition: &str) -> Option<(String, Operator, Value)> {
178        let condition = condition.trim();
179
180        // Try different operators
181        if let Some(pos) = condition.find(" >= ") {
182            let field = condition[..pos].trim().to_string();
183            let value_str = condition[pos + 4..].trim();
184            let value = Self::parse_value(value_str)?;
185            return Some((field, Operator::GreaterThanOrEqual, value));
186        }
187
188        if let Some(pos) = condition.find(" > ") {
189            let field = condition[..pos].trim().to_string();
190            let value_str = condition[pos + 3..].trim();
191            let value = Self::parse_value(value_str)?;
192            return Some((field, Operator::GreaterThan, value));
193        }
194
195        if let Some(pos) = condition.find(" <= ") {
196            let field = condition[..pos].trim().to_string();
197            let value_str = condition[pos + 4..].trim();
198            let value = Self::parse_value(value_str)?;
199            return Some((field, Operator::LessThanOrEqual, value));
200        }
201
202        if let Some(pos) = condition.find(" < ") {
203            let field = condition[..pos].trim().to_string();
204            let value_str = condition[pos + 3..].trim();
205            let value = Self::parse_value(value_str)?;
206            return Some((field, Operator::LessThan, value));
207        }
208
209        if let Some(pos) = condition.find(" == ") {
210            let field = condition[..pos].trim().to_string();
211            let value_str = condition[pos + 4..].trim();
212            let value = Self::parse_value(value_str)?;
213            return Some((field, Operator::Equal, value));
214        }
215
216        if let Some(pos) = condition.find(" != ") {
217            let field = condition[..pos].trim().to_string();
218            let value_str = condition[pos + 4..].trim();
219            let value = Self::parse_value(value_str)?;
220            return Some((field, Operator::NotEqual, value));
221        }
222
223        None
224    }
225
226    /// Parse a value from string
227    fn parse_value(value_str: &str) -> Option<Value> {
228        let value_str = value_str.trim();
229
230        // Boolean
231        if value_str == "true" {
232            return Some(Value::Boolean(true));
233        }
234        if value_str == "false" {
235            return Some(Value::Boolean(false));
236        }
237
238        // String (quoted)
239        if value_str.starts_with('"') && value_str.ends_with('"') {
240            let s = value_str[1..value_str.len() - 1].to_string();
241            return Some(Value::String(s));
242        }
243
244        // Number (float)
245        if let Ok(f) = value_str.parse::<f64>() {
246            return Some(Value::Number(f));
247        }
248
249        // Integer
250        if let Ok(i) = value_str.parse::<i64>() {
251            return Some(Value::Integer(i));
252        }
253
254        // Field reference
255        Some(Value::String(value_str.to_string()))
256    }
257
258    /// Parse then actions
259    fn parse_actions(then_lines: &[&str]) -> Result<Vec<ActionType>> {
260        let mut actions = Vec::new();
261
262        for line in then_lines {
263            let line = line.trim().trim_end_matches(';');
264
265            if line.is_empty() {
266                continue;
267            }
268
269            // Parse different action types
270            if let Some(action) = Self::parse_action_line(line) {
271                actions.push(action);
272            }
273        }
274
275        Ok(actions)
276    }
277
278    /// Parse a single action line
279    fn parse_action_line(line: &str) -> Option<ActionType> {
280        // Method calls: Object.setProperty(value)
281        if let Some(dot_pos) = line.find('.') {
282            if let Some(paren_pos) = line.find('(') {
283                if dot_pos < paren_pos {
284                    let object = line[..dot_pos].trim().to_string();
285                    let method = line[dot_pos + 1..paren_pos].trim().to_string();
286
287                    // Extract arguments
288                    if let Some(close_paren) = line.rfind(')') {
289                        let args_str = &line[paren_pos + 1..close_paren];
290                        let args = Self::parse_function_args(args_str);
291
292                        return Some(ActionType::MethodCall {
293                            object,
294                            method,
295                            args,
296                        });
297                    }
298                }
299            }
300        }
301
302        // Function calls: functionName(args)
303        if let Some(paren_pos) = line.find('(') {
304            let function = line[..paren_pos].trim().to_string();
305            if let Some(close_paren) = line.rfind(')') {
306                let args_str = &line[paren_pos + 1..close_paren];
307                let args = Self::parse_function_args(args_str);
308
309                return Some(ActionType::Call { function, args });
310            }
311        }
312
313        None
314    }
315
316    /// Parse function arguments
317    fn parse_function_args(args_str: &str) -> Vec<Value> {
318        if args_str.trim().is_empty() {
319            return Vec::new();
320        }
321
322        args_str
323            .split(',')
324            .map(|arg| {
325                let arg = arg.trim();
326                Self::parse_value(arg).unwrap_or(Value::String(arg.to_string()))
327            })
328            .collect()
329    }
330}