Skip to content
This repository was archived by the owner on Apr 2, 2026. It is now read-only.

Commit 4df5ec8

Browse files
committed
Added WithSlice
1 parent 56762fe commit 4df5ec8

2 files changed

Lines changed: 132 additions & 8 deletions

File tree

examples/logos.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use ariadne::{Color, Label, Report, ReportKind, Source};
66
use chumsky::{
7-
input::{Stream, ValueInput},
7+
input::{SliceInput, Stream, ValueInput},
88
prelude::*,
99
};
1010
use logos::Logos;
@@ -31,33 +31,38 @@ enum Token<'a> {
3131
#[token(")")]
3232
RParen,
3333

34+
#[regex("[A-Za-z_]+")]
35+
Ident,
36+
3437
#[regex(r"[ \t\f\n]+", logos::skip)]
3538
Whitespace,
3639
}
3740

3841
impl<'a> fmt::Display for Token<'a> {
3942
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4043
match self {
41-
Self::Float(s) => write!(f, "{}", s),
44+
Self::Float(s) => write!(f, "{s}"),
4245
Self::Add => write!(f, "+"),
4346
Self::Sub => write!(f, "-"),
4447
Self::Mul => write!(f, "*"),
4548
Self::Div => write!(f, "/"),
4649
Self::LParen => write!(f, "("),
4750
Self::RParen => write!(f, ")"),
4851
Self::Whitespace => write!(f, "<whitespace>"),
52+
Self::Ident => write!(f, "<ident>"),
4953
Self::Error => write!(f, "<error>"),
5054
}
5155
}
5256
}
5357

