use crate::ast::{BinaryOp, UnaryOp};
use crate::lexer::{Keyword, TokenKind};
#[must_use]
pub const fn prefix_binding_power(kind: &TokenKind) -> Option<u8> {
match kind {
TokenKind::Minus => Some(15),
TokenKind::BitNot => Some(15),
TokenKind::Keyword(Keyword::Not) => Some(3),
TokenKind::Integer(_)
| TokenKind::Float(_)
| TokenKind::LeftParen
| TokenKind::Question
| TokenKind::Colon
| TokenKind::String(_)
| TokenKind::Blob(_)
| TokenKind::Identifier(_)
| TokenKind::Star => Some(0),
TokenKind::Keyword(
Keyword::Null
| Keyword::True
| Keyword::False
| Keyword::Case
| Keyword::Cast
| Keyword::Exists
| Keyword::Count
| Keyword::Sum
| Keyword::Avg
| Keyword::Min
| Keyword::Max
| Keyword::Coalesce
| Keyword::Nullif,
) => Some(0),
_ => None,
}
}
#[must_use]
pub const fn infix_binding_power(kind: &TokenKind) -> Option<(u8, u8)> {
match kind {
TokenKind::Keyword(Keyword::Or) => Some((1, 2)),
TokenKind::Keyword(Keyword::And) => Some((3, 4)),
TokenKind::Eq
| TokenKind::NotEq
| TokenKind::Lt
| TokenKind::LtEq
| TokenKind::Gt
| TokenKind::GtEq => Some((5, 6)),
TokenKind::Keyword(Keyword::Is | Keyword::In | Keyword::Between | Keyword::Like) => {
Some((5, 6))
}
TokenKind::BitOr => Some((7, 8)),
TokenKind::BitAnd => Some((9, 10)),
TokenKind::LeftShift | TokenKind::RightShift => Some((11, 12)),
TokenKind::Plus | TokenKind::Minus | TokenKind::Concat => Some((13, 14)),
TokenKind::Star | TokenKind::Slash | TokenKind::Percent => Some((15, 16)),
_ => None,
}
}
#[must_use]
#[allow(dead_code)]
pub const fn postfix_binding_power(kind: &TokenKind) -> Option<u8> {
match kind {
TokenKind::Keyword(Keyword::Is) => Some(17),
TokenKind::Keyword(Keyword::As) => Some(17),
_ => None,
}
}
#[must_use]
pub const fn token_to_binary_op(kind: &TokenKind) -> Option<BinaryOp> {
match kind {
TokenKind::Plus => Some(BinaryOp::Add),
TokenKind::Minus => Some(BinaryOp::Sub),
TokenKind::Star => Some(BinaryOp::Mul),
TokenKind::Slash => Some(BinaryOp::Div),
TokenKind::Percent => Some(BinaryOp::Mod),
TokenKind::Eq => Some(BinaryOp::Eq),
TokenKind::NotEq => Some(BinaryOp::NotEq),
TokenKind::Lt => Some(BinaryOp::Lt),
TokenKind::LtEq => Some(BinaryOp::LtEq),
TokenKind::Gt => Some(BinaryOp::Gt),
TokenKind::GtEq => Some(BinaryOp::GtEq),
TokenKind::Keyword(Keyword::And) => Some(BinaryOp::And),
TokenKind::Keyword(Keyword::Or) => Some(BinaryOp::Or),
TokenKind::Concat => Some(BinaryOp::Concat),
TokenKind::Keyword(Keyword::Like) => Some(BinaryOp::Like),
TokenKind::BitAnd => Some(BinaryOp::BitAnd),
TokenKind::BitOr => Some(BinaryOp::BitOr),
TokenKind::LeftShift => Some(BinaryOp::LeftShift),
TokenKind::RightShift => Some(BinaryOp::RightShift),
_ => None,
}
}
#[must_use]
pub const fn token_to_unary_op(kind: &TokenKind) -> Option<UnaryOp> {
match kind {
TokenKind::Minus => Some(UnaryOp::Neg),
TokenKind::Keyword(Keyword::Not) => Some(UnaryOp::Not),
TokenKind::BitNot => Some(UnaryOp::BitNot),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_precedence_ordering() {
let add_bp = infix_binding_power(&TokenKind::Plus).unwrap();
let mul_bp = infix_binding_power(&TokenKind::Star).unwrap();
assert!(mul_bp.0 > add_bp.0);
let and_bp = infix_binding_power(&TokenKind::Keyword(Keyword::And)).unwrap();
let or_bp = infix_binding_power(&TokenKind::Keyword(Keyword::Or)).unwrap();
assert!(and_bp.0 > or_bp.0);
let eq_bp = infix_binding_power(&TokenKind::Eq).unwrap();
assert!(eq_bp.0 > and_bp.0);
}
#[test]
fn test_left_associativity() {
let (left, right) = infix_binding_power(&TokenKind::Plus).unwrap();
assert!(left < right);
}
#[test]
fn test_token_to_binary_op() {
assert_eq!(token_to_binary_op(&TokenKind::Plus), Some(BinaryOp::Add));
assert_eq!(token_to_binary_op(&TokenKind::Minus), Some(BinaryOp::Sub));
assert_eq!(token_to_binary_op(&TokenKind::Eq), Some(BinaryOp::Eq));
assert_eq!(token_to_binary_op(&TokenKind::LeftParen), None);
}
#[test]
fn test_token_to_unary_op() {
assert_eq!(token_to_unary_op(&TokenKind::Minus), Some(UnaryOp::Neg));
assert_eq!(
token_to_unary_op(&TokenKind::Keyword(Keyword::Not)),
Some(UnaryOp::Not)
);
assert_eq!(token_to_unary_op(&TokenKind::Plus), None);
}
}