1use std::fmt;
15use std::iter::Peekable;
16use std::str::Chars;
17
18#[derive(Debug, Clone, PartialEq)]
20pub enum Token {
21 Select,
23 From,
24 Where,
25 And,
26 Or,
27 Not,
28 Match,
29 Return,
30 Join,
31 Graph,
32 Path,
33 To,
34 Via,
35 On,
36 As,
37 Is,
38 Null,
39 Between,
40 Like,
41 In,
42 Order,
43 By,
44 Asc,
45 Desc,
46 Nulls,
47 First,
48 Last,
49 Limit,
50 Offset,
51 Inner,
52 Left,
53 Right,
54 Outer,
55 Full,
56 Cross,
57 Starts,
58 Ends,
59 With,
60 Contains,
61 True,
62 False,
63 Enrich,
64 Group,
65 Count,
66 Sum,
67 Avg,
68 Min,
69 Max,
70 Distinct,
71
72 Vector,
74 Search,
75 Similar,
76 Collection,
77 Metric,
78 Threshold,
79 K,
80 Hybrid,
81 Fusion,
82 Rerank,
83 Rrf,
84 Intersection,
85 Union,
86 Recursive,
87 All,
88 Weight,
89 L2,
90 Cosine,
91 InnerProduct,
92 Include,
93 Metadata,
94 Vectors,
95
96 Insert,
98 Into,
99 Values,
100 Update,
101 Set,
102 Delete,
103 Truncate,
104 Create,
105 Table,
106 Drop,
107 Alter,
108 Add,
109 Column,
110 Primary,
111 Explain,
113 For,
114 Format,
115 Json,
116 Key,
117 Default,
118 Compress,
119 Index,
120 Unique,
121 If,
122 Exists,
123 Returning,
124 Cascade,
125 Rename,
126 Using,
127
128 Node,
130 Edge,
131 Document,
132 Kv,
133
134 Timeseries,
136 Retention,
137 Queue,
138 Tree,
139 Push,
140 Pop,
141 Peek,
142 Purge,
143 Ack,
144 Nack,
145 Priority,
146
147 Neighborhood,
149 ShortestPath,
150 Centrality,
151 Community,
152 Components,
153 Cycles,
154 Traverse,
155 Depth,
156 Direction,
157 Algorithm,
158 Strategy,
159 MaxIterations,
160 MaxLength,
161 Mode,
162 Clustering,
163 TopologicalSort,
164 Properties,
165 Text,
166 Fuzzy,
167 MinScore,
168
169 Begin,
171 Commit,
172 Rollback,
173 Savepoint,
174 Release,
175 Start,
176 Transaction,
177 Work,
178
179 Vacuum,
181 Analyze,
182
183 Schema,
185 Sequence,
186 Increment,
187
188 Copy,
190 Header,
191 Delimiter,
192
193 View,
195 Materialized,
196 Refresh,
197
198 Partition,
200 Range,
201 List,
202 Hash,
203 Attach,
204 Detach,
205 Of,
206
207 Policy,
209 Enable,
210 Disable,
211 Security,
212 Row,
213 Level,
214
215 Foreign,
217 Server,
218 Wrapper,
219 Options,
220 Data,
221
222 Sessionize,
224 Gap,
225
226 Over,
230 Rows,
231 Preceding,
232 Following,
233 Unbounded,
234 Current,
235
236 String(String),
238 Integer(i64),
239 Float(f64),
240 JsonLiteral(String),
246
247 Ident(String),
249
250 Eq, Ne, Lt, Le, Gt, Ge, Plus, Minus, Star, Slash, Percent, LParen, RParen, LBracket, RBracket, LBrace, RBrace, Comma, Dot, Colon, Semi, Dollar, Question, FatArrow, Arrow, ArrowLeft, Dash, DotDot, Pipe, DoublePipe, Eof,
290}
291
292impl fmt::Display for Token {
293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294 match self {
295 Token::Select => write!(f, "SELECT"),
296 Token::From => write!(f, "FROM"),
297 Token::Where => write!(f, "WHERE"),
298 Token::And => write!(f, "AND"),
299 Token::Or => write!(f, "OR"),
300 Token::Not => write!(f, "NOT"),
301 Token::Match => write!(f, "MATCH"),
302 Token::Return => write!(f, "RETURN"),
303 Token::Join => write!(f, "JOIN"),
304 Token::Graph => write!(f, "GRAPH"),
305 Token::Path => write!(f, "PATH"),
306 Token::To => write!(f, "TO"),
307 Token::Via => write!(f, "VIA"),
308 Token::On => write!(f, "ON"),
309 Token::As => write!(f, "AS"),
310 Token::Is => write!(f, "IS"),
311 Token::Null => write!(f, "NULL"),
312 Token::Between => write!(f, "BETWEEN"),
313 Token::Like => write!(f, "LIKE"),
314 Token::In => write!(f, "IN"),
315 Token::Order => write!(f, "ORDER"),
316 Token::By => write!(f, "BY"),
317 Token::Asc => write!(f, "ASC"),
318 Token::Desc => write!(f, "DESC"),
319 Token::Nulls => write!(f, "NULLS"),
320 Token::First => write!(f, "FIRST"),
321 Token::Last => write!(f, "LAST"),
322 Token::Limit => write!(f, "LIMIT"),
323 Token::Offset => write!(f, "OFFSET"),
324 Token::Inner => write!(f, "INNER"),
325 Token::Left => write!(f, "LEFT"),
326 Token::Right => write!(f, "RIGHT"),
327 Token::Outer => write!(f, "OUTER"),
328 Token::Full => write!(f, "FULL"),
329 Token::Cross => write!(f, "CROSS"),
330 Token::Starts => write!(f, "STARTS"),
331 Token::Ends => write!(f, "ENDS"),
332 Token::With => write!(f, "WITH"),
333 Token::Contains => write!(f, "CONTAINS"),
334 Token::True => write!(f, "TRUE"),
335 Token::False => write!(f, "FALSE"),
336 Token::Enrich => write!(f, "ENRICH"),
337 Token::Group => write!(f, "GROUP"),
338 Token::Count => write!(f, "COUNT"),
339 Token::Sum => write!(f, "SUM"),
340 Token::Avg => write!(f, "AVG"),
341 Token::Min => write!(f, "MIN"),
342 Token::Max => write!(f, "MAX"),
343 Token::Distinct => write!(f, "DISTINCT"),
344 Token::Vector => write!(f, "VECTOR"),
345 Token::Search => write!(f, "SEARCH"),
346 Token::Similar => write!(f, "SIMILAR"),
347 Token::Collection => write!(f, "COLLECTION"),
348 Token::Metric => write!(f, "METRIC"),
349 Token::Threshold => write!(f, "THRESHOLD"),
350 Token::K => write!(f, "K"),
351 Token::Hybrid => write!(f, "HYBRID"),
352 Token::Fusion => write!(f, "FUSION"),
353 Token::Rerank => write!(f, "RERANK"),
354 Token::Rrf => write!(f, "RRF"),
355 Token::Intersection => write!(f, "INTERSECTION"),
356 Token::Union => write!(f, "UNION"),
357 Token::Recursive => write!(f, "RECURSIVE"),
358 Token::All => write!(f, "ALL"),
359 Token::Weight => write!(f, "WEIGHT"),
360 Token::L2 => write!(f, "L2"),
361 Token::Cosine => write!(f, "COSINE"),
362 Token::InnerProduct => write!(f, "INNER_PRODUCT"),
363 Token::Include => write!(f, "INCLUDE"),
364 Token::Metadata => write!(f, "METADATA"),
365 Token::Vectors => write!(f, "VECTORS"),
366 Token::Explain => write!(f, "EXPLAIN"),
367 Token::For => write!(f, "FOR"),
368 Token::Format => write!(f, "FORMAT"),
369 Token::Json => write!(f, "JSON"),
370 Token::Insert => write!(f, "INSERT"),
371 Token::Into => write!(f, "INTO"),
372 Token::Values => write!(f, "VALUES"),
373 Token::Update => write!(f, "UPDATE"),
374 Token::Set => write!(f, "SET"),
375 Token::Delete => write!(f, "DELETE"),
376 Token::Truncate => write!(f, "TRUNCATE"),
377 Token::Create => write!(f, "CREATE"),
378 Token::Table => write!(f, "TABLE"),
379 Token::Drop => write!(f, "DROP"),
380 Token::Alter => write!(f, "ALTER"),
381 Token::Add => write!(f, "ADD"),
382 Token::Column => write!(f, "COLUMN"),
383 Token::Primary => write!(f, "PRIMARY"),
384 Token::Key => write!(f, "KEY"),
385 Token::Default => write!(f, "DEFAULT"),
386 Token::Compress => write!(f, "COMPRESS"),
387 Token::Index => write!(f, "INDEX"),
388 Token::Unique => write!(f, "UNIQUE"),
389 Token::If => write!(f, "IF"),
390 Token::Exists => write!(f, "EXISTS"),
391 Token::Returning => write!(f, "RETURNING"),
392 Token::Cascade => write!(f, "CASCADE"),
393 Token::Rename => write!(f, "RENAME"),
394 Token::Using => write!(f, "USING"),
395 Token::Node => write!(f, "NODE"),
396 Token::Edge => write!(f, "EDGE"),
397 Token::Document => write!(f, "DOCUMENT"),
398 Token::Kv => write!(f, "KV"),
399 Token::Timeseries => write!(f, "TIMESERIES"),
400 Token::Retention => write!(f, "RETENTION"),
401 Token::Queue => write!(f, "QUEUE"),
402 Token::Tree => write!(f, "TREE"),
403 Token::Push => write!(f, "PUSH"),
404 Token::Pop => write!(f, "POP"),
405 Token::Peek => write!(f, "PEEK"),
406 Token::Purge => write!(f, "PURGE"),
407 Token::Ack => write!(f, "ACK"),
408 Token::Nack => write!(f, "NACK"),
409 Token::Priority => write!(f, "PRIORITY"),
410 Token::Neighborhood => write!(f, "NEIGHBORHOOD"),
411 Token::ShortestPath => write!(f, "SHORTEST_PATH"),
412 Token::Centrality => write!(f, "CENTRALITY"),
413 Token::Community => write!(f, "COMMUNITY"),
414 Token::Components => write!(f, "COMPONENTS"),
415 Token::Cycles => write!(f, "CYCLES"),
416 Token::Traverse => write!(f, "TRAVERSE"),
417 Token::Depth => write!(f, "DEPTH"),
418 Token::Direction => write!(f, "DIRECTION"),
419 Token::Algorithm => write!(f, "ALGORITHM"),
420 Token::Strategy => write!(f, "STRATEGY"),
421 Token::MaxIterations => write!(f, "MAX_ITERATIONS"),
422 Token::MaxLength => write!(f, "MAX_LENGTH"),
423 Token::Mode => write!(f, "MODE"),
424 Token::Clustering => write!(f, "CLUSTERING"),
425 Token::TopologicalSort => write!(f, "TOPOLOGICAL_SORT"),
426 Token::Properties => write!(f, "PROPERTIES"),
427 Token::Text => write!(f, "TEXT"),
428 Token::Fuzzy => write!(f, "FUZZY"),
429 Token::MinScore => write!(f, "MIN_SCORE"),
430 Token::Begin => write!(f, "BEGIN"),
431 Token::Commit => write!(f, "COMMIT"),
432 Token::Rollback => write!(f, "ROLLBACK"),
433 Token::Savepoint => write!(f, "SAVEPOINT"),
434 Token::Release => write!(f, "RELEASE"),
435 Token::Start => write!(f, "START"),
436 Token::Transaction => write!(f, "TRANSACTION"),
437 Token::Work => write!(f, "WORK"),
438 Token::Vacuum => write!(f, "VACUUM"),
439 Token::Analyze => write!(f, "ANALYZE"),
440 Token::Schema => write!(f, "SCHEMA"),
441 Token::Sequence => write!(f, "SEQUENCE"),
442 Token::Increment => write!(f, "INCREMENT"),
443 Token::Copy => write!(f, "COPY"),
444 Token::Header => write!(f, "HEADER"),
445 Token::Delimiter => write!(f, "DELIMITER"),
446 Token::View => write!(f, "VIEW"),
447 Token::Materialized => write!(f, "MATERIALIZED"),
448 Token::Refresh => write!(f, "REFRESH"),
449 Token::Partition => write!(f, "PARTITION"),
450 Token::Range => write!(f, "RANGE"),
451 Token::List => write!(f, "LIST"),
452 Token::Hash => write!(f, "HASH"),
453 Token::Attach => write!(f, "ATTACH"),
454 Token::Detach => write!(f, "DETACH"),
455 Token::Of => write!(f, "OF"),
456 Token::Policy => write!(f, "POLICY"),
457 Token::Enable => write!(f, "ENABLE"),
458 Token::Disable => write!(f, "DISABLE"),
459 Token::Security => write!(f, "SECURITY"),
460 Token::Row => write!(f, "ROW"),
461 Token::Level => write!(f, "LEVEL"),
462 Token::Foreign => write!(f, "FOREIGN"),
463 Token::Server => write!(f, "SERVER"),
464 Token::Wrapper => write!(f, "WRAPPER"),
465 Token::Options => write!(f, "OPTIONS"),
466 Token::Data => write!(f, "DATA"),
467 Token::Sessionize => write!(f, "SESSIONIZE"),
468 Token::Gap => write!(f, "GAP"),
469 Token::Over => write!(f, "OVER"),
470 Token::Rows => write!(f, "ROWS"),
471 Token::Preceding => write!(f, "PRECEDING"),
472 Token::Following => write!(f, "FOLLOWING"),
473 Token::Unbounded => write!(f, "UNBOUNDED"),
474 Token::Current => write!(f, "CURRENT"),
475 Token::String(s) => write!(f, "'{}'", s),
476 Token::Integer(n) => write!(f, "{}", n),
477 Token::Float(n) => write!(f, "{}", n),
478 Token::JsonLiteral(s) => write!(f, "{}", s),
479 Token::Ident(s) => write!(f, "{}", s),
480 Token::Eq => write!(f, "="),
481 Token::Ne => write!(f, "<>"),
482 Token::Lt => write!(f, "<"),
483 Token::Le => write!(f, "<="),
484 Token::Gt => write!(f, ">"),
485 Token::Ge => write!(f, ">="),
486 Token::Plus => write!(f, "+"),
487 Token::Minus => write!(f, "-"),
488 Token::Star => write!(f, "*"),
489 Token::Slash => write!(f, "/"),
490 Token::Percent => write!(f, "%"),
491 Token::LParen => write!(f, "("),
492 Token::RParen => write!(f, ")"),
493 Token::LBracket => write!(f, "["),
494 Token::RBracket => write!(f, "]"),
495 Token::LBrace => write!(f, "{{"),
496 Token::RBrace => write!(f, "}}"),
497 Token::Comma => write!(f, ","),
498 Token::Dot => write!(f, "."),
499 Token::Colon => write!(f, ":"),
500 Token::Semi => write!(f, ";"),
501 Token::Dollar => write!(f, "$"),
502 Token::Question => write!(f, "?"),
503 Token::FatArrow => write!(f, "=>"),
504 Token::Arrow => write!(f, "->"),
505 Token::ArrowLeft => write!(f, "<-"),
506 Token::Dash => write!(f, "-"),
507 Token::DotDot => write!(f, ".."),
508 Token::Pipe => write!(f, "|"),
509 Token::DoublePipe => write!(f, "||"),
510 Token::Eof => write!(f, "EOF"),
511 }
512 }
513}
514
515#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
517pub struct Position {
518 pub line: u32,
520 pub column: u32,
522 pub offset: u32,
524}
525
526impl Position {
527 pub fn new(line: u32, column: u32, offset: u32) -> Self {
529 Self {
530 line,
531 column,
532 offset,
533 }
534 }
535}
536
537impl fmt::Display for Position {
538 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
539 write!(f, "{}:{}", self.line, self.column)
540 }
541}
542
543#[derive(Debug, Clone)]
545pub struct Spanned {
546 pub token: Token,
548 pub start: Position,
550 pub end: Position,
552}
553
554impl Spanned {
555 pub fn new(token: Token, start: Position, end: Position) -> Self {
557 Self { token, start, end }
558 }
559}
560
561#[derive(Debug, Clone)]
563pub struct LexerError {
564 pub message: String,
566 pub position: Position,
568 pub limit_hit: Option<LexerLimitHit>,
572}
573
574#[derive(Debug, Clone, PartialEq, Eq)]
576pub enum LexerLimitHit {
577 IdentifierTooLong {
579 limit_name: &'static str,
580 value: usize,
581 },
582}
583
584impl LexerError {
585 pub fn new(message: impl Into<String>, position: Position) -> Self {
587 Self {
588 message: message.into(),
589 position,
590 limit_hit: None,
591 }
592 }
593
594 pub(crate) fn with_limit(
596 message: impl Into<String>,
597 position: Position,
598 limit_hit: LexerLimitHit,
599 ) -> Self {
600 Self {
601 message: message.into(),
602 position,
603 limit_hit: Some(limit_hit),
604 }
605 }
606}
607
608impl fmt::Display for LexerError {
609 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610 write!(f, "Lexer error at {}: {}", self.position, self.message)
611 }
612}
613
614impl std::error::Error for LexerError {}
615
616pub const JSON_LITERAL_MAX_BYTES: usize = 16 * 1024 * 1024;
623
624pub struct Lexer<'a> {
626 input: &'a str,
629 chars: Peekable<Chars<'a>>,
631 line: u32,
633 column: u32,
634 offset: u32,
635 peeked: Option<Spanned>,
637 putback: Option<(char, Position)>,
639 max_identifier_chars: usize,
641}
642
643impl<'a> Lexer<'a> {
644 pub(crate) fn source(&self) -> &'a str {
648 self.input
649 }
650
651 pub fn new(input: &'a str) -> Self {
653 Self::with_limits(input, crate::limits::ParserLimits::default())
654 }
655
656 pub fn with_limits(input: &'a str, limits: crate::limits::ParserLimits) -> Self {
658 Self {
659 input,
660 chars: input.chars().peekable(),
661 line: 1,
662 column: 1,
663 offset: 0,
664 peeked: None,
665 putback: None,
666 max_identifier_chars: limits.max_identifier_chars,
667 }
668 }
669
670 #[allow(dead_code)]
679 pub(crate) fn max_identifier_chars(&self) -> usize {
680 self.max_identifier_chars
681 }
682
683 fn position(&self) -> Position {
685 Position::new(self.line, self.column, self.offset)
686 }
687
688 fn unget(&mut self, ch: char, pos: Position) {
690 self.putback = Some((ch, pos));
691 }
692
693 fn advance(&mut self) -> Option<char> {
695 if let Some((ch, pos)) = self.putback.take() {
697 self.line = pos.line;
699 self.column = pos.column + 1;
700 self.offset = pos.offset + ch.len_utf8() as u32;
701 return Some(ch);
702 }
703
704 let ch = self.chars.next()?;
705 self.offset += ch.len_utf8() as u32;
706 if ch == '\n' {
707 self.line += 1;
708 self.column = 1;
709 } else {
710 self.column += 1;
711 }
712 Some(ch)
713 }
714
715 fn peek(&mut self) -> Option<char> {
717 if let Some((ch, _)) = &self.putback {
719 return Some(*ch);
720 }
721 self.chars.peek().copied()
722 }
723
724 #[allow(dead_code)]
730 fn skip_whitespace(&mut self) {
731 while let Some(ch) = self.peek() {
732 if ch.is_whitespace() {
733 self.advance();
734 } else if ch == '-' {
735 let pos = self.position();
737 self.advance();
738 if self.peek() == Some('-') {
739 self.advance();
741 while let Some(c) = self.peek() {
742 if c == '\n' {
743 break;
744 }
745 self.advance();
746 }
747 } else {
748 self.line = pos.line;
751 self.column = pos.column;
752 self.offset = pos.offset;
753 break;
756 }
757 } else {
758 break;
759 }
760 }
761 }
762
763 pub fn peek_token(&mut self) -> Result<&Spanned, LexerError> {
765 if self.peeked.is_none() {
766 self.peeked = Some(self.next_token_internal()?);
767 }
768 Ok(self.peeked.as_ref().unwrap())
769 }
770
771 pub fn next_token(&mut self) -> Result<Spanned, LexerError> {
773 if let Some(tok) = self.peeked.take() {
774 return Ok(tok);
775 }
776 self.next_token_internal()
777 }
778
779 fn next_token_internal(&mut self) -> Result<Spanned, LexerError> {
781 self.skip_whitespace_simple();
782
783 let start = self.position();
784
785 let ch = match self.peek() {
786 Some(c) => c,
787 None => {
788 return Ok(Spanned::new(Token::Eof, start, start));
789 }
790 };
791
792 let token = match ch {
794 '\'' | '"' => self.scan_string()?,
796
797 '0'..='9' => self.scan_number()?,
799
800 'a'..='z' | 'A'..='Z' | '_' => self.scan_identifier()?,
802
803 '=' => {
805 self.advance();
806 if self.peek() == Some('>') {
809 self.advance();
810 Token::FatArrow
811 } else {
812 Token::Eq
813 }
814 }
815 '<' => self.scan_less_than()?,
816 '>' => self.scan_greater_than()?,
817 '!' => {
818 self.advance();
819 if self.peek() == Some('=') {
820 self.advance();
821 Token::Ne
822 } else {
823 return Err(LexerError::new("Expected '=' after '!'", start));
824 }
825 }
826 '+' => {
827 self.advance();
828 Token::Plus
829 }
830 '-' => self.scan_minus()?,
831 '*' => {
832 self.advance();
833 Token::Star
834 }
835 '/' => {
836 self.advance();
837 Token::Slash
838 }
839 '%' => {
840 self.advance();
841 Token::Percent
842 }
843 '(' => {
844 self.advance();
845 Token::LParen
846 }
847 ')' => {
848 self.advance();
849 Token::RParen
850 }
851 '[' => {
852 self.advance();
853 Token::LBracket
854 }
855 ']' => {
856 self.advance();
857 Token::RBracket
858 }
859 '{' => {
860 if self.looks_like_json_object_start() {
867 return self.scan_json_literal(start);
868 }
869 self.advance();
870 Token::LBrace
871 }
872 '}' => {
873 self.advance();
874 Token::RBrace
875 }
876 ',' => {
877 self.advance();
878 Token::Comma
879 }
880 '.' => self.scan_dot()?,
881 ':' => {
882 self.advance();
883 Token::Colon
884 }
885 ';' => {
886 self.advance();
887 Token::Semi
888 }
889 '$' => {
890 self.advance();
891 Token::Dollar
892 }
893 '?' => {
894 self.advance();
895 Token::Question
896 }
897 '|' => {
898 self.advance();
899 if self.peek() == Some('|') {
900 self.advance();
901 Token::DoublePipe
902 } else {
903 Token::Pipe
904 }
905 }
906 _ => {
907 return Err(LexerError::new(
908 format!("Unexpected character: '{}'", ch),
909 start,
910 ));
911 }
912 };
913
914 let end = self.position();
915 Ok(Spanned::new(token, start, end))
916 }
917
918 fn skip_whitespace_simple(&mut self) {
920 while let Some(ch) = self.peek() {
921 if ch.is_whitespace() {
922 self.advance();
923 } else if ch == '-' && self.input[self.offset as usize..].starts_with("--") {
924 self.advance();
925 self.advance();
926 while let Some(c) = self.peek() {
927 if c == '\n' {
928 break;
929 }
930 self.advance();
931 }
932 } else if ch == '/' && self.input[self.offset as usize..].starts_with("/*") {
933 self.advance();
934 self.advance();
935 while let Some(c) = self.peek() {
936 self.advance();
937 if c == '*' && self.peek() == Some('/') {
938 self.advance();
939 break;
940 }
941 }
942 } else {
943 break;
944 }
945 }
946 }
947
948 fn scan_string(&mut self) -> Result<Token, LexerError> {
950 let quote = self.advance().unwrap(); let start = self.position();
952 let mut value = String::new();
953
954 loop {
955 match self.peek() {
956 None => {
957 return Err(LexerError::new("Unterminated string", start));
958 }
959 Some(c) if c == quote => {
960 self.advance();
961 if self.peek() == Some(quote) {
963 self.advance();
964 value.push(quote);
965 } else {
966 break;
967 }
968 }
969 Some('\\') => {
970 self.advance();
971 match self.peek() {
972 Some('n') => {
973 self.advance();
974 value.push('\n');
975 }
976 Some('r') => {
977 self.advance();
978 value.push('\r');
979 }
980 Some('t') => {
981 self.advance();
982 value.push('\t');
983 }
984 Some('\\') => {
985 self.advance();
986 value.push('\\');
987 }
988 Some(c) if c == quote => {
989 self.advance();
990 value.push(quote);
991 }
992 Some(c) => {
993 value.push('\\');
995 value.push(c);
996 self.advance();
997 }
998 None => {
999 return Err(LexerError::new("Unterminated string", start));
1000 }
1001 }
1002 }
1003 Some(c) => {
1004 self.advance();
1005 value.push(c);
1006 }
1007 }
1008 }
1009
1010 Ok(Token::String(value))
1011 }
1012
1013 fn scan_number(&mut self) -> Result<Token, LexerError> {
1015 let mut value = String::new();
1016 let mut is_float = false;
1017
1018 while let Some(ch) = self.peek() {
1020 if ch.is_ascii_digit() {
1021 value.push(ch);
1022 self.advance();
1023 } else {
1024 break;
1025 }
1026 }
1027
1028 if self.peek() == Some('.') {
1030 let dot_pos = self.position();
1032 self.advance(); if self.peek() == Some('.') {
1035 self.unget('.', dot_pos);
1037 } else if self.peek().map(|c| c.is_ascii_digit()).unwrap_or(false) {
1039 is_float = true;
1040 value.push('.');
1041 while let Some(ch) = self.peek() {
1042 if ch.is_ascii_digit() {
1043 value.push(ch);
1044 self.advance();
1045 } else {
1046 break;
1047 }
1048 }
1049 } else {
1050 self.unget('.', dot_pos);
1052 }
1053 }
1054
1055 if self.peek() == Some('e') || self.peek() == Some('E') {
1057 is_float = true;
1058 value.push(self.advance().unwrap());
1059
1060 if self.peek() == Some('+') || self.peek() == Some('-') {
1061 value.push(self.advance().unwrap());
1062 }
1063
1064 while let Some(ch) = self.peek() {
1065 if ch.is_ascii_digit() {
1066 value.push(ch);
1067 self.advance();
1068 } else {
1069 break;
1070 }
1071 }
1072 }
1073
1074 if is_float {
1075 match value.parse::<f64>() {
1076 Ok(n) => Ok(Token::Float(n)),
1077 Err(_) => Err(LexerError::new(
1078 format!("Invalid float: {}", value),
1079 self.position(),
1080 )),
1081 }
1082 } else {
1083 match value.parse::<i64>() {
1084 Ok(n) => Ok(Token::Integer(n)),
1085 Err(_) => Err(LexerError::new(
1086 format!("Invalid integer: {}", value),
1087 self.position(),
1088 )),
1089 }
1090 }
1091 }
1092
1093 fn scan_identifier(&mut self) -> Result<Token, LexerError> {
1095 let start_pos = self.position();
1096 let mut value = String::new();
1097 let max = self.max_identifier_chars;
1098
1099 while let Some(ch) = self.peek() {
1100 if ch.is_alphanumeric() || ch == '_' {
1101 if value.chars().count() >= max {
1102 return Err(LexerError::with_limit(
1106 format!(
1107 "identifier exceeds maximum length (max_identifier_chars = {})",
1108 max
1109 ),
1110 start_pos,
1111 LexerLimitHit::IdentifierTooLong {
1112 limit_name: "max_identifier_chars",
1113 value: max,
1114 },
1115 ));
1116 }
1117 value.push(ch);
1118 self.advance();
1119 } else {
1120 break;
1121 }
1122 }
1123
1124 let token = match value.to_uppercase().as_str() {
1126 "SELECT" => Token::Select,
1127 "FROM" => Token::From,
1128 "WHERE" => Token::Where,
1129 "AND" => Token::And,
1130 "OR" => Token::Or,
1131 "NOT" => Token::Not,
1132 "MATCH" => Token::Match,
1133 "RETURN" => Token::Return,
1134 "JOIN" => Token::Join,
1135 "GRAPH" => Token::Graph,
1136 "PATH" => Token::Path,
1137 "TO" => Token::To,
1138 "VIA" => Token::Via,
1139 "ON" => Token::On,
1140 "AS" => Token::As,
1141 "IS" => Token::Is,
1142 "NULL" => Token::Null,
1143 "BETWEEN" => Token::Between,
1144 "LIKE" => Token::Like,
1145 "IN" => Token::In,
1146 "ORDER" => Token::Order,
1147 "BY" => Token::By,
1148 "ASC" => Token::Asc,
1149 "DESC" => Token::Desc,
1150 "NULLS" => Token::Nulls,
1151 "FIRST" => Token::First,
1152 "LAST" => Token::Last,
1153 "LIMIT" => Token::Limit,
1154 "OFFSET" => Token::Offset,
1155 "INNER" => Token::Inner,
1156 "LEFT" => Token::Left,
1157 "RIGHT" => Token::Right,
1158 "OUTER" => Token::Outer,
1159 "FULL" => Token::Full,
1160 "CROSS" => Token::Cross,
1161 "STARTS" => Token::Starts,
1162 "ENDS" => Token::Ends,
1163 "WITH" => Token::With,
1164 "CONTAINS" => Token::Contains,
1165 "TRUE" => Token::True,
1166 "FALSE" => Token::False,
1167 "ENRICH" => Token::Enrich,
1168 "GROUP" => Token::Group,
1169 "COUNT" => Token::Count,
1170 "SUM" => Token::Sum,
1171 "AVG" => Token::Avg,
1172 "MIN" => Token::Min,
1173 "MAX" => Token::Max,
1174 "DISTINCT" => Token::Distinct,
1175 "VECTOR" => Token::Vector,
1176 "SEARCH" => Token::Search,
1177 "SIMILAR" => Token::Similar,
1178 "COLLECTION" => Token::Collection,
1179 "METRIC" => Token::Metric,
1180 "THRESHOLD" => Token::Threshold,
1181 "K" => Token::K,
1182 "HYBRID" => Token::Hybrid,
1183 "FUSION" => Token::Fusion,
1184 "RERANK" => Token::Rerank,
1185 "RRF" => Token::Rrf,
1186 "INTERSECTION" => Token::Intersection,
1187 "UNION" => Token::Union,
1188 "RECURSIVE" => Token::Recursive,
1189 "ALL" => Token::All,
1190 "WEIGHT" => Token::Weight,
1191 "L2" => Token::L2,
1192 "COSINE" => Token::Cosine,
1193 "INNER_PRODUCT" | "INNERPRODUCT" => Token::InnerProduct,
1194 "INCLUDE" => Token::Include,
1195 "METADATA" => Token::Metadata,
1196 "VECTORS" => Token::Vectors,
1197 "EXPLAIN" => Token::Explain,
1198 "FOR" => Token::For,
1199 "FORMAT" => Token::Format,
1200 "JSON" => Token::Json,
1201 "INSERT" => Token::Insert,
1202 "INTO" => Token::Into,
1203 "VALUES" => Token::Values,
1204 "UPDATE" => Token::Update,
1205 "SET" => Token::Set,
1206 "DELETE" => Token::Delete,
1207 "TRUNCATE" => Token::Truncate,
1208 "CREATE" => Token::Create,
1209 "TABLE" => Token::Table,
1210 "DROP" => Token::Drop,
1211 "ALTER" => Token::Alter,
1212 "ADD" => Token::Add,
1213 "COLUMN" => Token::Column,
1214 "PRIMARY" => Token::Primary,
1215 "KEY" => Token::Key,
1216 "DEFAULT" => Token::Default,
1217 "COMPRESS" => Token::Compress,
1218 "INDEX" => Token::Index,
1219 "UNIQUE" => Token::Unique,
1220 "IF" => Token::If,
1221 "EXISTS" => Token::Exists,
1222 "RETURNING" => Token::Returning,
1223 "CASCADE" => Token::Cascade,
1224 "RENAME" => Token::Rename,
1225 "USING" => Token::Using,
1226 "NODE" => Token::Node,
1227 "EDGE" => Token::Edge,
1228 "DOCUMENT" => Token::Document,
1229 "KV" => Token::Kv,
1230 "TIMESERIES" => Token::Timeseries,
1231 "RETENTION" => Token::Retention,
1232 "QUEUE" => Token::Queue,
1233 "TREE" => Token::Tree,
1234 "PUSH" => Token::Push,
1235 "POP" => Token::Pop,
1236 "PEEK" => Token::Peek,
1237 "PURGE" => Token::Purge,
1238 "ACK" => Token::Ack,
1239 "NACK" => Token::Nack,
1240 "PRIORITY" => Token::Priority,
1241 "LPUSH" => Token::Ident("LPUSH".to_string()),
1242 "RPUSH" => Token::Ident("RPUSH".to_string()),
1243 "LPOP" => Token::Ident("LPOP".to_string()),
1244 "RPOP" => Token::Ident("RPOP".to_string()),
1245 "NEIGHBORHOOD" => Token::Neighborhood,
1246 "SHORTEST_PATH" | "SHORTESTPATH" => Token::ShortestPath,
1247 "CENTRALITY" => Token::Centrality,
1248 "COMMUNITY" => Token::Community,
1249 "COMPONENTS" => Token::Components,
1250 "CYCLES" => Token::Cycles,
1251 "TRAVERSE" => Token::Traverse,
1252 "DEPTH" => Token::Depth,
1253 "DIRECTION" => Token::Direction,
1254 "ALGORITHM" => Token::Algorithm,
1255 "STRATEGY" => Token::Strategy,
1256 "MAX_ITERATIONS" | "MAXITERATIONS" => Token::MaxIterations,
1257 "MAX_LENGTH" | "MAXLENGTH" => Token::MaxLength,
1258 "MODE" => Token::Mode,
1259 "CLUSTERING" => Token::Clustering,
1260 "TOPOLOGICAL_SORT" | "TOPOLOGICALSORT" => Token::TopologicalSort,
1261 "PROPERTIES" => Token::Properties,
1262 "TEXT" => Token::Text,
1263 "FUZZY" => Token::Fuzzy,
1264 "MIN_SCORE" | "MINSCORE" => Token::MinScore,
1265 "BEGIN" => Token::Begin,
1266 "COMMIT" => Token::Commit,
1267 "ROLLBACK" => Token::Rollback,
1268 "SAVEPOINT" => Token::Savepoint,
1269 "RELEASE" => Token::Release,
1270 "START" => Token::Start,
1271 "TRANSACTION" => Token::Transaction,
1272 "WORK" => Token::Work,
1273 "VACUUM" => Token::Vacuum,
1274 "ANALYZE" => Token::Analyze,
1275 "SCHEMA" => Token::Schema,
1276 "SEQUENCE" => Token::Sequence,
1277 "INCREMENT" => Token::Increment,
1278 "COPY" => Token::Copy,
1279 "HEADER" => Token::Header,
1280 "DELIMITER" => Token::Delimiter,
1281 "VIEW" => Token::View,
1282 "MATERIALIZED" => Token::Materialized,
1283 "REFRESH" => Token::Refresh,
1284 "PARTITION" => Token::Partition,
1285 "RANGE" => Token::Range,
1286 "LIST" => Token::List,
1287 "HASH" => Token::Hash,
1288 "ATTACH" => Token::Attach,
1289 "DETACH" => Token::Detach,
1290 "OF" => Token::Of,
1291 "POLICY" => Token::Policy,
1292 "ENABLE" => Token::Enable,
1293 "DISABLE" => Token::Disable,
1294 "SECURITY" => Token::Security,
1295 "ROW" => Token::Row,
1296 "LEVEL" => Token::Level,
1297 "FOREIGN" => Token::Foreign,
1298 "SERVER" => Token::Server,
1299 "WRAPPER" => Token::Wrapper,
1300 "OPTIONS" => Token::Options,
1301 "DATA" => Token::Data,
1302 "SESSIONIZE" => Token::Sessionize,
1303 "GAP" => Token::Gap,
1304 "OVER" => Token::Over,
1305 "ROWS" => Token::Rows,
1306 "PRECEDING" => Token::Preceding,
1307 "FOLLOWING" => Token::Following,
1308 "UNBOUNDED" => Token::Unbounded,
1309 "CURRENT" => Token::Current,
1310 _ => Token::Ident(value),
1311 };
1312
1313 Ok(token)
1314 }
1315
1316 fn scan_less_than(&mut self) -> Result<Token, LexerError> {
1318 self.advance(); match self.peek() {
1320 Some('=') => {
1321 self.advance();
1322 Ok(Token::Le)
1323 }
1324 Some('>') => {
1325 self.advance();
1326 Ok(Token::Ne)
1327 }
1328 Some('-') => {
1329 self.advance();
1330 Ok(Token::ArrowLeft)
1331 }
1332 _ => Ok(Token::Lt),
1333 }
1334 }
1335
1336 fn scan_greater_than(&mut self) -> Result<Token, LexerError> {
1338 self.advance(); if self.peek() == Some('=') {
1340 self.advance();
1341 Ok(Token::Ge)
1342 } else {
1343 Ok(Token::Gt)
1344 }
1345 }
1346
1347 fn scan_minus(&mut self) -> Result<Token, LexerError> {
1349 self.advance(); match self.peek() {
1351 Some('>') => {
1352 self.advance();
1353 Ok(Token::Arrow)
1354 }
1355 Some('-') => {
1356 self.advance();
1358 while let Some(c) = self.peek() {
1359 if c == '\n' {
1360 break;
1361 }
1362 self.advance();
1363 }
1364 self.skip_whitespace_simple();
1366 if self.peek().is_none() {
1367 Ok(Token::Eof)
1368 } else {
1369 let next = self.next_token_internal()?;
1370 Ok(next.token)
1371 }
1372 }
1373 _ => Ok(Token::Dash),
1374 }
1375 }
1376
1377 fn scan_dot(&mut self) -> Result<Token, LexerError> {
1379 self.advance(); if self.peek() == Some('.') {
1381 self.advance();
1382 Ok(Token::DotDot)
1383 } else {
1384 Ok(Token::Dot)
1385 }
1386 }
1387
1388 fn looks_like_json_object_start(&self) -> bool {
1393 let bytes = self.input.as_bytes();
1394 let mut i = self.offset as usize;
1395 debug_assert!(bytes.get(i) == Some(&b'{'));
1397 i += 1;
1398 while i < bytes.len() {
1399 match bytes[i] {
1400 b' ' | b'\t' | b'\n' | b'\r' => i += 1,
1401 b'"' | b'}' => return true,
1402 _ => return false,
1403 }
1404 }
1405 false
1406 }
1407
1408 fn scan_json_literal(&mut self, start: Position) -> Result<Spanned, LexerError> {
1425 let start_offset = self.offset as usize;
1426 self.advance();
1428 let mut depth: u32 = 1;
1429 let mut in_string = false;
1430 let mut escape = false;
1431 loop {
1432 let ch = match self.peek() {
1433 Some(c) => c,
1434 None => {
1435 return Err(LexerError::new(
1436 format!(
1437 "unterminated JSON object literal (started at offset {})",
1438 start.offset
1439 ),
1440 self.position(),
1441 ));
1442 }
1443 };
1444
1445 let scanned_bytes = self.offset as usize - start_offset;
1447 if scanned_bytes > JSON_LITERAL_MAX_BYTES {
1448 return Err(LexerError::new(
1449 format!(
1450 "JSON object literal exceeds JSON_LITERAL_MAX_BYTES ({} bytes)",
1451 JSON_LITERAL_MAX_BYTES
1452 ),
1453 start,
1454 ));
1455 }
1456
1457 self.advance();
1458
1459 if escape {
1460 escape = false;
1461 continue;
1462 }
1463
1464 if in_string {
1465 match ch {
1466 '\\' => escape = true,
1467 '"' => in_string = false,
1468 _ => {}
1469 }
1470 continue;
1471 }
1472
1473 match ch {
1474 '"' => in_string = true,
1475 '{' => depth += 1,
1476 '}' => {
1477 depth -= 1;
1478 if depth == 0 {
1479 let end = self.position();
1480 let end_offset = self.offset as usize;
1481 if end_offset - start_offset > JSON_LITERAL_MAX_BYTES {
1483 return Err(LexerError::new(
1484 format!(
1485 "JSON object literal exceeds JSON_LITERAL_MAX_BYTES ({} bytes)",
1486 JSON_LITERAL_MAX_BYTES
1487 ),
1488 start,
1489 ));
1490 }
1491 let raw = self.input[start_offset..end_offset].to_string();
1492 return Ok(Spanned::new(Token::JsonLiteral(raw), start, end));
1493 }
1494 }
1495 _ => {}
1496 }
1497 }
1498 }
1499
1500 pub fn tokenize(&mut self) -> Result<Vec<Spanned>, LexerError> {
1502 let mut tokens = Vec::new();
1503 loop {
1504 let tok = self.next_token()?;
1505 let is_eof = tok.token == Token::Eof;
1506 tokens.push(tok);
1507 if is_eof {
1508 break;
1509 }
1510 }
1511 Ok(tokens)
1512 }
1513}
1514
1515#[cfg(test)]
1520mod tests {
1521 use super::*;
1522
1523 fn tokenize(input: &str) -> Vec<Token> {
1524 let mut lexer = Lexer::new(input);
1525 lexer
1526 .tokenize()
1527 .unwrap()
1528 .into_iter()
1529 .map(|s| s.token)
1530 .collect()
1531 }
1532
1533 #[test]
1534 fn test_keywords() {
1535 let tokens = tokenize("SELECT FROM WHERE AND OR NOT");
1536 assert_eq!(
1537 tokens,
1538 vec![
1539 Token::Select,
1540 Token::From,
1541 Token::Where,
1542 Token::And,
1543 Token::Or,
1544 Token::Not,
1545 Token::Eof
1546 ]
1547 );
1548 }
1549
1550 #[test]
1551 fn test_identifiers() {
1552 let tokens = tokenize("hosts users ip_address");
1553 assert_eq!(
1554 tokens,
1555 vec![
1556 Token::Ident("hosts".into()),
1557 Token::Ident("users".into()),
1558 Token::Ident("ip_address".into()),
1559 Token::Eof
1560 ]
1561 );
1562 }
1563
1564 #[test]
1565 fn test_numbers() {
1566 let tokens = tokenize("42 2.5 1e10 2.5e-3");
1567 assert_eq!(
1568 tokens,
1569 vec![
1570 Token::Integer(42),
1571 Token::Float(2.5),
1572 Token::Float(1e10),
1573 Token::Float(2.5e-3),
1574 Token::Eof
1575 ]
1576 );
1577 }
1578
1579 #[test]
1580 fn test_strings() {
1581 let tokens = tokenize("'hello' \"world\" 'it''s'");
1582 assert_eq!(
1583 tokens,
1584 vec![
1585 Token::String("hello".into()),
1586 Token::String("world".into()),
1587 Token::String("it's".into()),
1588 Token::Eof
1589 ]
1590 );
1591 }
1592
1593 #[test]
1594 fn test_operators() {
1595 let tokens = tokenize("= <> < <= > >= != + - * /");
1596 assert_eq!(
1597 tokens,
1598 vec![
1599 Token::Eq,
1600 Token::Ne,
1601 Token::Lt,
1602 Token::Le,
1603 Token::Gt,
1604 Token::Ge,
1605 Token::Ne,
1606 Token::Plus,
1607 Token::Dash,
1608 Token::Star,
1609 Token::Slash,
1610 Token::Eof
1611 ]
1612 );
1613 }
1614
1615 #[test]
1616 fn test_delimiters() {
1617 let tokens = tokenize("( ) [ ] { a } , . : ;");
1622 assert_eq!(
1623 tokens,
1624 vec![
1625 Token::LParen,
1626 Token::RParen,
1627 Token::LBracket,
1628 Token::RBracket,
1629 Token::LBrace,
1630 Token::Ident("a".into()),
1631 Token::RBrace,
1632 Token::Comma,
1633 Token::Dot,
1634 Token::Colon,
1635 Token::Semi,
1636 Token::Eof
1637 ]
1638 );
1639 }
1640
1641 #[test]
1642 fn test_json_literal_empty_object() {
1643 let tokens = tokenize("{ }");
1644 assert_eq!(tokens, vec![Token::JsonLiteral("{ }".into()), Token::Eof]);
1645 }
1646
1647 #[test]
1648 fn test_json_literal_simple() {
1649 let tokens = tokenize(r#"{"a":1}"#);
1650 assert_eq!(
1651 tokens,
1652 vec![Token::JsonLiteral(r#"{"a":1}"#.into()), Token::Eof]
1653 );
1654 }
1655
1656 #[test]
1657 fn test_json_literal_nested() {
1658 let raw = r#"{"a":{"b":[1,2,{"c":"}"}]}}"#;
1659 let tokens = tokenize(raw);
1660 assert_eq!(tokens, vec![Token::JsonLiteral(raw.into()), Token::Eof]);
1661 }
1662
1663 #[test]
1664 fn test_json_literal_escaped_quote_in_string() {
1665 let raw = r#"{"path":"O\"Brien}"}"#;
1667 let tokens = tokenize(raw);
1668 assert_eq!(tokens, vec![Token::JsonLiteral(raw.into()), Token::Eof]);
1669 }
1670
1671 #[test]
1672 fn test_json_literal_unbalanced_eof() {
1673 let mut lexer = Lexer::new(r#"{"a":1"#);
1674 let err = lexer.tokenize().expect_err("expected unterminated error");
1675 assert!(
1676 err.message.contains("unterminated JSON object literal"),
1677 "got: {}",
1678 err.message
1679 );
1680 }
1681
1682 #[test]
1683 fn test_json_literal_property_bag_compatible() {
1684 let tokens = tokenize("{name: 'value'}");
1687 assert_eq!(tokens[0], Token::LBrace);
1688 assert_eq!(*tokens.last().unwrap(), Token::Eof);
1689 }
1690
1691 #[test]
1692 fn test_graph_syntax() {
1693 let tokens = tokenize("-> <- - ..");
1694 assert_eq!(
1695 tokens,
1696 vec![
1697 Token::Arrow,
1698 Token::ArrowLeft,
1699 Token::Dash,
1700 Token::DotDot,
1701 Token::Eof
1702 ]
1703 );
1704 }
1705
1706 #[test]
1707 fn test_table_query() {
1708 let tokens = tokenize("SELECT ip, hostname FROM hosts WHERE os = 'Linux' LIMIT 10");
1709 assert_eq!(
1710 tokens,
1711 vec![
1712 Token::Select,
1713 Token::Ident("ip".into()),
1714 Token::Comma,
1715 Token::Ident("hostname".into()),
1716 Token::From,
1717 Token::Ident("hosts".into()),
1718 Token::Where,
1719 Token::Ident("os".into()),
1720 Token::Eq,
1721 Token::String("Linux".into()),
1722 Token::Limit,
1723 Token::Integer(10),
1724 Token::Eof
1725 ]
1726 );
1727 }
1728
1729 #[test]
1730 fn test_graph_query() {
1731 let tokens = tokenize("MATCH (h:Host)-[:HAS_SERVICE]->(s:Service) RETURN h, s");
1732 assert_eq!(
1733 tokens,
1734 vec![
1735 Token::Match,
1736 Token::LParen,
1737 Token::Ident("h".into()),
1738 Token::Colon,
1739 Token::Ident("Host".into()),
1740 Token::RParen,
1741 Token::Dash,
1742 Token::LBracket,
1743 Token::Colon,
1744 Token::Ident("HAS_SERVICE".into()),
1745 Token::RBracket,
1746 Token::Arrow,
1747 Token::LParen,
1748 Token::Ident("s".into()),
1749 Token::Colon,
1750 Token::Ident("Service".into()),
1751 Token::RParen,
1752 Token::Return,
1753 Token::Ident("h".into()),
1754 Token::Comma,
1755 Token::Ident("s".into()),
1756 Token::Eof
1757 ]
1758 );
1759 }
1760
1761 #[test]
1762 fn test_join_query() {
1763 let tokens = tokenize("FROM hosts h JOIN GRAPH (h)-[:HAS_VULN]->(v) ON h.ip = v.id");
1764 assert_eq!(
1765 tokens,
1766 vec![
1767 Token::From,
1768 Token::Ident("hosts".into()),
1769 Token::Ident("h".into()),
1770 Token::Join,
1771 Token::Graph,
1772 Token::LParen,
1773 Token::Ident("h".into()),
1774 Token::RParen,
1775 Token::Dash,
1776 Token::LBracket,
1777 Token::Colon,
1778 Token::Ident("HAS_VULN".into()),
1779 Token::RBracket,
1780 Token::Arrow,
1781 Token::LParen,
1782 Token::Ident("v".into()),
1783 Token::RParen,
1784 Token::On,
1785 Token::Ident("h".into()),
1786 Token::Dot,
1787 Token::Ident("ip".into()),
1788 Token::Eq,
1789 Token::Ident("v".into()),
1790 Token::Dot,
1791 Token::Ident("id".into()),
1792 Token::Eof
1793 ]
1794 );
1795 }
1796
1797 #[test]
1798 fn test_path_query() {
1799 let tokens = tokenize("PATH FROM host('192.168.1.1') TO host('10.0.0.1') VIA [:AUTH]");
1800 assert_eq!(
1801 tokens,
1802 vec![
1803 Token::Path,
1804 Token::From,
1805 Token::Ident("host".into()),
1806 Token::LParen,
1807 Token::String("192.168.1.1".into()),
1808 Token::RParen,
1809 Token::To,
1810 Token::Ident("host".into()),
1811 Token::LParen,
1812 Token::String("10.0.0.1".into()),
1813 Token::RParen,
1814 Token::Via,
1815 Token::LBracket,
1816 Token::Colon,
1817 Token::Ident("AUTH".into()),
1818 Token::RBracket,
1819 Token::Eof
1820 ]
1821 );
1822 }
1823
1824 #[test]
1825 fn test_variable_length_pattern() {
1826 let tokens = tokenize("(a)-[*1..5]->(b)");
1827 assert_eq!(
1828 tokens,
1829 vec![
1830 Token::LParen,
1831 Token::Ident("a".into()),
1832 Token::RParen,
1833 Token::Dash,
1834 Token::LBracket,
1835 Token::Star,
1836 Token::Integer(1),
1837 Token::DotDot,
1838 Token::Integer(5),
1839 Token::RBracket,
1840 Token::Arrow,
1841 Token::LParen,
1842 Token::Ident("b".into()),
1843 Token::RParen,
1844 Token::Eof
1845 ]
1846 );
1847 }
1848
1849 #[test]
1850 fn test_case_insensitive_keywords() {
1851 let tokens = tokenize("select FROM Where AND");
1852 assert_eq!(
1853 tokens,
1854 vec![
1855 Token::Select,
1856 Token::From,
1857 Token::Where,
1858 Token::And,
1859 Token::Eof
1860 ]
1861 );
1862 }
1863
1864 #[test]
1865 fn test_comments() {
1866 let tokens = tokenize("SELECT -- this is a comment\nip FROM hosts");
1867 assert_eq!(
1868 tokens,
1869 vec![
1870 Token::Select,
1871 Token::Ident("ip".into()),
1872 Token::From,
1873 Token::Ident("hosts".into()),
1874 Token::Eof
1875 ]
1876 );
1877 }
1878
1879 #[test]
1880 fn test_escaped_strings() {
1881 let tokens = tokenize(r"'hello\nworld' 'tab\there'");
1882 assert_eq!(
1883 tokens,
1884 vec![
1885 Token::String("hello\nworld".into()),
1886 Token::String("tab\there".into()),
1887 Token::Eof
1888 ]
1889 );
1890 }
1891
1892 #[test]
1893 fn test_keyword_matrix_and_alias_spellings() {
1894 let cases = [
1895 ("SELECT", Token::Select),
1896 ("FROM", Token::From),
1897 ("WHERE", Token::Where),
1898 ("AND", Token::And),
1899 ("OR", Token::Or),
1900 ("NOT", Token::Not),
1901 ("MATCH", Token::Match),
1902 ("RETURN", Token::Return),
1903 ("JOIN", Token::Join),
1904 ("GRAPH", Token::Graph),
1905 ("PATH", Token::Path),
1906 ("TO", Token::To),
1907 ("VIA", Token::Via),
1908 ("ON", Token::On),
1909 ("AS", Token::As),
1910 ("IS", Token::Is),
1911 ("NULL", Token::Null),
1912 ("BETWEEN", Token::Between),
1913 ("LIKE", Token::Like),
1914 ("IN", Token::In),
1915 ("ORDER", Token::Order),
1916 ("BY", Token::By),
1917 ("ASC", Token::Asc),
1918 ("DESC", Token::Desc),
1919 ("NULLS", Token::Nulls),
1920 ("FIRST", Token::First),
1921 ("LAST", Token::Last),
1922 ("LIMIT", Token::Limit),
1923 ("OFFSET", Token::Offset),
1924 ("INNER", Token::Inner),
1925 ("LEFT", Token::Left),
1926 ("RIGHT", Token::Right),
1927 ("OUTER", Token::Outer),
1928 ("FULL", Token::Full),
1929 ("CROSS", Token::Cross),
1930 ("STARTS", Token::Starts),
1931 ("ENDS", Token::Ends),
1932 ("WITH", Token::With),
1933 ("CONTAINS", Token::Contains),
1934 ("TRUE", Token::True),
1935 ("FALSE", Token::False),
1936 ("ENRICH", Token::Enrich),
1937 ("GROUP", Token::Group),
1938 ("COUNT", Token::Count),
1939 ("SUM", Token::Sum),
1940 ("AVG", Token::Avg),
1941 ("MIN", Token::Min),
1942 ("MAX", Token::Max),
1943 ("DISTINCT", Token::Distinct),
1944 ("VECTOR", Token::Vector),
1945 ("SEARCH", Token::Search),
1946 ("SIMILAR", Token::Similar),
1947 ("COLLECTION", Token::Collection),
1948 ("METRIC", Token::Metric),
1949 ("THRESHOLD", Token::Threshold),
1950 ("K", Token::K),
1951 ("HYBRID", Token::Hybrid),
1952 ("FUSION", Token::Fusion),
1953 ("RERANK", Token::Rerank),
1954 ("RRF", Token::Rrf),
1955 ("INTERSECTION", Token::Intersection),
1956 ("UNION", Token::Union),
1957 ("RECURSIVE", Token::Recursive),
1958 ("ALL", Token::All),
1959 ("WEIGHT", Token::Weight),
1960 ("L2", Token::L2),
1961 ("COSINE", Token::Cosine),
1962 ("INNER_PRODUCT", Token::InnerProduct),
1963 ("INNERPRODUCT", Token::InnerProduct),
1964 ("INCLUDE", Token::Include),
1965 ("METADATA", Token::Metadata),
1966 ("VECTORS", Token::Vectors),
1967 ("EXPLAIN", Token::Explain),
1968 ("FOR", Token::For),
1969 ("FORMAT", Token::Format),
1970 ("JSON", Token::Json),
1971 ("INSERT", Token::Insert),
1972 ("INTO", Token::Into),
1973 ("VALUES", Token::Values),
1974 ("UPDATE", Token::Update),
1975 ("SET", Token::Set),
1976 ("DELETE", Token::Delete),
1977 ("TRUNCATE", Token::Truncate),
1978 ("CREATE", Token::Create),
1979 ("TABLE", Token::Table),
1980 ("DROP", Token::Drop),
1981 ("ALTER", Token::Alter),
1982 ("ADD", Token::Add),
1983 ("COLUMN", Token::Column),
1984 ("PRIMARY", Token::Primary),
1985 ("KEY", Token::Key),
1986 ("DEFAULT", Token::Default),
1987 ("COMPRESS", Token::Compress),
1988 ("INDEX", Token::Index),
1989 ("UNIQUE", Token::Unique),
1990 ("IF", Token::If),
1991 ("EXISTS", Token::Exists),
1992 ("RETURNING", Token::Returning),
1993 ("CASCADE", Token::Cascade),
1994 ("RENAME", Token::Rename),
1995 ("USING", Token::Using),
1996 ("NODE", Token::Node),
1997 ("EDGE", Token::Edge),
1998 ("DOCUMENT", Token::Document),
1999 ("KV", Token::Kv),
2000 ("TIMESERIES", Token::Timeseries),
2001 ("RETENTION", Token::Retention),
2002 ("QUEUE", Token::Queue),
2003 ("TREE", Token::Tree),
2004 ("PUSH", Token::Push),
2005 ("POP", Token::Pop),
2006 ("PEEK", Token::Peek),
2007 ("PURGE", Token::Purge),
2008 ("ACK", Token::Ack),
2009 ("NACK", Token::Nack),
2010 ("PRIORITY", Token::Priority),
2011 ("LPUSH", Token::Ident("LPUSH".into())),
2012 ("RPUSH", Token::Ident("RPUSH".into())),
2013 ("LPOP", Token::Ident("LPOP".into())),
2014 ("RPOP", Token::Ident("RPOP".into())),
2015 ("NEIGHBORHOOD", Token::Neighborhood),
2016 ("SHORTEST_PATH", Token::ShortestPath),
2017 ("SHORTESTPATH", Token::ShortestPath),
2018 ("CENTRALITY", Token::Centrality),
2019 ("COMMUNITY", Token::Community),
2020 ("COMPONENTS", Token::Components),
2021 ("CYCLES", Token::Cycles),
2022 ("TRAVERSE", Token::Traverse),
2023 ("DEPTH", Token::Depth),
2024 ("DIRECTION", Token::Direction),
2025 ("ALGORITHM", Token::Algorithm),
2026 ("STRATEGY", Token::Strategy),
2027 ("MAX_ITERATIONS", Token::MaxIterations),
2028 ("MAXITERATIONS", Token::MaxIterations),
2029 ("MAX_LENGTH", Token::MaxLength),
2030 ("MAXLENGTH", Token::MaxLength),
2031 ("MODE", Token::Mode),
2032 ("CLUSTERING", Token::Clustering),
2033 ("TOPOLOGICAL_SORT", Token::TopologicalSort),
2034 ("TOPOLOGICALSORT", Token::TopologicalSort),
2035 ("PROPERTIES", Token::Properties),
2036 ("TEXT", Token::Text),
2037 ("FUZZY", Token::Fuzzy),
2038 ("MIN_SCORE", Token::MinScore),
2039 ("MINSCORE", Token::MinScore),
2040 ("BEGIN", Token::Begin),
2041 ("COMMIT", Token::Commit),
2042 ("ROLLBACK", Token::Rollback),
2043 ("SAVEPOINT", Token::Savepoint),
2044 ("RELEASE", Token::Release),
2045 ("START", Token::Start),
2046 ("TRANSACTION", Token::Transaction),
2047 ("WORK", Token::Work),
2048 ("VACUUM", Token::Vacuum),
2049 ("ANALYZE", Token::Analyze),
2050 ("SCHEMA", Token::Schema),
2051 ("SEQUENCE", Token::Sequence),
2052 ("INCREMENT", Token::Increment),
2053 ("COPY", Token::Copy),
2054 ("HEADER", Token::Header),
2055 ("DELIMITER", Token::Delimiter),
2056 ("VIEW", Token::View),
2057 ("MATERIALIZED", Token::Materialized),
2058 ("REFRESH", Token::Refresh),
2059 ("PARTITION", Token::Partition),
2060 ("RANGE", Token::Range),
2061 ("LIST", Token::List),
2062 ("HASH", Token::Hash),
2063 ("ATTACH", Token::Attach),
2064 ("DETACH", Token::Detach),
2065 ("OF", Token::Of),
2066 ("POLICY", Token::Policy),
2067 ("ENABLE", Token::Enable),
2068 ("DISABLE", Token::Disable),
2069 ("SECURITY", Token::Security),
2070 ("ROW", Token::Row),
2071 ("LEVEL", Token::Level),
2072 ("FOREIGN", Token::Foreign),
2073 ("SERVER", Token::Server),
2074 ("WRAPPER", Token::Wrapper),
2075 ("OPTIONS", Token::Options),
2076 ("DATA", Token::Data),
2077 ("plain_ident", Token::Ident("plain_ident".into())),
2078 ];
2079
2080 for (input, expected) in cases {
2081 let tokens = tokenize(input);
2082 assert_eq!(tokens, vec![expected, Token::Eof], "{input}");
2083 }
2084 }
2085
2086 #[test]
2087 fn test_display_all_token_variants() {
2088 let cases = [
2089 (Token::Select, "SELECT"),
2090 (Token::From, "FROM"),
2091 (Token::Where, "WHERE"),
2092 (Token::And, "AND"),
2093 (Token::Or, "OR"),
2094 (Token::Not, "NOT"),
2095 (Token::Match, "MATCH"),
2096 (Token::Return, "RETURN"),
2097 (Token::Join, "JOIN"),
2098 (Token::Graph, "GRAPH"),
2099 (Token::Path, "PATH"),
2100 (Token::To, "TO"),
2101 (Token::Via, "VIA"),
2102 (Token::On, "ON"),
2103 (Token::As, "AS"),
2104 (Token::Is, "IS"),
2105 (Token::Null, "NULL"),
2106 (Token::Between, "BETWEEN"),
2107 (Token::Like, "LIKE"),
2108 (Token::In, "IN"),
2109 (Token::Order, "ORDER"),
2110 (Token::By, "BY"),
2111 (Token::Asc, "ASC"),
2112 (Token::Desc, "DESC"),
2113 (Token::Nulls, "NULLS"),
2114 (Token::First, "FIRST"),
2115 (Token::Last, "LAST"),
2116 (Token::Limit, "LIMIT"),
2117 (Token::Offset, "OFFSET"),
2118 (Token::Inner, "INNER"),
2119 (Token::Left, "LEFT"),
2120 (Token::Right, "RIGHT"),
2121 (Token::Outer, "OUTER"),
2122 (Token::Full, "FULL"),
2123 (Token::Cross, "CROSS"),
2124 (Token::Starts, "STARTS"),
2125 (Token::Ends, "ENDS"),
2126 (Token::With, "WITH"),
2127 (Token::Contains, "CONTAINS"),
2128 (Token::True, "TRUE"),
2129 (Token::False, "FALSE"),
2130 (Token::Enrich, "ENRICH"),
2131 (Token::Group, "GROUP"),
2132 (Token::Count, "COUNT"),
2133 (Token::Sum, "SUM"),
2134 (Token::Avg, "AVG"),
2135 (Token::Min, "MIN"),
2136 (Token::Max, "MAX"),
2137 (Token::Distinct, "DISTINCT"),
2138 (Token::Vector, "VECTOR"),
2139 (Token::Search, "SEARCH"),
2140 (Token::Similar, "SIMILAR"),
2141 (Token::Collection, "COLLECTION"),
2142 (Token::Metric, "METRIC"),
2143 (Token::Threshold, "THRESHOLD"),
2144 (Token::K, "K"),
2145 (Token::Hybrid, "HYBRID"),
2146 (Token::Fusion, "FUSION"),
2147 (Token::Rerank, "RERANK"),
2148 (Token::Rrf, "RRF"),
2149 (Token::Intersection, "INTERSECTION"),
2150 (Token::Union, "UNION"),
2151 (Token::Recursive, "RECURSIVE"),
2152 (Token::All, "ALL"),
2153 (Token::Weight, "WEIGHT"),
2154 (Token::L2, "L2"),
2155 (Token::Cosine, "COSINE"),
2156 (Token::InnerProduct, "INNER_PRODUCT"),
2157 (Token::Include, "INCLUDE"),
2158 (Token::Metadata, "METADATA"),
2159 (Token::Vectors, "VECTORS"),
2160 (Token::Explain, "EXPLAIN"),
2161 (Token::For, "FOR"),
2162 (Token::Format, "FORMAT"),
2163 (Token::Json, "JSON"),
2164 (Token::Insert, "INSERT"),
2165 (Token::Into, "INTO"),
2166 (Token::Values, "VALUES"),
2167 (Token::Update, "UPDATE"),
2168 (Token::Set, "SET"),
2169 (Token::Delete, "DELETE"),
2170 (Token::Truncate, "TRUNCATE"),
2171 (Token::Create, "CREATE"),
2172 (Token::Table, "TABLE"),
2173 (Token::Drop, "DROP"),
2174 (Token::Alter, "ALTER"),
2175 (Token::Add, "ADD"),
2176 (Token::Column, "COLUMN"),
2177 (Token::Primary, "PRIMARY"),
2178 (Token::Key, "KEY"),
2179 (Token::Default, "DEFAULT"),
2180 (Token::Compress, "COMPRESS"),
2181 (Token::Index, "INDEX"),
2182 (Token::Unique, "UNIQUE"),
2183 (Token::If, "IF"),
2184 (Token::Exists, "EXISTS"),
2185 (Token::Returning, "RETURNING"),
2186 (Token::Cascade, "CASCADE"),
2187 (Token::Rename, "RENAME"),
2188 (Token::Using, "USING"),
2189 (Token::Node, "NODE"),
2190 (Token::Edge, "EDGE"),
2191 (Token::Document, "DOCUMENT"),
2192 (Token::Kv, "KV"),
2193 (Token::Timeseries, "TIMESERIES"),
2194 (Token::Retention, "RETENTION"),
2195 (Token::Queue, "QUEUE"),
2196 (Token::Tree, "TREE"),
2197 (Token::Push, "PUSH"),
2198 (Token::Pop, "POP"),
2199 (Token::Peek, "PEEK"),
2200 (Token::Purge, "PURGE"),
2201 (Token::Ack, "ACK"),
2202 (Token::Nack, "NACK"),
2203 (Token::Priority, "PRIORITY"),
2204 (Token::Neighborhood, "NEIGHBORHOOD"),
2205 (Token::ShortestPath, "SHORTEST_PATH"),
2206 (Token::Centrality, "CENTRALITY"),
2207 (Token::Community, "COMMUNITY"),
2208 (Token::Components, "COMPONENTS"),
2209 (Token::Cycles, "CYCLES"),
2210 (Token::Traverse, "TRAVERSE"),
2211 (Token::Depth, "DEPTH"),
2212 (Token::Direction, "DIRECTION"),
2213 (Token::Algorithm, "ALGORITHM"),
2214 (Token::Strategy, "STRATEGY"),
2215 (Token::MaxIterations, "MAX_ITERATIONS"),
2216 (Token::MaxLength, "MAX_LENGTH"),
2217 (Token::Mode, "MODE"),
2218 (Token::Clustering, "CLUSTERING"),
2219 (Token::TopologicalSort, "TOPOLOGICAL_SORT"),
2220 (Token::Properties, "PROPERTIES"),
2221 (Token::Text, "TEXT"),
2222 (Token::Fuzzy, "FUZZY"),
2223 (Token::MinScore, "MIN_SCORE"),
2224 (Token::Begin, "BEGIN"),
2225 (Token::Commit, "COMMIT"),
2226 (Token::Rollback, "ROLLBACK"),
2227 (Token::Savepoint, "SAVEPOINT"),
2228 (Token::Release, "RELEASE"),
2229 (Token::Start, "START"),
2230 (Token::Transaction, "TRANSACTION"),
2231 (Token::Work, "WORK"),
2232 (Token::Vacuum, "VACUUM"),
2233 (Token::Analyze, "ANALYZE"),
2234 (Token::Schema, "SCHEMA"),
2235 (Token::Sequence, "SEQUENCE"),
2236 (Token::Increment, "INCREMENT"),
2237 (Token::Copy, "COPY"),
2238 (Token::Header, "HEADER"),
2239 (Token::Delimiter, "DELIMITER"),
2240 (Token::View, "VIEW"),
2241 (Token::Materialized, "MATERIALIZED"),
2242 (Token::Refresh, "REFRESH"),
2243 (Token::Partition, "PARTITION"),
2244 (Token::Range, "RANGE"),
2245 (Token::List, "LIST"),
2246 (Token::Hash, "HASH"),
2247 (Token::Attach, "ATTACH"),
2248 (Token::Detach, "DETACH"),
2249 (Token::Of, "OF"),
2250 (Token::Policy, "POLICY"),
2251 (Token::Enable, "ENABLE"),
2252 (Token::Disable, "DISABLE"),
2253 (Token::Security, "SECURITY"),
2254 (Token::Row, "ROW"),
2255 (Token::Level, "LEVEL"),
2256 (Token::Foreign, "FOREIGN"),
2257 (Token::Server, "SERVER"),
2258 (Token::Wrapper, "WRAPPER"),
2259 (Token::Options, "OPTIONS"),
2260 (Token::Data, "DATA"),
2261 (Token::String("x".into()), "'x'"),
2262 (Token::Integer(7), "7"),
2263 (Token::Float(1.5), "1.5"),
2264 (Token::JsonLiteral(r#"{"x":1}"#.into()), r#"{"x":1}"#),
2265 (Token::Ident("id".into()), "id"),
2266 (Token::Eq, "="),
2267 (Token::Ne, "<>"),
2268 (Token::Lt, "<"),
2269 (Token::Le, "<="),
2270 (Token::Gt, ">"),
2271 (Token::Ge, ">="),
2272 (Token::Plus, "+"),
2273 (Token::Minus, "-"),
2274 (Token::Star, "*"),
2275 (Token::Slash, "/"),
2276 (Token::Percent, "%"),
2277 (Token::LParen, "("),
2278 (Token::RParen, ")"),
2279 (Token::LBracket, "["),
2280 (Token::RBracket, "]"),
2281 (Token::LBrace, "{"),
2282 (Token::RBrace, "}"),
2283 (Token::Comma, ","),
2284 (Token::Dot, "."),
2285 (Token::Colon, ":"),
2286 (Token::Semi, ";"),
2287 (Token::Dollar, "$"),
2288 (Token::FatArrow, "=>"),
2289 (Token::Arrow, "->"),
2290 (Token::ArrowLeft, "<-"),
2291 (Token::Dash, "-"),
2292 (Token::DotDot, ".."),
2293 (Token::Pipe, "|"),
2294 (Token::DoublePipe, "||"),
2295 (Token::Eof, "EOF"),
2296 ];
2297
2298 for (token, expected) in cases {
2299 assert_eq!(token.to_string(), expected);
2300 }
2301 }
2302
2303 #[test]
2304 fn fat_arrow_lexes_distinctly_from_eq() {
2305 assert_eq!(
2307 tokenize("resolution => 0.5"),
2308 vec![
2309 Token::Ident("resolution".into()),
2310 Token::FatArrow,
2311 Token::Float(0.5),
2312 Token::Eof,
2313 ]
2314 );
2315 assert_eq!(
2316 tokenize("x = 1"),
2317 vec![
2318 Token::Ident("x".into()),
2319 Token::Eq,
2320 Token::Integer(1),
2321 Token::Eof,
2322 ]
2323 );
2324 }
2325
2326 #[test]
2327 fn test_string_escape_and_error_matrix() {
2328 let tokens = tokenize(
2329 r#"'line\nrow' 'carriage\rreturn' 'tab\tstop' 'slash\\' 'quote\'' "dq\"" 'raw\z'"#,
2330 );
2331 assert_eq!(
2332 tokens,
2333 vec![
2334 Token::String("line\nrow".into()),
2335 Token::String("carriage\rreturn".into()),
2336 Token::String("tab\tstop".into()),
2337 Token::String("slash\\".into()),
2338 Token::String("quote'".into()),
2339 Token::String("dq\"".into()),
2340 Token::String(r"raw\z".into()),
2341 Token::Eof
2342 ]
2343 );
2344
2345 let mut lexer = Lexer::new("'unterminated");
2346 assert!(lexer
2347 .next_token()
2348 .unwrap_err()
2349 .message
2350 .contains("Unterminated string"));
2351
2352 let mut lexer = Lexer::new(r"'bad\");
2353 assert!(lexer
2354 .next_token()
2355 .unwrap_err()
2356 .message
2357 .contains("Unterminated string"));
2358 }
2359
2360 #[test]
2361 fn test_operator_comment_peek_limit_and_tokenize_paths() {
2362 let tokens = tokenize("!= % ; $ || | 123.abc 1..2 1e+2 <- -> /* block */ SELECT");
2363 assert_eq!(
2364 tokens,
2365 vec![
2366 Token::Ne,
2367 Token::Percent,
2368 Token::Semi,
2369 Token::Dollar,
2370 Token::DoublePipe,
2371 Token::Pipe,
2372 Token::Integer(123),
2373 Token::Dot,
2374 Token::Ident("abc".into()),
2375 Token::Integer(1),
2376 Token::DotDot,
2377 Token::Integer(2),
2378 Token::Float(1e2),
2379 Token::ArrowLeft,
2380 Token::Arrow,
2381 Token::Select,
2382 Token::Eof,
2383 ]
2384 );
2385
2386 let mut lexer = Lexer::new("SELECT FROM");
2387 assert_eq!(lexer.peek_token().unwrap().token, Token::Select);
2388 assert_eq!(lexer.next_token().unwrap().token, Token::Select);
2389 assert_eq!(lexer.next_token().unwrap().token, Token::From);
2390
2391 let mut lexer = Lexer::new("!");
2392 assert!(lexer
2393 .next_token()
2394 .unwrap_err()
2395 .message
2396 .contains("Expected '=' after '!'"));
2397
2398 let limits = crate::limits::ParserLimits {
2399 max_identifier_chars: 3,
2400 ..crate::limits::ParserLimits::default()
2401 };
2402 let mut lexer = Lexer::with_limits("abcd", limits);
2403 assert_eq!(lexer.max_identifier_chars(), 3);
2404 let err = lexer.next_token().unwrap_err();
2405 assert!(matches!(
2406 err.limit_hit,
2407 Some(LexerLimitHit::IdentifierTooLong { value: 3, .. })
2408 ));
2409 }
2410}