Skip to content

Commit b1a9004

Browse files
authored
Merge branch 'zesterer:main' into fixes/recursive-leak
2 parents 32c97f9 + b8fc4eb commit b1a9004

File tree

16 files changed

+175
-82
lines changed

16 files changed

+175
-82
lines changed

Cargo.lock

Lines changed: 31 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ unicode-ident = "1.0.10"
8585
ariadne = "0.2"
8686
pom = "3.2"
8787
nom = "7.1"
88-
winnow = "0.5.19"
88+
winnow = "0.6.0"
8989
serde_json = { version = "1.0", features = ["preserve_order"] }
9090
ciborium = { version = "0.2" }
9191
criterion = "0.4.0"
@@ -137,3 +137,7 @@ required-features = ["std"]
137137
[[example]]
138138
name = "io"
139139
required-features = ["std"]
140+
141+
[[example]]
142+
name = "foo"
143+
required-features = ["std"]

README2.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ you may need. It also has `no_std` support, making it suitable for embedded envi
2929
- 🏷️ **Pattern labelling** for dynamic, user-friendly error messages
3030
- 🗃️ **Caching** allows parsers to be created once and reused many times
3131
- ↔️ **Pratt parsing** support for unary and binary operators
32+
- 🪛 **no_std** support, allowing chumsky to run in embedded environments
3233

