From 6dfce6a4aed478fb5bcda5d329afc9e598b71158 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 21 Oct 2024 20:40:20 +0100 Subject: [PATCH 01/12] Began working on Mini ML example --- examples/mini_ml.rs | 139 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 examples/mini_ml.rs diff --git a/examples/mini_ml.rs b/examples/mini_ml.rs new file mode 100644 index 00000000..0052aae3 --- /dev/null +++ b/examples/mini_ml.rs @@ -0,0 +1,139 @@ +use chumsky::{ + input::{MapExtra, SpannedInput}, + pratt::*, + prelude::*, +}; + +#[derive(Debug, Clone, PartialEq)] +pub enum Token<'src> { + Ident(&'src str), + Num(f64), + Parens(Vec>), + + // Ops + Eq, + Plus, + Asterisk, + + // Keywords + Let, + In, +} + +pub type Spanned = (T, SimpleSpan); + +fn lexer<'src>( +) -> impl Parser<'src, &'src str, Vec>>, extra::Err>> { + recursive(|token| { + let keyword = text::ident().map(|s| match s { + "let" => Token::Let, + "in" => Token::In, + s => Token::Ident(s), + }); + + let num = text::int(10) + .then(just('.').then(text::digits(10)).or_not()) + .to_slice() + .map(|s: &str| Token::Num(s.parse().unwrap())); + + let op = choice(( + just("=").to(Token::Eq), + just("+").to(Token::Plus), + just("*").to(Token::Asterisk), + )); + + choice(( + keyword, + op, + num, + token + .repeated() + .collect() + .delimited_by(just('(').padded(), just(')').padded()) + .map(Token::Parens), + )) + .map_with(|t, e| (t, e.span())) + .padded() + }) + .repeated() + .collect() +} + +#[derive(Debug)] +pub enum Expr<'src> { + Local(&'src str), + Num(f64), + Let(&'src str, Box>, Box>), + Add(Box>, Box>), + Mul(Box>, Box>), + Call(Box>, 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) }, + ident.map(Expr::Local), + // let x = y in z + just(Token::Let) + .ignore_then(ident) + .then_ignore(just(Token::Eq)) + .then(expr.clone()) + .then_ignore(just(Token::In)) + .then(expr.clone()) + .map(|((lhs, rhs), then)| Expr::Let(lhs, Box::new(rhs), Box::new(then))), + )); + + atom.map_with(|expr, e| (expr, e.span())) + // ( x ) + .or(expr.nested_in( + select_ref! { Token::Parens(ts) = e => ts.as_slice().spanned(e.span()) }, + )) + .pratt(( + // Multiply + infix( + left(10), + just(Token::Asterisk), + |x, _, y, e: &mut MapExtra<'src, '_, _, _>| { + (Expr::Mul(Box::new(x), Box::new(y)), e.span()) + }, + ), + // Add + infix( + left(9), + just(Token::Plus), + |x, _, y, e: &mut MapExtra<'src, '_, _, _>| { + (Expr::Add(Box::new(x), Box::new(y)), e.span()) + }, + ), + // Calls + infix( + left(1), + empty(), + |x, _, y, e: &mut MapExtra<'src, '_, _, _>| { + (Expr::Call(Box::new(x), Box::new(y)), e.span()) + }, + ), + )) + }) +} + +fn main() { + let text = " + let x = (5 + 42) * 2 in + add x 3.5 + "; + + let tokens = lexer().parse(text).unwrap(); + + dbg!(&tokens); + + let expr = parser().parse(tokens.spanned((0..text.len()).into())); + + dbg!(expr); +} From 9475a0a72709ff9c0056f29178412cb991076a1f Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 21 Oct 2024 22:07:10 +0100 Subject: [PATCH 02/12] Added function parsing --- examples/mini_ml.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/examples/mini_ml.rs b/examples/mini_ml.rs index 0052aae3..c844a14a 100644 --- a/examples/mini_ml.rs +++ b/examples/mini_ml.rs @@ -18,6 +18,7 @@ pub enum Token<'src> { // Keywords Let, In, + Fn, } pub type Spanned = (T, SimpleSpan); @@ -28,6 +29,7 @@ fn lexer<'src>( let keyword = text::ident().map(|s| match s { "let" => Token::Let, "in" => Token::In, + "fn" => Token::Fn, s => Token::Ident(s), }); @@ -63,10 +65,11 @@ fn lexer<'src>( pub enum Expr<'src> { Local(&'src str), Num(f64), - Let(&'src str, Box>, Box>), + Let(Spanned<&'src str>, Box>, Box>), Add(Box>, Box>), Mul(Box>, Box>), Call(Box>, Box>), + Func(Vec>, Box>), } type ParserInput<'src> = SpannedInput, SimpleSpan, &'src [Spanned>]>; @@ -81,12 +84,18 @@ fn parser<'src>( ident.map(Expr::Local), // let x = y in z just(Token::Let) - .ignore_then(ident) + .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, Box::new(rhs), Box::new(then))), + // fn x y = z + just(Token::Fn) + .ignore_then(ident.map_with(|x, e| (x, e.span())).repeated().collect()) + .then_ignore(just(Token::Eq)) + .then(expr.clone()) + .map(|(args, body)| Expr::Func(args, Box::new(body))), )); atom.map_with(|expr, e| (expr, e.span())) @@ -125,7 +134,9 @@ fn parser<'src>( fn main() { let text = " - let x = (5 + 42) * 2 in + 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 "; From 01c1aa298548601d2392d65124cd4e9f95c382dc Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 22 Oct 2024 13:24:35 +0100 Subject: [PATCH 03/12] Improved pratt parser type inference by removing fold overloading --- examples/mini_ml.rs | 30 ++--- src/lib.rs | 11 +- src/pratt.rs | 297 +++++++++++++++++++++----------------------- 3 files changed, 155 insertions(+), 183 deletions(-) diff --git a/examples/mini_ml.rs b/examples/mini_ml.rs index c844a14a..41d071c0 100644 --- a/examples/mini_ml.rs +++ b/examples/mini_ml.rs @@ -105,29 +105,17 @@ fn parser<'src>( )) .pratt(( // Multiply - infix( - left(10), - just(Token::Asterisk), - |x, _, y, e: &mut MapExtra<'src, '_, _, _>| { - (Expr::Mul(Box::new(x), Box::new(y)), e.span()) - }, - ), + infix(left(10), just(Token::Asterisk), |x, _, y, e| { + (Expr::Mul(Box::new(x), Box::new(y)), e.span()) + }), // Add - infix( - left(9), - just(Token::Plus), - |x, _, y, e: &mut MapExtra<'src, '_, _, _>| { - (Expr::Add(Box::new(x), Box::new(y)), e.span()) - }, - ), + infix(left(9), just(Token::Plus), |x, _, y, e| { + (Expr::Add(Box::new(x), Box::new(y)), e.span()) + }), // Calls - infix( - left(1), - empty(), - |x, _, y, e: &mut MapExtra<'src, '_, _, _>| { - (Expr::Call(Box::new(x), Box::new(y)), e.span()) - }, - ), + infix(left(1), empty(), |x, _, y, e| { + (Expr::Call(Box::new(x), Box::new(y)), e.span()) + }), )) }) } diff --git a/src/lib.rs b/src/lib.rs index 8f4c5e25..c5949b51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2130,7 +2130,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 +2139,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..6a34e93d 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()); //! @@ -166,16 +162,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<'src, A: Copy, F: Copy, Atom, Op, I, E> Copy for Infix<'src, A, F, Atom, Op, I, E> {} +impl<'src, A: Clone, F: Clone, Atom, Op, I, E> Clone for Infix<'src, A, F, Atom, Op, I, E> { fn clone(&self) -> Self { Self { op_parser: self.op_parser.clone(), @@ -194,21 +190,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 +212,41 @@ 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, +{ + 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<'src, '_, I, E>) -> O { + (self.fold)(lhs, op, rhs, extra) + } } -// 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) -); - /// 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<'src, A: Copy, F: Copy, Atom, Op, I, E> Copy for Prefix<'src, A, F, Atom, Op, I, E> {} +impl<'src, A: Clone, F: Clone, Atom, Op, I, E> Clone for Prefix<'src, A, F, Atom, Op, I, E> { fn clone(&self) -> Self { Self { op_parser: self.op_parser.clone(), @@ -271,21 +262,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 +284,41 @@ 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, +{ + 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<'src, '_, I, E>) -> O { + (self.fold)(op, rhs, extra) + } } -// 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)); - /// 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<'src, A: Copy, F: Copy, Atom, Op, I, E> Copy for Postfix<'src, A, F, Atom, Op, I, E> {} +impl<'src, A: Clone, F: Clone, Atom, Op, I, E> Clone for Postfix<'src, A, F, Atom, Op, I, E> { fn clone(&self) -> Self { Self { op_parser: self.op_parser.clone(), @@ -344,23 +332,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,32 +356,30 @@ 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, +{ + 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<'src, '_, I, E>) -> O { + (self.fold)(lhs, op, extra) + } } -// 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)); - /// See [`Parser::pratt`]. #[derive(Copy, Clone)] pub struct Pratt { @@ -531,12 +516,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 +594,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 +681,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 +712,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 +739,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!( From 73fb95da58a043b46702dab0a8e853c1e9095c17 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 22 Oct 2024 13:26:36 +0100 Subject: [PATCH 04/12] fmt --- examples/mini_ml.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/mini_ml.rs b/examples/mini_ml.rs index 41d071c0..1d8cb3bc 100644 --- a/examples/mini_ml.rs +++ b/examples/mini_ml.rs @@ -1,8 +1,4 @@ -use chumsky::{ - input::{MapExtra, SpannedInput}, - pratt::*, - prelude::*, -}; +use chumsky::{input::SpannedInput, pratt::*, prelude::*}; #[derive(Debug, Clone, PartialEq)] pub enum Token<'src> { From 1b45d32c4a430934f4823e6a2521ad0231cf557f Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 22 Oct 2024 13:34:04 +0100 Subject: [PATCH 05/12] Added required feature --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index b0964350..0c388dbf 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"] From 466e3c7cd5b72c9697fb4ed67bcf6ddb3cc6f02e Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 22 Oct 2024 13:48:26 +0100 Subject: [PATCH 06/12] Elided lifetimes --- src/pratt.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pratt.rs b/src/pratt.rs index 6a34e93d..637ee45d 100644 --- a/src/pratt.rs +++ b/src/pratt.rs @@ -170,8 +170,8 @@ pub struct Infix<'src, A, F, Atom, Op, I, E> { phantom: EmptyPhantom<&'src (Atom, Op, I, E)>, } -impl<'src, A: Copy, F: Copy, Atom, Op, I, E> Copy for Infix<'src, A, F, Atom, Op, I, E> {} -impl<'src, A: Clone, F: Clone, Atom, Op, I, E> Clone for Infix<'src, A, F, Atom, Op, I, E> { +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(), @@ -245,8 +245,8 @@ pub struct Prefix<'src, A, F, Atom, Op, I, E> { phantom: EmptyPhantom<&'src (Atom, Op, I, E)>, } -impl<'src, A: Copy, F: Copy, Atom, Op, I, E> Copy for Prefix<'src, A, F, Atom, Op, I, E> {} -impl<'src, A: Clone, F: Clone, Atom, Op, I, E> Clone for Prefix<'src, A, F, Atom, Op, I, E> { +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(), @@ -317,8 +317,8 @@ pub struct Postfix<'src, A, F, Atom, Op, I, E> { phantom: EmptyPhantom<&'src (Atom, Op, I, E)>, } -impl<'src, A: Copy, F: Copy, Atom, Op, I, E> Copy for Postfix<'src, A, F, Atom, Op, I, E> {} -impl<'src, A: Clone, F: Clone, Atom, Op, I, E> Clone for Postfix<'src, A, F, Atom, Op, I, E> { +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(), From a38a9352cb4e603d9d34bfad34862a0ab3695ab1 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 22 Oct 2024 14:34:03 +0100 Subject: [PATCH 07/12] Added type solver to mini_ml --- examples/mini_ml.rs | 218 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 186 insertions(+), 32 deletions(-) diff --git a/examples/mini_ml.rs b/examples/mini_ml.rs index 1d8cb3bc..1d21f502 100644 --- a/examples/mini_ml.rs +++ b/examples/mini_ml.rs @@ -15,6 +15,8 @@ pub enum Token<'src> { Let, In, Fn, + True, + False, } pub type Spanned = (T, SimpleSpan); @@ -26,6 +28,8 @@ fn lexer<'src>( "let" => Token::Let, "in" => Token::In, "fn" => Token::Fn, + "true" => Token::True, + "false" => Token::False, s => Token::Ident(s), }); @@ -57,15 +61,26 @@ fn lexer<'src>( .collect() } -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum Expr<'src> { - Local(&'src str), + Var(&'src str), Num(f64), - Let(Spanned<&'src str>, Box>, Box>), + Bool(bool), Add(Box>, Box>), Mul(Box>, Box>), - Call(Box>, Box>), - Func(Vec>, 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>]>; @@ -77,7 +92,9 @@ fn parser<'src>( let ident = select_ref! { Token::Ident(x) => *x }; let atom = choice(( select_ref! { Token::Num(x) => Expr::Num(*x) }, - ident.map(Expr::Local), + 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()))) @@ -85,37 +102,163 @@ fn parser<'src>( .then(expr.clone()) .then_ignore(just(Token::In)) .then(expr.clone()) - .map(|((lhs, rhs), then)| Expr::Let(lhs, Box::new(rhs), Box::new(then))), - // fn x y = z - just(Token::Fn) - .ignore_then(ident.map_with(|x, e| (x, e.span())).repeated().collect()) - .then_ignore(just(Token::Eq)) - .then(expr.clone()) - .map(|(args, body)| Expr::Func(args, Box::new(body))), + .map(|((lhs, rhs), then)| Expr::Let { + lhs, + rhs: Box::new(rhs), + then: Box::new(then), + }), )); - atom.map_with(|expr, e| (expr, e.span())) + 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 ) - .or(expr.nested_in( + expr.nested_in( select_ref! { Token::Parens(ts) = e => ts.as_slice().spanned(e.span()) }, - )) - .pratt(( - // Multiply - infix(left(10), just(Token::Asterisk), |x, _, y, e| { - (Expr::Mul(Box::new(x), Box::new(y)), e.span()) - }), - // Add - infix(left(9), just(Token::Plus), |x, _, y, e| { - (Expr::Add(Box::new(x), Box::new(y)), e.span()) - }), - // Calls - infix(left(1), empty(), |x, _, y, e| { - (Expr::Call(Box::new(x), Box::new(y)), e.span()) - }), - )) + ), + )) + .pratt(( + // Multiply + infix(left(10), just(Token::Asterisk), |x, _, y, e| { + (Expr::Mul(Box::new(x), Box::new(y)), e.span()) + }), + // Add + infix(left(9), just(Token::Plus), |x, _, y, e| { + (Expr::Add(Box::new(x), Box::new(y)), e.span()) + }), + // Calls + infix(left(1), empty(), |x, _, y, e| { + ( + Expr::Apply { + func: Box::new(x), + arg: Box::new(y), + }, + e.span(), + ) + }), + )) }) } +#[derive(Copy, Clone, Debug, PartialEq)] +struct TyVar(usize); + +#[derive(Copy, Clone, Debug)] +enum TyInfo { + Unknown, + Ref(TyVar), + Num, + Bool, + Func(TyVar, TyVar), +} + +#[derive(Debug)] +enum Ty { + Num, + Bool, + Func(Box, Box), +} + +#[derive(Default)] +struct Solver { + vars: Vec, +} + +impl Solver { + fn create_ty(&mut self, info: TyInfo) -> TyVar { + self.vars.push(info); + TyVar(self.vars.len() - 1) + } + + fn unify(&mut self, a: TyVar, b: TyVar) { + match (self.vars[a.0], self.vars[b.0]) { + (TyInfo::Unknown, _) => self.vars[a.0] = TyInfo::Ref(b), + (_, TyInfo::Unknown) => self.vars[b.0] = TyInfo::Ref(a), + (TyInfo::Ref(a), _) => self.unify(a, b), + (_, TyInfo::Ref(b)) => self.unify(a, b), + (TyInfo::Num, TyInfo::Num) | (TyInfo::Bool, TyInfo::Bool) => {} + (TyInfo::Func(a_i, a_o), TyInfo::Func(b_i, b_o)) => { + self.unify(a_i, b_i); + self.unify(a_o, b_o); + } + (a, b) => panic!("Type mismatch between {a:?} and {b:?}"), + } + } + + fn check<'ast>(&mut self, expr: &Expr<'ast>, env: &mut Vec<(&'ast str, TyVar)>) -> TyVar { + match expr { + // Literal expressions are easy, their type doesn't need inferring. + Expr::Num(_) => self.create_ty(TyInfo::Num), + Expr::Bool(_) => self.create_ty(TyInfo::Bool), + // We search the environment backward until we find a binding matching the variable name. + Expr::Var(name) => { + env.iter_mut() + .rev() + .find(|(n, _)| n == name) + .expect("No such variable in scope") + .1 + } + // In a let expression, `rhs` gets bound with name `lhs` in the environment used to type-check `then`. + Expr::Let { lhs, rhs, then } => { + let rhs = self.check(&rhs.0, env); + env.push((lhs.0, rhs)); + let out = self.check(&then.0, env); + env.pop(); + out + } + // In a function, the argument becomes an unknown type in the environment used to type-check `body`. + Expr::Func { arg, body } => { + let arg_ty = self.create_ty(TyInfo::Unknown); + env.push((arg.0, arg_ty)); + let body = self.check(&body.0, env); + env.pop(); + self.create_ty(TyInfo::Func(arg_ty, body)) + } + // During function application, both argument and function are type-checked and then we force the latter to be a function of the former. + Expr::Apply { func, arg } => { + let func = self.check(&func.0, env); + let arg = self.check(&arg.0, env); + let out = self.create_ty(TyInfo::Unknown); + let func_ty = self.create_ty(TyInfo::Func(arg, out)); + self.unify(func_ty, func); + out + } + Expr::Add(l, r) | Expr::Mul(l, r) => { + let out = self.create_ty(TyInfo::Num); + let l = self.check(&l.0, env); + self.unify(out, l); + let r = self.check(&r.0, env); + self.unify(out, r); + out + } + } + } + + pub fn solve(&self, var: TyVar) -> Ty { + match self.vars[var.0] { + TyInfo::Unknown => panic!("Cannot infer type"), + 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))), + } + } +} + fn main() { let text = " let add = fn x y = x + y in @@ -128,7 +271,18 @@ fn main() { dbg!(&tokens); - let expr = parser().parse(tokens.spanned((0..text.len()).into())); + let expr = parser() + .parse(tokens.spanned((0..text.len()).into())) + .unwrap(); + + dbg!(&expr); + + let mut solver = Solver::default(); + + let program_ty = solver.check(&expr.0, &mut Vec::new()); - dbg!(expr); + println!( + "The expression outputs type `{:?}`", + solver.solve(program_ty) + ); } From 4fb64dfb4bba8281288e5a3ddd67187954e589a9 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 23 Oct 2024 17:13:50 +0100 Subject: [PATCH 08/12] Added better error reporting --- Cargo.toml | 2 +- examples/mini_ml.rs | 344 +++++++++++++++++++++++++++++++++----------- 2 files changed, 261 insertions(+), 85 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0c388dbf..3ed442c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,4 +144,4 @@ required-features = ["std"] [[example]] name = "mini_ml" -required-features = ["pratt"] +required-features = ["pratt", "label"] diff --git a/examples/mini_ml.rs b/examples/mini_ml.rs index 1d21f502..51bb061b 100644 --- a/examples/mini_ml.rs +++ b/examples/mini_ml.rs @@ -1,4 +1,8 @@ +use ariadne::{sources, Color, Label, Report, ReportKind}; use chumsky::{input::SpannedInput, pratt::*, prelude::*}; +use core::fmt; + +// Tokens and lexer #[derive(Debug, Clone, PartialEq)] pub enum Token<'src> { @@ -19,39 +23,54 @@ pub enum Token<'src> { 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| { - let keyword = 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), - }); - - let num = text::int(10) - .then(just('.').then(text::digits(10)).or_not()) - .to_slice() - .map(|s: &str| Token::Num(s.parse().unwrap())); - - let op = choice(( + 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), - )); - - choice(( - keyword, - op, - num, + // 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('(').padded(), just(')').padded()) + .delimited_by(just('('), just(')')) + .labelled("token tree") + .as_context() .map(Token::Parens), )) .map_with(|t, e| (t, e.span())) @@ -61,6 +80,8 @@ fn lexer<'src>( .collect() } +// AST and parser + #[derive(Clone, Debug)] pub enum Expr<'src> { Var(&'src str), @@ -151,9 +172,13 @@ fn parser<'src>( ) }), )) + .labelled("expression") + .as_context() }) } +// Type checker/solver + #[derive(Copy, Clone, Debug, PartialEq)] struct TyVar(usize); @@ -166,6 +191,18 @@ enum TyInfo { 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, @@ -173,84 +210,113 @@ enum Ty { Func(Box, Box), } -#[derive(Default)] -struct Solver { - vars: Vec, +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}"), + } + } } -impl Solver { - fn create_ty(&mut self, info: TyInfo) -> TyVar { - self.vars.push(info); +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) { - match (self.vars[a.0], self.vars[b.0]) { - (TyInfo::Unknown, _) => self.vars[a.0] = TyInfo::Ref(b), - (_, TyInfo::Unknown) => self.vars[b.0] = TyInfo::Ref(a), - (TyInfo::Ref(a), _) => self.unify(a, b), - (_, TyInfo::Ref(b)) => self.unify(a, b), + 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(a_i, b_i); - self.unify(a_o, b_o); + self.unify(b_i, a_i, span); // Order swapped: function args are contravariant + self.unify(a_o, b_o, span); } - (a, b) => panic!("Type mismatch between {a:?} and {b:?}"), + (a_info, b_info) => failure( + format!("Type mismatch between {a_info} and {b_info}"), + (format!("mismatch occurred here"), span), + vec![ + (format!("{a_info}"), self.vars[a.0].1), + (format!("{b_info}"), self.vars[b.0].1), + ], + self.src, + ), } } - fn check<'ast>(&mut self, expr: &Expr<'ast>, env: &mut Vec<(&'ast str, TyVar)>) -> TyVar { - match expr { - // Literal expressions are easy, their type doesn't need inferring. - Expr::Num(_) => self.create_ty(TyInfo::Num), - Expr::Bool(_) => self.create_ty(TyInfo::Bool), - // We search the environment backward until we find a binding matching the variable name. + 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_mut() + env.iter() .rev() .find(|(n, _)| n == name) - .expect("No such variable in scope") + .unwrap_or_else(|| { + failure( + format!("No such local '{name}'"), + ("not found in scope".to_string(), expr.1), + None, + self.src, + ) + }) .1 } - // In a let expression, `rhs` gets bound with name `lhs` in the environment used to type-check `then`. Expr::Let { lhs, rhs, then } => { - let rhs = self.check(&rhs.0, env); - env.push((lhs.0, rhs)); - let out = self.check(&then.0, env); + let rhs_ty = self.check(rhs, env); + env.push((lhs.0, rhs_ty)); + let out_ty = self.check(then, env); env.pop(); - out + out_ty } - // In a function, the argument becomes an unknown type in the environment used to type-check `body`. Expr::Func { arg, body } => { - let arg_ty = self.create_ty(TyInfo::Unknown); + let arg_ty = self.create_ty(TyInfo::Unknown, arg.1); env.push((arg.0, arg_ty)); - let body = self.check(&body.0, env); + let body_ty = self.check(body, env); env.pop(); - self.create_ty(TyInfo::Func(arg_ty, body)) + self.create_ty(TyInfo::Func(arg_ty, body_ty), expr.1) } - // During function application, both argument and function are type-checked and then we force the latter to be a function of the former. Expr::Apply { func, arg } => { - let func = self.check(&func.0, env); - let arg = self.check(&arg.0, env); - let out = self.create_ty(TyInfo::Unknown); - let func_ty = self.create_ty(TyInfo::Func(arg, out)); - self.unify(func_ty, func); - out + 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 = self.create_ty(TyInfo::Num); - let l = self.check(&l.0, env); - self.unify(out, l); - let r = self.check(&r.0, env); - self.unify(out, r); - out + 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] { - TyInfo::Unknown => panic!("Cannot infer type"), + match self.vars[var.0].0 { + TyInfo::Unknown => failure( + format!("Cannot infer type"), + ("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, @@ -259,30 +325,140 @@ impl Solver { } } +#[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 text = " + let src = " 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 "; - let tokens = lexer().parse(text).unwrap(); - - dbg!(&tokens); + let tokens = lexer() + .parse(src) + .into_result() + .unwrap_or_else(|errs| parse_failure(&errs[0], src)); let expr = parser() - .parse(tokens.spanned((0..text.len()).into())) - .unwrap(); - - dbg!(&expr); - - let mut solver = Solver::default(); + .parse(tokens.spanned((0..src.len()).into())) + .into_result() + .unwrap_or_else(|errs| parse_failure(&errs[0], src)); - let program_ty = solver.check(&expr.0, &mut Vec::new()); + let mut solver = Solver { + src, + vars: Vec::new(), + }; + let program_ty = solver.check(&expr, &mut Vec::new()); + println!("Result type: {:?}", solver.solve(program_ty)); - println!( - "The expression outputs type `{:?}`", - solver.solve(program_ty) - ); + let mut vm = Vm::default(); + println!("Result: {:?}", vm.eval(&expr)); } From 79384b7552c280be5c77bee65a947b36c31d8ae7 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 23 Oct 2024 20:09:53 +0100 Subject: [PATCH 09/12] Added pratt operator boxing and vector pratt operator impl --- examples/mini_ml.rs | 6 +- src/lib.rs | 5 + src/pratt.rs | 559 ++++++++++++++++++++++++++++++++++++++------ src/private.rs | 125 ++++++++++ 4 files changed, 616 insertions(+), 79 deletions(-) diff --git a/examples/mini_ml.rs b/examples/mini_ml.rs index 51bb061b..bb1bdec6 100644 --- a/examples/mini_ml.rs +++ b/examples/mini_ml.rs @@ -33,7 +33,7 @@ impl fmt::Display for Token<'_> { Token::Plus => write!(f, "+"), Token::Asterisk => write!(f, "*"), Token::Let => write!(f, "let"), - Token::In => write!(f, "'in"), + Token::In => write!(f, "in"), Token::Fn => write!(f, "fn"), Token::True => write!(f, "true"), Token::False => write!(f, "false"), @@ -244,7 +244,7 @@ impl Solver<'_> { } (a_info, b_info) => failure( format!("Type mismatch between {a_info} and {b_info}"), - (format!("mismatch occurred here"), span), + ("mismatch occurred here".to_string(), span), vec![ (format!("{a_info}"), self.vars[a.0].1), (format!("{b_info}"), self.vars[b.0].1), @@ -312,7 +312,7 @@ impl Solver<'_> { pub fn solve(&self, var: TyVar) -> Ty { match self.vars[var.0].0 { TyInfo::Unknown => failure( - format!("Cannot infer type"), + "Cannot infer type".to_string(), ("has unknown type".to_string(), self.vars[var.0].1), None, self.src, diff --git a/src/lib.rs b/src/lib.rs index c5949b51..409691f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -185,6 +185,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 +202,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. diff --git a/src/pratt.rs b/src/pratt.rs index 637ee45d..374b09a5 100644 --- a/src/pratt.rs +++ b/src/pratt.rs @@ -87,33 +87,307 @@ use super::*; -trait Operator<'a, I, O, E> +macro_rules! op_check_and_emit { + () => { + #[doc(hidden)] + fn do_parse_prefix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult { + self.do_parse_prefix::(inp, pre_expr, f) + } + #[doc(hidden)] + fn do_parse_prefix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult { + self.do_parse_prefix::(inp, pre_expr, f) + } + #[doc(hidden)] + fn do_parse_postfix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: (), + ) -> Result<(), ()> { + self.do_parse_postfix::(inp, pre_expr, lhs) + } + #[doc(hidden)] + fn do_parse_postfix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: O, + ) -> Result { + self.do_parse_postfix::(inp, pre_expr, lhs) + } + #[doc(hidden)] + fn do_parse_infix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: (), + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result<(), ()> { + self.do_parse_infix::(inp, pre_expr, lhs, &f) + } + #[doc(hidden)] + fn do_parse_infix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: O, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result { + self.do_parse_infix::(inp, pre_expr, lhs, &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; + /// 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)) + } - fn op_parser(&self) -> &Self::OpParser; + #[inline(always)] + #[doc(hidden)] + fn is_infix(&self) -> bool { + false + } + #[inline(always)] + #[doc(hidden)] + fn is_prefix(&self) -> bool { + false + } + #[inline(always)] + #[doc(hidden)] + fn is_postfix(&self) -> bool { + false + } + + #[doc(hidden)] fn associativity(&self) -> Associativity; - fn fold_infix( + + #[doc(hidden)] + fn do_parse_prefix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult + where + Self: Sized, + { + unimplemented!() + } + + #[doc(hidden)] + fn do_parse_postfix<'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::Cursor<'src, 'parse, I>, + lhs: M::Output, + ) -> Result, M::Output> + where + Self: Sized, + { + unimplemented!() } - fn fold_prefix(&self, _op: Self::Op, _rhs: O, _extra: &mut MapExtra<'a, '_, I, E>) -> O { - unreachable!() + + #[doc(hidden)] + fn do_parse_infix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: M::Output, + f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result, M::Output> + where + Self: Sized, + { + unimplemented!() } - fn fold_postfix(&self, _lhs: O, _op: Self::Op, _extra: &mut MapExtra<'a, '_, I, E>) -> O { - unreachable!() + + #[doc(hidden)] + fn do_parse_prefix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + 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::Cursor<'src, 'parse, I>, + 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>, + lhs: (), + ) -> Result<(), ()>; + #[doc(hidden)] + fn do_parse_postfix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: O, + ) -> Result; + #[doc(hidden)] + fn do_parse_infix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: (), + 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>, + lhs: O, + 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<'src, 'a, I, O, E> Clone for Boxed<'src, 'a, I, O, E> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl<'src, 'a, I, O, E> Operator<'src, I, O, E> for Boxed<'src, 'a, I, O, E> +where + I: Input<'src>, + E: ParserExtra<'src, I>, +{ + #[inline(always)] + fn is_infix(&self) -> bool { + self.0.is_infix() + } + #[inline(always)] + fn is_prefix(&self) -> bool { + self.0.is_prefix() + } + #[inline(always)] + fn is_postfix(&self) -> bool { + self.0.is_postfix() + } + + #[inline(always)] + fn associativity(&self) -> Associativity { + self.0.associativity() + } + + #[inline(always)] + fn do_parse_prefix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + 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>, + lhs: M::Output, + ) -> Result, M::Output> + where + Self: Sized, + { + M::invoke_pratt_op_postfix(self, inp, pre_expr, lhs) + } + + #[inline(always)] + fn do_parse_infix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: M::Output, + 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, lhs, f) + } + + fn do_parse_prefix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult { + self.0.do_parse_prefix_check(inp, pre_expr, f) + } + fn do_parse_prefix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> PResult { + self.0.do_parse_prefix_emit(inp, pre_expr, f) + } + fn do_parse_postfix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: (), + ) -> Result<(), ()> { + self.0.do_parse_postfix_check(inp, pre_expr, lhs) + } + fn do_parse_postfix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: O, + ) -> Result { + self.0.do_parse_postfix_emit(inp, pre_expr, lhs) + } + fn do_parse_infix_check<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: (), + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result<(), ()> { + self.0.do_parse_infix_check(inp, pre_expr, lhs, &f) + } + fn do_parse_infix_emit<'parse>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: O, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result { + self.0.do_parse_infix_emit(inp, pre_expr, lhs, &f) } } @@ -219,21 +493,41 @@ where A: Parser<'src, I, Op, E>, F: Fn(O, Op, O, &mut MapExtra<'src, '_, I, E>) -> O, { - type Op = Op; - type OpParser = A; - const IS_INFIX: bool = true; #[inline(always)] - fn op_parser(&self) -> &Self::OpParser { - &self.op_parser + fn is_infix(&self) -> bool { + true } + #[inline(always)] fn associativity(&self) -> Associativity { self.associativity } - #[inline(always)] - fn fold_infix(&self, lhs: O, op: Self::Op, rhs: O, extra: &mut MapExtra<'src, '_, I, E>) -> O { - (self.fold)(lhs, op, rhs, extra) + + #[inline] + fn do_parse_infix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: M::Output, + f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + ) -> Result, M::Output> + where + Self: Sized, + { + 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(()) => Err(lhs), + }, + Err(()) => Err(lhs), + } } + + op_check_and_emit!(); } /// See [`prefix`]. @@ -291,21 +585,38 @@ where A: Parser<'src, I, Op, E>, F: Fn(Op, O, &mut MapExtra<'src, '_, I, E>) -> O, { - type Op = Op; - type OpParser = A; - const IS_PREFIX: bool = true; #[inline(always)] - fn op_parser(&self) -> &Self::OpParser { - &self.op_parser + fn is_prefix(&self) -> bool { + true } + #[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<'src, '_, I, E>) -> O { - (self.fold)(op, rhs, extra) + + #[inline] + fn do_parse_prefix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + 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, self.associativity().left_power()) { + Ok(rhs) => Ok(M::combine(op, rhs, |op, rhs| { + (self.fold)(op, rhs, &mut MapExtra::new(pre_expr, inp)) + })), + Err(()) => Err(()), + }, + Err(()) => Err(()), + } } + + op_check_and_emit!(); } /// See [`postfix`]. @@ -363,21 +674,35 @@ where A: Parser<'src, I, Op, E>, F: Fn(O, Op, &mut MapExtra<'src, '_, I, E>) -> O, { - type Op = Op; - type OpParser = A; - const IS_POSTFIX: bool = true; #[inline(always)] - fn op_parser(&self) -> &Self::OpParser { - &self.op_parser + fn is_postfix(&self) -> bool { + true } + #[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<'src, '_, I, E>) -> O { - (self.fold)(lhs, op, extra) + + #[inline] + fn do_parse_postfix<'parse, M: Mode>( + &self, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + lhs: M::Output, + ) -> Result, M::Output> + where + Self: Sized, + { + 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(()) => Err(lhs), + } } + + op_check_and_emit!(); } /// See [`Parser::pratt`]. @@ -410,16 +735,9 @@ macro_rules! impl_pratt_for_tuple { // 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()), - } - }, + if $X.is_prefix() { + match $X.do_parse_prefix::(inp, pre_expr.cursor(), |inp, min_power| recursive::recurse(|| self.pratt_go::(inp, min_power))) { + Ok(out) => break 'choice out, Err(()) => inp.rewind(pre_expr.clone()), } } @@ -436,15 +754,16 @@ macro_rules! impl_pratt_for_tuple { // 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)) - }); + if $X.is_postfix() && assoc.right_power() >= min_power { + match $X.do_parse_postfix::(inp, pre_expr.cursor(), lhs) { + Ok(out) => { + lhs = out; continue }, - Err(()) => inp.rewind(pre_op.clone()), + Err(out) => { + lhs = out; + inp.rewind(pre_op.clone()) + }, } } )* @@ -452,22 +771,16 @@ macro_rules! impl_pratt_for_tuple { // 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()), + if $X.is_infix() && assoc.left_power() >= min_power { + match $X.do_parse_infix::(inp, pre_expr.cursor(), lhs, |inp, min_power| recursive::recurse(|| self.pratt_go::(inp, min_power))) { + Ok(out) => { + lhs = out; + continue + }, + Err(out) => { + lhs = out; + inp.rewind(pre_op.clone()) }, - Err(()) => inp.rewind(pre_op.clone()), } } )* @@ -499,6 +812,100 @@ macro_rules! impl_pratt_for_tuple { 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_); +#[inline] +fn pratt_go_slice<'a, M: Mode, I, O, E, Atom, Op>( + atom: &Atom, + ops: &[Op], + inp: &mut InputRef<'a, '_, I, E>, + min_power: u32, +) -> PResult +where + I: Input<'a>, + E: ParserExtra<'a, I>, + Atom: Parser<'a, I, O, E>, + Op: Operator<'a, I, O, E>, +{ + let pre_expr = inp.save(); + let mut lhs = 'choice: { + // Prefix unary operators + for op in ops { + if op.is_prefix() { + match op.do_parse_prefix::(inp, pre_expr.cursor(), |inp, min_power| { + recursive::recurse(|| { + pratt_go_slice::(atom, ops, inp, min_power) + }) + }) { + Ok(out) => break 'choice out, + Err(()) => inp.rewind(pre_expr.clone()), + } + } + } + + atom.go::(inp)? + }; + + 'luup: loop { + let pre_op = inp.save(); + + // Postfix unary operators + for op in ops { + let assoc = op.associativity(); + if op.is_postfix() && assoc.right_power() >= min_power { + match op.do_parse_postfix::(inp, pre_expr.cursor(), lhs) { + Ok(out) => { + lhs = out; + continue; + } + Err(out) => { + lhs = out; + inp.rewind(pre_op.clone()) + } + } + } + } + + // Infix binary operators + for op in ops { + let assoc = op.associativity(); + if op.is_infix() && assoc.left_power() >= min_power { + match op.do_parse_infix::(inp, pre_expr.cursor(), lhs, |inp, min_power| { + recursive::recurse(|| { + pratt_go_slice::(atom, ops, inp, min_power) + }) + }) { + Ok(out) => { + lhs = out; + continue 'luup; + } + Err(out) => { + lhs = out; + inp.rewind(pre_op.clone()) + } + } + } + } + + inp.rewind(pre_op); + break; + } + + Ok(lhs) +} + +impl<'a, I, O, E, Atom, Op> ParserSealed<'a, I, O, E> for Pratt> +where + I: Input<'a>, + E: ParserExtra<'a, I>, + Atom: Parser<'a, I, O, E>, + Op: Operator<'a, I, O, E>, +{ + fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { + pratt_go_slice::(&self.atom, &self.ops, inp, 0) + } + + go_extra!(O); +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/private.rs b/src/private.rs index 764bc1cf..dc552a13 100644 --- a/src/private.rs +++ b/src/private.rs @@ -75,6 +75,41 @@ 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::Cursor<'src, 'parse, I>, + 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>, + lhs: Self::Output, + ) -> 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>, + lhs: Self::Output, + 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 +182,51 @@ 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::Cursor<'src, 'parse, I>, + 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")] + 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>, + lhs: Self::Output, + ) -> 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, lhs) + } + #[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>, + lhs: Self::Output, + 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, lhs, &f) + } } /// Check mode - all output is discarded, and only uses parsers to check validity @@ -207,6 +287,51 @@ 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::Cursor<'src, 'parse, I>, + 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")] + 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>, + lhs: Self::Output, + ) -> 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, lhs) + } + #[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>, + lhs: Self::Output, + 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, lhs, &f) + } } // TODO: Consider removing these sealed traits in favour of `Sealed`, with the given methods just being on `Parser` From 568846c030a729ce4ee12388d0a4887b0158620e Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 23 Oct 2024 20:19:32 +0100 Subject: [PATCH 10/12] Appease the clippy gods --- src/pratt.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/pratt.rs b/src/pratt.rs index 374b09a5..7e75b7bd 100644 --- a/src/pratt.rs +++ b/src/pratt.rs @@ -184,9 +184,9 @@ where #[doc(hidden)] fn do_parse_prefix<'parse, M: Mode>( &self, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + _inp: &mut InputRef<'src, 'parse, I, E>, + _pre_expr: &input::Cursor<'src, 'parse, I>, + _f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, ) -> PResult where Self: Sized, @@ -197,9 +197,9 @@ where #[doc(hidden)] fn do_parse_postfix<'parse, M: Mode>( &self, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - lhs: M::Output, + _inp: &mut InputRef<'src, 'parse, I, E>, + _pre_expr: &input::Cursor<'src, 'parse, I>, + _lhs: M::Output, ) -> Result, M::Output> where Self: Sized, @@ -210,10 +210,10 @@ where #[doc(hidden)] fn do_parse_infix<'parse, M: Mode>( &self, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - lhs: M::Output, - f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + _inp: &mut InputRef<'src, 'parse, I, E>, + _pre_expr: &input::Cursor<'src, 'parse, I>, + _lhs: M::Output, + _f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, ) -> Result, M::Output> where Self: Sized, @@ -224,9 +224,9 @@ where #[doc(hidden)] fn do_parse_prefix_check<'parse>( &self, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + _inp: &mut InputRef<'src, 'parse, I, E>, + _pre_expr: &input::Cursor<'src, 'parse, I>, + _f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, ) -> PResult; #[doc(hidden)] fn do_parse_prefix_emit<'parse>( @@ -270,13 +270,13 @@ where /// A boxed pratt parser operator. See [`Operator`]. pub struct Boxed<'src, 'a, I, O, E>(RefC>); -impl<'src, 'a, I, O, E> Clone for Boxed<'src, 'a, I, O, E> { +impl Clone for Boxed<'_, '_, I, O, E> { fn clone(&self) -> Self { Self(self.0.clone()) } } -impl<'src, 'a, I, O, E> Operator<'src, I, O, E> for Boxed<'src, 'a, I, O, E> +impl<'src, I, O, E> Operator<'src, I, O, E> for Boxed<'src, '_, I, O, E> where I: Input<'src>, E: ParserExtra<'src, I>, From c7b6d5178a658562cf9a173cc5500805f723095f Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 25 Oct 2024 22:47:53 +0100 Subject: [PATCH 11/12] Recursively implement pratt::Operator --- examples/mini_ml.rs | 13 +- src/lib.rs | 1 + src/pratt.rs | 634 +++++++++++++++++++++++++------------------- src/private.rs | 42 ++- 4 files changed, 398 insertions(+), 292 deletions(-) diff --git a/examples/mini_ml.rs b/examples/mini_ml.rs index bb1bdec6..b3441072 100644 --- a/examples/mini_ml.rs +++ b/examples/mini_ml.rs @@ -152,15 +152,17 @@ fn parser<'src>( select_ref! { Token::Parens(ts) = e => ts.as_slice().spanned(e.span()) }, ), )) - .pratt(( + .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| { ( @@ -170,8 +172,9 @@ fn parser<'src>( }, e.span(), ) - }), - )) + }) + .boxed(), + ]) .labelled("expression") .as_context() }) diff --git a/src/lib.rs b/src/lib.rs index 409691f5..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::{ diff --git a/src/pratt.rs b/src/pratt.rs index 7e75b7bd..4b94d9ab 100644 --- a/src/pratt.rs +++ b/src/pratt.rs @@ -89,61 +89,99 @@ use super::*; macro_rules! op_check_and_emit { () => { - #[doc(hidden)] + #[inline(always)] fn do_parse_prefix_check<'parse>( &self, inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, + 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) + self.do_parse_prefix::(inp, pre_expr, &f) } - #[doc(hidden)] + #[inline(always)] fn do_parse_prefix_emit<'parse>( &self, inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, + 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) + self.do_parse_prefix::(inp, pre_expr, &f) } - #[doc(hidden)] + #[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, lhs) + self.do_parse_postfix::(inp, pre_expr, pre_op, lhs, min_power) } - #[doc(hidden)] + #[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, lhs) + self.do_parse_postfix::(inp, pre_expr, pre_op, lhs, min_power) } - #[doc(hidden)] + #[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, lhs, &f) + self.do_parse_infix::(inp, pre_expr, pre_op, lhs, min_power, &f) } - #[doc(hidden)] + #[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, lhs, &f) + self.do_parse_infix::(inp, pre_expr, pre_op, lhs, min_power, &f) } }; } @@ -162,77 +200,70 @@ where Boxed(RefC::new(self)) } - #[inline(always)] - #[doc(hidden)] - fn is_infix(&self) -> bool { - false - } - #[inline(always)] #[doc(hidden)] - fn is_prefix(&self) -> bool { - false - } #[inline(always)] - #[doc(hidden)] - fn is_postfix(&self) -> bool { - false - } - - #[doc(hidden)] - fn associativity(&self) -> Associativity; - - #[doc(hidden)] fn do_parse_prefix<'parse, M: Mode>( &self, _inp: &mut InputRef<'src, 'parse, I, E>, - _pre_expr: &input::Cursor<'src, 'parse, I>, - _f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + _pre_expr: &input::Checkpoint< + 'src, + 'parse, + I, + >::Checkpoint, + >, + _f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, ) -> PResult where Self: Sized, { - unimplemented!() + Err(()) } #[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>, - _lhs: M::Output, + _pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: M::Output, + _min_power: u32, ) -> Result, M::Output> where Self: Sized, { - unimplemented!() + Err(lhs) } #[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>, - _lhs: M::Output, - _f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + _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, { - unimplemented!() + Err(lhs) } #[doc(hidden)] fn do_parse_prefix_check<'parse>( &self, - _inp: &mut InputRef<'src, 'parse, I, E>, - _pre_expr: &input::Cursor<'src, 'parse, I>, - _f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + 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::Cursor<'src, 'parse, I>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, ) -> PResult; #[doc(hidden)] @@ -240,21 +271,27 @@ where &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)] @@ -262,7 +299,9 @@ where &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; } @@ -281,30 +320,12 @@ where I: Input<'src>, E: ParserExtra<'src, I>, { - #[inline(always)] - fn is_infix(&self) -> bool { - self.0.is_infix() - } - #[inline(always)] - fn is_prefix(&self) -> bool { - self.0.is_prefix() - } - #[inline(always)] - fn is_postfix(&self) -> bool { - self.0.is_postfix() - } - - #[inline(always)] - fn associativity(&self) -> Associativity { - self.0.associativity() - } - #[inline(always)] fn do_parse_prefix<'parse, M: Mode>( &self, inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, ) -> PResult where Self: Sized, @@ -317,12 +338,14 @@ where &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, lhs) + M::invoke_pratt_op_postfix(self, inp, pre_expr, pre_op, lhs, min_power) } #[inline(always)] @@ -330,64 +353,84 @@ where &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, - f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + 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, lhs, f) + 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::Cursor<'src, 'parse, I>, + 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::Cursor<'src, 'parse, I>, + 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, lhs) + 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, lhs) + 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, lhs, &f) + 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, lhs, &f) + self.0 + .do_parse_infix_emit(inp, pre_expr, pre_op, lhs, min_power, &f) } } @@ -493,37 +536,41 @@ where A: Parser<'src, I, Op, E>, F: Fn(O, Op, O, &mut MapExtra<'src, '_, I, E>) -> O, { - #[inline(always)] - fn is_infix(&self) -> bool { - true - } - - #[inline(always)] - fn associativity(&self) -> Associativity { - self.associativity - } - #[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, - f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: u32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, ) -> Result, M::Output> where Self: Sized, { - 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(()) => Err(lhs), - }, - Err(()) => Err(lhs), + 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) } } @@ -585,34 +632,30 @@ where A: Parser<'src, I, Op, E>, F: Fn(Op, O, &mut MapExtra<'src, '_, I, E>) -> O, { - #[inline(always)] - fn is_prefix(&self) -> bool { - true - } - - #[inline(always)] - fn associativity(&self) -> Associativity { - Associativity::Left(self.binding_power) - } - #[inline] fn do_parse_prefix<'parse, M: Mode>( &self, inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + 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, self.associativity().left_power()) { + 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, inp)) + (self.fold)(op, rhs, &mut MapExtra::new(pre_expr.cursor(), inp)) })), - Err(()) => Err(()), + Err(()) => { + inp.rewind(pre_expr.clone()); + Err(()) + } }, - Err(()) => Err(()), + Err(()) => { + inp.rewind(pre_expr.clone()); + Err(()) + } } } @@ -674,31 +717,30 @@ where A: Parser<'src, I, Op, E>, F: Fn(O, Op, &mut MapExtra<'src, '_, I, E>) -> O, { - #[inline(always)] - fn is_postfix(&self) -> bool { - true - } - - #[inline(always)] - fn associativity(&self) -> Associativity { - Associativity::Left(self.binding_power) - } - #[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, { - 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(()) => Err(lhs), + 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) } } @@ -712,195 +754,239 @@ 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.do_parse_prefix::(inp, pre_expr.cursor(), |inp, min_power| recursive::recurse(|| self.pratt_go::(inp, min_power))) { - Ok(out) => break 'choice out, - 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.do_parse_postfix::(inp, pre_expr.cursor(), lhs) { - Ok(out) => { - lhs = out; - continue - }, - Err(out) => { - lhs = out; - inp.rewind(pre_op.clone()) - }, - } - } - )* - - // Infix binary operators - $( - let assoc = $X.associativity(); - if $X.is_infix() && assoc.left_power() >= min_power { - match $X.do_parse_infix::(inp, pre_expr.cursor(), lhs, |inp, min_power| recursive::recurse(|| self.pratt_go::(inp, min_power))) { - Ok(out) => { - lhs = out; - continue - }, - Err(out) => { - lhs = out; - 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_); -#[inline] -fn pratt_go_slice<'a, M: Mode, I, O, E, Atom, Op>( - atom: &Atom, - ops: &[Op], - inp: &mut InputRef<'a, '_, I, E>, - min_power: u32, -) -> PResult +#[allow(unused_variables, non_snake_case)] +impl<'src, I, O, E, Op> Operator<'src, I, O, E> for Vec where - I: Input<'a>, - E: ParserExtra<'a, I>, - Atom: Parser<'a, I, O, E>, - Op: Operator<'a, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + Op: Operator<'src, I, O, E>, { - let pre_expr = inp.save(); - let mut lhs = 'choice: { - // Prefix unary operators - for op in ops { - if op.is_prefix() { - match op.do_parse_prefix::(inp, pre_expr.cursor(), |inp, min_power| { - recursive::recurse(|| { - pratt_go_slice::(atom, ops, inp, min_power) - }) - }) { - Ok(out) => break 'choice out, - Err(()) => inp.rewind(pre_expr.clone()), - } + #[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(()) + } - atom.go::(inp)? - }; + #[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) + } - 'luup: loop { - let pre_op = inp.save(); - - // Postfix unary operators - for op in ops { - let assoc = op.associativity(); - if op.is_postfix() && assoc.right_power() >= min_power { - match op.do_parse_postfix::(inp, pre_expr.cursor(), lhs) { - Ok(out) => { - lhs = out; - continue; - } - Err(out) => { - lhs = out; - inp.rewind(pre_op.clone()) - } - } + #[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) + } - // Infix binary operators - for op in ops { - let assoc = op.associativity(); - if op.is_infix() && assoc.left_power() >= min_power { - match op.do_parse_infix::(inp, pre_expr.cursor(), lhs, |inp, min_power| { - recursive::recurse(|| { - pratt_go_slice::(atom, ops, inp, min_power) - }) - }) { - Ok(out) => { - lhs = out; - continue 'luup; - } - Err(out) => { - lhs = out; - inp.rewind(pre_op.clone()) - } + 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; } - inp.rewind(pre_op); - break; + Ok(lhs) } - - Ok(lhs) } -impl<'a, I, O, E, Atom, Op> ParserSealed<'a, I, O, E> for Pratt> +#[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>, - Op: Operator<'a, I, O, E>, + Ops: Operator<'a, I, O, E>, { fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { - pratt_go_slice::(&self.atom, &self.ops, inp, 0) + self.pratt_go::(inp, 0) } go_extra!(O); diff --git a/src/private.rs b/src/private.rs index dc552a13..a4be9268 100644 --- a/src/private.rs +++ b/src/private.rs @@ -80,8 +80,8 @@ pub trait Mode { fn invoke_pratt_op_prefix<'src, 'parse, Op, I, O, E>( op: &Op, inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + 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>, @@ -92,7 +92,9 @@ pub trait Mode { 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>, @@ -103,8 +105,10 @@ pub trait Mode { 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, - f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + 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>, @@ -188,8 +192,8 @@ impl Mode for Emit { fn invoke_pratt_op_prefix<'src, 'parse, Op, I, O, E>( op: &Op, inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + 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>, @@ -199,33 +203,39 @@ impl Mode for Emit { 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, lhs) + 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, - f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + 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, lhs, &f) + op.do_parse_infix_emit(inp, pre_expr, pre_op, lhs, min_power, &f) } } @@ -293,8 +303,8 @@ impl Mode for Check { fn invoke_pratt_op_prefix<'src, 'parse, Op, I, O, E>( op: &Op, inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + 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>, @@ -304,33 +314,39 @@ impl Mode for Check { 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, lhs) + 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, - f: impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + 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, lhs, &f) + op.do_parse_infix_check(inp, pre_expr, pre_op, lhs, min_power, &f) } } From 5a01a4483cea80156530c0e8b77080143907a6ae Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 25 Oct 2024 22:53:40 +0100 Subject: [PATCH 12/12] Added docs to mini_ml --- examples/mini_ml.rs | 15 ++++++++------- examples/sample.mini_ml | 4 ++++ 2 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 examples/sample.mini_ml diff --git a/examples/mini_ml.rs b/examples/mini_ml.rs index b3441072..96bbc7c9 100644 --- a/examples/mini_ml.rs +++ b/examples/mini_ml.rs @@ -1,6 +1,11 @@ +//! 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 core::fmt; +use std::{env, fmt, fs}; // Tokens and lexer @@ -438,12 +443,8 @@ fn parse_failure(err: &Rich, src: &str) -> ! { } fn main() { - let src = " - 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 - "; + 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) 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