diff --git a/Cargo.toml b/Cargo.toml index b0964350..3ed442c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -141,3 +141,7 @@ required-features = ["std"] [[example]] name = "foo" required-features = ["std"] + +[[example]] +name = "mini_ml" +required-features = ["pratt", "label"] diff --git a/examples/mini_ml.rs b/examples/mini_ml.rs new file mode 100644 index 00000000..96bbc7c9 --- /dev/null +++ b/examples/mini_ml.rs @@ -0,0 +1,468 @@ +//! This is an entire lexer, parser, type-checker, and interpreter for a statically-typed ML-like functional +//! programming language. See `sample.mini_ml` for sample source code. +//! Run it with the following command: +//! cargo run --features=pratt,label --example mini_ml -- examples/sample.mini_ml + +use ariadne::{sources, Color, Label, Report, ReportKind}; +use chumsky::{input::SpannedInput, pratt::*, prelude::*}; +use std::{env, fmt, fs}; + +// Tokens and lexer + +#[derive(Debug, Clone, PartialEq)] +pub enum Token<'src> { + Ident(&'src str), + Num(f64), + Parens(Vec>), + + // Ops + Eq, + Plus, + Asterisk, + + // Keywords + Let, + In, + Fn, + True, + False, +} + +impl fmt::Display for Token<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Token::Ident(x) => write!(f, "{x}"), + Token::Num(x) => write!(f, "{x}"), + Token::Parens(_) => write!(f, "(...)"), + Token::Eq => write!(f, "="), + Token::Plus => write!(f, "+"), + Token::Asterisk => write!(f, "*"), + Token::Let => write!(f, "let"), + Token::In => write!(f, "in"), + Token::Fn => write!(f, "fn"), + Token::True => write!(f, "true"), + Token::False => write!(f, "false"), + } + } +} + +pub type Spanned = (T, SimpleSpan); + +fn lexer<'src>( +) -> impl Parser<'src, &'src str, Vec>>, extra::Err>> { + recursive(|token| { + choice(( + // Keywords + text::ident().map(|s| match s { + "let" => Token::Let, + "in" => Token::In, + "fn" => Token::Fn, + "true" => Token::True, + "false" => Token::False, + s => Token::Ident(s), + }), + // Operators + just("=").to(Token::Eq), + just("+").to(Token::Plus), + just("*").to(Token::Asterisk), + // Numbers + text::int(10) + .then(just('.').then(text::digits(10)).or_not()) + .to_slice() + .map(|s: &str| Token::Num(s.parse().unwrap())), + token + .repeated() + .collect() + .delimited_by(just('('), just(')')) + .labelled("token tree") + .as_context() + .map(Token::Parens), + )) + .map_with(|t, e| (t, e.span())) + .padded() + }) + .repeated() + .collect() +} + +// AST and parser + +#[derive(Clone, Debug)] +pub enum Expr<'src> { + Var(&'src str), + Num(f64), + Bool(bool), + Add(Box>, Box>), + Mul(Box>, Box>), + Let { + lhs: Spanned<&'src str>, + rhs: Box>, + then: Box>, + }, + Apply { + func: Box>, + arg: Box>, + }, + Func { + arg: Box>, + body: Box>, + }, +} + +type ParserInput<'src> = SpannedInput, SimpleSpan, &'src [Spanned>]>; + +fn parser<'src>( +) -> impl Parser<'src, ParserInput<'src>, Spanned>, extra::Err>>> +{ + recursive(|expr| { + let ident = select_ref! { Token::Ident(x) => *x }; + let atom = choice(( + select_ref! { Token::Num(x) => Expr::Num(*x) }, + just(Token::True).to(Expr::Bool(true)), + just(Token::False).to(Expr::Bool(false)), + ident.map(Expr::Var), + // let x = y in z + just(Token::Let) + .ignore_then(ident.map_with(|x, e| (x, e.span()))) + .then_ignore(just(Token::Eq)) + .then(expr.clone()) + .then_ignore(just(Token::In)) + .then(expr.clone()) + .map(|((lhs, rhs), then)| Expr::Let { + lhs, + rhs: Box::new(rhs), + then: Box::new(then), + }), + )); + + choice(( + atom.map_with(|expr, e| (expr, e.span())), + // fn x y = z + just(Token::Fn).ignore_then( + ident.map_with(|x, e| (x, e.span())).repeated().foldr_with( + just(Token::Eq).ignore_then(expr.clone()), + |arg, body, e| { + ( + Expr::Func { + arg: Box::new(arg), + body: Box::new(body), + }, + e.span(), + ) + }, + ), + ), + // ( x ) + expr.nested_in( + select_ref! { Token::Parens(ts) = e => ts.as_slice().spanned(e.span()) }, + ), + )) + .pratt(vec![ + // Multiply + infix(left(10), just(Token::Asterisk), |x, _, y, e| { + (Expr::Mul(Box::new(x), Box::new(y)), e.span()) + }) + .boxed(), + // Add + infix(left(9), just(Token::Plus), |x, _, y, e| { + (Expr::Add(Box::new(x), Box::new(y)), e.span()) + }) + .boxed(), + // Calls + infix(left(1), empty(), |x, _, y, e| { + ( + Expr::Apply { + func: Box::new(x), + arg: Box::new(y), + }, + e.span(), + ) + }) + .boxed(), + ]) + .labelled("expression") + .as_context() + }) +} + +// Type checker/solver + +#[derive(Copy, Clone, Debug, PartialEq)] +struct TyVar(usize); + +#[derive(Copy, Clone, Debug)] +enum TyInfo { + Unknown, + Ref(TyVar), + Num, + Bool, + Func(TyVar, TyVar), +} + +impl fmt::Display for TyInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + TyInfo::Unknown => write!(f, "?"), + TyInfo::Ref(_) => write!(f, ""), + TyInfo::Num => write!(f, "Num"), + TyInfo::Bool => write!(f, "Bool"), + TyInfo::Func(_, _) => write!(f, "(_ -> _)"), + } + } +} + +#[derive(Debug)] +enum Ty { + Num, + Bool, + Func(Box, Box), +} + +impl fmt::Display for Ty { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Ty::Num => write!(f, "Num"), + Ty::Bool => write!(f, "Bool"), + Ty::Func(x, y) => write!(f, "{x} -> {y}"), + } + } +} + +struct Solver<'src> { + src: &'src str, + vars: Vec<(TyInfo, SimpleSpan)>, +} + +impl Solver<'_> { + fn create_ty(&mut self, info: TyInfo, span: SimpleSpan) -> TyVar { + self.vars.push((info, span)); + TyVar(self.vars.len() - 1) + } + + fn unify(&mut self, a: TyVar, b: TyVar, span: SimpleSpan) { + match (self.vars[a.0].0, self.vars[b.0].0) { + (TyInfo::Unknown, _) => self.vars[a.0].0 = TyInfo::Ref(b), + (_, TyInfo::Unknown) => self.vars[b.0].0 = TyInfo::Ref(a), + (TyInfo::Ref(a), _) => self.unify(a, b, span), + (_, TyInfo::Ref(b)) => self.unify(a, b, span), + (TyInfo::Num, TyInfo::Num) | (TyInfo::Bool, TyInfo::Bool) => {} + (TyInfo::Func(a_i, a_o), TyInfo::Func(b_i, b_o)) => { + self.unify(b_i, a_i, span); // Order swapped: function args are contravariant + self.unify(a_o, b_o, span); + } + (a_info, b_info) => failure( + format!("Type mismatch between {a_info} and {b_info}"), + ("mismatch occurred here".to_string(), span), + vec![ + (format!("{a_info}"), self.vars[a.0].1), + (format!("{b_info}"), self.vars[b.0].1), + ], + self.src, + ), + } + } + + fn check<'src>( + &mut self, + expr: &Spanned>, + env: &mut Vec<(&'src str, TyVar)>, + ) -> TyVar { + match &expr.0 { + Expr::Num(_) => self.create_ty(TyInfo::Num, expr.1), + Expr::Bool(_) => self.create_ty(TyInfo::Bool, expr.1), + Expr::Var(name) => { + env.iter() + .rev() + .find(|(n, _)| n == name) + .unwrap_or_else(|| { + failure( + format!("No such local '{name}'"), + ("not found in scope".to_string(), expr.1), + None, + self.src, + ) + }) + .1 + } + Expr::Let { lhs, rhs, then } => { + let rhs_ty = self.check(rhs, env); + env.push((lhs.0, rhs_ty)); + let out_ty = self.check(then, env); + env.pop(); + out_ty + } + Expr::Func { arg, body } => { + let arg_ty = self.create_ty(TyInfo::Unknown, arg.1); + env.push((arg.0, arg_ty)); + let body_ty = self.check(body, env); + env.pop(); + self.create_ty(TyInfo::Func(arg_ty, body_ty), expr.1) + } + Expr::Apply { func, arg } => { + let func_ty = self.check(func, env); + let arg_ty = self.check(arg, env); + let out_ty = self.create_ty(TyInfo::Unknown, expr.1); + let func_req_ty = self.create_ty(TyInfo::Func(arg_ty, out_ty), func.1); + self.unify(func_req_ty, func_ty, expr.1); + out_ty + } + Expr::Add(l, r) | Expr::Mul(l, r) => { + let out_ty = self.create_ty(TyInfo::Num, expr.1); + let l_ty = self.check(l, env); + self.unify(out_ty, l_ty, expr.1); + let r_ty = self.check(r, env); + self.unify(out_ty, r_ty, expr.1); + out_ty + } + } + } + + pub fn solve(&self, var: TyVar) -> Ty { + match self.vars[var.0].0 { + TyInfo::Unknown => failure( + "Cannot infer type".to_string(), + ("has unknown type".to_string(), self.vars[var.0].1), + None, + self.src, + ), + TyInfo::Ref(var) => self.solve(var), + TyInfo::Num => Ty::Num, + TyInfo::Bool => Ty::Bool, + TyInfo::Func(i, o) => Ty::Func(Box::new(self.solve(i)), Box::new(self.solve(o))), + } + } +} + +#[derive(Clone, Debug)] +pub enum Value<'src> { + Num(f64), + Bool(bool), + Func { + arg: Spanned<&'src str>, + env: Scope<'src>, + body: &'src Spanned>, + }, +} + +impl Value<'_> { + pub fn num(self) -> f64 { + let Value::Num(x) = self else { panic!() }; + x + } +} + +type Scope<'src> = Vec<(Spanned<&'src str>, Value<'src>)>; + +#[derive(Default)] +pub struct Vm<'src> { + stack: Scope<'src>, +} + +impl<'src> Vm<'src> { + pub fn eval(&mut self, expr: &'src Spanned>) -> Value<'src> { + match &expr.0 { + Expr::Num(x) => Value::Num(*x), + Expr::Bool(x) => Value::Bool(*x), + Expr::Var(var) => self + .stack + .iter() + .rev() + .find(|(v, _)| v.0 == *var) + .unwrap() + .1 + .clone(), + Expr::Let { lhs, rhs, then } => { + let rhs = self.eval(rhs); + self.stack.push((*lhs, rhs)); + let then = self.eval(then); + self.stack.pop(); + then + } + Expr::Func { arg, body } => Value::Func { + arg: **arg, + env: self.stack.clone(), // TODO: Only save what's actually needed by the function body + body, + }, + Expr::Apply { func, arg } => { + let func = self.eval(func); + let arg_val = self.eval(arg); + let Value::Func { arg, body, mut env } = func else { + panic!() + }; + let old_len = self.stack.len(); + self.stack.append(&mut env); + self.stack.push((arg, arg_val)); + let out = self.eval(body); + self.stack.truncate(old_len); + out + } + Expr::Add(x, y) => Value::Num(self.eval(x).num() + self.eval(y).num()), + Expr::Mul(x, y) => Value::Num(self.eval(x).num() * self.eval(y).num()), + } + } +} + +fn failure( + msg: String, + label: (String, SimpleSpan), + extra_labels: impl IntoIterator, + src: &str, +) -> ! { + let fname = "example"; + Report::build(ReportKind::Error, fname, label.1.start) + .with_message(&msg) + .with_label( + Label::new((fname, label.1.into_range())) + .with_message(label.0) + .with_color(Color::Red), + ) + .with_labels(extra_labels.into_iter().map(|label2| { + Label::new((fname, label2.1.into_range())) + .with_message(label2.0) + .with_color(Color::Yellow) + })) + .finish() + .print(sources([(fname, src)])) + .unwrap(); + std::process::exit(1) +} + +fn parse_failure(err: &Rich, src: &str) -> ! { + failure( + err.reason().to_string(), + ( + err.found() + .map(|c| c.to_string()) + .unwrap_or_else(|| "end of input".to_string()), + *err.span(), + ), + err.contexts() + .map(|(l, s)| (format!("while parsing this {l}"), *s)), + src, + ) +} + +fn main() { + let filename = env::args().nth(1).expect("Expected file argument"); + let src = &fs::read_to_string(&filename).expect("Failed to read file"); + + let tokens = lexer() + .parse(src) + .into_result() + .unwrap_or_else(|errs| parse_failure(&errs[0], src)); + + let expr = parser() + .parse(tokens.spanned((0..src.len()).into())) + .into_result() + .unwrap_or_else(|errs| parse_failure(&errs[0], src)); + + let mut solver = Solver { + src, + vars: Vec::new(), + }; + let program_ty = solver.check(&expr, &mut Vec::new()); + println!("Result type: {:?}", solver.solve(program_ty)); + + let mut vm = Vm::default(); + println!("Result: {:?}", vm.eval(&expr)); +} diff --git a/examples/sample.mini_ml b/examples/sample.mini_ml new file mode 100644 index 00000000..3e54ba96 --- /dev/null +++ b/examples/sample.mini_ml @@ -0,0 +1,4 @@ +let add = fn x y = x + y in +let mul = fn x y = x * y in +let x = mul (add 5 42) 2 in +add x 3.5 diff --git a/src/lib.rs b/src/lib.rs index 8f4c5e25..01b0caf8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,6 +140,7 @@ use self::{ input::{ BorrowInput, Emitter, ExactSizeInput, InputRef, MapExtra, SliceInput, StrInput, ValueInput, }, + inspector::Inspector, prelude::*, primitive::Any, private::{ @@ -185,6 +186,9 @@ mod sync { pub(crate) type RefC = alloc::sync::Arc; pub(crate) type RefW = alloc::sync::Weak; pub(crate) type DynParser<'a, 'b, I, O, E> = dyn Parser<'a, I, O, E> + Send + Sync + 'b; + #[cfg(feature = "pratt")] + pub(crate) type DynOperator<'a, 'b, I, O, E> = + dyn pratt::Operator<'a, I, O, E> + Send + Sync + 'b; /// A trait that requires either nothing or `Send` and `Sync` bounds depending on whether the `sync` feature is /// enabled. Used to constrain API usage succinctly and easily. @@ -199,6 +203,8 @@ mod sync { pub(crate) type RefC = alloc::rc::Rc; pub(crate) type RefW = alloc::rc::Weak; pub(crate) type DynParser<'a, 'b, I, O, E> = dyn Parser<'a, I, O, E> + 'b; + #[cfg(feature = "pratt")] + pub(crate) type DynOperator<'a, 'b, I, O, E> = dyn pratt::Operator<'a, I, O, E> + 'b; /// A trait that requires either nothing or `Send` and `Sync` bounds depending on whether the `sync` feature is /// enabled. Used to constrain API usage succinctly and easily. @@ -2130,7 +2136,6 @@ pub trait Parser<'a, I: Input<'a>, O, E: ParserExtra<'a, I> = extra::Default>: /// ``` /// # use chumsky::prelude::*; /// use chumsky::pratt::*; - /// use std::ops::{Neg, Mul, Div, Add, Sub}; /// /// let int = text::int::<_, _, extra::Err>>(10) /// .from_str() @@ -2140,11 +2145,11 @@ pub trait Parser<'a, I: Input<'a>, O, E: ParserExtra<'a, I> = extra::Default>: /// let op = |c| just(c).padded(); /// /// let expr = int.pratt(( - /// prefix(2, op('-'), i64::neg), - /// infix(left(1), op('*'), i64::mul), - /// infix(left(1), op('/'), i64::div), - /// infix(left(0), op('+'), i64::add), - /// infix(left(0), op('-'), i64::sub), + /// prefix(2, op('-'), |_, x: i64, _| -x), + /// infix(left(1), op('*'), |x, _, y, _| x * y), + /// infix(left(1), op('/'), |x, _, y, _| x / y), + /// infix(left(0), op('+'), |x, _, y, _| x + y), + /// infix(left(0), op('-'), |x, _, y, _| x - y), /// )); /// /// // Pratt parsing can handle unary operators... diff --git a/src/pratt.rs b/src/pratt.rs index 6cffccca..4b94d9ab 100644 --- a/src/pratt.rs +++ b/src/pratt.rs @@ -23,10 +23,6 @@ //! combines its operands together into a syntax tree. These functions are given as the last arguments of [`infix`], //! [`prefix`], and [`postfix`]. //! -//! Fold functions have several overloads, allowing you to make use of only the operands, the operands and the -//! operators, and even additionally [`MapExtra`], providing access to the span, slice, and parser state. See the -//! documentation for each function to see which fold signatures can be used. -//! //! # Examples //! //! ``` @@ -68,18 +64,18 @@ //! //! let expr = atom.pratt(( //! // We want factorial to happen before any negation, so we need its precedence to be higher than `Expr::Neg`. -//! postfix(4, op('!'), |lhs| Expr::Factorial(Box::new(lhs))), +//! postfix(4, op('!'), |lhs, _, _| Expr::Factorial(Box::new(lhs))), //! // Just like in math, we want that if we write -x^2, our parser parses that as -(x^2), so we need it to have //! // exponents bind tighter than our prefix operators. -//! infix(right(3), op('^'), |l, r| Expr::Pow(Box::new(l), Box::new(r))), +//! infix(right(3), op('^'), |l, _, r, _| Expr::Pow(Box::new(l), Box::new(r))), //! // Notice the conflict with our `Expr::Sub`. This will still parse correctly. We want negation to happen before //! // `+` and `-`, so we set its precedence higher. -//! prefix(2, op('-'), |rhs| Expr::Neg(Box::new(rhs))), -//! prefix(2, op('*'), |rhs| Expr::Deref(Box::new(rhs))), +//! prefix(2, op('-'), |_, rhs, _| Expr::Neg(Box::new(rhs))), +//! prefix(2, op('*'), |_, rhs, _| Expr::Deref(Box::new(rhs))), //! // Our `-` and `+` bind the weakest, meaning that even if they occur first in an expression, they will be the //! // last executed. -//! infix(left(1), op('+'), |l, r| Expr::Add(Box::new(l), Box::new(r))), -//! infix(left(1), op('-'), |l, r| Expr::Sub(Box::new(l), Box::new(r))), +//! infix(left(1), op('+'), |l, _, r, _| Expr::Add(Box::new(l), Box::new(r))), +//! infix(left(1), op('-'), |l, _, r, _| Expr::Sub(Box::new(l), Box::new(r))), //! )) //! .map(|x| x.to_string()); //! @@ -91,33 +87,350 @@ use super::*; -trait Operator<'a, I, O, E> +macro_rules! op_check_and_emit { + () => { + #[inline(always)] + fn do_parse_prefix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint< + 'src, + 'parse, + I, + >::Checkpoint, + >, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult { + self.do_parse_prefix::(inp, pre_expr, &f) + } + #[inline(always)] + fn do_parse_prefix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint< + 'src, + 'parse, + I, + >::Checkpoint, + >, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult { + self.do_parse_prefix::(inp, pre_expr, &f) + } + #[inline(always)] + fn do_parse_postfix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint< + 'src, + 'parse, + I, + >::Checkpoint, + >, + lhs: (), + min_power: u32, + ) -> Result<(), ()> { + self.do_parse_postfix::(inp, pre_expr, pre_op, lhs, min_power) + } + #[inline(always)] + fn do_parse_postfix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint< + 'src, + 'parse, + I, + >::Checkpoint, + >, + lhs: O, + min_power: u32, + ) -> Result { + self.do_parse_postfix::(inp, pre_expr, pre_op, lhs, min_power) + } + #[inline(always)] + fn do_parse_infix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint< + 'src, + 'parse, + I, + >::Checkpoint, + >, + lhs: (), + min_power: u32, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result<(), ()> { + self.do_parse_infix::(inp, pre_expr, pre_op, lhs, min_power, &f) + } + #[inline(always)] + fn do_parse_infix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint< + 'src, + 'parse, + I, + >::Checkpoint, + >, + lhs: O, + min_power: u32, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result { + self.do_parse_infix::(inp, pre_expr, pre_op, lhs, min_power, &f) + } + }; +} + +/// A type implemented by pratt parser operators. +pub trait Operator<'src, I, O, E> where - I: Input<'a>, - E: ParserExtra<'a, I>, + I: Input<'src>, + E: ParserExtra<'src, I>, { - type Op; - type OpParser: Parser<'a, I, Self::Op, E>; - const IS_INFIX: bool = false; - const IS_PREFIX: bool = false; - const IS_POSTFIX: bool = false; - - fn op_parser(&self) -> &Self::OpParser; - fn associativity(&self) -> Associativity; - fn fold_infix( + /// Box this operator, allowing it to be used via dynamic dispatch. + fn boxed<'a>(self) -> Boxed<'src, 'a, I, O, E> + where + Self: Sized + MaybeSync + 'a, + { + Boxed(RefC::new(self)) + } + + #[doc(hidden)] + #[inline(always)] + fn do_parse_prefix<'parse, M: Mode>( &self, - _lhs: O, - _op: Self::Op, - _rhs: O, - _extra: &mut MapExtra<'a, '_, I, E>, - ) -> O { - unreachable!() + _inp: &mut InputRef<'src, 'parse, I, E>, + _pre_expr: &input::Checkpoint< + 'src, + 'parse, + I, + >::Checkpoint, + >, + _f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult + where + Self: Sized, + { + Err(()) } - fn fold_prefix(&self, _op: Self::Op, _rhs: O, _extra: &mut MapExtra<'a, '_, I, E>) -> O { - unreachable!() + + #[doc(hidden)] + #[inline(always)] + fn do_parse_postfix<'parse, M: Mode>( + &self, + _inp: &mut InputRef<'src, 'parse, I, E>, + _pre_expr: &input::Cursor<'src, 'parse, I>, + _pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: M::Output, + _min_power: u32, + ) -> Result, M::Output> + where + Self: Sized, + { + Err(lhs) } - fn fold_postfix(&self, _lhs: O, _op: Self::Op, _extra: &mut MapExtra<'a, '_, I, E>) -> O { - unreachable!() + + #[doc(hidden)] + #[inline(always)] + fn do_parse_infix<'parse, M: Mode>( + &self, + _inp: &mut InputRef<'src, 'parse, I, E>, + _pre_expr: &input::Cursor<'src, 'parse, I>, + _pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: M::Output, + _min_power: u32, + _f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result, M::Output> + where + Self: Sized, + { + Err(lhs) + } + + #[doc(hidden)] + fn do_parse_prefix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult; + #[doc(hidden)] + fn do_parse_prefix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult; + #[doc(hidden)] + fn do_parse_postfix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: (), + min_power: u32, + ) -> Result<(), ()>; + #[doc(hidden)] + fn do_parse_postfix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: O, + min_power: u32, + ) -> Result; + #[doc(hidden)] + fn do_parse_infix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: (), + min_power: u32, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result<(), ()>; + #[doc(hidden)] + fn do_parse_infix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: O, + min_power: u32, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result; +} + +/// A boxed pratt parser operator. See [`Operator`]. +pub struct Boxed<'src, 'a, I, O, E>(RefC>); + +impl Clone for Boxed<'_, '_, I, O, E> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl<'src, I, O, E> Operator<'src, I, O, E> for Boxed<'src, '_, I, O, E> +where + I: Input<'src>, + E: ParserExtra<'src, I>, +{ + #[inline(always)] + fn do_parse_prefix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult + where + Self: Sized, + { + M::invoke_pratt_op_prefix(self, inp, pre_expr, f) + } + + #[inline(always)] + fn do_parse_postfix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: M::Output, + min_power: u32, + ) -> Result, M::Output> + where + Self: Sized, + { + M::invoke_pratt_op_postfix(self, inp, pre_expr, pre_op, lhs, min_power) + } + + #[inline(always)] + fn do_parse_infix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: M::Output, + min_power: u32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result, M::Output> + where + Self: Sized, + { + M::invoke_pratt_op_infix(self, inp, pre_expr, pre_op, lhs, min_power, f) + } + + #[inline(always)] + fn do_parse_prefix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult { + self.0.do_parse_prefix_check(inp, pre_expr, f) + } + #[inline(always)] + fn do_parse_prefix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult { + self.0.do_parse_prefix_emit(inp, pre_expr, f) + } + #[inline(always)] + fn do_parse_postfix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: (), + min_power: u32, + ) -> Result<(), ()> { + self.0 + .do_parse_postfix_check(inp, pre_expr, pre_op, lhs, min_power) + } + #[inline(always)] + fn do_parse_postfix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: O, + min_power: u32, + ) -> Result { + self.0 + .do_parse_postfix_emit(inp, pre_expr, pre_op, lhs, min_power) + } + #[inline(always)] + fn do_parse_infix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: (), + min_power: u32, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result<(), ()> { + self.0 + .do_parse_infix_check(inp, pre_expr, pre_op, lhs, min_power, &f) + } + #[inline(always)] + fn do_parse_infix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: O, + min_power: u32, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result { + self.0 + .do_parse_infix_emit(inp, pre_expr, pre_op, lhs, min_power, &f) } } @@ -166,16 +479,16 @@ impl Associativity { } /// See [`infix`]. -pub struct Infix { +pub struct Infix<'src, A, F, Atom, Op, I, E> { op_parser: A, fold: F, associativity: Associativity, #[allow(dead_code)] - phantom: EmptyPhantom<(Op, Args)>, + phantom: EmptyPhantom<&'src (Atom, Op, I, E)>, } -impl Copy for Infix {} -impl Clone for Infix { +impl Copy for Infix<'_, A, F, Atom, Op, I, E> {} +impl Clone for Infix<'_, A, F, Atom, Op, I, E> { fn clone(&self) -> Self { Self { op_parser: self.op_parser.clone(), @@ -194,21 +507,20 @@ impl Clone for Infix { /// /// See [`left`] and [`right`] for information about associativity. /// -/// The fold function (the last argument) must have one of the following signatures: +/// The fold function (the last argument) tells the parser how to combine the operator and operands into a new +/// expression. It must have the following signature: /// /// ```ignore -/// // Combine the left and right operands -/// impl Fn(O, O) -> O -/// // Combine the left operand, the operator itself, and the right operand -/// impl Fn(O, Op, O) -> O -/// // Combine the left operand, the operator itself, the right operand, and a [`MapExtra`] covering the whole operation -/// impl Fn(O, Op, O, &mut MapExtra<'a, '_, I, E>) -> O +/// impl Fn(Atom, Op, Atom, &mut MapExtra<'a, '_, I, E>) -> O /// ``` -pub const fn infix( +pub const fn infix<'src, A, F, Atom, Op, I, E>( associativity: Associativity, op_parser: A, fold: F, -) -> Infix { +) -> Infix<'src, A, F, Atom, Op, I, E> +where + F: Fn(Atom, Op, Atom, &mut MapExtra<'src, '_, I, E>) -> Atom, +{ Infix { op_parser, fold, @@ -217,45 +529,65 @@ pub const fn infix( } } -macro_rules! infix_op { - (|$f:ident : Fn($($Arg:ty),*) -> O, $lhs:ident, $op:ident, $rhs:ident, $extra:ident| $invoke:expr) => { - impl<'a, I, O, E, A, F, Op> Operator<'a, I, O, E> for Infix - where - I: Input<'a>, - E: ParserExtra<'a, I>, - A: Parser<'a, I, Op, E>, - F: Fn($($Arg),*) -> O, - { - type Op = Op; - type OpParser = A; - const IS_INFIX: bool = true; - #[inline(always)] fn op_parser(&self) -> &Self::OpParser { &self.op_parser } - #[inline(always)] fn associativity(&self) -> Associativity { self.associativity } - #[inline(always)] fn fold_infix(&self, $lhs: O, $op: Self::Op, $rhs: O, $extra: &mut MapExtra<'a, '_, I, E>) -> O { let $f = &self.fold; $invoke } +impl<'src, I, O, E, A, F, Op> Operator<'src, I, O, E> for Infix<'src, A, F, O, Op, I, E> +where + I: Input<'src>, + E: ParserExtra<'src, I>, + A: Parser<'src, I, Op, E>, + F: Fn(O, Op, O, &mut MapExtra<'src, '_, I, E>) -> O, +{ + #[inline] + fn do_parse_infix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: M::Output, + min_power: u32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result, M::Output> + where + Self: Sized, + { + if self.associativity.left_power() >= min_power { + match self.op_parser.go::(inp) { + Ok(op) => match f(inp, self.associativity.right_power()) { + Ok(rhs) => Ok(M::combine( + M::combine(lhs, rhs, |lhs, rhs| (lhs, rhs)), + op, + |(lhs, rhs), op| { + (self.fold)(lhs, op, rhs, &mut MapExtra::new(pre_expr, inp)) + }, + )), + Err(()) => { + inp.rewind(pre_op.clone()); + Err(lhs) + } + }, + Err(()) => { + inp.rewind(pre_op.clone()); + Err(lhs) + } + } + } else { + Err(lhs) } - }; -} + } -// Allow `|lhs, rhs| ` to be used as a fold closure for infix operators -infix_op!(|f: Fn(O, O) -> O, lhs, _op, rhs, _extra| f(lhs, rhs)); -// Allow `|lhs, op, rhs| ` to be used as a fold closure for infix operators -infix_op!(|f: Fn(O, Op, O) -> O, lhs, op, rhs, _extra| f(lhs, op, rhs)); -// Allow `|lhs, op, rhs, extra| ` to be used as a fold closure for infix operators -infix_op!( - |f: Fn(O, Op, O, &mut MapExtra<'a, '_, I, E>) -> O, lhs, op, rhs, extra| f(lhs, op, rhs, extra) -); + op_check_and_emit!(); +} /// See [`prefix`]. -pub struct Prefix { +pub struct Prefix<'src, A, F, Atom, Op, I, E> { op_parser: A, fold: F, binding_power: u16, #[allow(dead_code)] - phantom: EmptyPhantom<(Op, Args)>, + phantom: EmptyPhantom<&'src (Atom, Op, I, E)>, } -impl Copy for Prefix {} -impl Clone for Prefix { +impl Copy for Prefix<'_, A, F, Atom, Op, I, E> {} +impl Clone for Prefix<'_, A, F, Atom, Op, I, E> { fn clone(&self) -> Self { Self { op_parser: self.op_parser.clone(), @@ -271,21 +603,20 @@ impl Clone for Prefix { /// /// Operators like negation, not, dereferencing, etc. are prefix unary operators in most languages. /// -/// The fold function (the last argument) must have one of the following signatures: +/// The fold function (the last argument) tells the parser how to combine the operator and operand into a new +/// expression. It must have the following signature: /// /// ```ignore -/// // Transform the operand -/// impl Fn(O) -> O -/// // Combine the operator itself and the operand -/// impl Fn(Op, O) -> O -/// // Combine the operator itself, the operand, and a [`MapExtra`] covering the whole operation -/// impl Fn(Op, O, &mut MapExtra<'a, '_, I, E>) -> O +/// impl Fn(Atom, Op, &mut MapExtra<'a, '_, I, E>) -> O /// ``` -pub const fn prefix( +pub const fn prefix<'src, A, F, Atom, Op, I, E>( binding_power: u16, op_parser: A, fold: F, -) -> Prefix { +) -> Prefix<'src, A, F, Atom, Op, I, E> +where + F: Fn(Op, Atom, &mut MapExtra<'src, '_, I, E>) -> Atom, +{ Prefix { op_parser, fold, @@ -294,43 +625,54 @@ pub const fn prefix( } } -macro_rules! prefix_op { - (|$f:ident : Fn($($Arg:ty),*) -> O, $op:ident, $rhs:ident, $extra:ident| $invoke:expr) => { - impl<'a, I, O, E, A, F, Op> Operator<'a, I, O, E> for Prefix - where - I: Input<'a>, - E: ParserExtra<'a, I>, - A: Parser<'a, I, Op, E>, - F: Fn($($Arg),*) -> O, - { - type Op = Op; - type OpParser = A; - const IS_PREFIX: bool = true; - #[inline(always)] fn op_parser(&self) -> &Self::OpParser { &self.op_parser } - #[inline(always)] fn associativity(&self) -> Associativity { Associativity::Left(self.binding_power) } - #[inline(always)] fn fold_prefix(&self, $op: Self::Op, $rhs: O, $extra: &mut MapExtra<'a, '_, I, E>) -> O { let $f = &self.fold; $invoke } +impl<'src, I, O, E, A, F, Op> Operator<'src, I, O, E> for Prefix<'src, A, F, O, Op, I, E> +where + I: Input<'src>, + E: ParserExtra<'src, I>, + A: Parser<'src, I, Op, E>, + F: Fn(Op, O, &mut MapExtra<'src, '_, I, E>) -> O, +{ + #[inline] + fn do_parse_prefix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult + where + Self: Sized, + { + match self.op_parser.go::(inp) { + Ok(op) => match f(inp, Associativity::Left(self.binding_power).left_power()) { + Ok(rhs) => Ok(M::combine(op, rhs, |op, rhs| { + (self.fold)(op, rhs, &mut MapExtra::new(pre_expr.cursor(), inp)) + })), + Err(()) => { + inp.rewind(pre_expr.clone()); + Err(()) + } + }, + Err(()) => { + inp.rewind(pre_expr.clone()); + Err(()) + } } - }; -} + } -// Allow `|rhs| ` to be used as a fold closure for prefix operators -prefix_op!(|f: Fn(O) -> O, _op, rhs, _extra| f(rhs)); -// Allow `|op, rhs| ` to be used as a fold closure for prefix operators -prefix_op!(|f: Fn(Op, O) -> O, op, rhs, _extra| f(op, rhs)); -// Allow `|op, rhs, span| ` to be used as a fold closure for prefix operators -prefix_op!(|f: Fn(Op, O, &mut MapExtra<'a, '_, I, E>) -> O, op, rhs, extra| f(op, rhs, extra)); + op_check_and_emit!(); +} /// See [`postfix`]. -pub struct Postfix { +pub struct Postfix<'src, A, F, Atom, Op, I, E> { op_parser: A, fold: F, binding_power: u16, #[allow(dead_code)] - phantom: EmptyPhantom<(Op, Args)>, + phantom: EmptyPhantom<&'src (Atom, Op, I, E)>, } -impl Copy for Postfix {} -impl Clone for Postfix { +impl Copy for Postfix<'_, A, F, Atom, Op, I, E> {} +impl Clone for Postfix<'_, A, F, Atom, Op, I, E> { fn clone(&self) -> Self { Self { op_parser: self.op_parser.clone(), @@ -344,23 +686,22 @@ impl Clone for Postfix { /// Specify a unary postfix operator for a pratt parser with the given binding power and /// [fold function](crate::pratt#fold-functions). /// -/// Operators like factorial, field access, function composition, etc. are postfix unary operators in most languages. +/// Operators like factorial, field access, etc. are postfix unary operators in most languages. /// -/// The fold function (the last argument) must have one of the following signatures: +/// The fold function (the last argument) tells the parser how to combine the operator and operand into a new +/// expression. It must have the following signature: /// /// ```ignore -/// // Transform the operand -/// impl Fn(O) -> O -/// // Combine the operand and the operator itself -/// impl Fn(O, Op) -> O -/// // Combine the operand, the operator itself, and a [`MapExtra`] covering the whole operation -/// impl Fn(O, Op, &mut MapExtra<'a, '_, I, E>) -> O +/// impl Fn(Op, Atom, &mut MapExtra<'a, '_, I, E>) -> O /// ``` -pub const fn postfix( +pub const fn postfix<'src, A, F, Atom, Op, I, E>( binding_power: u16, op_parser: A, fold: F, -) -> Postfix { +) -> Postfix<'src, A, F, Atom, Op, I, E> +where + F: Fn(Atom, Op, &mut MapExtra<'src, '_, I, E>) -> Atom, +{ Postfix { op_parser, fold, @@ -369,31 +710,42 @@ pub const fn postfix( } } -macro_rules! postfix_op { - (|$f:ident : Fn($($Arg:ty),*) -> O, $lhs:ident, $op:ident, $extra:ident| $invoke:expr) => { - impl<'a, I, O, E, A, F, Op> Operator<'a, I, O, E> for Postfix - where - I: Input<'a>, - E: ParserExtra<'a, I>, - A: Parser<'a, I, Op, E>, - F: Fn($($Arg),*) -> O, - { - type Op = Op; - type OpParser = A; - const IS_POSTFIX: bool = true; - #[inline(always)] fn op_parser(&self) -> &Self::OpParser { &self.op_parser } - #[inline(always)] fn associativity(&self) -> Associativity { Associativity::Left(self.binding_power) } - #[inline(always)] fn fold_postfix(&self, $lhs: O, $op: Self::Op, $extra: &mut MapExtra<'a, '_, I, E>) -> O { let $f = &self.fold; $invoke } +impl<'src, I, O, E, A, F, Op> Operator<'src, I, O, E> for Postfix<'src, A, F, O, Op, I, E> +where + I: Input<'src>, + E: ParserExtra<'src, I>, + A: Parser<'src, I, Op, E>, + F: Fn(O, Op, &mut MapExtra<'src, '_, I, E>) -> O, +{ + #[inline] + fn do_parse_postfix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: M::Output, + min_power: u32, + ) -> Result, M::Output> + where + Self: Sized, + { + if Associativity::Left(self.binding_power).right_power() >= min_power { + match self.op_parser.go::(inp) { + Ok(op) => Ok(M::combine(lhs, op, |lhs, op| { + (self.fold)(lhs, op, &mut MapExtra::new(pre_expr, inp)) + })), + Err(()) => { + inp.rewind(pre_op.clone()); + Err(lhs) + } + } + } else { + Err(lhs) } - }; -} + } -// Allow `|lhs| ` to be used as a fold closure for postfix operators -postfix_op!(|f: Fn(O) -> O, lhs, _op, _extra| f(lhs)); -// Allow `|lhs, op| ` to be used as a fold closure for postfix operators -postfix_op!(|f: Fn(O, Op) -> O, lhs, op, _extra| f(lhs, op)); -// Allow `|lhs, op, span| ` to be used as a fold closure for postfix operators -postfix_op!(|f: Fn(O, Op, &mut MapExtra<'a, '_, I, E>) -> O, lhs, op, extra| f(lhs, op, extra)); + op_check_and_emit!(); +} /// See [`Parser::pratt`]. #[derive(Copy, Clone)] @@ -402,117 +754,243 @@ pub struct Pratt { pub(crate) ops: Ops, } -macro_rules! impl_pratt_for_tuple { +macro_rules! impl_operator_for_tuple { () => {}; ($head:ident $($X:ident)*) => { - impl_pratt_for_tuple!($($X)*); - impl_pratt_for_tuple!(~ $head $($X)*); + impl_operator_for_tuple!($($X)*); + impl_operator_for_tuple!(~ $head $($X)*); }; (~ $($X:ident)+) => { #[allow(unused_variables, non_snake_case)] - impl<'a, Atom, $($X),*> Pratt { + impl<'src, I, O, E, $($X),*> Operator<'src, I, O, E> for ($($X,)*) + where + I: Input<'src>, + E: ParserExtra<'src, I>, + $($X: Operator<'src, I, O, E>),* + { #[inline] - fn pratt_go(&self, inp: &mut InputRef<'a, '_, I, E>, min_power: u32) -> PResult + fn do_parse_prefix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult where - I: Input<'a>, - E: ParserExtra<'a, I>, - Atom: Parser<'a, I, O, E>, - $($X: Operator<'a, I, O, E>),* + Self: Sized, { - let pre_expr = inp.save(); - let mut lhs = 'choice: { - let ($($X,)*) = &self.ops; - - // Prefix unary operators - $( - if $X::IS_PREFIX { - match $X.op_parser().go::(inp) { - Ok(op) => { - match recursive::recurse(|| self.pratt_go::(inp, $X.associativity().left_power())) { - Ok(rhs) => break 'choice M::combine(op, rhs, |op, rhs| { - $X.fold_prefix(op, rhs, &mut MapExtra::new(pre_expr.cursor(), inp)) - }), - Err(()) => inp.rewind(pre_expr.clone()), - } - }, - Err(()) => inp.rewind(pre_expr.clone()), - } - } - )* - - self.atom.go::(inp)? - }; - - loop { - let ($($X,)*) = &self.ops; - - let pre_op = inp.save(); - - // Postfix unary operators - $( - let assoc = $X.associativity(); - if $X::IS_POSTFIX && assoc.right_power() >= min_power { - match $X.op_parser().go::(inp) { - Ok(op) => { - lhs = M::combine(lhs, op, |lhs, op| { - $X.fold_postfix(lhs, op, &mut MapExtra::new(pre_expr.cursor(), inp)) - }); - continue - }, - Err(()) => inp.rewind(pre_op.clone()), - } - } - )* - - // Infix binary operators - $( - let assoc = $X.associativity(); - if $X::IS_INFIX && assoc.left_power() >= min_power { - match $X.op_parser().go::(inp) { - Ok(op) => match recursive::recurse(|| self.pratt_go::(inp, assoc.right_power())) { - Ok(rhs) => { - lhs = M::combine( - M::combine(lhs, rhs, |lhs, rhs| (lhs, rhs)), - op, - |(lhs, rhs), op| { - $X.fold_infix(lhs, op, rhs, &mut MapExtra::new(pre_expr.cursor(), inp)) - }, - ); - continue - }, - Err(()) => inp.rewind(pre_op.clone()), - }, - Err(()) => inp.rewind(pre_op.clone()), - } - } - )* - - inp.rewind(pre_op); - break; - } + let ($($X,)*) = self; + $( + match $X.do_parse_prefix::(inp, pre_expr, f) { + Ok(out) => return Ok(out), + Err(()) => {}, + } + )* + Err(()) + } - Ok(lhs) + #[inline] + fn do_parse_postfix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + mut lhs: M::Output, + min_power: u32, + ) -> Result, M::Output> + where + Self: Sized, + { + let ($($X,)*) = self; + $( + match $X.do_parse_postfix::(inp, pre_expr, pre_op, lhs, min_power) { + Ok(out) => return Ok(out), + Err(out) => lhs = out, + } + )* + Err(lhs) } - } - #[allow(unused_variables, non_snake_case)] - impl<'a, I, O, E, Atom, $($X),*> ParserSealed<'a, I, O, E> for Pratt - where - I: Input<'a>, - E: ParserExtra<'a, I>, - Atom: Parser<'a, I, O, E>, - $($X: Operator<'a, I, O, E>),* - { - fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { - self.pratt_go::(inp, 0) + #[inline] + fn do_parse_infix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + mut lhs: M::Output, + min_power: u32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result, M::Output> + where + Self: Sized, + { + let ($($X,)*) = self; + $( + match $X.do_parse_infix::(inp, pre_expr, pre_op, lhs, min_power, f) { + Ok(out) => return Ok(out), + Err(out) => lhs = out, + } + )* + Err(lhs) } - go_extra!(O); + op_check_and_emit!(); } }; } -impl_pratt_for_tuple!(A_ B_ C_ D_ E_ F_ G_ H_ I_ J_ K_ L_ M_ N_ O_ P_ Q_ R_ S_ T_ U_ V_ W_ X_ Y_ Z_); +impl_operator_for_tuple!(A_ B_ C_ D_ E_ F_ G_ H_ I_ J_ K_ L_ M_ N_ O_ P_ Q_ R_ S_ T_ U_ V_ W_ X_ Y_ Z_); + +#[allow(unused_variables, non_snake_case)] +impl<'src, I, O, E, Op> Operator<'src, I, O, E> for Vec +where + I: Input<'src>, + E: ParserExtra<'src, I>, + Op: Operator<'src, I, O, E>, +{ + #[inline] + fn do_parse_prefix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult + where + Self: Sized, + { + for op in self { + if let Ok(out) = op.do_parse_prefix::(inp, pre_expr, f) { + return Ok(out); + } + } + Err(()) + } + + #[inline] + fn do_parse_postfix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + mut lhs: M::Output, + min_power: u32, + ) -> Result, M::Output> + where + Self: Sized, + { + for op in self { + match op.do_parse_postfix::(inp, pre_expr, pre_op, lhs, min_power) { + Ok(out) => return Ok(out), + Err(out) => lhs = out, + } + } + Err(lhs) + } + + #[inline] + fn do_parse_infix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + mut lhs: M::Output, + min_power: u32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result, M::Output> + where + Self: Sized, + { + for op in self { + match op.do_parse_infix::(inp, pre_expr, pre_op, lhs, min_power, f) { + Ok(out) => return Ok(out), + Err(out) => lhs = out, + } + } + Err(lhs) + } + + op_check_and_emit!(); +} + +#[allow(unused_variables, non_snake_case)] +impl<'a, Atom, Ops> Pratt { + #[inline] + fn pratt_go( + &self, + inp: &mut InputRef<'a, '_, I, E>, + min_power: u32, + ) -> PResult + where + I: Input<'a>, + E: ParserExtra<'a, I>, + Atom: Parser<'a, I, O, E>, + Ops: Operator<'a, I, O, E>, + { + let pre_expr = inp.save(); + // Prefix unary operators + let mut lhs = match self + .ops + .do_parse_prefix::(inp, &pre_expr, &|inp, min_power| { + recursive::recurse(|| self.pratt_go::(inp, min_power)) + }) { + Ok(out) => out, + Err(()) => self.atom.go::(inp)?, + }; + + loop { + let pre_op = inp.save(); + + // Postfix unary operators + match self + .ops + .do_parse_postfix::(inp, pre_expr.cursor(), &pre_op, lhs, min_power) + { + Ok(out) => { + lhs = out; + continue; + } + Err(out) => lhs = out, + } + + // Infix binary operators + match self.ops.do_parse_infix::( + inp, + pre_expr.cursor(), + &pre_op, + lhs, + min_power, + &|inp, min_power| { + recursive::recurse(|| self.pratt_go::(inp, min_power)) + }, + ) { + Ok(out) => { + lhs = out; + continue; + } + Err(out) => lhs = out, + } + + inp.rewind(pre_op); + break; + } + + Ok(lhs) + } +} + +#[allow(unused_variables, non_snake_case)] +impl<'a, I, O, E, Atom, Ops> ParserSealed<'a, I, O, E> for Pratt +where + I: Input<'a>, + E: ParserExtra<'a, I>, + Atom: Parser<'a, I, O, E>, + Ops: Operator<'a, I, O, E>, +{ + fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { + self.pratt_go::(inp, 0) + } + + go_extra!(O); +} #[cfg(test)] mod tests { @@ -531,12 +1009,12 @@ mod tests { let atom = text::int(10).padded().from_str::().unwrapped(); atom.pratt(( - prefix(2, just('-'), |x: i64| -x), - postfix(2, just('!'), factorial), - infix(left(0), just('+'), |l, r| l + r), - infix(left(0), just('-'), |l, r| l - r), - infix(left(1), just('*'), |l, r| l * r), - infix(left(1), just('/'), |l, _, r| l / r), + prefix(2, just('-'), |_, x: i64, _| -x), + postfix(2, just('!'), |x, _, _| factorial(x)), + infix(left(0), just('+'), |l, _, r, _| l + r), + infix(left(0), just('-'), |l, _, r, _| l - r), + infix(left(1), just('*'), |l, _, r, _| l * r), + infix(left(1), just('/'), |l, _, r, _| l / r), )) } @@ -609,10 +1087,10 @@ mod tests { let atom = text::int(10).from_str().unwrapped().map(Expr::Literal); atom.pratt(( - infix(left(0), just('+'), |l, r| i(Expr::Add, l, r)), - infix(left(0), just('-'), |l, r| i(Expr::Sub, l, r)), - infix(right(1), just('*'), |l, r| i(Expr::Mul, l, r)), - infix(right(1), just('/'), |l, r| i(Expr::Div, l, r)), + infix(left(0), just('+'), |l, _, r, _| i(Expr::Add, l, r)), + infix(left(0), just('-'), |l, _, r, _| i(Expr::Sub, l, r)), + infix(right(1), just('*'), |l, _, r, _| i(Expr::Mul, l, r)), + infix(right(1), just('/'), |l, _, r, _| i(Expr::Div, l, r)), )) .map(|x| x.to_string()) } @@ -696,15 +1174,15 @@ mod tests { // Because we defined '*' and '/' as right associative operators, // in order to get these to function as expected, their strength // must be higher - prefix(2, just('-'), |r| u(Expr::Negate, r)), - prefix(2, just('~'), |r| u(Expr::Not, r)), + prefix(2, just('-'), |_, r, _| u(Expr::Negate, r)), + prefix(2, just('~'), |_, r, _| u(Expr::Not, r)), // This is what happens when not - prefix(1, just('§'), |r| u(Expr::Confusion, r)), + prefix(1, just('§'), |_, r, _| u(Expr::Confusion, r)), // -- Infix - infix(left(0), just('+'), |l, r| i(Expr::Add, l, r)), - infix(left(0), just('-'), |l, r| i(Expr::Sub, l, r)), - infix(right(1), just('*'), |l, r| i(Expr::Mul, l, r)), - infix(right(1), just('/'), |l, r| i(Expr::Div, l, r)), + infix(left(0), just('+'), |l, _, r, _| i(Expr::Add, l, r)), + infix(left(0), just('-'), |l, _, r, _| i(Expr::Sub, l, r)), + infix(right(1), just('*'), |l, _, r, _| i(Expr::Mul, l, r)), + infix(right(1), just('/'), |l, _, r, _| i(Expr::Div, l, r)), )) .map(|x| x.to_string()); @@ -727,14 +1205,14 @@ mod tests { // Because we defined '*' and '/' as right associative operators, // in order to get these to function as expected, their strength // must be higher - postfix(2, just('!'), |l| u(Expr::Factorial, l)), + postfix(2, just('!'), |l, _, _| u(Expr::Factorial, l)), // This is what happens when not - postfix(0, just('$'), |l| u(Expr::Value, l)), + postfix(0, just('$'), |l, _, _| u(Expr::Value, l)), // -- Infix - infix(left(1), just('+'), |l, r| i(Expr::Add, l, r)), - infix(left(1), just('-'), |l, r| i(Expr::Sub, l, r)), - infix(right(2), just('*'), |l, r| i(Expr::Mul, l, r)), - infix(right(2), just('/'), |l, r| i(Expr::Div, l, r)), + infix(left(1), just('+'), |l, _, r, _| i(Expr::Add, l, r)), + infix(left(1), just('-'), |l, _, r, _| i(Expr::Sub, l, r)), + infix(right(2), just('*'), |l, _, r, _| i(Expr::Mul, l, r)), + infix(right(2), just('/'), |l, _, r, _| i(Expr::Div, l, r)), )) .map(|x| x.to_string()); @@ -754,17 +1232,17 @@ mod tests { let parser = atom .pratt(( // -- Prefix - prefix(4, just('-'), |r| u(Expr::Negate, r)), - prefix(4, just('~'), |r| u(Expr::Not, r)), - prefix(1, just('§'), |r| u(Expr::Confusion, r)), + prefix(4, just('-'), |_, r, _| u(Expr::Negate, r)), + prefix(4, just('~'), |_, r, _| u(Expr::Not, r)), + prefix(1, just('§'), |_, r, _| u(Expr::Confusion, r)), // -- Postfix - postfix(5, just('!'), |l| u(Expr::Factorial, l)), - postfix(0, just('$'), |l| u(Expr::Value, l)), + postfix(5, just('!'), |l, _, _| u(Expr::Factorial, l)), + postfix(0, just('$'), |l, _, _| u(Expr::Value, l)), // -- Infix - infix(left(1), just('+'), |l, r| i(Expr::Add, l, r)), - infix(left(1), just('-'), |l, r| i(Expr::Sub, l, r)), - infix(right(2), just('*'), |l, r| i(Expr::Mul, l, r)), - infix(right(2), just('/'), |l, r| i(Expr::Div, l, r)), + infix(left(1), just('+'), |l, _, r, _| i(Expr::Add, l, r)), + infix(left(1), just('-'), |l, _, r, _| i(Expr::Sub, l, r)), + infix(right(2), just('*'), |l, _, r, _| i(Expr::Mul, l, r)), + infix(right(2), just('/'), |l, _, r, _| i(Expr::Div, l, r)), )) .map(|x| x.to_string()); assert_eq!( diff --git a/src/private.rs b/src/private.rs index 764bc1cf..a4be9268 100644 --- a/src/private.rs +++ b/src/private.rs @@ -75,6 +75,45 @@ pub trait Mode { I: Input<'a>, E: ParserExtra<'a, I>, P: ConfigParser<'a, I, O, E> + ?Sized; + + #[cfg(feature = "pratt")] + fn invoke_pratt_op_prefix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>; + #[cfg(feature = "pratt")] + fn invoke_pratt_op_postfix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: Self::Output, + min_power: u32, + ) -> Result, Self::Output> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>; + #[cfg(feature = "pratt")] + fn invoke_pratt_op_infix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: Self::Output, + min_power: u32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result, Self::Output> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>; } /// Emit mode - generates parser output @@ -147,6 +186,57 @@ impl Mode for Emit { { parser.go_emit_cfg(inp, cfg) } + + #[cfg(feature = "pratt")] + #[inline(always)] + fn invoke_pratt_op_prefix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + { + op.do_parse_prefix_emit(inp, pre_expr, &f) + } + #[cfg(feature = "pratt")] + #[inline(always)] + fn invoke_pratt_op_postfix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: Self::Output, + min_power: u32, + ) -> Result, Self::Output> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + { + op.do_parse_postfix_emit(inp, pre_expr, pre_op, lhs, min_power) + } + #[cfg(feature = "pratt")] + #[inline(always)] + fn invoke_pratt_op_infix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: Self::Output, + min_power: u32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result, Self::Output> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + { + op.do_parse_infix_emit(inp, pre_expr, pre_op, lhs, min_power, &f) + } } /// Check mode - all output is discarded, and only uses parsers to check validity @@ -207,6 +297,57 @@ impl Mode for Check { { parser.go_check_cfg(inp, cfg) } + + #[cfg(feature = "pratt")] + #[inline(always)] + fn invoke_pratt_op_prefix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + { + op.do_parse_prefix_check(inp, pre_expr, &f) + } + #[cfg(feature = "pratt")] + #[inline(always)] + fn invoke_pratt_op_postfix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: Self::Output, + min_power: u32, + ) -> Result, Self::Output> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + { + op.do_parse_postfix_check(inp, pre_expr, pre_op, lhs, min_power) + } + #[cfg(feature = "pratt")] + #[inline(always)] + fn invoke_pratt_op_infix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: Self::Output, + min_power: u32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result, Self::Output> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + { + op.do_parse_infix_check(inp, pre_expr, pre_op, lhs, min_power, &f) + } } // TODO: Consider removing these sealed traits in favour of `Sealed`, with the given methods just being on `Parser`