use token_lang::{Token, TokenKind};
use crate::Parser;
pub trait Pratt<'t, K: TokenKind> {
type Output;
fn prefix(&mut self, parser: &mut Parser<'t, K>) -> Option<Self::Output>;
fn infix_binding(&self, kind: &K) -> Option<(u8, u8)>;
fn infix(
&mut self,
op: &'t Token<K>,
left: Self::Output,
right: Self::Output,
) -> Option<Self::Output>;
fn expression(&mut self, parser: &mut Parser<'t, K>, min_bp: u8) -> Option<Self::Output> {
let mut left = self.prefix(parser)?;
while let Some(kind) = parser.peek_kind() {
let Some((left_bp, right_bp)) = self.infix_binding(kind) else {
break;
};
if left_bp < min_bp {
break;
}
let op = parser.bump()?;
let right = self.expression(parser, right_bp)?;
left = self.infix(op, left, right)?;
}
Some(left)
}
fn parse(&mut self, parser: &mut Parser<'t, K>) -> Option<Self::Output> {
self.expression(parser, 0)
}
}
#[cfg(test)]
mod tests {
use token_lang::Span;
use super::*;
#[derive(Clone, Copy, Debug)]
enum K {
Num(i64),
Plus,
Minus,
Star,
Caret,
Eof,
}
impl TokenKind for K {
fn is_eof(&self) -> bool {
matches!(self, K::Eof)
}
}
struct Sexpr;
impl<'t> Pratt<'t, K> for Sexpr {
type Output = alloc::string::String;
fn prefix(&mut self, p: &mut Parser<'t, K>) -> Option<Self::Output> {
use alloc::string::ToString;
match p.bump()?.kind() {
K::Num(n) => Some(n.to_string()),
K::Minus => {
let operand = self.expression(p, 100)?;
Some(alloc::format!("(neg {operand})"))
}
_ => None,
}
}
fn infix_binding(&self, k: &K) -> Option<(u8, u8)> {
match k {
K::Plus | K::Minus => Some((1, 2)),
K::Star => Some((3, 4)),
K::Caret => Some((6, 5)), _ => None,
}
}
fn infix(
&mut self,
op: &'t Token<K>,
left: Self::Output,
right: Self::Output,
) -> Option<Self::Output> {
let sym = match op.kind() {
K::Plus => "+",
K::Minus => "-",
K::Star => "*",
K::Caret => "^",
_ => return None,
};
Some(alloc::format!("({sym} {left} {right})"))
}
}
fn lex(kinds: &[K]) -> alloc::vec::Vec<Token<K>> {
kinds
.iter()
.enumerate()
.map(|(i, k)| Token::new(*k, Span::new(i as u32, i as u32 + 1)))
.collect()
}
fn parse(kinds: &[K]) -> Option<alloc::string::String> {
let tokens = lex(kinds);
let mut p = Parser::new(&tokens);
Sexpr.parse(&mut p)
}
#[test]
fn test_precedence_multiplication_binds_tighter() {
let out = parse(&[K::Num(1), K::Plus, K::Num(2), K::Star, K::Num(3), K::Eof]);
assert_eq!(out.as_deref(), Some("(+ 1 (* 2 3))"));
}
#[test]
fn test_left_associative_addition() {
let out = parse(&[K::Num(1), K::Minus, K::Num(2), K::Minus, K::Num(3), K::Eof]);
assert_eq!(out.as_deref(), Some("(- (- 1 2) 3)"));
}
#[test]
fn test_right_associative_power() {
let out = parse(&[K::Num(2), K::Caret, K::Num(3), K::Caret, K::Num(2), K::Eof]);
assert_eq!(out.as_deref(), Some("(^ 2 (^ 3 2))"));
}
#[test]
fn test_prefix_operator() {
let out = parse(&[K::Minus, K::Num(2), K::Star, K::Num(3), K::Eof]);
assert_eq!(out.as_deref(), Some("(* (neg 2) 3)"));
}
}