refactoring Scanner
change tpkuplrxroqtttxusswzrkvzxvrwtxus
commit 3c3782e30cbbb13cfcc73a5ccf1a3e5b1565e3e0
author Alpha Chen <alpha@kejadlen.dev>
date
parent uxqmmzyn
diff --git a/rust/src/scanner.rs b/rust/src/scanner.rs
index abfe0d3..9a897d8 100644
--- a/rust/src/scanner.rs
+++ b/rust/src/scanner.rs
@@ -4,8 +4,8 @@ pub struct Scanner<'a> {
     line: usize,
 }
 
-fn is_alpha(c: char) -> bool {
-    c.is_ascii_alphabetic() || c == '_'
+fn is_alpha(c: &char) -> bool {
+    c.is_ascii_alphabetic() || *c == '_'
 }
 
 impl<'a> Scanner<'a> {
@@ -17,44 +17,32 @@ impl<'a> Scanner<'a> {
         }
     }
 
-    fn advance(&mut self) -> char {
+    fn advance(&mut self) -> Option<char> {
         self.current += 1;
-        self.source[self.current - 1] as char
+        self.source.get(self.current - 1).map(|&x| x as char)
     }
 
-    fn is_match(&mut self, expected: char) -> bool {
-        if self.peek() != Some(expected) {
-            return false;
+    fn advance_if<F: Fn(&char) -> bool>(&mut self, f: F) -> bool {
+        if matches!(self.peek(), Some(c) if f(&c)) {
+            self.current += 1;
+            true
+        } else {
+            false
         }
-
-        self.current += 1;
-        true
     }
 
     fn skip_whitespace(&mut self) {
         loop {
-            match self.peek() {
-                Some(' ') | Some('\r') | Some('\t') => {
-                    self.advance();
-                    break;
-                }
-                Some('\n') => {
-                    self.line += 1;
-                    self.advance();
-                    break;
-                }
-                Some('/') => {
-                    if matches!(self.peek_next(), Some(c) if c == '/') {
-                        while matches!(self.peek(), Some(c) if c != '\n') {
-                            self.advance();
-                        }
-                    } else {
-                        return;
-                    }
-                }
-                _ => {
-                    return;
-                }
+            while self.advance_if(|&c| c == ' ' || c == '\r' || c == '\t') {}
+
+            if self.peek() == Some('/') && self.peek_next() == Some('/') {
+                while self.advance_if(|&c| c != '\n') {}
+            }
+
+            if self.advance_if(|&c| c == '\n') {
+                self.line += 1;
+            } else {
+                return;
             }
         }
     }
@@ -68,42 +56,34 @@ impl<'a> Scanner<'a> {
     }
 
     fn string(&mut self) -> Token<'a> {
-        while matches!(self.peek(), Some(c) if c != '"') {
-            if self.peek() == Some('\n') {
-                self.line += 1;
+        loop {
+            match self.advance() {
+                Some('"') => break,
+                Some('\n') => {
+                    self.line += 1;
+                }
+                Some(_) => {}
+                None => return Token::error(self, "Unterminated string."),
             }
-            self.advance();
         }
 
-        if self.peek().is_none() {
-            // Maybe this should return an Err?
-            return Token::error(&self, "Unterminated string.");
-        }
-
-        self.advance();
         Token::new(self, TokenKind::String)
     }
 
     fn number(&mut self) -> Token<'a> {
-        while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
-            self.advance();
-        }
+        while self.advance_if(char::is_ascii_digit) {}
 
         if self.peek() == Some('.') && matches!(self.peek_next(), Some(c) if c.is_ascii_digit()) {
             self.advance();
 
-            while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
-                self.advance();
-            }
+            while self.advance_if(char::is_ascii_digit) {}
         }
 
         Token::new(self, TokenKind::Number)
     }
 
     fn identifier(&mut self) -> Token<'a> {
-        while matches!(self.peek(), Some(c) if is_alpha(c) || c.is_ascii_digit()) {
-            self.advance();
-        }
+        while self.advance_if(|c| is_alpha(c) || c.is_ascii_digit()) {}
 
         Token::new(self, self.identifier_type())
     }
@@ -143,55 +123,59 @@ impl<'a> Iterator for Scanner<'a> {
     fn next(&mut self) -> Option<Self::Item> {
         self.skip_whitespace();
 
-        if self.peek().is_none() {
-            return Some(Token::new(self, TokenKind::Eof));
-        }
-
-        Some(match self.advance() {
-            c if is_alpha(c) => self.identifier(),
-            c if c.is_ascii_digit() => self.number(),
-            '(' => Token::new(self, TokenKind::LeftParen),
-            ')' => Token::new(self, TokenKind::RightParen),
-            '{' => Token::new(self, TokenKind::LeftBrace),
-            '}' => Token::new(self, TokenKind::RightBrace),
-            ';' => Token::new(self, TokenKind::Semicolon),
-            ',' => Token::new(self, TokenKind::Comma),
-            '.' => Token::new(self, TokenKind::Dot),
-            '-' => Token::new(self, TokenKind::Minus),
-            '+' => Token::new(self, TokenKind::Plus),
-            '/' => Token::new(self, TokenKind::Slash),
-            '*' => Token::new(self, TokenKind::Star),
-            '!' => {
-                if self.is_match('=') {
-                    Token::new(self, TokenKind::BangEqual)
-                } else {
-                    Token::new(self, TokenKind::Bang)
+        self.advance()
+            .map(|c| match c {
+                c if is_alpha(&c) => self.identifier(),
+                c if c.is_ascii_digit() => self.number(),
+                '(' => Token::new(self, TokenKind::LeftParen),
+                ')' => Token::new(self, TokenKind::RightParen),
+                '{' => Token::new(self, TokenKind::LeftBrace),
+                '}' => Token::new(self, TokenKind::RightBrace),
+                ';' => Token::new(self, TokenKind::Semicolon),
+                ',' => Token::new(self, TokenKind::Comma),
+                '.' => Token::new(self, TokenKind::Dot),
+                '-' => Token::new(self, TokenKind::Minus),
+                '+' => Token::new(self, TokenKind::Plus),
+                '/' => Token::new(self, TokenKind::Slash),
+                '*' => Token::new(self, TokenKind::Star),
+                '!' => {
+                    if self.advance_if(|&c| c == '=') {
+                        Token::new(self, TokenKind::BangEqual)
+                    } else {
+                        Token::new(self, TokenKind::Bang)
+                    }
                 }
-            }
-            '=' => {
-                if self.is_match('=') {
-                    Token::new(self, TokenKind::EqualEqual)
-                } else {
-                    Token::new(self, TokenKind::Equal)
+                '=' => {
+                    if self.advance_if(|&c| c == '=') {
+                        Token::new(self, TokenKind::EqualEqual)
+                    } else {
+                        Token::new(self, TokenKind::Equal)
+                    }
                 }
-            }
-            '<' => {
-                if self.is_match('=') {
-                    Token::new(self, TokenKind::LessEqual)
-                } else {
-                    Token::new(self, TokenKind::Less)
+                '<' => {
+                    if self.advance_if(|&c| c == '=') {
+                        Token::new(self, TokenKind::LessEqual)
+                    } else {
+                        Token::new(self, TokenKind::Less)
+                    }
                 }
-            }
-            '>' => {
-                if self.is_match('=') {
-                    Token::new(self, TokenKind::GreaterEqual)
-                } else {
-                    Token::new(self, TokenKind::Greater)
+                '>' => {
+                    if self.advance_if(|&c| c == '=') {
+                        Token::new(self, TokenKind::GreaterEqual)
+                    } else {
+                        Token::new(self, TokenKind::Greater)
+                    }
                 }
-            }
-            '"' => self.string(),
-            _ => Token::error(self, "Unexpected character."),
-        })
+                '"' => self.string(),
+                _ => Token::error(self, "Unexpected character."),
+            })
+            .or({
+                Some(Token {
+                    kind: TokenKind::Eof,
+                    value: "",
+                    line: self.line,
+                })
+            })
     }
 }