aprender_test_showcase/core/
parser.rs1use crate::core::{CalcError, CalcResult, Operation};
6
7#[derive(Debug, Clone, PartialEq)]
9pub enum Token {
10 Number(f64),
12 Operator(Operation),
14 LeftParen,
16 RightParen,
18}
19
20impl Token {
21 #[must_use]
23 pub const fn is_operator(&self) -> bool {
24 matches!(self, Self::Operator(_))
25 }
26
27 #[must_use]
29 pub const fn is_number(&self) -> bool {
30 matches!(self, Self::Number(_))
31 }
32
33 #[must_use]
35 pub const fn is_left_paren(&self) -> bool {
36 matches!(self, Self::LeftParen)
37 }
38
39 #[must_use]
41 pub const fn is_right_paren(&self) -> bool {
42 matches!(self, Self::RightParen)
43 }
44}
45
46#[derive(Debug, Clone, PartialEq)]
48pub enum AstNode {
49 Number(f64),
51 BinaryOp {
53 left: Box<AstNode>,
55 op: Operation,
57 right: Box<AstNode>,
59 },
60 Negate(Box<AstNode>),
62}
63
64impl AstNode {
65 #[must_use]
67 pub fn number(value: f64) -> Self {
68 Self::Number(value)
69 }
70
71 #[must_use]
73 pub fn binary(left: AstNode, op: Operation, right: AstNode) -> Self {
74 Self::BinaryOp {
75 left: Box::new(left),
76 op,
77 right: Box::new(right),
78 }
79 }
80
81 #[must_use]
83 pub fn negate(inner: AstNode) -> Self {
84 Self::Negate(Box::new(inner))
85 }
86}
87
88#[derive(Debug)]
90pub struct Tokenizer<'a> {
91 input: &'a str,
92 pos: usize,
93}
94
95impl<'a> Tokenizer<'a> {
96 #[must_use]
98 pub fn new(input: &'a str) -> Self {
99 Self { input, pos: 0 }
100 }
101
102 #[must_use]
104 pub fn remaining(&self) -> &str {
105 &self.input[self.pos..]
106 }
107
108 pub fn tokenize(&mut self) -> CalcResult<Vec<Token>> {
110 let mut tokens = Vec::new();
111 while let Some(token) = self.next_token()? {
112 tokens.push(token);
113 }
114 Ok(tokens)
115 }
116
117 pub fn next_token(&mut self) -> CalcResult<Option<Token>> {
119 self.skip_whitespace();
120
121 if self.pos >= self.input.len() {
122 return Ok(None);
123 }
124
125 let ch = self
126 .current_char()
127 .ok_or_else(|| CalcError::ParseError("Unexpected end of input".into()))?;
128
129 let token = match ch {
130 '0'..='9' | '.' => self.read_number()?,
131 '+' => {
132 self.advance();
133 Token::Operator(Operation::Add)
134 }
135 '-' => {
136 self.advance();
137 Token::Operator(Operation::Subtract)
138 }
139 '*' => {
140 self.advance();
141 Token::Operator(Operation::Multiply)
142 }
143 '/' => {
144 self.advance();
145 Token::Operator(Operation::Divide)
146 }
147 '%' => {
148 self.advance();
149 Token::Operator(Operation::Modulo)
150 }
151 '^' => {
152 self.advance();
153 Token::Operator(Operation::Power)
154 }
155 '(' => {
156 self.advance();
157 Token::LeftParen
158 }
159 ')' => {
160 self.advance();
161 Token::RightParen
162 }
163 _ => {
164 return Err(CalcError::ParseError(format!(
165 "Unexpected character: '{ch}'"
166 )));
167 }
168 };
169
170 Ok(Some(token))
171 }
172
173 fn current_char(&self) -> Option<char> {
174 self.input[self.pos..].chars().next()
175 }
176
177 fn advance(&mut self) {
178 if let Some(ch) = self.current_char() {
179 self.pos += ch.len_utf8();
180 }
181 }
182
183 fn skip_whitespace(&mut self) {
184 while let Some(ch) = self.current_char() {
185 if ch.is_whitespace() {
186 self.advance();
187 } else {
188 break;
189 }
190 }
191 }
192
193 fn read_number(&mut self) -> CalcResult<Token> {
194 let start = self.pos;
195 let mut has_dot = false;
196
197 while let Some(ch) = self.current_char() {
198 if ch.is_ascii_digit() {
199 self.advance();
200 } else if ch == '.' && !has_dot {
201 has_dot = true;
202 self.advance();
203 } else {
204 break;
205 }
206 }
207
208 let num_str = &self.input[start..self.pos];
209 let value: f64 = num_str
210 .parse()
211 .map_err(|_| CalcError::ParseError(format!("Invalid number: '{num_str}'")))?;
212
213 Ok(Token::Number(value))
214 }
215}
216
217#[derive(Debug)]
228pub struct Parser {
229 tokens: Vec<Token>,
230 pos: usize,
231}
232
233impl Parser {
234 #[must_use]
236 pub fn new(tokens: Vec<Token>) -> Self {
237 Self { tokens, pos: 0 }
238 }
239
240 pub fn parse_str(input: &str) -> CalcResult<AstNode> {
242 let trimmed = input.trim();
243 if trimmed.is_empty() {
244 return Err(CalcError::EmptyExpression);
245 }
246
247 let mut tokenizer = Tokenizer::new(trimmed);
248 let tokens = tokenizer.tokenize()?;
249
250 if tokens.is_empty() {
251 return Err(CalcError::EmptyExpression);
252 }
253
254 let mut parser = Self::new(tokens);
255 let ast = parser.parse_expression()?;
256
257 if parser.pos < parser.tokens.len() {
259 return Err(CalcError::ParseError(format!(
260 "Unexpected token at position {}",
261 parser.pos
262 )));
263 }
264
265 Ok(ast)
266 }
267
268 pub fn parse(&mut self) -> CalcResult<AstNode> {
270 if self.tokens.is_empty() {
271 return Err(CalcError::EmptyExpression);
272 }
273 self.parse_expression()
274 }
275
276 fn current(&self) -> Option<&Token> {
277 self.tokens.get(self.pos)
278 }
279
280 fn advance(&mut self) -> Option<&Token> {
281 let token = self.tokens.get(self.pos);
282 if token.is_some() {
283 self.pos += 1;
284 }
285 token
286 }
287
288 fn parse_expression(&mut self) -> CalcResult<AstNode> {
289 let mut left = self.parse_term()?;
290
291 while let Some(token) = self.current() {
292 let op = match token {
293 Token::Operator(Operation::Add) => Operation::Add,
294 Token::Operator(Operation::Subtract) => Operation::Subtract,
295 _ => break,
296 };
297 self.advance();
298 let right = self.parse_term()?;
299 left = AstNode::binary(left, op, right);
300 }
301
302 Ok(left)
303 }
304
305 fn parse_term(&mut self) -> CalcResult<AstNode> {
306 let mut left = self.parse_factor()?;
307
308 while let Some(token) = self.current() {
309 let op = match token {
310 Token::Operator(Operation::Multiply) => Operation::Multiply,
311 Token::Operator(Operation::Divide) => Operation::Divide,
312 Token::Operator(Operation::Modulo) => Operation::Modulo,
313 _ => break,
314 };
315 self.advance();
316 let right = self.parse_factor()?;
317 left = AstNode::binary(left, op, right);
318 }
319
320 Ok(left)
321 }
322
323 fn parse_factor(&mut self) -> CalcResult<AstNode> {
324 let base = self.parse_base()?;
325
326 if matches!(self.current(), Some(Token::Operator(Operation::Power))) {
328 self.advance();
329 let exponent = self.parse_factor()?; return Ok(AstNode::binary(base, Operation::Power, exponent));
331 }
332
333 Ok(base)
334 }
335
336 fn parse_base(&mut self) -> CalcResult<AstNode> {
337 if matches!(self.current(), Some(Token::Operator(Operation::Subtract))) {
339 self.advance();
340 let inner = self.parse_base()?;
341 return Ok(AstNode::negate(inner));
342 }
343
344 self.parse_primary()
345 }
346
347 fn parse_primary(&mut self) -> CalcResult<AstNode> {
348 let token = self
349 .advance()
350 .ok_or_else(|| CalcError::ParseError("Unexpected end of expression".into()))?;
351
352 match token {
353 Token::Number(n) => Ok(AstNode::number(*n)),
354 Token::LeftParen => {
355 let expr = self.parse_expression()?;
356 match self.advance() {
357 Some(Token::RightParen) => Ok(expr),
358 Some(t) => Err(CalcError::ParseError(format!(
359 "Expected ')' but found {t:?}"
360 ))),
361 None => Err(CalcError::ParseError("Unclosed parenthesis".into())),
362 }
363 }
364 _ => Err(CalcError::ParseError(format!(
365 "Unexpected token: {token:?}"
366 ))),
367 }
368 }
369}
370
371#[cfg(test)]
372mod tests {
373 use super::*;
374
375 #[test]
378 fn test_token_is_operator() {
379 assert!(Token::Operator(Operation::Add).is_operator());
380 assert!(!Token::Number(5.0).is_operator());
381 assert!(!Token::LeftParen.is_operator());
382 }
383
384 #[test]
385 fn test_token_is_number() {
386 assert!(Token::Number(5.0).is_number());
387 assert!(!Token::Operator(Operation::Add).is_number());
388 }
389
390 #[test]
391 fn test_token_is_left_paren() {
392 assert!(Token::LeftParen.is_left_paren());
393 assert!(!Token::RightParen.is_left_paren());
394 }
395
396 #[test]
397 fn test_token_is_right_paren() {
398 assert!(Token::RightParen.is_right_paren());
399 assert!(!Token::LeftParen.is_right_paren());
400 }
401
402 #[test]
405 fn test_ast_node_number() {
406 let node = AstNode::number(42.0);
407 assert_eq!(node, AstNode::Number(42.0));
408 }
409
410 #[test]
411 fn test_ast_node_binary() {
412 let node = AstNode::binary(AstNode::number(1.0), Operation::Add, AstNode::number(2.0));
413 match node {
414 AstNode::BinaryOp { left, op, right } => {
415 assert_eq!(*left, AstNode::Number(1.0));
416 assert_eq!(op, Operation::Add);
417 assert_eq!(*right, AstNode::Number(2.0));
418 }
419 _ => panic!("Expected BinaryOp"),
420 }
421 }
422
423 #[test]
424 fn test_ast_node_negate() {
425 let node = AstNode::negate(AstNode::number(5.0));
426 match node {
427 AstNode::Negate(inner) => {
428 assert_eq!(*inner, AstNode::Number(5.0));
429 }
430 _ => panic!("Expected Negate"),
431 }
432 }
433
434 #[test]
437 fn test_tokenize_single_number() {
438 let mut t = Tokenizer::new("42");
439 let tokens = t.tokenize().unwrap();
440 assert_eq!(tokens, vec![Token::Number(42.0)]);
441 }
442
443 #[test]
444 fn test_tokenize_decimal_number() {
445 let mut t = Tokenizer::new("3.14");
446 let tokens = t.tokenize().unwrap();
447 assert_eq!(tokens, vec![Token::Number(3.14)]);
448 }
449
450 #[test]
451 fn test_tokenize_operators() {
452 let mut t = Tokenizer::new("+ - * / % ^");
453 let tokens = t.tokenize().unwrap();
454 assert_eq!(
455 tokens,
456 vec![
457 Token::Operator(Operation::Add),
458 Token::Operator(Operation::Subtract),
459 Token::Operator(Operation::Multiply),
460 Token::Operator(Operation::Divide),
461 Token::Operator(Operation::Modulo),
462 Token::Operator(Operation::Power),
463 ]
464 );
465 }
466
467 #[test]
468 fn test_tokenize_parentheses() {
469 let mut t = Tokenizer::new("()");
470 let tokens = t.tokenize().unwrap();
471 assert_eq!(tokens, vec![Token::LeftParen, Token::RightParen]);
472 }
473
474 #[test]
475 fn test_tokenize_expression() {
476 let mut t = Tokenizer::new("2 + 3 * 4");
477 let tokens = t.tokenize().unwrap();
478 assert_eq!(
479 tokens,
480 vec![
481 Token::Number(2.0),
482 Token::Operator(Operation::Add),
483 Token::Number(3.0),
484 Token::Operator(Operation::Multiply),
485 Token::Number(4.0),
486 ]
487 );
488 }
489
490 #[test]
491 fn test_tokenize_with_parens() {
492 let mut t = Tokenizer::new("(2 + 3) * 4");
493 let tokens = t.tokenize().unwrap();
494 assert_eq!(
495 tokens,
496 vec![
497 Token::LeftParen,
498 Token::Number(2.0),
499 Token::Operator(Operation::Add),
500 Token::Number(3.0),
501 Token::RightParen,
502 Token::Operator(Operation::Multiply),
503 Token::Number(4.0),
504 ]
505 );
506 }
507
508 #[test]
509 fn test_tokenize_no_spaces() {
510 let mut t = Tokenizer::new("1+2*3");
511 let tokens = t.tokenize().unwrap();
512 assert_eq!(tokens.len(), 5);
513 }
514
515 #[test]
516 fn test_tokenize_invalid_char() {
517 let mut t = Tokenizer::new("2 @ 3");
518 let result = t.tokenize();
519 assert!(matches!(result, Err(CalcError::ParseError(_))));
520 }
521
522 #[test]
523 fn test_tokenize_empty() {
524 let mut t = Tokenizer::new("");
525 let tokens = t.tokenize().unwrap();
526 assert!(tokens.is_empty());
527 }
528
529 #[test]
530 fn test_tokenize_whitespace_only() {
531 let mut t = Tokenizer::new(" ");
532 let tokens = t.tokenize().unwrap();
533 assert!(tokens.is_empty());
534 }
535
536 #[test]
537 fn test_tokenizer_remaining() {
538 let mut t = Tokenizer::new("1 + 2");
539 t.next_token().unwrap();
540 assert_eq!(t.remaining(), " + 2");
541 }
542
543 #[test]
544 fn test_tokenize_leading_decimal() {
545 let mut t = Tokenizer::new(".5");
546 let tokens = t.tokenize().unwrap();
547 assert_eq!(tokens, vec![Token::Number(0.5)]);
548 }
549
550 #[test]
553 fn test_parse_single_number() {
554 let ast = Parser::parse_str("42").unwrap();
555 assert_eq!(ast, AstNode::Number(42.0));
556 }
557
558 #[test]
559 fn test_parse_decimal() {
560 let ast = Parser::parse_str("3.14").unwrap();
561 assert_eq!(ast, AstNode::Number(3.14));
562 }
563
564 #[test]
565 fn test_parse_simple_addition() {
566 let ast = Parser::parse_str("2 + 3").unwrap();
567 assert_eq!(
568 ast,
569 AstNode::binary(AstNode::number(2.0), Operation::Add, AstNode::number(3.0))
570 );
571 }
572
573 #[test]
574 fn test_parse_simple_subtraction() {
575 let ast = Parser::parse_str("5 - 2").unwrap();
576 assert_eq!(
577 ast,
578 AstNode::binary(
579 AstNode::number(5.0),
580 Operation::Subtract,
581 AstNode::number(2.0)
582 )
583 );
584 }
585
586 #[test]
587 fn test_parse_simple_multiplication() {
588 let ast = Parser::parse_str("3 * 4").unwrap();
589 assert_eq!(
590 ast,
591 AstNode::binary(
592 AstNode::number(3.0),
593 Operation::Multiply,
594 AstNode::number(4.0)
595 )
596 );
597 }
598
599 #[test]
600 fn test_parse_simple_division() {
601 let ast = Parser::parse_str("8 / 2").unwrap();
602 assert_eq!(
603 ast,
604 AstNode::binary(
605 AstNode::number(8.0),
606 Operation::Divide,
607 AstNode::number(2.0)
608 )
609 );
610 }
611
612 #[test]
613 fn test_parse_simple_modulo() {
614 let ast = Parser::parse_str("7 % 3").unwrap();
615 assert_eq!(
616 ast,
617 AstNode::binary(
618 AstNode::number(7.0),
619 Operation::Modulo,
620 AstNode::number(3.0)
621 )
622 );
623 }
624
625 #[test]
626 fn test_parse_simple_power() {
627 let ast = Parser::parse_str("2 ^ 3").unwrap();
628 assert_eq!(
629 ast,
630 AstNode::binary(AstNode::number(2.0), Operation::Power, AstNode::number(3.0))
631 );
632 }
633
634 #[test]
635 fn test_parse_precedence_mul_over_add() {
636 let ast = Parser::parse_str("2 + 3 * 4").unwrap();
638 match ast {
640 AstNode::BinaryOp {
641 op: Operation::Add, ..
642 } => {}
643 _ => panic!("Expected Add at top level"),
644 }
645 }
646
647 #[test]
648 fn test_parse_precedence_power_highest() {
649 let ast = Parser::parse_str("2 * 3 ^ 2").unwrap();
651 match ast {
652 AstNode::BinaryOp {
653 op: Operation::Multiply,
654 ..
655 } => {}
656 _ => panic!("Expected Multiply at top level"),
657 }
658 }
659
660 #[test]
661 fn test_parse_power_right_associative() {
662 let ast = Parser::parse_str("2 ^ 3 ^ 2").unwrap();
664 match ast {
665 AstNode::BinaryOp {
666 left,
667 op: Operation::Power,
668 right,
669 } => {
670 assert_eq!(*left, AstNode::Number(2.0));
671 match *right {
672 AstNode::BinaryOp {
673 op: Operation::Power,
674 ..
675 } => {}
676 _ => panic!("Expected Power on right"),
677 }
678 }
679 _ => panic!("Expected Power at top level"),
680 }
681 }
682
683 #[test]
684 fn test_parse_parentheses() {
685 let ast = Parser::parse_str("(2 + 3) * 4").unwrap();
687 match ast {
688 AstNode::BinaryOp {
689 op: Operation::Multiply,
690 left,
691 ..
692 } => match *left {
693 AstNode::BinaryOp {
694 op: Operation::Add, ..
695 } => {}
696 _ => panic!("Expected Add inside parens"),
697 },
698 _ => panic!("Expected Multiply at top level"),
699 }
700 }
701
702 #[test]
703 fn test_parse_nested_parentheses() {
704 let ast = Parser::parse_str("((2 + 3))").unwrap();
705 match ast {
706 AstNode::BinaryOp {
707 op: Operation::Add, ..
708 } => {}
709 _ => panic!("Expected Add"),
710 }
711 }
712
713 #[test]
714 fn test_parse_unary_minus() {
715 let ast = Parser::parse_str("-5").unwrap();
716 match ast {
717 AstNode::Negate(inner) => {
718 assert_eq!(*inner, AstNode::Number(5.0));
719 }
720 _ => panic!("Expected Negate"),
721 }
722 }
723
724 #[test]
725 fn test_parse_unary_minus_in_expression() {
726 let ast = Parser::parse_str("3 + -2").unwrap();
727 match ast {
728 AstNode::BinaryOp {
729 op: Operation::Add,
730 right,
731 ..
732 } => match *right {
733 AstNode::Negate(_) => {}
734 _ => panic!("Expected Negate on right"),
735 },
736 _ => panic!("Expected Add"),
737 }
738 }
739
740 #[test]
741 fn test_parse_double_negative() {
742 let ast = Parser::parse_str("--5").unwrap();
743 match ast {
744 AstNode::Negate(inner) => match *inner {
745 AstNode::Negate(_) => {}
746 _ => panic!("Expected nested Negate"),
747 },
748 _ => panic!("Expected Negate"),
749 }
750 }
751
752 #[test]
753 fn test_parse_empty_expression() {
754 let result = Parser::parse_str("");
755 assert!(matches!(result, Err(CalcError::EmptyExpression)));
756 }
757
758 #[test]
759 fn test_parse_whitespace_only() {
760 let result = Parser::parse_str(" ");
761 assert!(matches!(result, Err(CalcError::EmptyExpression)));
762 }
763
764 #[test]
765 fn test_parse_unclosed_paren() {
766 let result = Parser::parse_str("(2 + 3");
767 assert!(matches!(result, Err(CalcError::ParseError(_))));
768 }
769
770 #[test]
771 fn test_parse_extra_close_paren() {
772 let result = Parser::parse_str("2 + 3)");
773 assert!(matches!(result, Err(CalcError::ParseError(_))));
774 }
775
776 #[test]
777 fn test_parse_missing_operand() {
778 let result = Parser::parse_str("2 +");
779 assert!(matches!(result, Err(CalcError::ParseError(_))));
780 }
781
782 #[test]
783 fn test_parse_consecutive_operators() {
784 let result = Parser::parse_str("2 + * 3");
786 assert!(matches!(result, Err(CalcError::ParseError(_))));
787 }
788
789 #[test]
790 fn test_parse_complex_expression() {
791 let ast = Parser::parse_str("42 * (3 + 7)").unwrap();
793 match ast {
794 AstNode::BinaryOp {
795 op: Operation::Multiply,
796 left,
797 right,
798 } => {
799 assert_eq!(*left, AstNode::Number(42.0));
800 match *right {
801 AstNode::BinaryOp {
802 op: Operation::Add, ..
803 } => {}
804 _ => panic!("Expected Add in parens"),
805 }
806 }
807 _ => panic!("Expected Multiply"),
808 }
809 }
810
811 #[test]
812 fn test_parser_new() {
813 let tokens = vec![Token::Number(5.0)];
814 let parser = Parser::new(tokens);
815 assert_eq!(parser.pos, 0);
816 }
817
818 #[test]
819 fn test_parser_parse_method() {
820 let tokens = vec![Token::Number(42.0)];
821 let mut parser = Parser::new(tokens);
822 let ast = parser.parse().unwrap();
823 assert_eq!(ast, AstNode::Number(42.0));
824 }
825
826 #[test]
827 fn test_parser_parse_empty_tokens() {
828 let mut parser = Parser::new(vec![]);
829 let result = parser.parse();
830 assert!(matches!(result, Err(CalcError::EmptyExpression)));
831 }
832}