1use std::fmt;
16
17#[derive(Debug, Clone, PartialEq)]
22pub enum Token {
23 Name(String),
26 Star,
28 Dot,
30 DotDot,
32
33 At,
36 DoubleColon,
38 Slash,
40 DoubleSlash,
42 Pipe,
44 Plus,
46 Minus,
48 Eq,
50 Ne,
52 Lt,
54 Gt,
56 Le,
58 Ge,
60 Multiply,
62
63 Or,
66 And,
68 Mod,
70 Div,
72 Ancestor,
74 AncestorOrSelf,
76 Attribute,
78 Child,
80 Descendant,
82 DescendantOrSelf,
84 Following,
86 FollowingSibling,
88 Namespace,
90 Parent,
92 Preceding,
94 PrecedingSibling,
96 Self_,
98
99 StringLiteral(String),
102 NumberLiteral(f64),
104
105 LParen,
107 RParen,
108 LBracket,
109 RBracket,
110 LBrace, RBrace,
112 Comma,
113 Dollar,
115
116 Eof,
119}
120
121impl fmt::Display for Token {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 match self {
124 Token::Name(n) => write!(f, "{}", n),
125 Token::Star => write!(f, "*"),
126 Token::Dot => write!(f, "."),
127 Token::DotDot => write!(f, ".."),
128 Token::At => write!(f, "@"),
129 Token::DoubleColon => write!(f, "::"),
130 Token::Slash => write!(f, "/"),
131 Token::DoubleSlash => write!(f, "//"),
132 Token::Pipe => write!(f, "|"),
133 Token::Plus => write!(f, "+"),
134 Token::Minus => write!(f, "-"),
135 Token::Eq => write!(f, "="),
136 Token::Ne => write!(f, "!="),
137 Token::Lt => write!(f, "<"),
138 Token::Gt => write!(f, ">"),
139 Token::Le => write!(f, "<="),
140 Token::Ge => write!(f, ">="),
141 Token::Multiply => write!(f, "*"),
142 Token::Or => write!(f, "or"),
143 Token::And => write!(f, "and"),
144 Token::Mod => write!(f, "mod"),
145 Token::Div => write!(f, "div"),
146 Token::Ancestor => write!(f, "ancestor"),
147 Token::AncestorOrSelf => write!(f, "ancestor-or-self"),
148 Token::Attribute => write!(f, "attribute"),
149 Token::Child => write!(f, "child"),
150 Token::Descendant => write!(f, "descendant"),
151 Token::DescendantOrSelf => write!(f, "descendant-or-self"),
152 Token::Following => write!(f, "following"),
153 Token::FollowingSibling => write!(f, "following-sibling"),
154 Token::Namespace => write!(f, "namespace"),
155 Token::Parent => write!(f, "parent"),
156 Token::Preceding => write!(f, "preceding"),
157 Token::PrecedingSibling => write!(f, "preceding-sibling"),
158 Token::Self_ => write!(f, "self"),
159 Token::StringLiteral(s) => write!(f, "'{}'", s),
160 Token::NumberLiteral(n) => write!(f, "{}", n),
161 Token::LParen => write!(f, "("),
162 Token::RParen => write!(f, ")"),
163 Token::LBracket => write!(f, "["),
164 Token::RBracket => write!(f, "]"),
165 Token::LBrace => write!(f, "{{"),
166 Token::RBrace => write!(f, "}}"),
167 Token::Comma => write!(f, ","),
168 Token::Dollar => write!(f, "$"),
169 Token::Eof => write!(f, "<EOF>"),
170 }
171 }
172}
173
174#[derive(Debug, Clone)]
182pub struct Lexer {
183 input: Vec<u8>,
185 pos: usize,
187 ch: u8,
189 at_start: bool,
191}
192
193impl Lexer {
194 pub fn new(input: &str) -> Self {
195 let bytes = input.as_bytes().to_vec();
196 let ch = if bytes.is_empty() { 0 } else { bytes[0] };
197 Self {
198 input: bytes,
199 pos: 0,
200 ch,
201 at_start: true,
202 }
203 }
204
205 fn advance(&mut self) {
207 self.pos += 1;
208 self.ch = if self.pos < self.input.len() {
209 self.input[self.pos]
210 } else {
211 0
212 };
213 }
214
215 fn peek(&self) -> u8 {
217 if self.pos + 1 < self.input.len() {
218 self.input[self.pos + 1]
219 } else {
220 0
221 }
222 }
223
224 fn skip_ws(&mut self) {
226 while self.ch != 0
227 && (self.ch == b' ' || self.ch == b'\t' || self.ch == b'\n' || self.ch == b'\r')
228 {
229 self.advance();
230 }
231 }
232
233 fn read_name(&mut self) -> String {
235 let start = self.pos;
236 while self.ch != 0
237 && (self.ch.is_ascii_alphanumeric()
238 || self.ch == b'_'
239 || self.ch == b'-'
240 || self.ch == b'.')
241 {
242 self.advance();
243 }
244 String::from_utf8_lossy(&self.input[start..self.pos]).to_string()
245 }
246
247 fn try_keyword_or_axis(&self, name: &str) -> Option<Token> {
249 match name {
250 "or" => Some(Token::Or),
251 "and" => Some(Token::And),
252 "mod" => Some(Token::Mod),
253 "div" => Some(Token::Div),
254 "ancestor" => Some(Token::Ancestor),
255 "ancestor-or-self" => Some(Token::AncestorOrSelf),
256 "attribute" => Some(Token::Attribute),
257 "child" => Some(Token::Child),
258 "descendant" => Some(Token::Descendant),
259 "descendant-or-self" => Some(Token::DescendantOrSelf),
260 "following" => Some(Token::Following),
261 "following-sibling" => Some(Token::FollowingSibling),
262 "namespace" => Some(Token::Namespace),
263 "parent" => Some(Token::Parent),
264 "preceding" => Some(Token::Preceding),
265 "preceding-sibling" => Some(Token::PrecedingSibling),
266 "self" => Some(Token::Self_),
267 _ => None,
268 }
269 }
270
271 fn read_number(&mut self) -> f64 {
273 let start = self.pos;
274 while self.ch != 0 && self.ch.is_ascii_digit() {
276 self.advance();
277 }
278 if self.ch == b'.' && self.peek().is_ascii_digit() {
280 self.advance(); while self.ch != 0 && self.ch.is_ascii_digit() {
282 self.advance();
283 }
284 }
285 let s = String::from_utf8_lossy(&self.input[start..self.pos]).to_string();
286 s.parse::<f64>().unwrap_or(0.0)
287 }
288
289 fn read_string(&mut self, quote: u8) -> String {
291 self.advance(); let start = self.pos;
293 while self.ch != 0 && self.ch != quote {
294 self.advance();
295 }
296 let s = String::from_utf8_lossy(&self.input[start..self.pos]).to_string();
297 if self.ch == quote {
298 self.advance(); }
300 s
301 }
302
303 pub fn next_token(&mut self) -> Token {
305 self.skip_ws();
306
307 if self.ch == 0 {
308 return Token::Eof;
309 }
310
311 let was_at_start = self.at_start;
313 self.at_start = false;
314
315 match self.ch {
317 b'(' => {
318 self.advance();
319 return Token::LParen;
320 }
321 b')' => {
322 self.advance();
323 return Token::RParen;
324 }
325 b'[' => {
326 self.advance();
327 return Token::LBracket;
328 }
329 b']' => {
330 self.advance();
331 return Token::RBracket;
332 }
333 b'{' => {
334 self.advance();
335 return Token::LBrace;
336 }
337 b'}' => {
338 self.advance();
339 return Token::RBrace;
340 }
341 b',' => {
342 self.advance();
343 return Token::Comma;
344 }
345 b'$' => {
346 self.advance();
347 return Token::Dollar;
348 }
349 b'|' => {
350 self.advance();
351 return Token::Pipe;
352 }
353 b'+' => {
354 self.advance();
355 return Token::Plus;
356 }
357 b'@' => {
358 self.advance();
359 return Token::At;
360 }
361 b'.' => {
362 if self.peek() == b'.' {
363 self.advance();
364 self.advance();
365 return Token::DotDot;
366 }
367 if self.peek().is_ascii_digit() {
369 return Token::NumberLiteral(self.read_number());
370 }
371 self.advance();
372 return Token::Dot;
373 }
374 b'-' => {
375 self.advance();
376 return Token::Minus;
379 }
380 b'=' => {
381 self.advance();
382 return Token::Eq;
383 }
384 b'!' => {
385 if self.peek() == b'=' {
386 self.advance();
387 self.advance();
388 return Token::Ne;
389 }
390 self.advance();
392 return self.next_token();
393 }
394 b'<' => {
395 self.advance();
396 if self.ch == b'=' {
397 self.advance();
398 return Token::Le;
399 }
400 return Token::Lt;
401 }
402 b'>' => {
403 self.advance();
404 if self.ch == b'=' {
405 self.advance();
406 return Token::Ge;
407 }
408 return Token::Gt;
409 }
410 b'/' => {
411 self.advance();
412 if self.ch == b'/' {
413 self.advance();
414 return Token::DoubleSlash;
415 }
416 return Token::Slash;
417 }
418 b'*' => {
419 self.advance();
420 return Token::Star; }
422 b':' => {
423 if self.peek() == b':' {
424 self.advance();
425 self.advance();
426 return Token::DoubleColon;
427 }
428 self.advance();
433 return self.next_token();
434 }
435 b'\'' | b'"' => {
436 let quote = self.ch;
437 let s = self.read_string(quote);
438 return Token::StringLiteral(s);
439 }
440 _ => {}
441 }
442
443 if self.ch.is_ascii_digit() {
445 return Token::NumberLiteral(self.read_number());
446 }
447
448 if self.ch.is_ascii_alphabetic() || self.ch == b'_' {
450 let name = self.read_name();
451
452 if self.ch == b':' && self.peek() != b':' {
454 self.advance(); if self.ch.is_ascii_alphabetic() || self.ch == b'_' || self.ch == b'*' {
456 if self.ch == b'*' {
457 self.advance();
458 let full = format!("{}:*", name);
459 return Token::Name(full);
460 }
461 let local = self.read_name();
462 return Token::Name(format!("{}:{}", name, local));
463 }
464 return Token::Name(name);
471 }
472
473 if self.ch == b':' && self.peek() == b':' {
477 if let Some(axis) = self.try_keyword_or_axis(&name) {
478 return axis;
479 }
480 return Token::Name(name);
483 }
484
485 if let Some(keyword) = self.try_keyword_or_axis(&name) {
487 return keyword;
488 }
489
490 return Token::Name(name);
491 }
492
493 self.advance();
495 self.next_token()
496 }
497}
498
499#[cfg(test)]
504mod tests {
505 use super::*;
506
507 fn tokenize(s: &str) -> Vec<Token> {
508 let mut lexer = Lexer::new(s);
509 let mut tokens = Vec::new();
510 loop {
511 let tok = lexer.next_token();
512 let is_eof = matches!(tok, Token::Eof);
513 tokens.push(tok);
514 if is_eof {
515 break;
516 }
517 }
518 tokens
519 }
520
521 #[test]
522 fn test_empty() {
523 let tokens = tokenize("");
524 assert_eq!(tokens.len(), 1);
525 assert_eq!(tokens[0], Token::Eof);
526 }
527
528 #[test]
529 fn test_simple_path() {
530 let tokens = tokenize("child::para");
531 assert_eq!(
532 tokens,
533 vec![
534 Token::Child,
535 Token::DoubleColon,
536 Token::Name("para".into()),
537 Token::Eof,
538 ]
539 );
540 }
541
542 #[test]
543 fn test_absolute_path() {
544 let tokens = tokenize("/child::para");
545 assert_eq!(
546 tokens,
547 vec![
548 Token::Slash,
549 Token::Child,
550 Token::DoubleColon,
551 Token::Name("para".into()),
552 Token::Eof,
553 ]
554 );
555 }
556
557 #[test]
558 fn test_short_form() {
559 let tokens = tokenize("para");
560 assert_eq!(tokens, vec![Token::Name("para".into()), Token::Eof]);
561 }
562
563 #[test]
564 fn test_attribute() {
565 let tokens = tokenize("@attr");
566 assert_eq!(
567 tokens,
568 vec![Token::At, Token::Name("attr".into()), Token::Eof]
569 );
570 }
571
572 #[test]
573 fn test_predicate() {
574 let tokens = tokenize("para[1]");
575 assert_eq!(
576 tokens,
577 vec![
578 Token::Name("para".into()),
579 Token::LBracket,
580 Token::NumberLiteral(1.0),
581 Token::RBracket,
582 Token::Eof,
583 ]
584 );
585 }
586
587 #[test]
588 fn test_function_call() {
589 let tokens = tokenize("position()");
590 assert_eq!(
591 tokens,
592 vec![
593 Token::Name("position".into()),
594 Token::LParen,
595 Token::RParen,
596 Token::Eof,
597 ]
598 );
599 }
600
601 #[test]
602 fn test_string_literal() {
603 let tokens = tokenize("'hello'");
604 assert_eq!(
605 tokens,
606 vec![Token::StringLiteral("hello".into()), Token::Eof]
607 );
608 }
609
610 #[test]
611 fn test_number() {
612 let tokens = tokenize("42");
613 assert_eq!(tokens, vec![Token::NumberLiteral(42.0), Token::Eof]);
614 }
615
616 #[test]
617 fn test_decimal() {
618 let tokens = tokenize("3.14");
619 assert_eq!(tokens, vec![Token::NumberLiteral(3.14), Token::Eof]);
620 }
621
622 #[test]
623 fn test_operators() {
624 let tokens = tokenize("a = b and c != d or e < f");
625 assert!(tokens.contains(&Token::Eq));
626 assert!(tokens.contains(&Token::And));
627 assert!(tokens.contains(&Token::Ne));
628 assert!(tokens.contains(&Token::Or));
629 assert!(tokens.contains(&Token::Lt));
630 }
631
632 #[test]
633 fn test_union() {
634 let tokens = tokenize("a | b");
635 assert_eq!(
636 tokens,
637 vec![
638 Token::Name("a".into()),
639 Token::Pipe,
640 Token::Name("b".into()),
641 Token::Eof,
642 ]
643 );
644 }
645
646 #[test]
647 fn test_double_slash() {
648 let tokens = tokenize("//para");
649 assert_eq!(
650 tokens,
651 vec![Token::DoubleSlash, Token::Name("para".into()), Token::Eof]
652 );
653 }
654
655 #[test]
656 fn test_qname() {
657 let tokens = tokenize("xslt:template");
658 assert_eq!(
659 tokens,
660 vec![Token::Name("xslt:template".into()), Token::Eof]
661 );
662 }
663
664 #[test]
665 fn test_wildcard() {
666 let tokens = tokenize("*");
667 assert_eq!(tokens, vec![Token::Star, Token::Eof]);
668 }
669
670 #[test]
671 fn test_ns_wildcard() {
672 let tokens = tokenize("ns:*");
673 assert_eq!(tokens, vec![Token::Name("ns:*".into()), Token::Eof]);
674 }
675
676 #[test]
677 fn test_dot_dot() {
678 let tokens = tokenize("..");
679 assert_eq!(tokens, vec![Token::DotDot, Token::Eof]);
680 }
681
682 #[test]
683 fn test_axis_keyword() {
684 let tokens = tokenize("ancestor-or-self::node()");
685 assert_eq!(
686 tokens,
687 vec![
688 Token::AncestorOrSelf,
689 Token::DoubleColon,
690 Token::Name("node".into()),
691 Token::LParen,
692 Token::RParen,
693 Token::Eof,
694 ]
695 );
696 }
697
698 #[test]
699 fn test_complex_expression() {
700 let tokens = tokenize("/html/body//div[@class='main']/p[1]");
701 let names: Vec<String> = tokens
703 .iter()
704 .filter_map(|t| match t {
705 Token::Name(n) => Some(n.clone()),
706 Token::Div => Some("div".to_string()),
707 Token::Mod => Some("mod".to_string()),
708 Token::And => Some("and".to_string()),
709 Token::Or => Some("or".to_string()),
710 _ => None,
711 })
712 .collect();
713 assert_eq!(names, vec!["html", "body", "div", "class", "p"]);
714 }
715
716 #[test]
717 fn test_variable() {
718 let tokens = tokenize("$var");
719 assert_eq!(
720 tokens,
721 vec![Token::Dollar, Token::Name("var".into()), Token::Eof]
722 );
723 }
724}