5458
#[derive(Debug)]
55-
enum SExpr {
59+
enum SExpr<'a> {
5660
Float(f64),
5761
Add,
5862
Sub,
5963
Mul,
6064
Div,
65+
Ident(&'a str),
6166
List(Vec<Self>),
6267
}
6368

@@ -71,9 +76,9 @@ enum SExpr {
7176
// - Has an input type of type `I`, the one we declared as a type parameter
7277
// - Produces an `SExpr` as its output
7378
// - Uses `Rich`, a built-in error type provided by chumsky, for error generation
74-
fn parser<'a, I>() -> impl Parser<'a, I, SExpr, extra::Err<Rich<'a, Token<'a>>>>
79+
fn parser<'a, I>() -> impl Parser<'a, I, SExpr<'a>, extra::Err<Rich<'a, Token<'a>>>>
7580
where
76-
I: ValueInput<'a, Token = Token<'a>, Span = SimpleSpan>,
81+
I: ValueInput<'a, Token = Token<'a>, Span = SimpleSpan> + SliceInput<'a, Slice = &'a str>,
7782
{
7883
recursive(|sexpr| {
7984
let atom = select! {
@@ -84,17 +89,19 @@ where
8489
Token::Div => SExpr::Div,
8590
};
8691

92+
let ident = just(Token::Ident).slice().map(SExpr::Ident);
93+
8794
let list = sexpr
8895
.repeated()
8996
.collect()
9097
.map(SExpr::List)
9198
.delimited_by(just(Token::LParen), just(Token::RParen));
9299

93-
atom.or(list)
100+
atom.or(ident).or(list)
94101
})
95102
}
96103

97-
impl SExpr {
104+
impl<'a> SExpr<'a> {
98105
// Recursively evaluate an s-expression
99106
fn eval(&self) -> Result<f64, &'static str> {
100107
match self {
@@ -103,6 +110,7 @@ impl SExpr {
103110
Self::Sub => Err("Cannot evaluate operator '-'"),
104111
Self::Mul => Err("Cannot evaluate operator '*'"),
105112
Self::Div => Err("Cannot evaluate operator '/'"),
113+
Self::Ident(_) => Err("Identifiers not supported"),
106114
Self::List(list) => match &list[..] {
107115
[Self::Add, tail @ ..] => tail.iter().map(SExpr::eval).sum(),
108116
[Self::Mul, tail @ ..] => tail.iter().map(SExpr::eval).product(),
@@ -142,7 +150,8 @@ fn main() {
142150
let token_stream = Stream::from_iter(token_iter)
143151
// Tell chumsky to split the (Token, SimpleSpan) stream into its parts so that it can handle the spans for us
144152
// This involves giving chumsky an 'end of input' span: we just use a zero-width span at the end of the string
145-
.spanned((SRC.len()..SRC.len()).into());
153+
.spanned((SRC.len()..SRC.len()).into())
154+
.with_slice(SRC);
146155

147156
// Parse the token stream with our chumsky parser
148157
match parser().parse(token_stream).into_result() {

src/input.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,22 @@ pub trait Input<'a>: Sealed + 'a {
135135
phantom: PhantomData,
136136
}
137137
}
138+
139+
/// Make this input implement [`SliceInput`] by using the given slice when performing slicing operations.
140+
///
141+
/// This is useful if you want to have an input that produces 'high-level' tokens be able to refer back to a slice
142+
/// of the raw input that it originated from.
143+
///
144+
/// # Examples
145+
///
146+
/// See the `logos` example in the main repository.
147+
fn with_slice<S>(self, slice: S) -> WithSlice<S, Self>
148+
where
149+
Self: Sized,
150+
S: SliceInput<'a>,
151+
{
152+
WithSlice { input: self, slice }
153+
}
138154
}
139155

140156
/// Implement by inputs that have a known size (including spans)
@@ -892,6 +908,105 @@ impl<'a, R: Read + Seek + 'a> ValueInput<'a> for IoInput<R> {
892908
}
893909
}
894910

911+
/// An input wrapper that provides slices via the given closure. See [`Input::with_slice`].
912+
#[derive(Copy, Clone)]
913+
pub struct WithSlice<S, I> {
914+
input: I,
915+
slice: S,
916+
}
917+
918+
impl<S, I> Sealed for WithSlice<S, I> {}
919+
impl<'a, S: 'a, I: Input<'a>> Input<'a> for WithSlice<S, I> {
920+
type Offset = I::Offset;
921+
type Token = I::Token;
922+
type Span = I::Span;
923+
924+
#[inline(always)]
925+
fn start(&self) -> Self::Offset {
926+
self.input.start()
927+
}
928+
929+
type TokenMaybe = I::TokenMaybe;
930+
931+
#[inline(always)]
932+
unsafe fn next_maybe(&self, offset: Self::Offset) -> (Self::Offset, Option<Self::TokenMaybe>) {
933+
self.input.next_maybe(offset)
934+
}
935+
936+
#[inline(always)]
937+
unsafe fn span(&self, range: Range<Self::Offset>) -> Self::Span {
938+
self.input.span(range)
939+
}
940+
941+
#[inline(always)]
942+
fn prev(offs: Self::Offset) -> Self::Offset {
943+
I::prev(offs)
944+
}
945+
}
946+
947+
impl<'a, S: 'a, I: Input<'a>> ExactSizeInput<'a> for WithSlice<S, I>
948+
where
949+
S: ExactSizeInput<'a, Span = I::Span, Offset = <I::Span as Span>::Offset>,
950+
{
951+
#[inline(always)]
952+
unsafe fn span_from(&self, from: RangeFrom<Self::Offset>) -> Self::Span {
953+
// SAFETY: offset was generated by previous call to `Input::next`
954+
// TODO: Is this sensible?
955+
let from = unsafe { self.input.span(from.start..from.start) };
956+
self.slice.span_from(from.start()..)
957+
}
958+
}
959+
960+
impl<'a, S: 'a, I: ValueInput<'a>> ValueInput<'a> for WithSlice<S, I> {
961+
#[inline(always)]
962+
unsafe fn next(&self, offset: Self::Offset) -> (Self::Offset, Option<Self::Token>) {
963+
self.input.next(offset)
964+
}
965+
}
966+
967+
impl<'a, S: 'a, I: BorrowInput<'a>> BorrowInput<'a> for WithSlice<S, I> {
968+
#[inline(always)]
969+
unsafe fn next_ref(&self, offset: Self::Offset) -> (Self::Offset, Option<&'a Self::Token>) {
970+
self.input.next_ref(offset)
971+
}
972+
}
973+
974+
impl<'a, S: 'a, I: Input<'a>> SliceInput<'a> for WithSlice<S, I>
975+
where
976+
S: SliceInput<'a, Span = I::Span, Offset = <I::Span as Span>::Offset>,
977+
{
978+
type Slice = S::Slice;
979+
980+
#[inline(always)]
981+
fn full_slice(&self) -> Self::Slice {
982+
self.slice.full_slice()
983+
}
984+
985+
#[inline(always)]
986+
fn slice(&self, range: Range<Self::Offset>) -> Self::Slice {
987+
// SAFETY: offset was generated by previous call to `Input::next`
988+
let span = unsafe { self.input.span(range) };
989+
self.slice.slice(span.start()..span.end())
990+
}
991+
992+
#[inline(always)]
993+
fn slice_from(&self, from: RangeFrom<Self::Offset>) -> Self::Slice {
994+
// SAFETY: offset was generated by previous call to `Input::next`
995+
// let span = unsafe { self.input.span_from(from) };
996+
// TODO: Is this sensible?
997+
let span = unsafe { self.input.span(from.start..from.start) };
998+
self.slice.slice_from(span.start()..)
999+
}
1000+
}
1001+
1002+
impl<'a, S: 'a, C, I> StrInput<'a, C> for WithSlice<S, I>
1003+
where
1004+
I: StrInput<'a, C>,
1005+
S: SliceInput<'a, Span = I::Span, Offset = <I::Span as Span>::Offset, Slice = &'a C::Str>,
1006+
C: Char<Str = S>,
1007+
{
1008+
}
1009+
8951010
/// Represents a location in an input that can be rewound to.
8961011
///
8971012
/// Markers can be created with [`InputRef::save`] and rewound to with [`InputRef::rewind`].

0 commit comments

Comments
 (0)