1use crate::utils::ChUtils;
8
9#[derive(Clone, Debug, PartialEq)]
18pub struct Sub {
19 pub tokens: Vec<Token>,
20 pub method: SubMethod,
21}
22
23impl Sub {
24 pub fn new(tokens: Vec<Token>, method: SubMethod) -> Self {
26 Self { tokens, method }
27 }
28
29 pub fn empty() -> Self {
31 Self {
32 tokens: Vec::new(),
33 method: SubMethod::PAREN,
34 }
35 }
36}
37
38#[derive(Clone, Debug, PartialEq)]
43pub enum SubMethod {
44 PAREN,
45 ABS,
46}
47
48#[derive(Clone, Debug, PartialEq)]
49pub enum TokenType {
50 ILLEGAL,
51
52 NUMBER,
54
55 SUBEXP,
57 POINTER,
58 LPAREN,
59 RPAREN,
60 LABS,
61 RABS,
62
63 PLUS,
65 MINUS,
66 PRODUCT,
67 DIVIDE,
68 PERCENTAGE,
69 POWER,
70 FACTORIAL,
71
72 SQRT,
74 SIN,
75 COS,
76 TAN,
77 LOG,
78 LN,
79 EXP,
80 FLOOR,
81 CEIL,
82 ROUND,
83}
84
85#[derive(Clone, Debug, PartialEq)]
87pub struct Token {
88 pub typ: TokenType,
89 pub literal: String,
90 pub sub: Sub,
91 pub index: (i32, i32),
95}
96
97impl TokenType {
98 pub fn to_submethod(&self) -> SubMethod {
103 match self {
104 TokenType::LABS => SubMethod::ABS,
105 TokenType::RABS => SubMethod::ABS,
106 _ => SubMethod::PAREN, }
108 }
109}
110
111impl Token {
112 pub fn new(typ: TokenType, literal: String, sub: Sub, index: (i32, i32)) -> Self {
114 Self {
115 typ,
116 literal,
117 sub,
118 index,
119 }
120 }
121
122 pub fn new_sub(tokens: Vec<Token>, method: SubMethod) -> Self {
124 Self {
125 typ: TokenType::SUBEXP,
126 literal: String::new(),
127 sub: Sub { tokens, method },
128 index: Token::unknown_index(),
129 }
130 }
131
132 pub fn new_pointer(i: usize, method: SubMethod) -> Self {
135 Self {
136 typ: TokenType::POINTER,
137 literal: format!("{i}"),
138 sub: Sub::new(Vec::new(), method),
139 index: Token::unknown_index(),
140 }
141 }
142
143 pub fn from(mut literal: String, index: (i32, i32)) -> Self {
146 let typ = if literal.is_number() {
147 TokenType::NUMBER
148 } else {
149 match literal.trim().to_lowercase().as_str() {
150 "+" => TokenType::PLUS,
151 "-" => TokenType::MINUS,
152 "*" | "•" => TokenType::PRODUCT,
153 "/" | ":" => TokenType::DIVIDE,
154 "(" => TokenType::LPAREN,
155 ")" => TokenType::RPAREN,
156 "%" => TokenType::PERCENTAGE,
157 "^" => TokenType::POWER,
158 "!" => TokenType::FACTORIAL,
159 "[" => TokenType::LABS,
160 "]" => TokenType::RABS,
161 "sqrt" => TokenType::SQRT,
162 "sin" => TokenType::SIN,
163 "cos" => TokenType::COS,
164 "tan" => TokenType::TAN,
165 "log" => TokenType::LOG,
166 "ln" => TokenType::LN,
167 "exp" => TokenType::EXP,
168 "floor" => TokenType::FLOOR,
169 "ceil" => TokenType::CEIL,
170 "round" => TokenType::ROUND,
171 _ => TokenType::ILLEGAL,
172 }
173 };
174
175 literal.retain(|c| !c.is_whitespace());
177
178 Self {
179 typ,
180 literal,
181 sub: Sub::empty(),
182 index,
183 }
184 }
185
186 pub fn empty() -> Self {
188 Self {
189 typ: TokenType::ILLEGAL,
190 literal: String::new(),
191 sub: Sub::empty(),
192 index: (0, 0),
193 }
194 }
195
196 pub fn to_submethod(&self) -> SubMethod {
201 match &self.typ {
202 TokenType::LABS => SubMethod::ABS,
203 TokenType::RABS => SubMethod::ABS,
204 _ => SubMethod::PAREN, }
206 }
207
208 pub fn unknown_index() -> (i32, i32) {
210 (-1, -1)
211 }
212
213 pub fn take_pointer_index(&self) -> Option<usize> {
216 if self.typ != TokenType::POINTER {
217 return None;
218 }
219
220 self.literal.as_str().parse::<usize>().ok()
221 }
222
223 pub fn is_illegal(&self) -> bool {
224 matches!(self.typ, TokenType::ILLEGAL)
225 }
226
227 pub fn is_number(&self) -> bool {
228 matches!(self.typ, TokenType::NUMBER)
229 }
230
231 pub fn is_lparen(&self) -> bool {
232 matches!(self.typ, TokenType::LPAREN)
233 }
234
235 pub fn is_rparen(&self) -> bool {
236 matches!(self.typ, TokenType::RPAREN)
237 }
238
239 pub fn is_pointer(&self) -> bool {
240 matches!(self.typ, TokenType::POINTER)
241 }
242
243 pub fn is_sub_exp(&self) -> bool {
244 matches!(self.typ, TokenType::SUBEXP)
245 }
246
247 pub fn is_power(&self) -> bool {
248 matches!(self.typ, TokenType::POWER)
249 }
250
251 pub fn is_labs(&self) -> bool {
252 matches!(self.typ, TokenType::LABS)
253 }
254
255 pub fn is_rabs(&self) -> bool {
256 matches!(self.typ, TokenType::RABS)
257 }
258
259 pub fn is_factorial(&self) -> bool {
260 matches!(self.typ, TokenType::FACTORIAL)
261 }
262
263 pub fn is_function(&self) -> bool {
264 matches!(
265 self.typ,
266 TokenType::SQRT
267 | TokenType::SIN
268 | TokenType::COS
269 | TokenType::TAN
270 | TokenType::LOG
271 | TokenType::LN
272 | TokenType::EXP
273 | TokenType::FLOOR
274 | TokenType::CEIL
275 | TokenType::ROUND
276 )
277 }
278
279 pub fn matchto(&self, t: &Token) -> bool {
283 let m = match self.typ {
284 TokenType::LPAREN => TokenType::RPAREN,
285 TokenType::LABS => TokenType::RABS,
286 _ => TokenType::ILLEGAL,
287 };
288
289 m == t.typ
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 use std::collections::HashMap;
297
298 #[test]
299 fn new_sub_struct() {
300 let test_data: Vec<Sub> = vec![
301 Sub {
302 tokens: Vec::new(),
303 method: SubMethod::PAREN,
304 },
305 Sub {
306 tokens: Vec::new(),
307 method: SubMethod::ABS,
308 },
309 ];
310
311 for sub in test_data {
312 let res = Sub::new(sub.clone().tokens, sub.clone().method);
313 assert_eq!(res, sub)
314 }
315 }
316
317 #[test]
318 fn empty() {
319 let test_data: Vec<Sub> = vec![Sub {
320 tokens: Vec::new(),
321 method: SubMethod::PAREN,
322 }];
323
324 for sub in test_data {
325 let res = Sub::empty();
326 assert_eq!(res, sub)
327 }
328 }
329
330 #[test]
331 fn to_submethod() {
332 assert_eq!(TokenType::LABS.to_submethod(), SubMethod::ABS);
333 assert_eq!(TokenType::RABS.to_submethod(), SubMethod::ABS);
334 assert_eq!(TokenType::LPAREN.to_submethod(), SubMethod::PAREN);
335 assert_eq!(TokenType::RPAREN.to_submethod(), SubMethod::PAREN);
336 }
337
338 #[test]
339 fn new() {
340 let test_data: Vec<Token> = vec![
341 Token {
342 typ: TokenType::PLUS,
343 literal: String::from("+"),
344 sub: Sub::empty(),
345 index: (0, 0),
346 },
347 Token {
348 typ: TokenType::MINUS,
349 literal: String::from("-"),
350 sub: Sub::empty(),
351 index: (1, 1),
352 },
353 Token {
354 typ: TokenType::DIVIDE,
355 literal: String::from("/"),
356 sub: Sub::empty(),
357 index: (2, 2),
358 },
359 Token {
360 typ: TokenType::SUBEXP,
361 literal: String::from(""),
362 sub: Sub::new(
363 Vec::from([
364 Token::from(String::from("2"), (0, 0)),
365 Token::from(String::from("+"), (1, 1)),
366 Token::from(String::from("5"), (2, 2)),
367 ]),
368 SubMethod::PAREN,
369 ),
370 index: (0, 2),
371 },
372 ];
373
374 for t in test_data {
375 let res = Token::new(
376 t.clone().typ,
377 t.clone().literal,
378 t.clone().sub,
379 t.clone().index,
380 );
381
382 assert_eq!(res.typ, t.clone().typ);
383 assert_eq!(res.literal, t.clone().literal);
384 assert_eq!(res.sub, t.clone().sub);
385 assert_eq!(res.index, t.clone().index);
386 }
387 }
388
389 #[test]
390 fn new_sub() {
391 let test_data: HashMap<Vec<String>, Token> = HashMap::from([
392 (
393 vec![String::from("4"), String::from("+"), String::from("2")],
394 Token {
395 typ: TokenType::SUBEXP,
396 literal: String::new(),
397 sub: Sub::new(
398 Vec::from([
399 Token::from(String::from("4"), (0, 0)),
400 Token::from(String::from("+"), (0, 0)),
401 Token::from(String::from("2"), (0, 0)),
402 ]),
403 SubMethod::PAREN,
404 ),
405 index: Token::unknown_index(),
406 },
407 ),
408 (
409 vec![String::from("2"), String::from("+"), String::from("+")],
410 Token {
411 typ: TokenType::SUBEXP,
412 literal: String::new(),
413 sub: Sub::new(
414 Vec::from([
415 Token::from(String::from("2"), (0, 0)),
416 Token::from(String::from("+"), (0, 0)),
417 Token::from(String::from("+"), (0, 0)),
418 ]),
419 SubMethod::PAREN,
420 ),
421 index: Token::unknown_index(),
422 },
423 ),
424 ]);
425
426 for (t, expected) in test_data {
427 let tokens = t.into_iter().map(|tt| Token::from(tt, (0, 0))).collect();
428 let res = Token::new_sub(tokens, SubMethod::PAREN);
429
430 assert_eq!(res.typ, expected.clone().typ);
431 assert_eq!(res.literal, expected.clone().literal);
432 assert_eq!(res.sub, expected.clone().sub);
433 assert_eq!(res.index, expected.clone().index);
434 }
435 }
436
437 #[test]
438 fn new_pointer() {
439 let test_data: HashMap<usize, Token> = HashMap::from([
440 (
441 0,
442 Token::new(
443 TokenType::POINTER,
444 String::from("0"),
445 Sub::new(Vec::new(), SubMethod::PAREN),
446 (-1, -1),
447 ),
448 ),
449 (
450 99,
451 Token::new(
452 TokenType::POINTER,
453 String::from("99"),
454 Sub::new(Vec::new(), SubMethod::ABS),
455 (-1, -1),
456 ),
457 ),
458 ]);
459
460 for (i, expected) in test_data {
461 let token: Token = Token::new_pointer(i, expected.clone().sub.method);
462 assert_eq!(token, expected);
463 }
464 }
465
466 #[test]
467 fn from() {
468 let test_data: HashMap<(String, (i32, i32)), Token> = HashMap::from([
469 (
470 (String::from("42"), (0, 1)),
471 Token::new(TokenType::NUMBER, String::from("42"), Sub::empty(), (0, 1)),
472 ),
473 (
474 (String::from("}"), (0, 0)),
475 Token::new(TokenType::ILLEGAL, String::from("}"), Sub::empty(), (0, 0)),
476 ),
477 (
478 (String::from("+"), (0, 0)),
479 Token::new(TokenType::PLUS, String::from("+"), Sub::empty(), (0, 0)),
480 ),
481 (
482 (String::from("-"), (0, 0)),
483 Token::new(TokenType::MINUS, String::from("-"), Sub::empty(), (0, 0)),
484 ),
485 (
486 (String::from("*"), (0, 0)),
487 Token::new(TokenType::PRODUCT, String::from("*"), Sub::empty(), (0, 0)),
488 ),
489 (
490 (String::from("•"), (0, 0)),
491 Token::new(TokenType::PRODUCT, String::from("•"), Sub::empty(), (0, 0)),
492 ),
493 (
494 (String::from("/"), (0, 0)),
495 Token::new(TokenType::DIVIDE, String::from("/"), Sub::empty(), (0, 0)),
496 ),
497 (
498 (String::from(":"), (0, 0)),
499 Token::new(TokenType::DIVIDE, String::from(":"), Sub::empty(), (0, 0)),
500 ),
501 (
502 (String::from("%"), (0, 0)),
503 Token::new(
504 TokenType::PERCENTAGE,
505 String::from("%"),
506 Sub::empty(),
507 (0, 0),
508 ),
509 ),
510 ]);
511
512 for (v, expected) in test_data {
513 let res = Token::from(v.0, v.1);
514 assert_eq!(res, expected);
515 }
516 }
517
518 #[test]
519 fn unknown_index() {
520 assert_eq!(Token::unknown_index(), (-1, -1));
521 }
522
523 #[test]
524 fn take_pointer_index() {
525 let test_data: HashMap<Option<usize>, Token> = HashMap::from([
526 (None, Token::from(String::from("25"), (0, 1))),
527 (None, Token::from(String::from("-"), (0, 0))),
528 (Some(0), Token::new_pointer(0, SubMethod::PAREN)),
529 (Some(9), Token::new_pointer(9, SubMethod::PAREN)),
530 ]);
531
532 for (expected, token) in test_data {
533 assert_eq!(expected, token.take_pointer_index());
534 }
535 }
536
537 #[test]
538 fn is_illegal() {
539 let test_data: HashMap<bool, Token> = HashMap::from([
540 (false, Token::from(String::from("-25"), (0, 1))),
541 (false, Token::from(String::from("-"), (0, 0))),
542 (true, Token::from(String::from("}"), (0, 0))),
543 (true, Token::from(String::from("|"), (0, 0))),
544 ]);
545
546 for (expected, token) in test_data {
547 assert_eq!(expected, token.is_illegal());
548 }
549 }
550
551 #[test]
552 fn is_lparen() {
553 let test_data: HashMap<bool, Token> = HashMap::from([
554 (false, Token::from(String::from("-25"), (0, 1))),
555 (false, Token::from(String::from("-"), (0, 0))),
556 (false, Token::from(String::from(")"), (0, 0))),
557 (true, Token::from(String::from("("), (0, 0))),
558 ]);
559
560 for (expected, token) in test_data {
561 assert_eq!(expected, token.is_lparen());
562 }
563 }
564
565 #[test]
566 fn is_rparen() {
567 let test_data: HashMap<bool, Token> = HashMap::from([
568 (false, Token::from(String::from("-25"), (0, 1))),
569 (false, Token::from(String::from("-"), (0, 0))),
570 (false, Token::from(String::from("("), (0, 0))),
571 (true, Token::from(String::from(")"), (0, 0))),
572 ]);
573
574 for (expected, token) in test_data {
575 assert_eq!(expected, token.is_rparen());
576 }
577 }
578
579 #[test]
580 fn is_pointer() {
581 let test_data: HashMap<bool, Token> = HashMap::from([
582 (false, Token::from(String::from("-25"), (0, 1))),
583 (false, Token::from(String::from("-"), (0, 0))),
584 (false, Token::from(String::from("("), (0, 0))),
585 (true, Token::new_pointer(0, SubMethod::PAREN)),
586 ]);
587
588 for (expected, token) in test_data {
589 assert_eq!(expected, token.is_pointer());
590 }
591 }
592
593 #[test]
594 fn is_sub_exp() {
595 let test_data: HashMap<bool, Token> = HashMap::from([
596 (false, Token::from(String::from("-25"), (0, 1))),
597 (false, Token::from(String::from("-"), (0, 0))),
598 (false, Token::from(String::from("("), (0, 0))),
599 (true, Token::new_sub(vec![], SubMethod::PAREN)),
600 ]);
601
602 for (expected, token) in test_data {
603 assert_eq!(expected, token.is_sub_exp());
604 }
605 }
606
607 #[test]
608 fn is_power() {
609 let test_data: HashMap<bool, Token> = HashMap::from([
610 (false, Token::from(String::from("-25"), (0, 1))),
611 (false, Token::from(String::from("-"), (0, 0))),
612 (false, Token::from(String::from("("), (0, 0))),
613 (true, Token::from(String::from("^"), (0, 0))),
614 ]);
615
616 for (expected, token) in test_data {
617 assert_eq!(expected, token.is_power());
618 }
619 }
620
621 #[test]
622 fn is_labs() {
623 let test_data: HashMap<bool, Token> = HashMap::from([
624 (false, Token::from(String::from("-25"), (0, 1))),
625 (false, Token::from(String::from("-"), (0, 0))),
626 (false, Token::from(String::from("]"), (0, 0))),
627 (true, Token::from(String::from("["), (0, 0))),
628 ]);
629
630 for (expected, token) in test_data {
631 assert_eq!(expected, token.is_labs());
632 }
633 }
634
635 #[test]
636 fn is_rabs() {
637 let test_data: HashMap<bool, Token> = HashMap::from([
638 (false, Token::from(String::from("-25"), (0, 1))),
639 (false, Token::from(String::from("-"), (0, 0))),
640 (false, Token::from(String::from("["), (0, 0))),
641 (true, Token::from(String::from("]"), (0, 0))),
642 ]);
643
644 for (expected, token) in test_data {
645 assert_eq!(expected, token.is_rabs());
646 }
647 }
648
649 #[test]
650 fn matchto() {
651 let test_data: HashMap<bool, (Token, Token)> = HashMap::from([
652 (
653 true,
654 (
655 Token::from(String::from("("), (0, 0)),
656 Token::from(String::from(")"), (0, 0)),
657 ),
658 ),
659 (
660 true,
661 (
662 Token::from(String::from("["), (0, 0)),
663 Token::from(String::from("]"), (0, 0)),
664 ),
665 ),
666 (
667 false,
668 (
669 Token::from(String::from("0"), (0, 0)),
670 Token::from(String::from("1"), (0, 0)),
671 ),
672 ),
673 ]);
674
675 for (expected, tokens) in test_data {
676 assert_eq!(expected, tokens.0.matchto(&tokens.1));
677 }
678 }
679}