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