eight 1.0.0-alpha.2

Modular asynchronous embedded key-value database
Documentation
use super::token::Token;
use std::mem;

#[derive(Default, PartialEq, Debug)]
enum State {
    String,
    #[default]
    Identifier,
    Comment,
}

#[derive(Default)]
pub(super) struct Lexer {
    source: String,
    line: usize,
    column: usize,
    state: State,
    tokens: Vec<Token>,
    temp: String,
}

impl Lexer {
    pub fn new(source: String) -> Self {
        Self {
            source,
            line: 1,
            ..Default::default()
        }
    }

    pub fn execute(&mut self) {
        let source = mem::take(&mut self.source);

        for character in source.chars() {
            match self.state {
                State::String => self.handle_string(character),
                State::Identifier => self.handle_identifier(character),
                State::Comment => self.handle_comment(character),
            }

            if character == '\n' {
                self.line += 1;
                self.column = 0;
            } else {
                self.column += 1;
            }
        }

        self.make_token();
    }

    pub fn collect(&mut self) -> Vec<Vec<Token>> {
        let tokens = mem::take(&mut self.tokens);

        let mut new_tokens = Vec::new();
        let mut temporary = Vec::new();

        for token in tokens {
            if &token.value == ";" {
                new_tokens.push(mem::take(&mut temporary));
            } else {
                temporary.push(token);
            }
        }

        new_tokens
    }

    fn handle_string(&mut self, character: char) {
        if character == '"' {
            match self.temp.pop() {
                Some('\\') => self.temp.push(character),
                Some(value) => {
                    self.temp.push(value);
                    self.make_token();
                }
                None => self.make_token(),
            }
        } else {
            self.temp.push(character);
        }
    }

    fn handle_identifier(&mut self, character: char) {
        match character {
            ' ' | '\t' | '\n' | '\r' => self.make_token(),
            ';' => {
                self.make_token();

                self.temp = ";".to_string();
                self.make_token();
            }
            '"' => self.state = State::String,
            '#' => self.state = State::Comment,
            other => self.temp.push(other),
        }
    }

    fn handle_comment(&mut self, character: char) {
        if character == '\n' {
            self.state = State::default();
        }
    }

    fn make_token(&mut self) {
        let old_state = mem::take(&mut self.state);
        if self.temp.is_empty() && old_state != State::String {
            return;
        }

        let token = Token {
            value: mem::take(&mut self.temp),
            line: self.line,
            column: self.column,
        };

        self.tokens.push(token);
    }
}

pub(super) fn lex(source: String) -> Vec<Vec<Token>> {
    let mut lexer = Lexer::new(source);
    lexer.execute();

    lexer.collect()
}