Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(parse)!: Switch from pure parsers to stream mutation #268

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3ab6ce3
feat(stream)!: Allow mutable iteration
epage Jun 26, 2023
ef0c93d
feat(error): Split out PResult from IResult
epage Jun 27, 2023
51e05a1
fix(parse)!: Clarify parse_next as parse_peek
epage Jun 27, 2023
fa952e6
feat(parse): Allow mutable parsing
epage Jun 27, 2023
9a44119
refactor(parser): Switch 'parse' to use 'parse_next'
epage Jun 28, 2023
2c078c4
fix(parser)!: Switch inherent method from parse_next to parse_peek
epage Jun 28, 2023
cfad286
refactor(parser): Switch built-in impls to use parse_next
epage Jun 28, 2023
a88ada7
refactor(parser): Switch inherent parsers to parse_next
epage Jun 28, 2023
9693203
refactor(parser): Switch inherent parser impls to parse_next
epage Jun 28, 2023
65afaf8
refactor(ascii): Remove superfluous closures
epage Jun 28, 2023
9d0daa0
refactor(test): Rely on trait, rather than direct calls
epage Jun 28, 2023
7546818
fix(parser)!: Make `FnMut`s take `&mut I`
epage Jun 28, 2023
bad7f64
refactor(comb): Un-unpeek sequence parsers
epage Jun 29, 2023
b0d4e89
refactor(comb): Un-unpeek core parsers
epage Jun 29, 2023
b7825c6
refactor(comb): Un-unpeek branch parsers
epage Jun 30, 2023
ab92bb7
refactor(comb): Un-unpeek multi parsers
epage Jun 30, 2023
7fce700
refactor(macro): Un-unpeek dispatch parser
epage Jun 29, 2023
9d41210
refactor(trace): Un-unpeek trace parser
epage Jun 29, 2023
f113ca8
refactor(token): Un-unpeek parsers
epage Jun 30, 2023
b445a34
refactor(ascii): Un-unpeek parsers
epage Jun 30, 2023
26932c3
refactor(bin): Un-peek binary parsers
epage Jul 3, 2023
49750f3
refactor(bin): Un-peek bit parsers
epage Jul 3, 2023
7a5cdfe
refactor(fuzz): Un-peek parsers
epage Jul 5, 2023
e8872d6
refactor(example): Un-unpeek parsers
epage Jun 30, 2023
166262c
docs: Reduce reliance on peeking
epage Jul 5, 2023
4e44eae
docs: Update examples to actual parsers
epage Jul 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions benches/contains_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ fn contains_token(c: &mut criterion::Criterion) {
group.throughput(criterion::Throughput::Bytes(len as u64));

group.bench_with_input(criterion::BenchmarkId::new("slice", name), &len, |b, _| {
b.iter(|| black_box(parser_slice.parse_next(black_box(sample)).unwrap()));
b.iter(|| black_box(parser_slice.parse_peek(black_box(sample)).unwrap()));
});
group.bench_with_input(criterion::BenchmarkId::new("array", name), &len, |b, _| {
b.iter(|| black_box(parser_array.parse_next(black_box(sample)).unwrap()));
b.iter(|| black_box(parser_array.parse_peek(black_box(sample)).unwrap()));
});
group.bench_with_input(criterion::BenchmarkId::new("tuple", name), &len, |b, _| {
b.iter(|| black_box(parser_tuple.parse_next(black_box(sample)).unwrap()));
b.iter(|| black_box(parser_tuple.parse_peek(black_box(sample)).unwrap()));
});
group.bench_with_input(
criterion::BenchmarkId::new("closure-or", name),
&len,
|b, _| {
b.iter(|| black_box(parser_closure_or.parse_next(black_box(sample)).unwrap()));
b.iter(|| black_box(parser_closure_or.parse_peek(black_box(sample)).unwrap()));
},
);
group.bench_with_input(
Expand All @@ -40,7 +40,7 @@ fn contains_token(c: &mut criterion::Criterion) {
b.iter(|| {
black_box(
parser_closure_matches
.parse_next(black_box(sample))
.parse_peek(black_box(sample))
.unwrap(),
)
});
Expand All @@ -50,22 +50,22 @@ fn contains_token(c: &mut criterion::Criterion) {
group.finish();
}

fn parser_slice(input: &str) -> IResult<&str, usize> {
fn parser_slice(input: &mut &str) -> PResult<usize> {
let contains = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'][..];
repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input)
}

fn parser_array(input: &str) -> IResult<&str, usize> {
fn parser_array(input: &mut &str) -> PResult<usize> {
let contains = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input)
}

fn parser_tuple(input: &str) -> IResult<&str, usize> {
fn parser_tuple(input: &mut &str) -> PResult<usize> {
let contains = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input)
}

fn parser_closure_or(input: &str) -> IResult<&str, usize> {
fn parser_closure_or(input: &mut &str) -> PResult<usize> {
let contains = |c: char| {
c == '0'
|| c == '1'
Expand All @@ -81,7 +81,7 @@ fn parser_closure_or(input: &str) -> IResult<&str, usize> {
repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input)
}

fn parser_closure_matches(input: &str) -> IResult<&str, usize> {
fn parser_closure_matches(input: &mut &str) -> PResult<usize> {
let contains = |c: char| matches!(c, '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9');
repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input)
}
Expand Down
31 changes: 15 additions & 16 deletions benches/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,56 +13,55 @@ use winnow::stream::ParseSlice;

type Stream<'i> = &'i [u8];

fn parser(i: Stream<'_>) -> IResult<Stream<'_>, u64> {
be_u64(i)
fn parser(i: &mut Stream<'_>) -> PResult<u64> {
be_u64.parse_next(i)
}

fn number(c: &mut Criterion) {
let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];

parser(&data[..]).expect("should parse correctly");
parser
.parse_peek(&data[..])
.expect("should parse correctly");
c.bench_function("number", move |b| {
b.iter(|| parser(&data[..]).unwrap());
b.iter(|| parser.parse_peek(&data[..]).unwrap());
});
}

fn float_bytes(c: &mut Criterion) {
println!(
"float_bytes result: {:?}",
float::<_, f64, Error<_>>(&b"-1.234E-12"[..])
float::<_, f64, Error<_>>.parse_peek(&b"-1.234E-12"[..])
);
c.bench_function("float bytes", |b| {
b.iter(|| float::<_, f64, Error<_>>(&b"-1.234E-12"[..]));
b.iter(|| float::<_, f64, Error<_>>.parse_peek(&b"-1.234E-12"[..]));
});
}

fn float_str(c: &mut Criterion) {
println!(
"float_str result: {:?}",
float::<_, f64, Error<_>>("-1.234E-12")
float::<_, f64, Error<_>>.parse_peek("-1.234E-12")
);
c.bench_function("float str", |b| {
b.iter(|| float::<_, f64, Error<_>>("-1.234E-12"));
b.iter(|| float::<_, f64, Error<_>>.parse_peek("-1.234E-12"));
});
}

fn std_float(input: &[u8]) -> IResult<&[u8], f64, Error<&[u8]>> {
fn std_float(input: &mut &[u8]) -> PResult<f64> {
match input.parse_slice() {
Some(n) => Ok((&[], n)),
None => Err(ErrMode::Backtrack(Error {
input,
kind: ErrorKind::Slice,
})),
Some(n) => Ok(n),
None => Err(ErrMode::Backtrack(ErrorKind::Slice)),
}
}

fn std_float_bytes(c: &mut Criterion) {
println!(
"std_float_bytes result: {:?}",
std_float(&b"-1.234E-12"[..])
std_float.parse_peek(&b"-1.234E-12"[..])
);
c.bench_function("std_float bytes", |b| {
b.iter(|| std_float(&b"-1.234E-12"[..]));
b.iter(|| std_float.parse_peek(&b"-1.234E-12"[..]));
});
}

Expand Down
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
39 changes: 19 additions & 20 deletions examples/arithmetic/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ use winnow::{
combinator::delimited,
combinator::fold_repeat,
token::one_of,
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..,
Expand All @@ -33,8 +32,8 @@ pub fn expr(i: &str) -> IResult<&str, i64> {
// 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..,
Expand All @@ -55,7 +54,7 @@ fn term(i: &str) -> IResult<&str, i64> {
// 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((
Expand All @@ -69,35 +68,35 @@ fn factor(i: &str) -> IResult<&str, i64> {
}

// We parse any expr surrounded by parens, ignoring all whitespaces around those
fn parens(i: &str) -> IResult<&str, i64> {
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)));
}
64 changes: 35 additions & 29 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},
IResult,
};

#[derive(Debug)]
Expand Down Expand Up @@ -44,47 +43,47 @@ 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((
|i| {
let (i, add) = preceded("+", term).parse_next(i)?;
Ok((i, (Oper::Add, add)))
|i: &mut &str| {
let add = preceded("+", term).parse_next(i)?;
Ok((Oper::Add, add))
},
|i| {
let (i, sub) = preceded("-", term).parse_next(i)?;
Ok((i, (Oper::Sub, sub)))
|i: &mut &str| {
let sub = preceded("-", term).parse_next(i)?;
Ok((Oper::Sub, sub))
},
)),
)
.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((
|i| {
let (i, mul) = preceded("*", factor).parse_next(i)?;
Ok((i, (Oper::Mul, mul)))
|i: &mut &str| {
let mul = preceded("*", factor).parse_next(i)?;
Ok((Oper::Mul, mul))
},
|i| {
let (i, div) = preceded("/", factor).parse_next(i)?;
Ok((i, (Oper::Div, div)))
|i: &mut &str| {
let div = preceded("/", factor).parse_next(i)?;
Ok((Oper::Div, div))
},
)),
)
.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)
Expand All @@ -94,7 +93,7 @@ fn factor(i: &str) -> IResult<&str, Expr> {
.parse_next(i)
}

fn parens(i: &str) -> IResult<&str, Expr> {
fn parens(i: &mut &str) -> PResult<Expr> {
delimited(
multispace,
delimited("(", expr.map(|e| Expr::Paren(Box::new(e))), ")"),
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
2 changes: 1 addition & 1 deletion examples/css/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl Args {
#[test]
fn parse_color() {
assert_eq!(
hex_color("#2F14DF"),
hex_color.parse_peek("#2F14DF"),
Ok((
"",
parser::Color {
Expand Down
Loading