3334
*Note: Error diagnostic rendering is performed by [Ariadne](https://github.com/zesterer/ariadne)*
3435

benches/json.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ mod winnow {
361361
combinator::{preceded, separated_pair, terminated},
362362
error::{InputError, ParserError},
363363
prelude::*,
364-
token::{any, none_of, one_of, tag, take_while},
364+
token::{any, none_of, one_of, take_while},
365365
};
366366

367367
use super::JsonZero;
@@ -441,9 +441,9 @@ mod winnow {
441441
preceded(
442442
space,
443443
dispatch!(peek(any);
444-
b'n' => tag("null").value(JsonZero::Null),
445-
b't' => tag("true").value(JsonZero::Bool(true)),
446-
b'f' => tag("false").value(JsonZero::Bool(false)),
444+
b'n' => "null".value(JsonZero::Null),
445+
b't' => "true".value(JsonZero::Bool(true)),
446+
b'f' => "false".value(JsonZero::Bool(false)),
447447
b'-' | b'0'..=b'9' => number.map(JsonZero::Num),
448448
b'"' => string.map(JsonZero::Str),
449449
b'[' => array.map(JsonZero::Array),
@@ -463,6 +463,7 @@ mod winnow {
463463
}
464464
}
465465

466+
#[allow(clippy::empty_docs)] // TODO: Remove, pest does things clippy doesn't like for some reason
466467
mod pest {
467468
use super::JsonZero;
468469

examples/foo.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ fn eval<'a>(
180180
}
181181

182182
fn main() {
183-
let src = std::fs::read_to_string(std::env::args().nth(1).unwrap()).unwrap();
183+
let usage = "Run `cargo run --example foo -- examples/sample.foo`";
184+
let src = std::fs::read_to_string(std::env::args().nth(1).expect(usage)).expect(usage);
184185

185186
match parser().parse(&src).into_result() {
186187
Ok(ast) => match eval(&ast, &mut Vec::new(), &mut Vec::new()) {

examples/indent.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use chumsky::prelude::*;
22

33
#[derive(Clone, Debug)]
4-
enum Stmt {
4+
pub enum Stmt {
55
Expr,
66
Loop(Vec<Stmt>),
77
}

examples/json.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use chumsky::prelude::*;
77
use std::{collections::HashMap, env, fs};
88

99
#[derive(Clone, Debug)]
10-
enum Json {
10+
pub enum Json {
1111
Invalid,
1212
Null,
1313
Bool(bool),

src/combinator.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ where
354354
I: Input<'a>,
355355
E: ParserExtra<'a, I>,
356356
A: Parser<'a, I, OA, E>,
357-
F: Fn(OA, &mut MapExtra<'a, '_, I, E>) -> O,
357+
F: Fn(OA, &mut MapExtra<'a, '_, '_, I, E>) -> O,
358358
{
359359
#[inline(always)]
360360
fn go<M: Mode>(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult<M, O> {
@@ -373,7 +373,7 @@ where
373373
I: Input<'a>,
374374
E: ParserExtra<'a, I>,
375375
A: IterParser<'a, I, OA, E>,
376-
F: Fn(OA, &mut MapExtra<'a, '_, I, E>) -> O,
376+
F: Fn(OA, &mut MapExtra<'a, '_, '_, I, E>) -> O,
377377
{
378378
type IterState<M: Mode> = A::IterState<M>
379379
where
@@ -546,7 +546,7 @@ where
546546
match (self.mapper)(out, span) {
547547
Ok(out) => Ok(M::bind(|| out)),
548548
Err(err) => {
549-
inp.add_alt_err(inp.offset().offset, err);
549+
inp.add_alt_err(before.offset, err);
550550
Err(())
551551
}
552552
}
@@ -579,7 +579,7 @@ where
579579
I: Input<'a>,
580580
E: ParserExtra<'a, I>,
581581
A: Parser<'a, I, OA, E>,
582-
F: Fn(OA, &mut MapExtra<'a, '_, I, E>) -> Result<O, E::Error>,
582+
F: Fn(OA, &mut MapExtra<'a, '_, '_, I, E>) -> Result<O, E::Error>,
583583
{
584584
#[inline(always)]
585585
fn go<M: Mode>(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult<M, O> {
@@ -1427,6 +1427,7 @@ where
14271427
A: Parser<'a, I, OA, E>,
14281428
{
14291429
#[inline(always)]
1430+
#[allow(clippy::nonminimal_bool)] // TODO: Remove this, lint is currently buggy
14301431
fn go<M: Mode>(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult<M, ()> {
14311432
if self.at_most == !0 && self.at_least == 0 {
14321433
loop {
@@ -2362,7 +2363,7 @@ where
23622363
A: IterParser<'a, I, OA, E>,
23632364
B: Parser<'a, I, O, E>,
23642365
E: ParserExtra<'a, I>,
2365-
F: Fn(OA, O, &mut MapExtra<'a, '_, I, E>) -> O,
2366+
F: Fn(OA, O, &mut MapExtra<'a, '_, '_, I, E>) -> O,
23662367
{
23672368
#[inline(always)]
23682369
fn go<M: Mode>(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult<M, O>
@@ -2497,7 +2498,7 @@ where
24972498
A: Parser<'a, I, O, E>,
24982499
B: IterParser<'a, I, OB, E>,
24992500
E: ParserExtra<'a, I>,
2500-
F: Fn(O, OB, &mut MapExtra<'a, '_, I, E>) -> O,
2501+
F: Fn(O, OB, &mut MapExtra<'a, '_, '_, I, E>) -> O,
25012502
{
25022503
#[inline(always)]
25032504
fn go<M: Mode>(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult<M, O>
@@ -2688,7 +2689,7 @@ where
26882689
I: Input<'a>,
26892690
E: ParserExtra<'a, I>,
26902691
A: Parser<'a, I, OA, E>,
2691-
F: Fn(OA, &mut MapExtra<'a, '_, I, E>, &mut Emitter<E::Error>) -> U,
2692+
F: Fn(OA, &mut MapExtra<'a, '_, '_, I, E>, &mut Emitter<E::Error>) -> U,
26922693
{
26932694
#[inline(always)]
26942695
fn go<M: Mode>(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult<M, U>

src/error.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//! like [`Cheap`], [`Simple`] or [`Rich`].
88
99
use super::*;
10+
#[cfg(not(feature = "std"))]
1011
use alloc::string::ToString;
1112

1213
/// A trait that describes parser error types.
@@ -426,6 +427,7 @@ impl<'a, T, L> RichReason<'a, T, L> {
426427
mut fmt_span: impl FnMut(&S, &mut fmt::Formatter<'_>) -> fmt::Result,
427428
mut fmt_label: impl FnMut(&L, &mut fmt::Formatter<'_>) -> fmt::Result,
428429
span: Option<&S>,
430+
#[cfg(feature = "label")] context: &[(L, S)],
429431
) -> fmt::Result {
430432
match self {
431433
RichReason::ExpectedFound { expected, found } => {
@@ -467,6 +469,13 @@ impl<'a, T, L> RichReason<'a, T, L> {
467469
}
468470
}
469471
}
472+
#[cfg(feature = "label")]
473+
for (l, s) in context {
474+
write!(f, " in ")?;
475+
fmt_label(l, f)?;
476+
write!(f, " at ")?;
477+
fmt_span(s, f)?;
478+
}
470479
Ok(())
471480
}
472481
}
@@ -526,7 +535,15 @@ where
526535
L: fmt::Display,
527536
{
528537
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
529-
self.inner_fmt(f, T::fmt, |_: &(), _| Ok(()), L::fmt, None)
538+
self.inner_fmt(
539+
f,
540+
T::fmt,
541+
|_: &(), _| Ok(()),
542+
L::fmt,
543+
None,
544+
#[cfg(feature = "label")]
545+
&[],
546+
)
530547
}
531548
}
532549

@@ -557,6 +574,8 @@ impl<'a, T, S, L> Rich<'a, T, S, L> {
557574
fmt_span,
558575
fmt_label,
559576
if with_spans { Some(&self.span) } else { None },
577+
#[cfg(feature = "label")]
578+
&self.context,
560579
)
561580
}
562581
}

src/input.rs

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,8 @@
88
pub use crate::stream::{BoxedExactSizeStream, BoxedStream, Stream};
99

1010
use super::*;
11-
#[cfg(feature = "memoization")]
12-
use hashbrown::HashMap;
1311
#[cfg(feature = "std")]
14-
use std::{
15-
cell::RefCell,
16-
io::{BufReader, Read, Seek},
17-
};
12+
use std::io::{BufReader, Read, Seek};
1813

1914
/// A trait for types that represents a stream of input tokens. Unlike [`Iterator`], this type
2015
/// supports backtracking and a few other features required by the crate.
@@ -1518,36 +1513,28 @@ impl<E> Emitter<E> {
15181513
}
15191514

15201515
/// See [`Parser::map_with`].
1521-
pub struct MapExtra<'a, 'b, I: Input<'a>, E: ParserExtra<'a, I>> {
1522-
before: I::Offset,
1523-
after: I::Offset,
1524-
inp: &'b I,
1525-
state: &'b mut E::State,
1526-
ctx: &'b E::Context,
1516+
/// Note: 'a is the lifetime of the Input, 'b is the lifetime of the embedded InputRef, 'parse if the lifetime of the parser and corresponding state.
1517+
pub struct MapExtra<'a, 'b, 'parse, I: Input<'a>, E: ParserExtra<'a, I>> {
1518+
before: Offset<'a, 'parse, I>,
1519+
inp: &'b mut InputRef<'a, 'parse, I, E>,
15271520
}
15281521

1529-
impl<'a, 'b, I: Input<'a>, E: ParserExtra<'a, I>> MapExtra<'a, 'b, I, E> {
1530-
#[inline(always)]
1531-
pub(crate) fn new<'parse>(
1522+
impl<'a, 'b, 'parse, I: Input<'a>, E: ParserExtra<'a, I>> MapExtra<'a, 'b, 'parse, I, E> {
1523+
/// Create new MapExtra
1524+
/// SAFETY: `before` Offset must be from a previous call to inp.offset().
1525+
pub(crate) fn new(
15321526
before: Offset<'a, 'parse, I>,
15331527
inp: &'b mut InputRef<'a, 'parse, I, E>,
15341528
) -> Self {
1535-
Self {
1536-
before: before.offset,
1537-
after: inp.offset,
1538-
ctx: inp.ctx,
1539-
state: inp.state,
1540-
inp: inp.input,
1541-
}
1529+
MapExtra { before, inp }
15421530
}
1543-
15441531
/// Get the span corresponding to the output.
1532+
// SAFETY: The offsets both came from the same input
1533+
// TODO: Should this make `MapExtra::new` unsafe? Probably, but it's an internal API and we simply wouldn't
1534+
// ever abuse it in this way, even accidentally.
15451535
#[inline(always)]
15461536
pub fn span(&self) -> I::Span {
1547-
// SAFETY: The offsets both came from the same input
1548-
// TODO: Should this make `MapExtra::new` unsafe? Probably, but it's an internal API and we simply wouldn't
1549-
// ever abuse it in this way, even accidentally.
1550-
unsafe { self.inp.span(self.before..self.after) }
1537+
self.inp.span(self.before..self.inp.offset())
15511538
}
15521539

15531540
/// Get the slice corresponding to the output.
@@ -1556,18 +1543,18 @@ impl<'a, 'b, I: Input<'a>, E: ParserExtra<'a, I>> MapExtra<'a, 'b, I, E> {
15561543
where
15571544
I: SliceInput<'a>,
15581545
{
1559-
self.inp.slice(self.before..self.after)
1546+
self.inp.slice(self.before..self.inp.offset())
15601547
}
15611548

15621549
/// Get the parser state.
15631550
#[inline(always)]
15641551
pub fn state(&mut self) -> &mut E::State {
1565-
self.state
1552+
self.inp.state()
15661553
}
15671554

15681555
/// Get the current parser context.
15691556
#[inline(always)]
15701557
pub fn ctx(&self) -> &E::Context {
1571-
self.ctx
1558+
self.inp.ctx()
15721559
}
15731560
}

0 commit comments

Comments
 (0)