Skip to content

Commit

Permalink
refactor(example): Un-unpeek parsers
Browse files Browse the repository at this point in the history
  • Loading branch information
epage committed Jul 5, 2023
1 parent 1a76001 commit a00dc99
Show file tree
Hide file tree
Showing 25 changed files with 664 additions and 710 deletions.
6 changes: 4 additions & 2 deletions examples/arithmetic/bench.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
mod parser;

use winnow::prelude::*;

use parser::expr;

#[allow(clippy::eq_op, clippy::erasing_op)]
fn arithmetic(c: &mut criterion::Criterion) {
let data = " 2*2 / ( 5 - 1) + 3 / 4 * (2 - 7 + 567 *12 /2) + 3*(1+2*( 45 /2));";

assert_eq!(
expr(data),
expr.parse_peek(data),
Ok((";", 2 * 2 / (5 - 1) + 3 * (1 + 2 * (45 / 2)),))
);
c.bench_function("arithmetic", |b| {
b.iter(|| expr(data).unwrap());
b.iter(|| expr.parse_peek(data).unwrap());
});
}

Expand Down
5 changes: 2 additions & 3 deletions examples/arithmetic/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use winnow::prelude::*;
use winnow::unpeek;

mod parser;
mod parser_ast;
Expand All @@ -11,15 +10,15 @@ fn main() -> Result<(), lexopt::Error> {

println!("{} =", input);
match args.implementation {
Impl::Eval => match unpeek(parser::expr).parse(input) {
Impl::Eval => match parser::expr.parse(input) {
Ok(result) => {
println!(" {}", result);
}
Err(err) => {
println!(" {}", err);
}
},
Impl::Ast => match unpeek(parser_ast::expr).parse(input) {
Impl::Ast => match parser_ast::expr.parse(input) {
Ok(result) => {
println!(" {:#?}", result);
}
Expand Down
55 changes: 27 additions & 28 deletions examples/arithmetic/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@ use winnow::{
combinator::delimited,
combinator::fold_repeat,
token::one_of,
unpeek, IResult,
};

// Parser definition

pub fn expr(i: &str) -> IResult<&str, i64> {
let (i, init) = term(i)?;
pub fn expr(i: &mut &str) -> PResult<i64> {
let init = term.parse_next(i)?;

fold_repeat(
0..,
(one_of(['+', '-']), unpeek(term)),
(one_of(['+', '-']), term),
move || init,
|acc, (op, val): (char, i64)| {
if op == '+' {
Expand All @@ -27,18 +26,18 @@ pub fn expr(i: &str) -> IResult<&str, i64> {
}
},
)
.parse_peek(i)
.parse_next(i)
}

// We read an initial factor and for each time we find
// a * or / operator followed by another factor, we do
// the math by folding everything
fn term(i: &str) -> IResult<&str, i64> {
let (i, init) = factor(i)?;
fn term(i: &mut &str) -> PResult<i64> {
let init = factor.parse_next(i)?;

fold_repeat(
0..,
(one_of(['*', '/']), unpeek(factor)),
(one_of(['*', '/']), factor),
move || init,
|acc, (op, val): (char, i64)| {
if op == '*' {
Expand All @@ -48,56 +47,56 @@ fn term(i: &str) -> IResult<&str, i64> {
}
},
)
.parse_peek(i)
.parse_next(i)
}

// We transform an integer string into a i64, ignoring surrounding whitespaces
// We look for a digit suite, and try to convert it.
// If either str::from_utf8 or FromStr::from_str fail,
// we fallback to the parens parser defined above
fn factor(i: &str) -> IResult<&str, i64> {
fn factor(i: &mut &str) -> PResult<i64> {
delimited(
spaces,
alt((
digits.try_map(FromStr::from_str),
delimited('(', unpeek(expr), ')'),
unpeek(parens),
delimited('(', expr, ')'),
parens,
)),
spaces,
)
.parse_peek(i)
.parse_next(i)
}

// We parse any expr surrounded by parens, ignoring all whitespaces around those
fn parens(i: &str) -> IResult<&str, i64> {
delimited('(', unpeek(expr), ')').parse_peek(i)
fn parens(i: &mut &str) -> PResult<i64> {
delimited('(', expr, ')').parse_next(i)
}

#[test]
fn factor_test() {
assert_eq!(factor("3"), Ok(("", 3)));
assert_eq!(factor(" 12"), Ok(("", 12)));
assert_eq!(factor("537 "), Ok(("", 537)));
assert_eq!(factor(" 24 "), Ok(("", 24)));
assert_eq!(factor.parse_peek("3"), Ok(("", 3)));
assert_eq!(factor.parse_peek(" 12"), Ok(("", 12)));
assert_eq!(factor.parse_peek("537 "), Ok(("", 537)));
assert_eq!(factor.parse_peek(" 24 "), Ok(("", 24)));
}

#[test]
fn term_test() {
assert_eq!(term(" 12 *2 / 3"), Ok(("", 8)));
assert_eq!(term(" 2* 3 *2 *2 / 3"), Ok(("", 8)));
assert_eq!(term(" 48 / 3/2"), Ok(("", 8)));
assert_eq!(term.parse_peek(" 12 *2 / 3"), Ok(("", 8)));
assert_eq!(term.parse_peek(" 2* 3 *2 *2 / 3"), Ok(("", 8)));
assert_eq!(term.parse_peek(" 48 / 3/2"), Ok(("", 8)));
}

#[test]
fn expr_test() {
assert_eq!(expr(" 1 + 2 "), Ok(("", 3)));
assert_eq!(expr(" 12 + 6 - 4+ 3"), Ok(("", 17)));
assert_eq!(expr(" 1 + 2*3 + 4"), Ok(("", 11)));
assert_eq!(expr.parse_peek(" 1 + 2 "), Ok(("", 3)));
assert_eq!(expr.parse_peek(" 12 + 6 - 4+ 3"), Ok(("", 17)));
assert_eq!(expr.parse_peek(" 1 + 2*3 + 4"), Ok(("", 11)));
}

#[test]
fn parens_test() {
assert_eq!(expr(" ( 2 )"), Ok(("", 2)));
assert_eq!(expr(" 2* ( 3 + 4 ) "), Ok(("", 14)));
assert_eq!(expr(" 2*2 / ( 5 - 1) + 3"), Ok(("", 4)));
assert_eq!(expr.parse_peek(" ( 2 )"), Ok(("", 2)));
assert_eq!(expr.parse_peek(" 2* ( 3 + 4 ) "), Ok(("", 14)));
assert_eq!(expr.parse_peek(" 2*2 / ( 5 - 1) + 3"), Ok(("", 4)));
}
84 changes: 45 additions & 39 deletions examples/arithmetic/parser_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use winnow::{
combinator::alt,
combinator::repeat,
combinator::{delimited, preceded},
unpeek, IResult,
};

#[derive(Debug)]
Expand Down Expand Up @@ -44,63 +43,63 @@ impl Display for Expr {
}
}

pub fn expr(i: &str) -> IResult<&str, Expr> {
let (i, initial) = term(i)?;
let (i, remainder) = repeat(
pub fn expr(i: &mut &str) -> PResult<Expr> {
let initial = term(i)?;
let remainder = repeat(
0..,
alt((
unpeek(|i| {
let (i, add) = preceded("+", unpeek(term)).parse_peek(i)?;
Ok((i, (Oper::Add, add)))
}),
unpeek(|i| {
let (i, sub) = preceded("-", unpeek(term)).parse_peek(i)?;
Ok((i, (Oper::Sub, sub)))
}),
|i: &mut &str| {
let add = preceded("+", term).parse_next(i)?;
Ok((Oper::Add, add))
},
|i: &mut &str| {
let sub = preceded("-", term).parse_next(i)?;
Ok((Oper::Sub, sub))
},
)),
)
.parse_peek(i)?;
.parse_next(i)?;

Ok((i, fold_exprs(initial, remainder)))
Ok(fold_exprs(initial, remainder))
}

fn term(i: &str) -> IResult<&str, Expr> {
let (i, initial) = factor(i)?;
let (i, remainder) = repeat(
fn term(i: &mut &str) -> PResult<Expr> {
let initial = factor(i)?;
let remainder = repeat(
0..,
alt((
unpeek(|i| {
let (i, mul) = preceded("*", unpeek(factor)).parse_peek(i)?;
Ok((i, (Oper::Mul, mul)))
}),
unpeek(|i| {
let (i, div) = preceded("/", unpeek(factor)).parse_peek(i)?;
Ok((i, (Oper::Div, div)))
}),
|i: &mut &str| {
let mul = preceded("*", factor).parse_next(i)?;
Ok((Oper::Mul, mul))
},
|i: &mut &str| {
let div = preceded("/", factor).parse_next(i)?;
Ok((Oper::Div, div))
},
)),
)
.parse_peek(i)?;
.parse_next(i)?;

Ok((i, fold_exprs(initial, remainder)))
Ok(fold_exprs(initial, remainder))
}

fn factor(i: &str) -> IResult<&str, Expr> {
fn factor(i: &mut &str) -> PResult<Expr> {
alt((
delimited(multispace, digit, multispace)
.try_map(FromStr::from_str)
.map(Expr::Value),
unpeek(parens),
parens,
))
.parse_peek(i)
.parse_next(i)
}

fn parens(i: &str) -> IResult<&str, Expr> {
fn parens(i: &mut &str) -> PResult<Expr> {
delimited(
multispace,
delimited("(", unpeek(expr).map(|e| Expr::Paren(Box::new(e))), ")"),
delimited("(", expr.map(|e| Expr::Paren(Box::new(e))), ")"),
multispace,
)
.parse_peek(i)
.parse_next(i)
}

fn fold_exprs(initial: Expr, remainder: Vec<(Oper, Expr)>) -> Expr {
Expand All @@ -118,42 +117,49 @@ fn fold_exprs(initial: Expr, remainder: Vec<(Oper, Expr)>) -> Expr {
#[test]
fn factor_test() {
assert_eq!(
factor(" 3 ").map(|(i, x)| (i, format!("{:?}", x))),
factor
.parse_peek(" 3 ")
.map(|(i, x)| (i, format!("{:?}", x))),
Ok(("", String::from("Value(3)")))
);
}

#[test]
fn term_test() {
assert_eq!(
term(" 3 * 5 ").map(|(i, x)| (i, format!("{:?}", x))),
term.parse_peek(" 3 * 5 ")
.map(|(i, x)| (i, format!("{:?}", x))),
Ok(("", String::from("Mul(Value(3), Value(5))")))
);
}

#[test]
fn expr_test() {
assert_eq!(
expr(" 1 + 2 * 3 ").map(|(i, x)| (i, format!("{:?}", x))),
expr.parse_peek(" 1 + 2 * 3 ")
.map(|(i, x)| (i, format!("{:?}", x))),
Ok(("", String::from("Add(Value(1), Mul(Value(2), Value(3)))")))
);
assert_eq!(
expr(" 1 + 2 * 3 / 4 - 5 ").map(|(i, x)| (i, format!("{:?}", x))),
expr.parse_peek(" 1 + 2 * 3 / 4 - 5 ")
.map(|(i, x)| (i, format!("{:?}", x))),
Ok((
"",
String::from("Sub(Add(Value(1), Div(Mul(Value(2), Value(3)), Value(4))), Value(5))")
))
);
assert_eq!(
expr(" 72 / 2 / 3 ").map(|(i, x)| (i, format!("{:?}", x))),
expr.parse_peek(" 72 / 2 / 3 ")
.map(|(i, x)| (i, format!("{:?}", x))),
Ok(("", String::from("Div(Div(Value(72), Value(2)), Value(3))")))
);
}

#[test]
fn parens_test() {
assert_eq!(
expr(" ( 1 + 2 ) * 3 ").map(|(i, x)| (i, format!("{:?}", x))),
expr.parse_peek(" ( 1 + 2 ) * 3 ")
.map(|(i, x)| (i, format!("{:?}", x))),
Ok((
"",
String::from("Mul(Paren(Add(Value(1), Value(2))), Value(3))")
Expand Down
5 changes: 2 additions & 3 deletions examples/css/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use winnow::prelude::*;
use winnow::unpeek;

mod parser;

Expand All @@ -11,7 +10,7 @@ fn main() -> Result<(), lexopt::Error> {
let input = args.input.as_deref().unwrap_or("#AAAAAA");

println!("{} =", input);
match unpeek(hex_color).parse(input) {
match hex_color.parse(input) {
Ok(result) => {
println!(" {:?}", result);
}
Expand Down Expand Up @@ -50,7 +49,7 @@ impl Args {
#[test]
fn parse_color() {
assert_eq!(
hex_color("#2F14DF"),
hex_color.parse_peek("#2F14DF"),
Ok((
"",
parser::Color {
Expand Down
24 changes: 8 additions & 16 deletions examples/css/parser.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use winnow::prelude::*;
use winnow::token::take_while;
use winnow::unpeek;

#[derive(Debug, Eq, PartialEq)]
pub struct Color {
Expand All @@ -11,29 +10,22 @@ pub struct Color {

impl std::str::FromStr for Color {
// The error must be owned
type Err = winnow::error::Error<String>;
type Err = winnow::error::ErrorKind;

fn from_str(s: &str) -> Result<Self, Self::Err> {
unpeek(hex_color)
.parse(s)
.map_err(winnow::error::Error::into_owned)
hex_color.parse(s)
}
}

pub fn hex_color(input: &str) -> IResult<&str, Color> {
let (input, _) = "#".parse_peek(input)?;
let (input, (red, green, blue)) = (
unpeek(hex_primary),
unpeek(hex_primary),
unpeek(hex_primary),
)
.parse_peek(input)?;
pub fn hex_color(input: &mut &str) -> PResult<Color> {
let _ = "#".parse_next(input)?;
let (red, green, blue) = (hex_primary, hex_primary, hex_primary).parse_next(input)?;

Ok((input, Color { red, green, blue }))
Ok(Color { red, green, blue })
}

fn hex_primary(input: &str) -> IResult<&str, u8> {
fn hex_primary(input: &mut &str) -> PResult<u8> {
take_while(2, |c: char| c.is_ascii_hexdigit())
.try_map(|input| u8::from_str_radix(input, 16))
.parse_peek(input)
.parse_next(input)
}
Loading

0 comments on commit a00dc99

Please sign in to comment.