自己从零实现一个脚本语言_ 01_scanner

在阅读 阅读了 制作解释器 http://www.craftinginterpreters.com/

中文版 非常感谢 中文译者 https://github.com/GuoYaxiang/craftinginterpreters_zh

参考了 https://github.com/mariosangiorgio/rulox

看着他的代码, 我在思考他为甚这么实现, 随后模仿了他的实现, 我有没有更好的办法呢?

收获了 增加了一些rust的熟练度, 提高了自己手撕源码的能力, 理解了编程语言的本质, 掌握了字符串的处理, 加深了自己对递归的使用

use std::str::Chars;
use itertools::{multipeek, MultiPeek};


// 判断是否是空白字符
fn is_whitespace(c: char) -> bool {
    match c {
        ' ' | '\r' | '\t' | '\n' => true,
        _ => false,
    }
}

// 判断是否是数字字符
fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

// 判断是否是英文字母或者下划线
fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

// 判断是否是字母数字或者下划线
fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}


#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,   // (
    RightParen,  // )
    LeftBrace,   // {
    RightBrace,  // }
    Comma,       // ,
    Dot,         // .
    Minus,       // -
    Plus,        // +
    Semicolon,   // ;
    Slash,       // /
    Star,        // *
    Bang,        // !
    BangEqual,   // !=
    Equal,       // =
    EqualEqual,  // ==
    Greater,     // >
    GreaterEqual,// >=
    Less,        // <
    LessEqual,   // <=
    Identifier(String),    // 标识符或者变量名
    StringLiteral(String), // 字符串
    NumberLiteral(f64),    // 数值

    And,         // and
    Class,       // class
    Else,        // else
    False,       // false
    Def,         // def
    For,         // for
    If,          // if
    Null,        // Null
    Or,          // or
    Print,       // print
    Return,      // return
    Super,       // super
    This,        // This
    True,        // true
    Var,         // var
    While,       // while
    Comment,     // 注释
    Whitespace,  // 空白
    Continue,    // continue
    Break        // break
}


// 表示源文件中 token所在的位置
#[derive(Debug, Clone, Copy)]
pub struct TokenPosition {
    pub line: usize,
    column: usize,
}

impl TokenPosition {
    fn new() -> Self {
        TokenPosition {
            line: 1,
            column: 1,
        }
    }
}

#[derive(Debug, Clone)]
pub enum ScannerError {
    MissingStringTerminator(TokenPosition), // 字符串没有以 " 结尾
    UnexpectedCharacter(char, TokenPosition),  // 未知字符
}

pub type Lexeme = String;

// 记号流
#[derive(Debug)]
pub struct TokenWithContext {
    pub token: Token, // 记号具体的类型
    pub lexeme: String, // 源字符
    pub position: TokenPosition,  // 标记该记号第一个字符在源文件的位置
}


pub struct Scanner<'a> {
    source: MultiPeek<Chars<'a>>, // 源文件
    current_position: TokenPosition, // 扫描器当前位置
    current_lexeme: String,  // 扫描器当前扫描的片段
}

impl<'a> Scanner<'a> {
    pub fn new(source: &'a str) -> Self {
        Scanner {
            source: multipeek(source.chars()),
            current_position: TokenPosition::new(),
            current_lexeme: String::new(),
        }
    }
}

impl<'a> Scanner<'a> {
    // 获得数字字符
    fn number(&mut self) -> Token {
        // 从当前位置 推进 最远非数字字符
        self.advance_while(&is_digit);

        // 如果后面跟着一个小数点, 且小数点后跟着一个数字, 则继续推进
        if self.peek_check2(&|c| c == '.', &is_digit) {
            // 推进一个小数点
            self.advance();
            // 循环小数结尾
            self.advance_while(&is_digit);
        }
        let num = self.current_lexeme.parse::<f64>().unwrap();

        Token::NumberLiteral(num)
    }

    // 获得 双引号内 字符串字符
    fn string(&mut self) -> Result<Token, ScannerError> {
        // 从 " 开始 一直找到 " 或者 \n 的前一个字符
        self.advance_while(&|c| c != '\n' && c != '"');

        // 校验下一个字符是否为 "
        if !self.advance_if_match('"') {  // 如果 不为 " 未知字符出错
            return Err(ScannerError::MissingStringTerminator(self.current_position));
        }

        // 获得 双引号 内的字符串字符
        // let s = self.current_lexeme
        //     .chars()
        //     .skip(1)
        //     .take(self.current_lexeme.len() - 2)
        //     .collect::<String>();

        let s = self.current_lexeme[1..self.current_lexeme.len() - 1].to_string();

        return Ok(Token::StringLiteral(s));
    }

