use std::{iter::Peekable, vec::IntoIter};
use diagnostic_quick::{QError, QResult};
pub type PrecedenceNumber = u16;
#[derive(Copy, Clone)]
pub enum Associativity {
Left,
Right,
Neither,
}
#[derive(PartialEq, PartialOrd, Copy, Clone)]
pub struct Precedence(PrecedenceNumber);
impl Precedence {
pub const fn new(i: PrecedenceNumber) -> Self {
Self(i)
}
const fn raise(mut self) -> Precedence {
self.0 += 1;
self
}
const fn lower(mut self) -> Precedence {
self.0 -= 1;
self
}
const fn normalize(mut self) -> Precedence {
self.0 *= 10;
self
}
const fn min() -> Precedence {
Precedence(PrecedenceNumber::MIN)
}
const fn max() -> Precedence {
Precedence(PrecedenceNumber::MAX)
}
}
#[derive(Copy, Clone)]
pub enum Affix {
Infix(Precedence, Associativity),
Prefix(Precedence),
Suffix(Precedence),
None,
}
impl Affix {
pub fn infix_left(p: PrecedenceNumber) -> Self {
Self::Infix(Precedence(p), Associativity::Left)
}
pub fn infix_right(p: PrecedenceNumber) -> Self {
Self::Infix(Precedence(p), Associativity::Right)
}
pub fn infix(p: PrecedenceNumber, a: Associativity) -> Self {
Self::Infix(Precedence(p), a)
}
pub fn suffix(p: PrecedenceNumber) -> Self {
Self::Prefix(Precedence(p))
}
pub fn prefix(p: PrecedenceNumber) -> Self {
Self::Prefix(Precedence(p))
}
}
pub trait PrattParser {
type Input: Clone;
type Output: Sized;
fn query(&mut self, input: &Self::Input) -> QResult<Affix>;
fn primary(&mut self, input: Self::Input) -> QResult<Self::Output>;
fn infix(&mut self, lhs: Self::Output, op: Self::Input, rhs: Self::Output) -> QResult<Self::Output>;
fn prefix(&mut self, op: Self::Input, rhs: Self::Output) -> QResult<Self::Output>;
fn suffix(&mut self, lhs: Self::Output, op: Self::Input) -> QResult<Self::Output>;
fn parse(&mut self, inputs: &[Self::Input]) -> QResult<Self::Output> {
let mut stream = inputs.to_vec().into_iter().peekable();
self.parse_input(&mut stream, Precedence(0))
}
fn parse_input(&mut self, tail: &mut Peekable<IntoIter<Self::Input>>, rbp: Precedence) -> QResult<Self::Output> {
if let Some(head) = tail.next() {
let info = self.query(&head)?;
let mut nbp = self.nbp(info);
let mut node = self.nud(head, tail, info);
while let Some(head) = tail.peek() {
let info = self.query(head)?;
let lbp = self.lbp(info);
if rbp < lbp && lbp < nbp {
let head = tail.next().unwrap();
nbp = self.nbp(info);
node = self.led(head, tail, info, node?);
}
else {
break;
}
}
node
}
else {
Err(QError::syntax_error("EmptyInput"))
}
}
fn nud(&mut self, head: Self::Input, tail: &mut Peekable<IntoIter<Self::Input>>, info: Affix) -> QResult<Self::Output> {
match info {
Affix::Prefix(precedence) => {
let rhs = self.parse_input(tail, precedence.normalize().lower());
self.prefix(head, rhs?)
}
Affix::None => self.primary(head),
Affix::Suffix(_) => Err(QError::syntax_error("Unexpected Postfix")),
Affix::Infix(_, _) => Err(QError::syntax_error("Unexpected Infix")),
}
}
fn led(&mut self, head: Self::Input, tail: &mut Peekable<IntoIter<Self::Input>>, info: Affix, lhs: Self::Output) -> QResult<Self::Output> {
match info {
Affix::Infix(precedence, associativity) => {
let precedence = precedence.normalize();
let rhs = match associativity {
Associativity::Left => self.parse_input(tail, precedence),
Associativity::Right => self.parse_input(tail, precedence.lower()),
Associativity::Neither => self.parse_input(tail, precedence.raise()),
};
self.infix(lhs, head, rhs?)
}
Affix::Suffix(_) => self.suffix(lhs, head),
Affix::None => Err(QError::syntax_error("Unexpected NilFix")),
Affix::Prefix(_) => Err(QError::syntax_error("Unexpected Prefix")),
}
}
fn lbp(&mut self, info: Affix) -> Precedence {
match info {
Affix::None => Precedence::min(),
Affix::Prefix(_) => Precedence::min(),
Affix::Suffix(precedence) => precedence.normalize(),
Affix::Infix(precedence, _) => precedence.normalize(),
}
}
fn nbp(&mut self, info: Affix) -> Precedence {
match info {
Affix::None => Precedence::max(),
Affix::Prefix(_) => Precedence::max(),
Affix::Suffix(_) => Precedence::max(),
Affix::Infix(precedence, Associativity::Left) => precedence.normalize().raise(),
Affix::Infix(precedence, Associativity::Right) => precedence.normalize().raise(),
Affix::Infix(precedence, Associativity::Neither) => precedence.normalize(),
}
}
}