matdb 0.1.0

An experimental embedded SQL-like DBMS
Documentation
use crate::{
    ast::{Column, Expr, UnOp},
    value::Value,
};

use super::{lexer::Token, Parser, ParserError};

fn descape_string(s: &str) -> String {
    (s[1..s.len() - 1].to_string()).replace("''", "'")
}

impl<'a> Parser<'a> {
    pub(super) fn parse_column(&mut self) -> Result<Column, ParserError> {
        let first = self.try_consume_symbol()?;
        if self.try_consume_dot().is_ok() {
            let column = self.try_consume_symbol()?;
            Ok(Column(first.to_string(), column.to_string()))
        } else {
            // (kind of a) HACK: empty string is later replaced by the parser
            Ok(Column("".to_string(), first.to_string()))
        }
    }

    fn try_consume_atom(&mut self) -> Result<Expr, ParserError> {
        match self.peek()? {
            Token::Int(i) => {
                let _ = self.lexer.next().unwrap();
                Ok(Expr::Literal(Value::Int(i.parse().unwrap())))
            }
            Token::Question => {
                let _ = self.lexer.next().unwrap();
                Ok(Expr::Binding(self.try_consume_int()?))
            }
            Token::String(s) => {
                let _ = self.lexer.next().unwrap();
                Ok(Expr::Literal(Value::String(descape_string(&s))))
            }
            Token::Null => {
                let _ = self.lexer.next().unwrap();
                Ok(Expr::Literal(Value::Null))
            }
            Token::Symbol(_) => {
                let col = self.parse_column().map(|c| Expr::Column(c))?;

                if let Ok(Token::Arrow) = self.peek() {
                    let _ = self.lexer.next().unwrap();
                    let table = self.try_consume_mapping()?;

                    let rhs = match self.peek()? {
                        Token::OpenParen => Some({
                            let _ = self.lexer.next().unwrap();
                            let v = self.parse_expr_list()?;
                            self.try_consume_close_paren()?;
                            self.try_consume_arrow()?;
                            v
                        }),
                        Token::Arrow => {
                            let _ = self.lexer.next().unwrap();
                            None
                        }
                        token => Err(ParserError::Unexpected(token))?,
                    };
                    let expr = self.parse_expr()?;

                    Ok(Expr::Edge(vec![col], table, rhs, Box::new(expr)))
                } else {
                    Ok(col)
                }
            }
            Token::Not => {
                let _ = self.lexer.next().unwrap();
                self.try_consume_atom()
                    .map(|c| Expr::Unary(UnOp::Not, Box::new(c)))
            }
            Token::OpenParen => {
                let _ = self.lexer.next().unwrap();
                let inner = self.parse_expr_list()?;
                self.try_consume_close_paren()?;

                if let Ok(Token::Arrow) = self.peek() {
                    let _ = self.lexer.next().unwrap();
                    let table = self.try_consume_mapping()?;

                    let rhs = match self.peek()? {
                        Token::OpenParen => Some({
                            let _ = self.lexer.next().unwrap();
                            let v = self.parse_expr_list()?;
                            self.try_consume_close_paren()?;
                            self.try_consume_arrow()?;
                            v
                        }),
                        Token::Arrow => {
                            let _ = self.lexer.next().unwrap();
                            None
                        }
                        token => Err(ParserError::Unexpected(token))?,
                    };
                    let expr = self.parse_expr()?;

                    if inner.is_empty()
                        || rhs.as_ref().map(|rhs| rhs.is_empty()).unwrap_or_default()
                    {
                        return Err(ParserError::EdgeNoExpressions);
                    }

                    Ok(Expr::Edge(inner, table, rhs, Box::new(expr)))
                } else if inner.len() == 1 {
                    Ok(inner[0].clone())
                } else {
                    return Err(ParserError::UnexpectedEndOfEdge);
                }
            }
            token => Err(ParserError::Unexpected(token)),
        }
    }

    pub(super) fn parse_expr(&mut self) -> Result<Expr, ParserError> {
        let mut atoms = Vec::new();
        let mut bin_operators = Vec::new();

        atoms.push(self.try_consume_atom()?);

        while let Ok(binop) = self.try_consume_binary_operator() {
            bin_operators.push(binop);
            atoms.push(self.try_consume_atom()?);
        }

        while let Some((i, op)) = bin_operators
            .iter()
            .copied()
            .enumerate()
            .min_by(|a, b| a.1.precedence().cmp(&b.1.precedence()))
        {
            let lhs = i;
            let rhs = i + 1;

            bin_operators.remove(i);

            atoms[lhs] = Expr::Bin(
                Box::new(atoms[lhs].clone()),
                op,
                Box::new(atoms.remove(rhs)),
            );
        }

        Ok(atoms.pop().unwrap())
    }

    pub(super) fn parse_expr_list(&mut self) -> Result<Vec<Expr>, ParserError> {
        self.make_parse_list(Self::parse_expr)
    }
}