Untitled

mail@pastecode.io avatarunknown
rust
a month ago
6.0 kB
6
Indexable
Never
pub mod scanner {
    #[derive(Debug, PartialEq, Clone)]
    pub enum TokenType {
        // single-character tokens
        LeftParen,
        RightParen,
        Comma,
        Dot,
        SemiColon,
        Plus,
        Minus,
        Star,
        Slash,
        // one or two character tokens
        Bang,
        BangEqual,
        Equal,
        EqualEqual,
        Greater,
        GreaterEqual,
        Less,
        LessEqual,
        // literals
        Identifier,
        String,
        Number,
        // keywords
        Print,
        Let,
        // other
        EOF,
    }
    #[derive(Debug, PartialEq, Clone)]
    pub enum Literal {
        String(String),
        Number(f64),
        Boolean(bool),
        Nil,
        None,
    }

    #[derive(Debug, PartialEq, Clone)]
    pub struct Token {
        pub token_type: TokenType,
        pub lexeme: String,
        pub literal: Literal,
        pub line: u32,
    }

    impl Token {
        pub fn new(token_type: TokenType, lexeme: String, literal: Literal, line: u32) -> Token {
            Token {
                token_type,
                lexeme,
                literal,
                line,
            }
        }
    }

    #[derive(Debug, PartialEq, Clone)]
    pub struct Scanner {
        source: String,
        tokens: Vec<Token>,
        start: usize,
        current: usize,
        line: u32,
    }

    impl Scanner {
        fn is_at_end(&mut self) -> bool {
            self.current >= self.source.len()
        }
        fn advance(&mut self) -> char {
            self.current += 1;
            self.source.chars().nth(self.current - 1).unwrap()
        }
        fn peek(&mut self) -> char {
            if self.is_at_end() {
                return '\0';
            }
            self.source.chars().nth(self.current).unwrap()
        }
        fn peek_next(&mut self) -> char {
            if self.current + 1 >= self.source.len() {
                return '\0';
            }
            self.source.chars().nth(self.current + 1).unwrap()
        }
        fn previous(&mut self) -> char {
            self.source.chars().nth(self.current - 1).unwrap()
        }

        fn skip_whitespace(&mut self) {
            loop {
                let c = self.peek();
                match c {
                    ' ' | '\r' | '\t' => {
                        self.advance();
                    }
                    '\n' => {
                        self.line += 1;
                        self.advance();
                    }
                    '/' => {
                        if self.peek_next() == '/' {
                            // A comment goes until the end of the line.
                            while self.peek() != '\n' && !self.is_at_end() {
                                self.advance();
                            }
                        } else {
                            return;
                        }
                    }
                    _ => return,
                }
            }
        }
    }

    impl Scanner {
        fn add_token(&mut self, token_type: TokenType) {
            let text = self.source[self.start..self.current].to_string();
            self.tokens
                .push(Token::new(token_type, text, Literal::None, self.line));
        }
        fn add_token_literal(&mut self, token_type: TokenType, literal: Literal) {
            let text = self.source[self.start..self.current].to_string();
            self.tokens
                .push(Token::new(token_type, text, literal, self.line));
        }

        fn number(&mut self) {
            while self.peek().is_numeric() {
                self.advance();
            }

            // Look for a fractional part.
            if self.peek() == '.' && self.peek_next().is_numeric() {
                // Consume the "."
                self.advance();

                while self.peek().is_numeric() {
                    self.advance();
                }
            }

            let number = self.source[self.start..self.current]
                .parse::<f64>()
                .unwrap();
            self.add_token_literal(TokenType::Number, Literal::Number(number));
        }

        fn scan_token(&mut self) {
            self.skip_whitespace();
            self.start = self.current;
            let c = self.advance();

            match c {
                '(' => self.add_token(TokenType::LeftParen),
                ')' => self.add_token(TokenType::RightParen),
                ',' => self.add_token(TokenType::Comma),
                '.' => self.add_token(TokenType::Dot),
                ';' => self.add_token(TokenType::SemiColon),
                '+' => self.add_token(TokenType::Plus),
                '-' => self.add_token(TokenType::Minus),
                '*' => self.add_token(TokenType::Star),
                '/' => self.add_token(TokenType::Slash),

                _ => {
                    if c.is_numeric() {
                        self.number();
                        return;
                    }

                    println!("Unexpected character: {}", c);
                }
            }
        }
    }

    impl Scanner {
        pub fn new(source: String) -> Scanner {
            Scanner {
                source,
                tokens: Vec::new(),
                start: 0,
                current: 0,
                line: 1,
            }
        }

        pub fn scan_tokens(&mut self) -> Vec<Token> {
            println!("Scanning: {:?}", self.source);

            while !self.is_at_end() {
                self.scan_token();
            }

            return self.tokens.clone();
        }
    }
}

fn main() {
    //read src/test.script
    let source =
        std::fs::read_to_string("src/test.script").expect("Something went wrong reading the file");

    let mut scanner = scanner::Scanner::new(source);
    let tokens = scanner.scan_tokens();
    for token in tokens {
        println!("{:?}", token);
    }
}