Skip to main content

mate_rs/
token.rs

1//
2// Copyright 2022-present theiskaa. All rights reserved.
3// Use of this source code is governed by MIT license
4// that can be found in the LICENSE file.
5//
6
7use crate::utils::ChUtils;
8
9// The structure model for high level sub expression implementations.
10// That could hold the actual source: tokens, and transformation method.
11//
12// For example: in case of parentheses and combinable
13// operations(*, /, %) method have to be [PAREN].
14// Or, in case of absolute values the method have to be [ABS],
15// to let calculator know the approach it has to take to
16// return final result of tokens.
17#[derive(Clone, Debug, PartialEq)]
18pub struct Sub {
19    pub tokens: Vec<Token>,
20    pub method: SubMethod,
21}
22
23impl Sub {
24    // Generate a new sub structure data.
25    pub fn new(tokens: Vec<Token>, method: SubMethod) -> Self {
26        Self { tokens, method }
27    }
28
29    // Generates a empty representation of token's sub element.
30    pub fn empty() -> Self {
31        Self {
32            tokens: Vec::new(),
33            method: SubMethod::PAREN,
34        }
35    }
36}
37
38// The method type of sub expression -> [Sub].
39// Used to decide the final calculation method of sub expression tokens.
40// For example, in case of [PAREN] the result will be default result of calculated [tokens].
41// Or, in case of [ABS] the result always gonna be positive value.
42#[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    // Internal
53    NUMBER,
54
55    // Sub related tokens
56    SUBEXP,
57    POINTER,
58    LPAREN,
59    RPAREN,
60    LABS,
61    RABS,
62
63    // Operations
64    PLUS,
65    MINUS,
66    PRODUCT,
67    DIVIDE,
68    PERCENTAGE,
69    POWER,
70    FACTORIAL,
71
72    // Math functions
73    SQRT,
74    SIN,
75    COS,
76    TAN,
77    LOG,
78    LN,
79    EXP,
80    FLOOR,
81    CEIL,
82    ROUND,
83}
84
85// The main structure of input's each parsed character.
86#[derive(Clone, Debug, PartialEq)]
87pub struct Token {
88    pub typ: TokenType,
89    pub literal: String,
90    pub sub: Sub,
91    // the index range of concrete token.
92    // [-1] represents the unknown index.
93    // left side is the starting point and right side is ending point.
94    pub index: (i32, i32),
95}
96
97impl TokenType {
98    // A function to get valid sub-method from token type.
99    //
100    // - [TokenType::ABS] is [SubMethod::ABS].
101    // - [TokenType::LPAREN] & [TokenType::RPAREN] is [SubMethod::PAREN].
102    pub fn to_submethod(&self) -> SubMethod {
103        match self {
104            TokenType::LABS => SubMethod::ABS,
105            TokenType::RABS => SubMethod::ABS,
106            _ => SubMethod::PAREN, // + LPAREN, RPAREN
107        }
108    }
109}
110
111impl Token {
112    // Define a new Token value by providing all fields.
113    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    // Create a new sub token model with just sub tokens.
123    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    // Creates a pointer token, that newer will be used
133    // at normal token result.
134    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    // Create a new token model from a literal.
144    // The type is decided automatically by checking it.
145    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        // Clear the white-spaces from literal.
176        literal.retain(|c| !c.is_whitespace());
177
178        Self {
179            typ,
180            literal,
181            sub: Sub::empty(),
182            index,
183        }
184    }
185
186    // Creates an empty Token model.
187    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    // A function to get valid sub-method from token.
197    //
198    // - If [self.typ] is [ABS] is [SubMethod::ABS].
199    // - If [self.typ] is [LPAREN] or [RPAREN] is [SubMethod::PAREN].
200    pub fn to_submethod(&self) -> SubMethod {
201        match &self.typ {
202            TokenType::LABS => SubMethod::ABS,
203            TokenType::RABS => SubMethod::ABS,
204            _ => SubMethod::PAREN, // + LPAREN, RPAREN
205        }
206    }
207
208    // Returns the default unknown index representation.
209    pub fn unknown_index() -> (i32, i32) {
210        (-1, -1)
211    }
212
213    // Takes the pointer's index as [usize].
214    // If current token is not an pointer token, returned option will be [None].
215    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    // Checks the "parentheses" family tokens' matching to each other.
280    // So, if pointed(self) token is left-parentheses
281    // given token(t) should be right-parentheses, if not returns false.
282    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}