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}
71
72// The main structure of input's each parsed character.
73#[derive(Clone, Debug, PartialEq)]
74pub struct Token {
75    pub typ: TokenType,
76    pub literal: String,
77    pub sub: Sub,
78    // the index range of concrete token.
79    // [-1] represents the unknown index.
80    // left side is the starting point and right side is ending point.
81    pub index: (i32, i32),
82}
83
84impl TokenType {
85    // A function to get valid sub-method from token type.
86    //
87    // - [TokenType::ABS] is [SubMethod::ABS].
88    // - [TokenType::LPAREN] & [TokenType::RPAREN] is [SubMethod::PAREN].
89    pub fn to_submethod(&self) -> SubMethod {
90        match self {
91            TokenType::LABS => SubMethod::ABS,
92            TokenType::RABS => SubMethod::ABS,
93            _ => SubMethod::PAREN, // + LPAREN, RPAREN
94        }
95    }
96}
97
98impl Token {
99    // Define a new Token value by providing all fields.
100    pub fn new(typ: TokenType, literal: String, sub: Sub, index: (i32, i32)) -> Self {
101        Self {
102            typ,
103            literal,
104            sub,
105            index,
106        }
107    }
108
109    // Create a new sub token model with just sub tokens.
110    pub fn new_sub(tokens: Vec<Token>, method: SubMethod) -> Self {
111        Self {
112            typ: TokenType::SUBEXP,
113            literal: String::new(),
114            sub: Sub { tokens, method },
115            index: Token::unknown_index(),
116        }
117    }
118
119    // Creates a pointer token, that newer will be used
120    // at normal token result.
121    pub fn new_pointer(i: usize, method: SubMethod) -> Self {
122        Self {
123            typ: TokenType::POINTER,
124            literal: format!("{}", i),
125            sub: Sub::new(Vec::new(), method),
126            index: Token::unknown_index(),
127        }
128    }
129
130    // Create a new token model from a literal.
131    // The type is decided automatically by checking it.
132    pub fn from(mut literal: String, index: (i32, i32)) -> Self {
133        let typ: TokenType;
134
135        if literal.is_number() {
136            typ = TokenType::NUMBER;
137        } else {
138            typ = match literal.trim() {
139                "+" => TokenType::PLUS,
140                "-" => TokenType::MINUS,
141                "*" => TokenType::PRODUCT,
142                "•" => TokenType::PRODUCT,
143                "/" => TokenType::DIVIDE,
144                ":" => TokenType::DIVIDE,
145                "(" => TokenType::LPAREN,
146                ")" => TokenType::RPAREN,
147                "%" => TokenType::PERCENTAGE,
148                "^" => TokenType::POWER,
149                "[" => TokenType::LABS,
150                "]" => TokenType::RABS,
151                _ => TokenType::ILLEGAL,
152            }
153        }
154
155        // Clear the white-spaces from literal.
156        literal.retain(|c| !c.is_whitespace());
157
158        return Self {
159            typ,
160            literal,
161            sub: Sub::empty(),
162            index,
163        };
164    }
165
166    // Creates an empty Token model.
167    pub fn empty() -> Self {
168        Self {
169            typ: TokenType::ILLEGAL,
170            literal: String::new(),
171            sub: Sub::empty(),
172            index: (0, 0),
173        }
174    }
175
176    // A function to get valid sub-method from token.
177    //
178    // - If [self.typ] is [ABS] is [SubMethod::ABS].
179    // - If [self.typ] is [LPAREN] or [RPAREN] is [SubMethod::PAREN].
180    pub fn to_submethod(&self) -> SubMethod {
181        match &self.typ {
182            TokenType::LABS => SubMethod::ABS,
183            TokenType::RABS => SubMethod::ABS,
184            _ => SubMethod::PAREN, // + LPAREN, RPAREN
185        }
186    }
187
188    // Returns the default unknown index representation.
189    pub fn unknown_index() -> (i32, i32) {
190        (-1, -1)
191    }
192
193    // Takes the pointer's index as [usize].
194    // If current token is not an pointer token, returned option will be [None].
195    pub fn take_pointer_index(&self) -> Option<usize> {
196        if self.typ != TokenType::POINTER {
197            return None;
198        }
199
200        match self.literal.as_str().parse::<usize>() {
201            Err(_) => return None,
202            Ok(v) => return Some(v),
203        };
204    }
205
206    // Checks if pointed token's type is illegal or not.
207    pub fn is_illegal(&self) -> bool {
208        match self.typ {
209            TokenType::ILLEGAL => true,
210            _ => false,
211        }
212    }
213
214    // Checks if pointed token's type is left-parentheses or not.
215    pub fn is_lparen(&self) -> bool {
216        match self.typ {
217            TokenType::LPAREN => true,
218            _ => false,
219        }
220    }
221
222    // Checks if pointed token's type is right-parentheses or not.
223    pub fn is_rparen(&self) -> bool {
224        match self.typ {
225            TokenType::RPAREN => true,
226            _ => false,
227        }
228    }
229
230    // Checks if pointed token's type is pointer or not.
231    pub fn is_pointer(&self) -> bool {
232        match self.typ {
233            TokenType::POINTER => true,
234            _ => false,
235        }
236    }
237
238    // Checks if pointed token's type is sub-expression or not.
239    pub fn is_sub_exp(&self) -> bool {
240        match self.typ {
241            TokenType::SUBEXP => true,
242            _ => false,
243        }
244    }
245
246    // Checks if pointed token's type is POWER or not.
247    pub fn is_power(&self) -> bool {
248        match self.typ {
249            TokenType::POWER => true,
250            _ => false,
251        }
252    }
253
254    // Checks if pointed token's type is LABS or not.
255    pub fn is_labs(&self) -> bool {
256        match self.typ {
257            TokenType::LABS => true,
258            _ => false,
259        }
260    }
261
262    // Checks if pointed token's type is RABS or not.
263    pub fn is_rabs(&self) -> bool {
264        match self.typ {
265            TokenType::RABS => true,
266            _ => false,
267        }
268    }
269
270    // Checks the "parentheses" family tokens' matching to each other.
271    // So, if pointed(self) token is left-parentheses
272    // given token(t) should be right-parentheses, if not returns false.
273    pub fn matchto(&self, t: Token) -> bool {
274        let m = match self.typ {
275            TokenType::LPAREN => TokenType::RPAREN,
276            TokenType::LABS => TokenType::RABS,
277            _ => TokenType::ILLEGAL,
278        };
279
280        return m == t.typ;
281    }
282}
283
284#[cfg(test)]
285mod tests {
286    use super::*;
287    use std::collections::HashMap;
288
289    #[test]
290    fn new_sub_struct() {
291        let test_data: Vec<Sub> = vec![
292            Sub {
293                tokens: Vec::new(),
294                method: SubMethod::PAREN,
295            },
296            Sub {
297                tokens: Vec::new(),
298                method: SubMethod::ABS,
299            },
300        ];
301
302        for sub in test_data {
303            let res = Sub::new(sub.clone().tokens, sub.clone().method);
304            assert_eq!(res, sub)
305        }
306    }
307
308    #[test]
309    fn empty() {
310        let test_data: Vec<Sub> = vec![Sub {
311            tokens: Vec::new(),
312            method: SubMethod::PAREN,
313        }];
314
315        for sub in test_data {
316            let res = Sub::empty();
317            assert_eq!(res, sub)
318        }
319    }
320
321    #[test]
322    fn to_submethod() {
323        assert_eq!(TokenType::LABS.to_submethod(), SubMethod::ABS);
324        assert_eq!(TokenType::RABS.to_submethod(), SubMethod::ABS);
325        assert_eq!(TokenType::LPAREN.to_submethod(), SubMethod::PAREN);
326        assert_eq!(TokenType::RPAREN.to_submethod(), SubMethod::PAREN);
327    }
328
329    #[test]
330    fn new() {
331        let test_data: Vec<Token> = vec![
332            Token {
333                typ: TokenType::PLUS,
334                literal: String::from("+"),
335                sub: Sub::empty(),
336                index: (0, 0),
337            },
338            Token {
339                typ: TokenType::MINUS,
340                literal: String::from("-"),
341                sub: Sub::empty(),
342                index: (1, 1),
343            },
344            Token {
345                typ: TokenType::DIVIDE,
346                literal: String::from("/"),
347                sub: Sub::empty(),
348                index: (2, 2),
349            },
350            Token {
351                typ: TokenType::SUBEXP,
352                literal: String::from(""),
353                sub: Sub::new(
354                    Vec::from([
355                        Token::from(String::from("2"), (0, 0)),
356                        Token::from(String::from("+"), (1, 1)),
357                        Token::from(String::from("5"), (2, 2)),
358                    ]),
359                    SubMethod::PAREN,
360                ),
361                index: (0, 2),
362            },
363        ];
364
365        for t in test_data {
366            let res = Token::new(
367                t.clone().typ,
368                t.clone().literal,
369                t.clone().sub,
370                t.clone().index,
371            );
372
373            assert_eq!(res.typ, t.clone().typ);
374            assert_eq!(res.literal, t.clone().literal);
375            assert_eq!(res.sub, t.clone().sub);
376            assert_eq!(res.index, t.clone().index);
377        }
378    }
379
380    #[test]
381    fn new_sub() {
382        let test_data: HashMap<Vec<String>, Token> = HashMap::from([
383            (
384                vec![String::from("4"), String::from("+"), String::from("2")],
385                Token {
386                    typ: TokenType::SUBEXP,
387                    literal: String::new(),
388                    sub: Sub::new(
389                        Vec::from([
390                            Token::from(String::from("4"), (0, 0)),
391                            Token::from(String::from("+"), (0, 0)),
392                            Token::from(String::from("2"), (0, 0)),
393                        ]),
394                        SubMethod::PAREN,
395                    ),
396                    index: Token::unknown_index(),
397                },
398            ),
399            (
400                vec![String::from("2"), String::from("+"), String::from("+")],
401                Token {
402                    typ: TokenType::SUBEXP,
403                    literal: String::new(),
404                    sub: Sub::new(
405                        Vec::from([
406                            Token::from(String::from("2"), (0, 0)),
407                            Token::from(String::from("+"), (0, 0)),
408                            Token::from(String::from("+"), (0, 0)),
409                        ]),
410                        SubMethod::PAREN,
411                    ),
412                    index: Token::unknown_index(),
413                },
414            ),
415        ]);
416
417        for (t, expected) in test_data {
418            let tokens = t.into_iter().map(|tt| Token::from(tt, (0, 0))).collect();
419            let res = Token::new_sub(tokens, SubMethod::PAREN);
420
421            assert_eq!(res.typ, expected.clone().typ);
422            assert_eq!(res.literal, expected.clone().literal);
423            assert_eq!(res.sub, expected.clone().sub);
424            assert_eq!(res.index, expected.clone().index);
425        }
426    }
427
428    #[test]
429    fn new_pointer() {
430        let test_data: HashMap<usize, Token> = HashMap::from([
431            (
432                0,
433                Token::new(
434                    TokenType::POINTER,
435                    String::from("0"),
436                    Sub::new(Vec::new(), SubMethod::PAREN),
437                    (-1, -1),
438                ),
439            ),
440            (
441                99,
442                Token::new(
443                    TokenType::POINTER,
444                    String::from("99"),
445                    Sub::new(Vec::new(), SubMethod::ABS),
446                    (-1, -1),
447                ),
448            ),
449        ]);
450
451        for (i, expected) in test_data {
452            let token: Token = Token::new_pointer(i, expected.clone().sub.method);
453            assert_eq!(token, expected);
454        }
455    }
456
457    #[test]
458    fn from() {
459        let test_data: HashMap<(String, (i32, i32)), Token> = HashMap::from([
460            (
461                (String::from("42"), (0, 1)),
462                Token::new(TokenType::NUMBER, String::from("42"), Sub::empty(), (0, 1)),
463            ),
464            (
465                (String::from("}"), (0, 0)),
466                Token::new(TokenType::ILLEGAL, String::from("}"), Sub::empty(), (0, 0)),
467            ),
468            (
469                (String::from("+"), (0, 0)),
470                Token::new(TokenType::PLUS, String::from("+"), Sub::empty(), (0, 0)),
471            ),
472            (
473                (String::from("-"), (0, 0)),
474                Token::new(TokenType::MINUS, String::from("-"), Sub::empty(), (0, 0)),
475            ),
476            (
477                (String::from("*"), (0, 0)),
478                Token::new(TokenType::PRODUCT, String::from("*"), Sub::empty(), (0, 0)),
479            ),
480            (
481                (String::from("•"), (0, 0)),
482                Token::new(TokenType::PRODUCT, String::from("•"), Sub::empty(), (0, 0)),
483            ),
484            (
485                (String::from("/"), (0, 0)),
486                Token::new(TokenType::DIVIDE, String::from("/"), Sub::empty(), (0, 0)),
487            ),
488            (
489                (String::from(":"), (0, 0)),
490                Token::new(TokenType::DIVIDE, String::from(":"), Sub::empty(), (0, 0)),
491            ),
492            (
493                (String::from("%"), (0, 0)),
494                Token::new(
495                    TokenType::PERCENTAGE,
496                    String::from("%"),
497                    Sub::empty(),
498                    (0, 0),
499                ),
500            ),
501        ]);
502
503        for (v, expected) in test_data {
504            let res = Token::from(v.0, v.1);
505            assert_eq!(res, expected);
506        }
507    }
508
509    #[test]
510    fn unknown_index() {
511        assert_eq!(Token::unknown_index(), (-1, -1));
512    }
513
514    #[test]
515    fn take_pointer_index() {
516        let test_data: HashMap<Option<usize>, Token> = HashMap::from([
517            (None, Token::from(String::from("25"), (0, 1))),
518            (None, Token::from(String::from("-"), (0, 0))),
519            (Some(0), Token::new_pointer(0, SubMethod::PAREN)),
520            (Some(9), Token::new_pointer(9, SubMethod::PAREN)),
521        ]);
522
523        for (expected, token) in test_data {
524            assert_eq!(expected, token.take_pointer_index());
525        }
526    }
527
528    #[test]
529    fn is_illegal() {
530        let test_data: HashMap<bool, Token> = HashMap::from([
531            (false, Token::from(String::from("-25"), (0, 1))),
532            (false, Token::from(String::from("-"), (0, 0))),
533            (true, Token::from(String::from("}"), (0, 0))),
534            (true, Token::from(String::from("|"), (0, 0))),
535        ]);
536
537        for (expected, token) in test_data {
538            assert_eq!(expected, token.is_illegal());
539        }
540    }
541
542    #[test]
543    fn is_lparen() {
544        let test_data: HashMap<bool, Token> = HashMap::from([
545            (false, Token::from(String::from("-25"), (0, 1))),
546            (false, Token::from(String::from("-"), (0, 0))),
547            (false, Token::from(String::from(")"), (0, 0))),
548            (true, Token::from(String::from("("), (0, 0))),
549        ]);
550
551        for (expected, token) in test_data {
552            assert_eq!(expected, token.is_lparen());
553        }
554    }
555
556    #[test]
557    fn is_rparen() {
558        let test_data: HashMap<bool, Token> = HashMap::from([
559            (false, Token::from(String::from("-25"), (0, 1))),
560            (false, Token::from(String::from("-"), (0, 0))),
561            (false, Token::from(String::from("("), (0, 0))),
562            (true, Token::from(String::from(")"), (0, 0))),
563        ]);
564
565        for (expected, token) in test_data {
566            assert_eq!(expected, token.is_rparen());
567        }
568    }
569
570    #[test]
571    fn is_pointer() {
572        let test_data: HashMap<bool, Token> = HashMap::from([
573            (false, Token::from(String::from("-25"), (0, 1))),
574            (false, Token::from(String::from("-"), (0, 0))),
575            (false, Token::from(String::from("("), (0, 0))),
576            (true, Token::new_pointer(0, SubMethod::PAREN)),
577        ]);
578
579        for (expected, token) in test_data {
580            assert_eq!(expected, token.is_pointer());
581        }
582    }
583
584    #[test]
585    fn is_sub_exp() {
586        let test_data: HashMap<bool, Token> = HashMap::from([
587            (false, Token::from(String::from("-25"), (0, 1))),
588            (false, Token::from(String::from("-"), (0, 0))),
589            (false, Token::from(String::from("("), (0, 0))),
590            (true, Token::new_sub(vec![], SubMethod::PAREN)),
591        ]);
592
593        for (expected, token) in test_data {
594            assert_eq!(expected, token.is_sub_exp());
595        }
596    }
597
598    #[test]
599    fn is_power() {
600        let test_data: HashMap<bool, Token> = HashMap::from([
601            (false, Token::from(String::from("-25"), (0, 1))),
602            (false, Token::from(String::from("-"), (0, 0))),
603            (false, Token::from(String::from("("), (0, 0))),
604            (true, Token::from(String::from("^"), (0, 0))),
605        ]);
606
607        for (expected, token) in test_data {
608            assert_eq!(expected, token.is_power());
609        }
610    }
611
612    #[test]
613    fn is_labs() {
614        let test_data: HashMap<bool, Token> = HashMap::from([
615            (false, Token::from(String::from("-25"), (0, 1))),
616            (false, Token::from(String::from("-"), (0, 0))),
617            (false, Token::from(String::from("]"), (0, 0))),
618            (true, Token::from(String::from("["), (0, 0))),
619        ]);
620
621        for (expected, token) in test_data {
622            assert_eq!(expected, token.is_labs());
623        }
624    }
625
626    #[test]
627    fn is_rabs() {
628        let test_data: HashMap<bool, Token> = HashMap::from([
629            (false, Token::from(String::from("-25"), (0, 1))),
630            (false, Token::from(String::from("-"), (0, 0))),
631            (false, Token::from(String::from("["), (0, 0))),
632            (true, Token::from(String::from("]"), (0, 0))),
633        ]);
634
635        for (expected, token) in test_data {
636            assert_eq!(expected, token.is_rabs());
637        }
638    }
639
640    #[test]
641    fn matchto() {
642        let test_data: HashMap<bool, (Token, Token)> = HashMap::from([
643            (
644                true,
645                (
646                    Token::from(String::from("("), (0, 0)),
647                    Token::from(String::from(")"), (0, 0)),
648                ),
649            ),
650            (
651                true,
652                (
653                    Token::from(String::from("["), (0, 0)),
654                    Token::from(String::from("]"), (0, 0)),
655                ),
656            ),
657            (
658                false,
659                (
660                    Token::from(String::from("0"), (0, 0)),
661                    Token::from(String::from("1"), (0, 0)),
662                ),
663            ),
664        ]);
665
666        for (expected, tokens) in test_data {
667            assert_eq!(expected, tokens.0.matchto(tokens.1));
668        }
669    }
670}