1use mentedb_core::edge::EdgeType;
4use mentedb_core::error::{MenteError, MenteResult};
5use mentedb_core::memory::MemoryType;
6use uuid::Uuid;
7
8use crate::ast::*;
9use crate::lexer::{Token, TokenKind};
10use mentedb_core::types::MemoryId;
11
12fn flatten_pure_and(cond: &Condition) -> Option<Vec<Filter>> {
17 fn collect(c: &Condition, out: &mut Vec<Filter>) -> bool {
18 match c {
19 Condition::Leaf(f) => {
20 out.push(f.clone());
21 true
22 }
23 Condition::And(children) => children.iter().all(|ch| collect(ch, out)),
24 _ => false,
25 }
26 }
27 let mut out = Vec::new();
28 if collect(cond, &mut out) {
29 Some(out)
30 } else {
31 None
32 }
33}
34
35pub struct Parser<'a> {
36 tokens: &'a [Token],
37 pos: usize,
38}
39
40impl<'a> Parser<'a> {
41 pub fn new(tokens: &'a [Token]) -> Self {
42 Self { tokens, pos: 0 }
43 }
44
45 pub fn parse(tokens: &[Token]) -> MenteResult<Statement> {
46 let mut parser = Parser::new(tokens);
47 parser.parse_statement()
48 }
49
50 fn peek(&self) -> &Token {
51 &self.tokens[self.pos.min(self.tokens.len() - 1)]
52 }
53
54 fn advance(&mut self) -> &Token {
55 let tok = &self.tokens[self.pos.min(self.tokens.len() - 1)];
56 if self.pos < self.tokens.len() {
57 self.pos += 1;
58 }
59 tok
60 }
61
62 fn expect(&mut self, kind: TokenKind) -> MenteResult<&Token> {
63 let tok = self.peek();
64 if tok.kind != kind {
65 return Err(MenteError::Query(format!(
66 "expected {:?}, found {:?} ('{}') at position {}",
67 kind, tok.kind, tok.lexeme, tok.position
68 )));
69 }
70 Ok(self.advance())
71 }
72
73 fn at(&self, kind: TokenKind) -> bool {
74 self.peek().kind == kind
75 }
76
77 fn parse_statement(&mut self) -> MenteResult<Statement> {
78 match self.peek().kind {
79 TokenKind::Recall => self.parse_recall(),
80 TokenKind::Relate => self.parse_relate(),
81 TokenKind::Forget => self.parse_forget(),
82 TokenKind::Consolidate => self.parse_consolidate(),
83 TokenKind::Traverse => self.parse_traverse(),
84 _ => Err(MenteError::Query(format!(
85 "expected statement keyword, found {:?} at position {}",
86 self.peek().kind,
87 self.peek().position
88 ))),
89 }
90 }
91
92 fn parse_recall(&mut self) -> MenteResult<Statement> {
93 self.advance(); if self.at(TokenKind::Memories) {
97 self.advance();
98 }
99
100 let mut near = None;
101 let mut filters = Vec::new();
102 let mut condition: Option<Condition> = None;
103 let mut limit = None;
104 let mut order_by = None;
105
106 if self.at(TokenKind::Near) {
108 self.advance();
109 near = Some(self.parse_vector()?);
110 }
111
112 if self.at(TokenKind::Where) {
116 self.advance();
117 let cond = self.parse_condition()?;
118 match flatten_pure_and(&cond) {
119 Some(leaves) => filters = leaves,
120 None => condition = Some(cond),
121 }
122 }
123
124 if self.at(TokenKind::OrderBy) {
126 self.advance();
127 if self.at(TokenKind::By) {
129 self.advance();
130 }
131 let field = self.parse_field()?;
132 let descending = false; order_by = Some(OrderBy { field, descending });
134 }
135
136 if self.at(TokenKind::Limit) {
138 self.advance();
139 let tok = self.advance();
140 let n: usize = tok
141 .lexeme
142 .parse()
143 .map_err(|_| MenteError::Query(format!("invalid limit value: {}", tok.lexeme)))?;
144 limit = Some(n);
145 }
146
147 if self.at(TokenKind::As) {
150 self.advance();
151 self.expect(TokenKind::Of)?;
152 let tok = self.advance();
153 let t: i64 = tok.lexeme.parse().map_err(|_| {
154 MenteError::Query(format!("invalid AS OF timestamp: {}", tok.lexeme))
155 })?;
156 let valid_at = Filter {
157 field: Field::ValidAt,
158 op: Operator::Eq,
159 value: Value::Integer(t),
160 };
161 match condition.take() {
164 Some(c) => {
165 condition = Some(Condition::And(vec![c, Condition::Leaf(valid_at)]));
166 }
167 None => filters.push(valid_at),
168 }
169 }
170
171 Ok(Statement::Recall(RecallStatement {
172 filters,
173 condition,
174 near,
175 limit,
176 order_by,
177 }))
178 }
179
180 fn parse_relate(&mut self) -> MenteResult<Statement> {
181 self.advance(); let source = self.parse_uuid()?;
184 self.expect(TokenKind::Arrow)?;
185 let target = self.parse_uuid()?;
186 self.expect(TokenKind::As)?;
187 let edge_type = self.parse_edge_type()?;
188
189 let mut weight = None;
190 if self.at(TokenKind::With) {
191 self.advance();
192 self.expect(TokenKind::Identifier)?; self.expect(TokenKind::Eq)?;
195 let tok = self.advance();
196 let w: f32 = tok
197 .lexeme
198 .parse()
199 .map_err(|_| MenteError::Query(format!("invalid weight value: {}", tok.lexeme)))?;
200 weight = Some(w);
201 }
202
203 Ok(Statement::Relate(RelateStatement {
204 source,
205 target,
206 edge_type,
207 weight,
208 }))
209 }
210
211 fn parse_forget(&mut self) -> MenteResult<Statement> {
212 self.advance(); let target = self.parse_uuid()?;
214 Ok(Statement::Forget(ForgetStatement { target }))
215 }
216
217 fn parse_consolidate(&mut self) -> MenteResult<Statement> {
218 self.advance(); let mut filters = Vec::new();
220 if self.at(TokenKind::Where) {
221 self.advance();
222 filters = self.parse_filters()?;
223 }
224 Ok(Statement::Consolidate(ConsolidateStatement { filters }))
225 }
226
227 fn parse_traverse(&mut self) -> MenteResult<Statement> {
228 self.advance(); let start = self.parse_uuid()?;
230
231 self.expect(TokenKind::Depth)?;
232 let tok = self.advance();
233 let depth: usize = tok
234 .lexeme
235 .parse()
236 .map_err(|_| MenteError::Query(format!("invalid depth value: {}", tok.lexeme)))?;
237
238 let mut edge_filter = None;
239 if self.at(TokenKind::Where) {
240 self.advance();
241 self.expect(TokenKind::EdgeType)?;
243 self.expect(TokenKind::Eq)?;
244 let et = self.parse_edge_type()?;
245 edge_filter = Some(vec![et]);
246 }
247
248 Ok(Statement::Traverse(TraverseStatement {
249 start,
250 depth,
251 edge_filter,
252 }))
253 }
254
255 fn parse_filters(&mut self) -> MenteResult<Vec<Filter>> {
256 let mut filters = vec![self.parse_filter()?];
257 while self.at(TokenKind::And) {
258 self.advance();
259 filters.push(self.parse_filter()?);
260 }
261 Ok(filters)
262 }
263
264 fn parse_condition(&mut self) -> MenteResult<Condition> {
268 let mut node = self.parse_and_condition()?;
269 while self.at(TokenKind::Or) {
270 self.advance();
271 let rhs = self.parse_and_condition()?;
272 node = match node {
273 Condition::Or(mut v) => {
274 v.push(rhs);
275 Condition::Or(v)
276 }
277 other => Condition::Or(vec![other, rhs]),
278 };
279 }
280 Ok(node)
281 }
282
283 fn parse_and_condition(&mut self) -> MenteResult<Condition> {
284 let mut node = self.parse_not_condition()?;
285 while self.at(TokenKind::And) {
286 self.advance();
287 let rhs = self.parse_not_condition()?;
288 node = match node {
289 Condition::And(mut v) => {
290 v.push(rhs);
291 Condition::And(v)
292 }
293 other => Condition::And(vec![other, rhs]),
294 };
295 }
296 Ok(node)
297 }
298
299 fn parse_not_condition(&mut self) -> MenteResult<Condition> {
300 if self.at(TokenKind::Not) {
301 self.advance();
302 let inner = self.parse_not_condition()?;
303 return Ok(Condition::Not(Box::new(inner)));
304 }
305 self.parse_primary_condition()
306 }
307
308 fn parse_primary_condition(&mut self) -> MenteResult<Condition> {
309 if self.at(TokenKind::LParen) {
310 self.advance();
311 let inner = self.parse_condition()?;
312 self.expect(TokenKind::RParen)?;
313 return Ok(inner);
314 }
315 Ok(Condition::Leaf(self.parse_filter()?))
316 }
317
318 fn parse_filter(&mut self) -> MenteResult<Filter> {
319 let field = self.parse_field()?;
320 let op = self.parse_operator()?;
321 let value = if op == Operator::In {
322 self.parse_list_value(&field)?
323 } else {
324 self.parse_value(&field)?
325 };
326 Ok(Filter { field, op, value })
327 }
328
329 fn parse_field(&mut self) -> MenteResult<Field> {
330 let tok = self.advance();
331 match tok.kind {
332 TokenKind::Identifier if tok.lexeme.eq_ignore_ascii_case("content") => {
333 Ok(Field::Content)
334 }
335 TokenKind::Type => Ok(Field::Type),
336 TokenKind::Tag => Ok(Field::Tag),
337 TokenKind::Agent => Ok(Field::Agent),
338 TokenKind::Space => Ok(Field::Space),
339 TokenKind::Salience => Ok(Field::Salience),
340 TokenKind::Confidence => Ok(Field::Confidence),
341 TokenKind::Created => Ok(Field::Created),
342 TokenKind::Accessed => Ok(Field::Accessed),
343 _ => Err(MenteError::Query(format!(
344 "expected field name, found '{}' at position {}",
345 tok.lexeme, tok.position
346 ))),
347 }
348 }
349
350 fn parse_operator(&mut self) -> MenteResult<Operator> {
351 let tok = self.advance();
352 match tok.kind {
353 TokenKind::Eq => Ok(Operator::Eq),
354 TokenKind::Neq => Ok(Operator::Neq),
355 TokenKind::Gt => Ok(Operator::Gt),
356 TokenKind::Lt => Ok(Operator::Lt),
357 TokenKind::Gte => Ok(Operator::Gte),
358 TokenKind::Lte => Ok(Operator::Lte),
359 TokenKind::SimilarTo => Ok(Operator::SimilarTo),
360 TokenKind::In => Ok(Operator::In),
361 TokenKind::Contains => Ok(Operator::Contains),
362 _ => Err(MenteError::Query(format!(
363 "expected operator, found '{}' at position {}",
364 tok.lexeme, tok.position
365 ))),
366 }
367 }
368
369 fn parse_list_value(&mut self, field: &Field) -> MenteResult<Value> {
371 self.expect(TokenKind::LBracket)?;
372 let mut items = Vec::new();
373 if !self.at(TokenKind::RBracket) {
374 loop {
375 items.push(self.parse_value(field)?);
376 if self.at(TokenKind::Comma) {
377 self.advance();
378 } else {
379 break;
380 }
381 }
382 }
383 self.expect(TokenKind::RBracket)?;
384 Ok(Value::List(items))
385 }
386
387 fn parse_value(&mut self, field: &Field) -> MenteResult<Value> {
388 if *field == Field::Type {
390 return self.parse_memory_type_value();
391 }
392
393 let tok = self.advance();
394 match tok.kind {
395 TokenKind::StringLit => {
396 let inner = tok.lexeme[1..tok.lexeme.len() - 1].to_string();
398 if let Ok(uuid) = inner.parse::<MemoryId>() {
400 return Ok(Value::Uuid(uuid.into()));
401 }
402 Ok(Value::Text(inner))
403 }
404 TokenKind::IntegerLit => {
405 let n: i64 = tok
406 .lexeme
407 .parse()
408 .map_err(|_| MenteError::Query(format!("invalid integer: {}", tok.lexeme)))?;
409 Ok(Value::Integer(n))
410 }
411 TokenKind::FloatLit => {
412 let n: f64 = tok
413 .lexeme
414 .parse()
415 .map_err(|_| MenteError::Query(format!("invalid float: {}", tok.lexeme)))?;
416 Ok(Value::Number(n))
417 }
418 TokenKind::UuidLit => {
419 let uuid: Uuid = tok
420 .lexeme
421 .parse()
422 .map_err(|_| MenteError::Query(format!("invalid UUID: {}", tok.lexeme)))?;
423 Ok(Value::Uuid(uuid))
424 }
425 TokenKind::Identifier => {
426 let lower = tok.lexeme.to_lowercase();
427 match lower.as_str() {
428 "true" => Ok(Value::Bool(true)),
429 "false" => Ok(Value::Bool(false)),
430 _ => Ok(Value::Text(tok.lexeme.clone())),
431 }
432 }
433 TokenKind::LBracket => {
434 self.pos -= 1;
436 let v = self.parse_vector()?;
437 Ok(Value::Vector(v))
438 }
439 _ => Err(MenteError::Query(format!(
440 "expected value, found '{}' at position {}",
441 tok.lexeme, tok.position
442 ))),
443 }
444 }
445
446 fn parse_memory_type_value(&mut self) -> MenteResult<Value> {
447 let tok = self.advance();
448 let name = match tok.kind {
449 TokenKind::Identifier | TokenKind::StringLit => {
450 if tok.kind == TokenKind::StringLit {
451 tok.lexeme[1..tok.lexeme.len() - 1].to_string()
452 } else {
453 tok.lexeme.clone()
454 }
455 }
456 _ => {
457 return Err(MenteError::Query(format!(
458 "expected memory type, found '{}' at position {}",
459 tok.lexeme, tok.position
460 )));
461 }
462 };
463
464 let mt = match name.to_lowercase().as_str() {
465 "episodic" => MemoryType::Episodic,
466 "semantic" => MemoryType::Semantic,
467 "procedural" => MemoryType::Procedural,
468 "antipattern" | "anti_pattern" => MemoryType::AntiPattern,
469 "reasoning" => MemoryType::Reasoning,
470 "correction" => MemoryType::Correction,
471 _ => {
472 return Err(MenteError::Query(format!("unknown memory type: {}", name)));
473 }
474 };
475 Ok(Value::MemoryType(mt))
476 }
477
478 fn parse_edge_type(&mut self) -> MenteResult<EdgeType> {
479 let tok = self.advance();
480 let name = match tok.kind {
481 TokenKind::Identifier | TokenKind::StringLit => {
482 if tok.kind == TokenKind::StringLit {
483 tok.lexeme[1..tok.lexeme.len() - 1].to_string()
484 } else {
485 tok.lexeme.clone()
486 }
487 }
488 _ => {
489 return Err(MenteError::Query(format!(
490 "expected edge type, found '{}' at position {}",
491 tok.lexeme, tok.position
492 )));
493 }
494 };
495
496 match name.to_lowercase().as_str() {
497 "caused" => Ok(EdgeType::Caused),
498 "before" => Ok(EdgeType::Before),
499 "related" => Ok(EdgeType::Related),
500 "contradicts" => Ok(EdgeType::Contradicts),
501 "supports" => Ok(EdgeType::Supports),
502 "supersedes" => Ok(EdgeType::Supersedes),
503 "derived" => Ok(EdgeType::Derived),
504 "partof" | "part_of" => Ok(EdgeType::PartOf),
505 _ => Err(MenteError::Query(format!("unknown edge type: {}", name))),
506 }
507 }
508
509 fn parse_uuid(&mut self) -> MenteResult<MemoryId> {
510 let tok = self.advance();
511 match tok.kind {
512 TokenKind::UuidLit => tok
513 .lexeme
514 .parse()
515 .map_err(|_| MenteError::Query(format!("invalid UUID: {}", tok.lexeme))),
516 TokenKind::StringLit => {
517 let inner = &tok.lexeme[1..tok.lexeme.len() - 1];
518 inner.parse().map_err(|_| {
519 MenteError::Query(format!("invalid UUID in string: {}", tok.lexeme))
520 })
521 }
522 _ => Err(MenteError::Query(format!(
523 "expected UUID, found '{}' at position {}",
524 tok.lexeme, tok.position
525 ))),
526 }
527 }
528
529 fn parse_vector(&mut self) -> MenteResult<Vec<f32>> {
530 self.expect(TokenKind::LBracket)?;
531 let mut values = Vec::new();
532 if !self.at(TokenKind::RBracket) {
533 let tok = self.advance();
534 let v: f32 = tok.lexeme.parse().map_err(|_| {
535 MenteError::Query(format!("invalid float in vector: {}", tok.lexeme))
536 })?;
537 values.push(v);
538 while self.at(TokenKind::Comma) {
539 self.advance();
540 let tok = self.advance();
541 let v: f32 = tok.lexeme.parse().map_err(|_| {
542 MenteError::Query(format!("invalid float in vector: {}", tok.lexeme))
543 })?;
544 values.push(v);
545 }
546 }
547 self.expect(TokenKind::RBracket)?;
548 Ok(values)
549 }
550}
551
552#[cfg(test)]
553mod tests {
554 use super::*;
555 use crate::lexer::tokenize;
556
557 #[test]
558 fn test_parse_recall_with_type_filter() {
559 let tokens = tokenize("RECALL memories WHERE type = episodic LIMIT 5").unwrap();
560 let stmt = Parser::parse(&tokens).unwrap();
561 match stmt {
562 Statement::Recall(r) => {
563 assert_eq!(r.filters.len(), 1);
564 assert_eq!(r.filters[0].field, Field::Type);
565 assert_eq!(r.filters[0].value, Value::MemoryType(MemoryType::Episodic));
566 assert_eq!(r.limit, Some(5));
567 }
568 _ => panic!("expected Recall"),
569 }
570 }
571
572 #[test]
573 fn test_pure_and_stays_on_flat_fast_path() {
574 let tokens = tokenize("RECALL WHERE type = semantic AND tag = \"x\" LIMIT 5").unwrap();
577 match Parser::parse(&tokens).unwrap() {
578 Statement::Recall(r) => {
579 assert_eq!(r.filters.len(), 2, "both leaves flattened");
580 assert!(r.condition.is_none(), "pure AND must not build a tree");
581 }
582 _ => panic!("expected Recall"),
583 }
584 }
585
586 #[test]
587 fn test_or_builds_condition_tree() {
588 let tokens = tokenize("RECALL WHERE type = semantic OR type = procedural LIMIT 5").unwrap();
589 match Parser::parse(&tokens).unwrap() {
590 Statement::Recall(r) => {
591 assert!(r.filters.is_empty(), "OR clause is carried by the tree");
592 match r.condition {
593 Some(Condition::Or(branches)) => assert_eq!(branches.len(), 2),
594 other => panic!("expected Or condition, got {other:?}"),
595 }
596 }
597 _ => panic!("expected Recall"),
598 }
599 }
600
601 #[test]
602 fn test_grouping_and_not_precedence() {
603 let tokens = tokenize(
605 "RECALL WHERE (type = semantic OR type = procedural) AND NOT tag = \"x\" LIMIT 5",
606 )
607 .unwrap();
608 match Parser::parse(&tokens).unwrap() {
609 Statement::Recall(r) => match r.condition {
610 Some(Condition::And(parts)) => {
611 assert_eq!(parts.len(), 2);
612 assert!(matches!(parts[0], Condition::Or(_)), "left is the OR group");
613 assert!(matches!(parts[1], Condition::Not(_)), "right is the NOT");
614 }
615 other => panic!("expected And condition, got {other:?}"),
616 },
617 _ => panic!("expected Recall"),
618 }
619 }
620
621 #[test]
622 fn test_parse_in_operator() {
623 let tokens =
624 tokenize("RECALL memories WHERE type IN [episodic, semantic] LIMIT 5").unwrap();
625 match Parser::parse(&tokens).unwrap() {
626 Statement::Recall(r) => {
627 assert_eq!(r.filters.len(), 1);
628 assert_eq!(r.filters[0].op, Operator::In);
629 assert_eq!(
630 r.filters[0].value,
631 Value::List(vec![
632 Value::MemoryType(MemoryType::Episodic),
633 Value::MemoryType(MemoryType::Semantic),
634 ])
635 );
636 }
637 _ => panic!("expected Recall"),
638 }
639 }
640
641 #[test]
642 fn test_parse_in_operator_text_list() {
643 let tokens = tokenize("RECALL memories WHERE tag IN [\"work\", \"home\"]").unwrap();
644 match Parser::parse(&tokens).unwrap() {
645 Statement::Recall(r) => {
646 assert_eq!(r.filters[0].field, Field::Tag);
647 assert_eq!(r.filters[0].op, Operator::In);
648 assert_eq!(
649 r.filters[0].value,
650 Value::List(vec![Value::Text("work".into()), Value::Text("home".into()),])
651 );
652 }
653 _ => panic!("expected Recall"),
654 }
655 }
656
657 #[test]
658 fn test_parse_contains_operator() {
659 let tokens = tokenize("RECALL memories WHERE content CONTAINS \"coffee\"").unwrap();
660 match Parser::parse(&tokens).unwrap() {
661 Statement::Recall(r) => {
662 assert_eq!(r.filters[0].field, Field::Content);
663 assert_eq!(r.filters[0].op, Operator::Contains);
664 assert_eq!(r.filters[0].value, Value::Text("coffee".into()));
665 }
666 _ => panic!("expected Recall"),
667 }
668 }
669
670 #[test]
671 fn test_parse_recall_as_of() {
672 let tokens =
675 tokenize("RECALL memories WHERE type = semantic LIMIT 5 AS OF 1700000000").unwrap();
676 let stmt = Parser::parse(&tokens).unwrap();
677 match stmt {
678 Statement::Recall(r) => {
679 assert_eq!(r.limit, Some(5));
680 assert_eq!(r.filters.len(), 2);
681 let valid_at = r
682 .filters
683 .iter()
684 .find(|f| f.field == Field::ValidAt)
685 .expect("expected a ValidAt filter from AS OF");
686 assert_eq!(valid_at.value, Value::Integer(1_700_000_000));
687 }
688 _ => panic!("expected Recall"),
689 }
690 }
691
692 #[test]
693 fn test_parse_recall_as_of_without_where() {
694 let tokens = tokenize("RECALL memories AS OF 42").unwrap();
695 let stmt = Parser::parse(&tokens).unwrap();
696 match stmt {
697 Statement::Recall(r) => {
698 assert_eq!(r.filters.len(), 1);
699 assert_eq!(r.filters[0].field, Field::ValidAt);
700 assert_eq!(r.filters[0].value, Value::Integer(42));
701 }
702 _ => panic!("expected Recall"),
703 }
704 }
705
706 #[test]
707 fn test_parse_recall_similar_to() {
708 let tokens =
709 tokenize(r#"RECALL memories WHERE content ~> "database migration" LIMIT 10"#).unwrap();
710 let stmt = Parser::parse(&tokens).unwrap();
711 match stmt {
712 Statement::Recall(r) => {
713 assert_eq!(r.filters.len(), 1);
714 assert_eq!(r.filters[0].op, Operator::SimilarTo);
715 assert_eq!(r.limit, Some(10));
716 }
717 _ => panic!("expected Recall"),
718 }
719 }
720
721 #[test]
722 fn test_parse_recall_near() {
723 let tokens = tokenize("RECALL memories NEAR [0.1, 0.2, 0.3] LIMIT 10").unwrap();
724 let stmt = Parser::parse(&tokens).unwrap();
725 match stmt {
726 Statement::Recall(r) => {
727 assert_eq!(r.near, Some(vec![0.1, 0.2, 0.3]));
728 assert_eq!(r.limit, Some(10));
729 }
730 _ => panic!("expected Recall"),
731 }
732 }
733
734 #[test]
735 fn test_parse_relate() {
736 let tokens = tokenize(
737 "RELATE 550e8400-e29b-41d4-a716-446655440000 -> 660e8400-e29b-41d4-a716-446655440000 AS caused WITH weight = 0.9"
738 ).unwrap();
739 let stmt = Parser::parse(&tokens).unwrap();
740 match stmt {
741 Statement::Relate(r) => {
742 assert_eq!(r.edge_type, EdgeType::Caused);
743 assert_eq!(r.weight, Some(0.9));
744 }
745 _ => panic!("expected Relate"),
746 }
747 }
748
749 #[test]
750 fn test_parse_forget() {
751 let tokens = tokenize("FORGET 550e8400-e29b-41d4-a716-446655440000").unwrap();
752 let stmt = Parser::parse(&tokens).unwrap();
753 match stmt {
754 Statement::Forget(f) => {
755 assert_eq!(
756 f.target,
757 "550e8400-e29b-41d4-a716-446655440000"
758 .parse::<MemoryId>()
759 .unwrap()
760 );
761 }
762 _ => panic!("expected Forget"),
763 }
764 }
765
766 #[test]
767 fn test_parse_consolidate() {
768 let tokens =
769 tokenize(r#"CONSOLIDATE WHERE type = episodic AND accessed < "2024-01-01""#).unwrap();
770 let stmt = Parser::parse(&tokens).unwrap();
771 match stmt {
772 Statement::Consolidate(c) => {
773 assert_eq!(c.filters.len(), 2);
774 }
775 _ => panic!("expected Consolidate"),
776 }
777 }
778
779 #[test]
780 fn test_parse_traverse() {
781 let tokens = tokenize(
782 "TRAVERSE 550e8400-e29b-41d4-a716-446655440000 DEPTH 3 WHERE edge_type = caused",
783 )
784 .unwrap();
785 let stmt = Parser::parse(&tokens).unwrap();
786 match stmt {
787 Statement::Traverse(t) => {
788 assert_eq!(t.depth, 3);
789 assert_eq!(t.edge_filter, Some(vec![EdgeType::Caused]));
790 }
791 _ => panic!("expected Traverse"),
792 }
793 }
794}