    // 判断是否是关键字
    fn identifier(&mut self) -> Token {
        // 循环推进字符
        self.advance_while(&is_alphanumeric);

        match self.current_lexeme.as_str() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "def" => Token::Def,
            "if" => Token::If,
            "Null" => Token::Null,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            "break" => Token::Break,
            "continue" => Token::Continue,
            identifier => Token::Identifier(identifier.into()),
        }
    }

    // 推进一个字符, 并返回外部
    fn advance(&mut self) -> Option<char> {
        let next_char = self.source.next();

        if let Some(c) = next_char {
            self.current_lexeme.push(c); // 增加该字符到当前扫描的片段

            if c == '\n' {  // 换行 或者空行处理
                self.current_position.line += 1;
                self.current_position.column = 1;
            } else {
                self.current_position.column += 1;
            }
        }
        next_char
    }

    // 接收一个闭包, 传入一个前看字符 到闭包中进行校验
    fn peek_check1(&mut self, closure: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&c) => {
                return closure(c);
            }
            None => {
                return false;
            }
        }
    }

    // 接收两个闭包, 传入前看两个字符, 依次校验
    fn peek_check2(&mut self, closure1: &Fn(char) -> bool, closure2: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();

        match self.source.peek() {
            Some(&c1) => {
                match self.source.peek() {
                    Some(&c2) => {
                        return closure1(c1) && closure2(c2);
                    }
                    None => return false
                }
            }
            None => {
                return false;
            }
        }
    }

    // 外部传进来一个字符, 和前看字符比较, 如果相同, 消耗掉返回true
    fn advance_if_match(&mut self, expected: char) -> bool {
        if self.peek_check1(&|c| c == expected) {
            self.advance();
            return true;
        }
        false
    }

    // 循环 前看字符 并传入闭包中, 如果前看字符满足闭包条件返回true, 则推进一个字符, 直到 闭包返回 false
    fn advance_while(&mut self, closure: &Fn(char) -> bool) {
        while self.peek_check1(closure) {
            self.advance();  // 如果前看1个字符 符合闭包, 则推进一个字符
        }
    }

    fn scan_next(&mut self) -> Option<Result<TokenWithContext, ScannerError>> {
        let current_position = self.current_position;
        self.current_lexeme.clear();  // 每次执行扫描, 清除上次扫描保存的字符

        // 获得下个字符
        let next_char = match self.advance() {
            Some(c) => c,
            None => return None,
        };

        // 根据字符分类
        let token_ret = match next_char {
            '(' => Ok(Token::LeftParen),
            ')' => Ok(Token::RightParen),
            '{' => Ok(Token::LeftBrace),
            '}' => Ok(Token::RightBrace),
            ',' => Ok(Token::Comma),
            '.' => Ok(Token::Dot),
            '-' => Ok(Token::Minus),
            '+' => Ok(Token::Plus),
            ';' => Ok(Token::Semicolon),
            '*' => Ok(Token::Star),
            '!' => {
                if self.advance_if_match('=') {
                    Ok(Token::BangEqual)
                } else {
                    Ok(Token::Bang)
                }
            }
            '=' => {
                if self.advance_if_match('=') {
                    Ok(Token::EqualEqual)
                } else {
                    Ok(Token::Equal)
                }
            }
            '>' => {
                if self.advance_if_match('=') {
                    Ok(Token::GreaterEqual)
                } else {
                    Ok(Token::Greater)
                }
            }
            '<' => {
                if self.advance_if_match('=') {
                    Ok(Token::LessEqual)
                } else {
                    Ok(Token::Less)
                }
            }
            '/' => {
                if self.advance_if_match('/') {
                    // 注释 需要获取到这行的最后一个字符
                    self.advance_while(&|c| c != '\n');
                    Ok(Token::Comment)
                } else {
                    Ok(Token::Slash)
                }
            }
            '"' => self.string(),
            c if is_whitespace(c) => {
                Ok(Token::Whitespace)
            }
            c if is_digit(c) => Ok(self.number()),
            c if is_alpha(c) => Ok(self.identifier()),  // 最后校验是否是关键字

            c => Err(ScannerError::UnexpectedCharacter(c, self.current_position))
        };


        Some(token_ret.map(|token| {
            TokenWithContext {
                token: token,
                lexeme: self.current_lexeme.clone(),
                position: current_position,
            }
        }))
    }
}


// 迭代获取扫描到的 TokenWithContext
impl<'a> Iterator for Scanner<'a> {
    type Item = Result<TokenWithContext, ScannerError>;
    fn next(&mut self) -> Option<Self::Item> {
        self.scan_next()
    }
}


#[cfg(test)]
mod tests {
    use crate::Scanner;

    #[test]
    fn it_works() {
        let mut s = Scanner::new(r#"1+2+3"#);
        for i in s {
            println!("{:?}", i);
        }
    }
}