1use crate::ast::{
4 Comparison, ComparisonExpression, ComparisonOperand, ComparisonOperator, Literal, ObjectPath,
5 ObservationExpression, PathStep, Pattern, Qualifier,
6};
7use crate::error::{ParseError, Result, Span};
8use crate::lexer::{Token, TokenKind};
9
10pub(crate) struct Parser<'a> {
11 tokens: &'a [Token],
12 pos: usize,
13 src_len: usize,
15}
16
17impl<'a> Parser<'a> {
18 pub(crate) fn new(tokens: &'a [Token], src: &'a str) -> Self {
19 Parser {
20 tokens,
21 pos: 0,
22 src_len: src.len(),
23 }
24 }
25
26 fn peek(&self) -> Option<&TokenKind> {
29 self.tokens.get(self.pos).map(|t| &t.kind)
30 }
31
32 fn current_span(&self) -> Span {
33 match self.tokens.get(self.pos) {
34 Some(t) => t.span,
35 None => Span::new(self.src_len, self.src_len),
36 }
37 }
38
39 fn advance(&mut self) -> Option<&Token> {
40 let tok = self.tokens.get(self.pos);
41 if tok.is_some() {
42 self.pos += 1;
43 }
44 tok
45 }
46
47 fn at_end(&self) -> bool {
48 self.pos >= self.tokens.len()
49 }
50
51 fn eat(&mut self, want: &TokenKind) -> bool {
53 if self.peek() == Some(want) {
54 self.pos += 1;
55 true
56 } else {
57 false
58 }
59 }
60
61 fn expect(&mut self, want: &TokenKind, what: &str) -> Result<()> {
62 if self.eat(want) {
63 Ok(())
64 } else {
65 Err(ParseError::new(
66 format!("expected {what}"),
67 self.current_span(),
68 ))
69 }
70 }
71
72 fn error_here(&self, msg: impl Into<String>) -> ParseError {
73 ParseError::new(msg, self.current_span())
74 }
75
76 pub(crate) fn parse_object_path(&mut self) -> Result<ObjectPath> {
79 let object_type = match self.advance().map(|t| &t.kind) {
81 Some(TokenKind::Identifier(s)) => s.clone(),
82 _ => return Err(self.error_here("expected object type identifier")),
83 };
84 self.expect(&TokenKind::Colon, "':' after object type")?;
85
86 let mut steps = Vec::new();
88 steps.push(PathStep::Key(self.parse_key_component()?));
89
90 loop {
92 match self.peek() {
93 Some(TokenKind::Dot) => {
94 self.advance();
95 steps.push(PathStep::Key(self.parse_key_component()?));
96 }
97 Some(TokenKind::LBracket) => {
98 self.advance();
99 let step = match self.peek() {
100 Some(TokenKind::Star) => {
101 self.advance();
102 PathStep::AnyIndex
103 }
104 Some(TokenKind::Integer(n)) => {
105 let n = *n;
106 self.advance();
107 if n < 0 {
108 return Err(self.error_here("list index must be non-negative"));
109 }
110 PathStep::Index(n as u64)
111 }
112 _ => return Err(self.error_here("expected index or '*' in '[...]'")),
113 };
114 self.expect(&TokenKind::RBracket, "']' to close index")?;
115 steps.push(step);
116 }
117 _ => break,
118 }
119 }
120 Ok(ObjectPath { object_type, steps })
121 }
122
123 fn parse_key_component(&mut self) -> Result<String> {
124 match self.advance().map(|t| &t.kind) {
125 Some(TokenKind::Identifier(s)) => Ok(s.clone()),
126 Some(TokenKind::String(s)) => Ok(s.clone()),
127 _ => Err(self.error_here("expected property name")),
128 }
129 }
130
131 pub(crate) fn parse_comparison_expression(&mut self) -> Result<ComparisonExpression> {
134 let mut left = self.parse_comparison_and()?;
135 while self.eat(&TokenKind::Or) {
136 let right = self.parse_comparison_and()?;
137 left = ComparisonExpression::Or(Box::new(left), Box::new(right));
138 }
139 Ok(left)
140 }
141
142 fn parse_comparison_and(&mut self) -> Result<ComparisonExpression> {
143 let mut left = self.parse_prop_test()?;
144 while self.eat(&TokenKind::And) {
145 let right = self.parse_prop_test()?;
146 left = ComparisonExpression::And(Box::new(left), Box::new(right));
147 }
148 Ok(left)
149 }
150
151 fn parse_prop_test(&mut self) -> Result<ComparisonExpression> {
152 if self.eat(&TokenKind::LParen) {
154 let inner = self.parse_comparison_expression()?;
155 self.expect(&TokenKind::RParen, "')' to close comparison group")?;
156 return Ok(inner);
157 }
158
159 if self.eat(&TokenKind::Exists) {
161 let path = self.parse_object_path()?;
162 return Ok(ComparisonExpression::Test(Comparison {
163 path,
164 operator: ComparisonOperator::Exists,
165 negated: false,
166 value: ComparisonOperand::Literal(Literal::Boolean(true)),
168 }));
169 }
170
171 let path = self.parse_object_path()?;
173 let negated = self.eat(&TokenKind::Not);
174 let operator = self.parse_comparison_operator()?;
175 let value = if operator == ComparisonOperator::In {
176 self.parse_set_literal()?
177 } else {
178 ComparisonOperand::Literal(self.parse_literal()?)
179 };
180 Ok(ComparisonExpression::Test(Comparison {
181 path,
182 operator,
183 negated,
184 value,
185 }))
186 }
187
188 fn parse_comparison_operator(&mut self) -> Result<ComparisonOperator> {
189 let op = match self.peek() {
190 Some(TokenKind::Equal) => ComparisonOperator::Equal,
191 Some(TokenKind::NotEqual) => ComparisonOperator::NotEqual,
192 Some(TokenKind::GreaterThan) => ComparisonOperator::GreaterThan,
193 Some(TokenKind::GreaterThanOrEqual) => ComparisonOperator::GreaterThanOrEqual,
194 Some(TokenKind::LessThan) => ComparisonOperator::LessThan,
195 Some(TokenKind::LessThanOrEqual) => ComparisonOperator::LessThanOrEqual,
196 Some(TokenKind::In) => ComparisonOperator::In,
197 Some(TokenKind::Like) => ComparisonOperator::Like,
198 Some(TokenKind::Matches) => ComparisonOperator::Matches,
199 Some(TokenKind::IsSubset) => ComparisonOperator::IsSubset,
200 Some(TokenKind::IsSuperset) => ComparisonOperator::IsSuperset,
201 _ => return Err(self.error_here("expected a comparison operator")),
202 };
203 self.advance();
204 Ok(op)
205 }
206
207 fn parse_set_literal(&mut self) -> Result<ComparisonOperand> {
208 self.expect(&TokenKind::LParen, "'(' to open set literal")?;
209 let mut items = vec![self.parse_literal()?];
210 while self.eat(&TokenKind::Comma) {
211 items.push(self.parse_literal()?);
212 }
213 self.expect(&TokenKind::RParen, "')' to close set literal")?;
214 Ok(ComparisonOperand::Set(items))
215 }
216
217 fn parse_literal(&mut self) -> Result<Literal> {
218 let lit = match self.peek() {
219 Some(TokenKind::String(s)) => Literal::String(s.clone()),
220 Some(TokenKind::Integer(n)) => Literal::Integer(*n),
221 Some(TokenKind::Float(f)) => Literal::Float(*f),
222 Some(TokenKind::Boolean(b)) => Literal::Boolean(*b),
223 Some(TokenKind::Timestamp(s)) => Literal::Timestamp(s.clone()),
224 Some(TokenKind::Binary(s)) => Literal::Binary(s.clone()),
225 Some(TokenKind::Hex(s)) => Literal::Hex(s.clone()),
226 _ => return Err(self.error_here("expected a literal value")),
227 };
228 self.advance();
229 Ok(lit)
230 }
231
232 pub(crate) fn parse_observation_expression(&mut self) -> Result<ObservationExpression> {
235 let mut left = self.parse_observation_or()?;
236 while self.eat(&TokenKind::FollowedBy) {
237 let right = self.parse_observation_or()?;
238 left = ObservationExpression::FollowedBy(Box::new(left), Box::new(right));
239 }
240 Ok(left)
241 }
242
243 fn parse_observation_or(&mut self) -> Result<ObservationExpression> {
244 let mut left = self.parse_observation_and()?;
245 while self.eat(&TokenKind::Or) {
246 let right = self.parse_observation_and()?;
247 left = ObservationExpression::Or(Box::new(left), Box::new(right));
248 }
249 Ok(left)
250 }
251
252 fn parse_observation_and(&mut self) -> Result<ObservationExpression> {
253 let mut left = self.parse_observation_qualified()?;
254 while self.eat(&TokenKind::And) {
255 let right = self.parse_observation_qualified()?;
256 left = ObservationExpression::And(Box::new(left), Box::new(right));
257 }
258 Ok(left)
259 }
260
261 fn parse_observation_qualified(&mut self) -> Result<ObservationExpression> {
262 let mut expr = self.parse_observation_primary()?;
263 loop {
264 let qualifier = match self.peek() {
265 Some(TokenKind::Within) => {
266 self.advance();
267 let seconds = self.parse_number_as_f64()?;
268 self.expect(&TokenKind::Seconds, "SECONDS after WITHIN value")?;
269 Qualifier::Within { seconds }
270 }
271 Some(TokenKind::Repeats) => {
272 self.advance();
273 let count = self.parse_non_negative_int()?;
274 self.expect(&TokenKind::Times, "TIMES after REPEATS value")?;
275 Qualifier::Repeats { count }
276 }
277 Some(TokenKind::Start) => {
278 self.advance();
279 let start = self.parse_timestamp_string()?;
280 self.expect(&TokenKind::Stop, "STOP after START timestamp")?;
281 let stop = self.parse_timestamp_string()?;
282 Qualifier::StartStop { start, stop }
283 }
284 _ => break,
285 };
286 expr = ObservationExpression::Qualified {
287 expression: Box::new(expr),
288 qualifier,
289 };
290 }
291 Ok(expr)
292 }
293
294 fn parse_observation_primary(&mut self) -> Result<ObservationExpression> {
295 if self.eat(&TokenKind::LBracket) {
296 let comp = self.parse_comparison_expression()?;
297 self.expect(&TokenKind::RBracket, "']' to close observation")?;
298 return Ok(ObservationExpression::Observation(Box::new(comp)));
299 }
300 if self.eat(&TokenKind::LParen) {
301 let inner = self.parse_observation_expression()?;
302 self.expect(&TokenKind::RParen, "')' to close grouped observation")?;
303 return Ok(inner);
304 }
305 Err(self.error_here("expected '[' or '(' to start an observation"))
306 }
307
308 fn parse_number_as_f64(&mut self) -> Result<f64> {
309 match self.peek() {
310 Some(TokenKind::Integer(n)) => {
311 let v = *n as f64;
312 self.advance();
313 Ok(v)
314 }
315 Some(TokenKind::Float(f)) => {
316 let v = *f;
317 self.advance();
318 Ok(v)
319 }
320 _ => Err(self.error_here("expected a numeric value")),
321 }
322 }
323
324 fn parse_non_negative_int(&mut self) -> Result<u64> {
325 match self.peek() {
326 Some(TokenKind::Integer(n)) if *n >= 0 => {
327 let v = *n as u64;
328 self.advance();
329 Ok(v)
330 }
331 _ => Err(self.error_here("expected a non-negative integer")),
332 }
333 }
334
335 fn parse_timestamp_string(&mut self) -> Result<String> {
336 match self.peek() {
337 Some(TokenKind::Timestamp(s)) => {
338 let v = s.clone();
339 self.advance();
340 Ok(v)
341 }
342 _ => Err(self.error_here("expected a t'...' timestamp literal")),
343 }
344 }
345}
346
347pub fn parse(src: &str) -> Result<Pattern> {
349 let tokens = crate::lexer::tokenize(src)?;
350 let mut parser = Parser::new(&tokens, src);
351 let expression = parser.parse_observation_expression()?;
352 if !parser.at_end() {
353 return Err(parser.error_here("unexpected trailing tokens after pattern"));
354 }
355 Ok(Pattern { expression })
356}
357
358#[cfg(test)]
359mod tests {
360 use super::*;
361 use crate::ast::PathStep;
362
363 fn parse_path(src: &str) -> crate::ast::ObjectPath {
364 let toks = crate::lexer::tokenize(src).unwrap();
365 let mut p = Parser::new(&toks, src);
366 p.parse_object_path().unwrap()
367 }
368
369 #[test]
370 fn parses_simple_path() {
371 let path = parse_path("ipv4-addr:value");
372 assert_eq!(path.object_type, "ipv4-addr");
373 assert_eq!(path.steps, vec![PathStep::Key("value".to_string())]);
374 }
375
376 #[test]
377 fn parses_nested_keys() {
378 let path = parse_path("file:hashes.MD5");
379 assert_eq!(path.object_type, "file");
380 assert_eq!(
381 path.steps,
382 vec![
383 PathStep::Key("hashes".to_string()),
384 PathStep::Key("MD5".to_string())
385 ]
386 );
387 }
388
389 #[test]
390 fn parses_index_and_any_index() {
391 let path = parse_path("network-traffic:protocols[0]");
392 assert_eq!(
393 path.steps,
394 vec![PathStep::Key("protocols".to_string()), PathStep::Index(0)]
395 );
396
397 let path = parse_path("x:list[*]");
398 assert_eq!(
399 path.steps,
400 vec![PathStep::Key("list".to_string()), PathStep::AnyIndex]
401 );
402 }
403
404 #[test]
405 fn parses_quoted_key() {
406 let path = parse_path("file:hashes.'SHA-256'");
407 assert_eq!(
408 path.steps,
409 vec![
410 PathStep::Key("hashes".to_string()),
411 PathStep::Key("SHA-256".to_string())
412 ]
413 );
414 }
415
416 use crate::ast::{
417 Comparison, ComparisonExpression, ComparisonOperand, ComparisonOperator, Literal,
418 };
419
420 fn parse_comp(src: &str) -> ComparisonExpression {
421 let toks = crate::lexer::tokenize(src).unwrap();
422 let mut p = Parser::new(&toks, src);
423 p.parse_comparison_expression().unwrap()
424 }
425
426 #[test]
427 fn parses_single_comparison() {
428 let c = parse_comp("ipv4-addr:value = '1.2.3.4'");
429 match c {
430 ComparisonExpression::Test(Comparison {
431 operator,
432 negated,
433 value,
434 ..
435 }) => {
436 assert_eq!(operator, ComparisonOperator::Equal);
437 assert!(!negated);
438 assert_eq!(
439 value,
440 ComparisonOperand::Literal(Literal::String("1.2.3.4".into()))
441 );
442 }
443 _ => panic!("expected a single test"),
444 }
445 }
446
447 #[test]
448 fn parses_not_operator() {
449 let c = parse_comp("file:size != 0");
450 match c {
451 ComparisonExpression::Test(Comparison {
452 operator, value, ..
453 }) => {
454 assert_eq!(operator, ComparisonOperator::NotEqual);
455 assert_eq!(value, ComparisonOperand::Literal(Literal::Integer(0)));
456 }
457 _ => panic!("expected test"),
458 }
459 }
460
461 #[test]
462 fn parses_not_keyword_prefix() {
463 let c = parse_comp("file:name NOT = 'x'");
465 match c {
466 ComparisonExpression::Test(Comparison { negated, .. }) => assert!(negated),
467 _ => panic!("expected test"),
468 }
469 }
470
471 #[test]
472 fn parses_in_set() {
473 let c = parse_comp("ipv4-addr:value IN ('1.1.1.1', '8.8.8.8')");
474 match c {
475 ComparisonExpression::Test(Comparison {
476 operator, value, ..
477 }) => {
478 assert_eq!(operator, ComparisonOperator::In);
479 assert_eq!(
480 value,
481 ComparisonOperand::Set(vec![
482 Literal::String("1.1.1.1".into()),
483 Literal::String("8.8.8.8".into()),
484 ])
485 );
486 }
487 _ => panic!("expected test"),
488 }
489 }
490
491 #[test]
492 fn parses_exists() {
493 let c = parse_comp("EXISTS file:name");
494 match c {
495 ComparisonExpression::Test(Comparison { operator, path, .. }) => {
496 assert_eq!(operator, ComparisonOperator::Exists);
497 assert_eq!(path.object_type, "file");
498 }
499 _ => panic!("expected test"),
500 }
501 }
502
503 #[test]
504 fn comparison_and_binds_tighter_than_or() {
505 let c = parse_comp("file:name = 'a' OR file:name = 'b' AND file:size = 1");
507 match c {
508 ComparisonExpression::Or(_, right) => match *right {
509 ComparisonExpression::And(_, _) => {}
510 _ => panic!("right side of OR should be an AND"),
511 },
512 _ => panic!("top should be OR"),
513 }
514 }
515
516 #[test]
517 fn parses_parenthesized_comparison() {
518 let c = parse_comp("(file:name = 'a' OR file:name = 'b') AND file:size = 1");
519 match c {
520 ComparisonExpression::And(left, _) => match *left {
521 ComparisonExpression::Or(_, _) => {}
522 _ => panic!("left of AND should be OR"),
523 },
524 _ => panic!("top should be AND"),
525 }
526 }
527
528 use crate::ast::{ObservationExpression, Qualifier};
529 use crate::parser::parse;
530
531 #[test]
532 fn parses_single_observation() {
533 let p = parse("[ipv4-addr:value = '1.2.3.4']").unwrap();
534 match p.expression {
535 ObservationExpression::Observation(_) => {}
536 _ => panic!("expected single observation"),
537 }
538 }
539
540 #[test]
541 fn observation_and_binds_tighter_than_or() {
542 let p = parse("[file:name='a'] OR [file:name='b'] AND [file:size=1]").unwrap();
544 match p.expression {
545 ObservationExpression::Or(_, right) => match *right {
546 ObservationExpression::And(_, _) => {}
547 _ => panic!("right of OR should be AND"),
548 },
549 _ => panic!("top should be OR"),
550 }
551 }
552
553 #[test]
554 fn followedby_is_loosest() {
555 let p = parse("[file:name='a'] FOLLOWEDBY [file:name='b'] OR [file:name='c']").unwrap();
557 match p.expression {
558 ObservationExpression::FollowedBy(_, right) => match *right {
559 ObservationExpression::Or(_, _) => {}
560 _ => panic!("right of FOLLOWEDBY should be OR"),
561 },
562 _ => panic!("top should be FOLLOWEDBY"),
563 }
564 }
565
566 #[test]
567 fn parses_parenthesized_observation() {
568 let p = parse("([file:name='a'] OR [file:name='b']) FOLLOWEDBY [file:size=1]").unwrap();
569 match p.expression {
570 ObservationExpression::FollowedBy(left, _) => match *left {
571 ObservationExpression::Or(_, _) => {}
572 _ => panic!("left should be OR"),
573 },
574 _ => panic!("top should be FOLLOWEDBY"),
575 }
576 }
577
578 #[test]
579 fn parses_within_qualifier() {
580 let p = parse("[file:name='a'] REPEATS 2 TIMES WITHIN 60 SECONDS").unwrap();
581 match p.expression {
583 ObservationExpression::Qualified {
584 qualifier: Qualifier::Within { seconds },
585 expression,
586 } => {
587 assert_eq!(seconds, 60.0);
588 match *expression {
589 ObservationExpression::Qualified {
590 qualifier: Qualifier::Repeats { count },
591 ..
592 } => {
593 assert_eq!(count, 2);
594 }
595 _ => panic!("inner should be REPEATS"),
596 }
597 }
598 _ => panic!("outer should be WITHIN"),
599 }
600 }
601
602 #[test]
603 fn parses_start_stop_qualifier() {
604 let p = parse("[file:name='a'] START t'2020-01-01T00:00:00Z' STOP t'2020-01-02T00:00:00Z'")
605 .unwrap();
606 match p.expression {
607 ObservationExpression::Qualified {
608 qualifier: Qualifier::StartStop { start, stop },
609 ..
610 } => {
611 assert_eq!(start, "2020-01-01T00:00:00Z");
612 assert_eq!(stop, "2020-01-02T00:00:00Z");
613 }
614 _ => panic!("expected START..STOP"),
615 }
616 }
617
618 #[test]
619 fn trailing_tokens_error() {
620 assert!(parse("[file:name='a'] [file:name='b']").is_err());
621 }
622}