use core::iter::Peekable;
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::ops::BitOr;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use crate::iterators::Pair;
use crate::RuleType;
pub use crate::pratt_precedence;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Assoc {
Left,
Right,
}
pub type Prec = u32;
const PREC_STEP: Prec = 10;
pub struct Op<R: RuleType> {
rule: R,
affix: Affix,
next: Option<Box<Op<R>>>,
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Affix {
Prefix,
Postfix,
Infix(Assoc),
}
impl<R: RuleType> Op<R> {
pub const fn prefix(rule: R) -> Self {
Self {
rule,
affix: Affix::Prefix,
next: None,
}
}
pub const fn postfix(rule: R) -> Self {
Self {
rule,
affix: Affix::Postfix,
next: None,
}
}
pub const fn infix(rule: R, assoc: Assoc) -> Self {
Self {
rule,
affix: Affix::Infix(assoc),
next: None,
}
}
}
impl<R: RuleType> BitOr for Op<R> {
type Output = Self;
fn bitor(mut self, rhs: Self) -> Self {
fn assign_next<R: RuleType>(op: &mut Op<R>, next: Op<R>) {
if let Some(ref mut child) = op.next {
assign_next(child, next);
} else {
op.next = Some(Box::new(next));
}
}
assign_next(&mut self, rhs);
self
}
}
pub struct PrattParser<R: RuleType> {
prec: Prec,
ops: BTreeMap<R, (Affix, Prec)>,
has_prefix: bool,
has_postfix: bool,
has_infix: bool,
}
impl<R: RuleType> Default for PrattParser<R> {
fn default() -> Self {
Self::new()
}
}
impl<R: RuleType> PrattParser<R> {
pub fn new() -> Self {
Self {
prec: PREC_STEP,
ops: BTreeMap::new(),
has_prefix: false,
has_postfix: false,
has_infix: false,
}
}
pub fn op(mut self, op: Op<R>) -> Self {
self.prec += PREC_STEP;
let mut iter = Some(op);
while let Some(Op { rule, affix, next }) = iter.take() {
match affix {
Affix::Prefix => self.has_prefix = true,
Affix::Postfix => self.has_postfix = true,
Affix::Infix(_) => self.has_infix = true,
}
self.ops.insert(rule, (affix, self.prec));
iter = next.map(|op| *op);
}
self
}
pub fn map_primary<'pratt, 'a, 'i, X, T>(
&'pratt self,
primary: X,
) -> PrattParserMap<'pratt, 'a, 'i, R, X, T>
where
X: FnMut(Pair<'i, R>) -> T,
R: 'pratt,
{
PrattParserMap {
pratt: self,
primary,
prefix: None,
postfix: None,
infix: None,
phantom: PhantomData,
}
}
fn get(&self, rule: &R) -> Option<(Affix, Prec)> {
self.ops.get(rule).copied()
}
}
#[doc(hidden)]
pub trait PrattParserOps<R: RuleType> {
fn get(&self, rule: &R) -> Option<(Affix, Prec)>;
}
impl<R: RuleType> PrattParserOps<R> for PrattParser<R> {
fn get(&self, rule: &R) -> Option<(Affix, Prec)> {
PrattParser::get(self, rule)
}
}
pub struct ConstPrattParser<R: RuleType + 'static, const N: usize> {
ops: [(R, Affix, Prec); N],
}
impl<R: RuleType + 'static, const N: usize> ConstPrattParser<R, N> {
pub const fn new_const(ops: [(Op<R>, bool); N]) -> Self {
const {
assert!(N > 0, "ConstPrattParser requires at least one operator");
}
assert!(
ops[0].1,
"the first operator must start a new precedence level (`true`)"
);
let mut internal_ops: [(R, Affix, Prec); N] = [(ops[0].0.rule, Affix::Prefix, 0); N];
let mut prec = 0;
let mut index = 0;
while index < N {
let (op, new_level) = &ops[index];
assert!(
op.next.is_none(),
"chained operators (created with `|`) are not supported in ConstPrattParser"
);
if *new_level {
prec += PREC_STEP;
}
internal_ops[index] = (op.rule, op.affix, prec);
index += 1;
}
let _ = ManuallyDrop::new(ops);
Self { ops: internal_ops }
}
pub fn map_primary<'pratt, 'a, 'i, X, T>(
&'pratt self,
primary: X,
) -> PrattParserMap<'pratt, 'a, 'i, R, X, T, Self>
where
X: FnMut(Pair<'i, R>) -> T,
R: 'pratt,
{
PrattParserMap {
pratt: self,
primary,
prefix: None,
postfix: None,
infix: None,
phantom: PhantomData,
}
}
#[inline]
fn get(&self, rule: &R) -> Option<(Affix, Prec)> {
let mut i = N;
while i > 0 {
i -= 1;
if self.ops[i].0 == *rule {
return Some((self.ops[i].1, self.ops[i].2));
}
}
None
}
}
impl<R: RuleType + 'static, const N: usize> PrattParserOps<R> for ConstPrattParser<R, N> {
fn get(&self, rule: &R) -> Option<(Affix, Prec)> {
ConstPrattParser::get(self, rule)
}
}
type PrefixFn<'a, 'i, R, T> = Box<dyn FnMut(Pair<'i, R>, T) -> T + 'a>;
type PostfixFn<'a, 'i, R, T> = Box<dyn FnMut(T, Pair<'i, R>) -> T + 'a>;
type InfixFn<'a, 'i, R, T> = Box<dyn FnMut(T, Pair<'i, R>, T) -> T + 'a>;
pub struct PrattParserMap<'pratt, 'a, 'i, R, F, T, P = PrattParser<R>>
where
R: RuleType,
F: FnMut(Pair<'i, R>) -> T,
P: PrattParserOps<R>,
{
pratt: &'pratt P,
primary: F,
prefix: Option<PrefixFn<'a, 'i, R, T>>,
postfix: Option<PostfixFn<'a, 'i, R, T>>,
infix: Option<InfixFn<'a, 'i, R, T>>,
phantom: PhantomData<T>,
}
impl<'pratt, 'a, 'i, R, F, T, P> PrattParserMap<'pratt, 'a, 'i, R, F, T, P>
where
R: RuleType + 'pratt,
F: FnMut(Pair<'i, R>) -> T,
P: PrattParserOps<R> + 'pratt,
{
pub fn map_prefix<X>(mut self, prefix: X) -> Self
where
X: FnMut(Pair<'i, R>, T) -> T + 'a,
{
self.prefix = Some(Box::new(prefix));
self
}
pub fn map_postfix<X>(mut self, postfix: X) -> Self
where
X: FnMut(T, Pair<'i, R>) -> T + 'a,
{
self.postfix = Some(Box::new(postfix));
self
}
pub fn map_infix<X>(mut self, infix: X) -> Self
where
X: FnMut(T, Pair<'i, R>, T) -> T + 'a,
{
self.infix = Some(Box::new(infix));
self
}
pub fn parse<I: Iterator<Item = Pair<'i, R>>>(&mut self, pairs: I) -> T {
self.expr(&mut pairs.peekable(), 0)
}
fn expr<I: Iterator<Item = Pair<'i, R>>>(&mut self, pairs: &mut Peekable<I>, rbp: Prec) -> T {
let mut lhs = self.nud(pairs);
while rbp < self.lbp(pairs) {
lhs = self.led(pairs, lhs);
}
lhs
}
fn nud<I: Iterator<Item = Pair<'i, R>>>(&mut self, pairs: &mut Peekable<I>) -> T {
let pair = pairs.next().expect("Pratt parsing expects non-empty Pairs");
match self.pratt.get(&pair.as_rule()) {
Some((Affix::Prefix, prec)) => {
let rhs = self.expr(pairs, prec - 1);
match self.prefix.as_mut() {
Some(prefix) => prefix(pair, rhs),
None => panic!("Could not map {}, no `.map_prefix(...)` specified", pair),
}
}
None => (self.primary)(pair),
_ => panic!("Expected prefix or primary expression, found {}", pair),
}
}
fn led<I: Iterator<Item = Pair<'i, R>>>(&mut self, pairs: &mut Peekable<I>, lhs: T) -> T {
let pair = pairs.next().unwrap();
match self.pratt.get(&pair.as_rule()) {
Some((Affix::Infix(assoc), prec)) => {
let rhs = match assoc {
Assoc::Left => self.expr(pairs, prec),
Assoc::Right => self.expr(pairs, prec - 1),
};
match self.infix.as_mut() {
Some(infix) => infix(lhs, pair, rhs),
None => panic!("Could not map {}, no `.map_infix(...)` specified", pair),
}
}
Some((Affix::Postfix, _)) => match self.postfix.as_mut() {
Some(postfix) => postfix(lhs, pair),
None => panic!("Could not map {}, no `.map_postfix(...)` specified", pair),
},
_ => panic!("Expected postfix or infix expression, found {}", pair),
}
}
fn lbp<I: Iterator<Item = Pair<'i, R>>>(&mut self, pairs: &mut Peekable<I>) -> Prec {
match pairs.peek() {
Some(pair) => match self.pratt.get(&pair.as_rule()) {
Some((_, prec)) => prec,
None => panic!("Expected operator, found {}", pair),
},
None => 0,
}
}
}
#[macro_export]
macro_rules! pratt_precedence {
(
$(
$first_head:ident :: $first_tail:ident $first_args:tt
$( | $head:ident :: $tail:ident $args:tt )*
),* $(,)?
) => {
[$(
( $first_head :: $first_tail $first_args, true )
$(, ( $head :: $tail $args, false ) )*
),*]
};
($($t:tt)*) => {
compile_error!(
"unsupported operator syntax in `pratt_precedence!`: \
each operator must be a two-segment call like `Op::infix(Rule::add, Assoc::Left)`; \
fully qualified paths and turbofish forms are not accepted"
)
};
}