Skip to content

Instantly share code, notes, and snippets.

@lynzrand
Created February 3, 2026 02:19
Show Gist options
  • Select an option

  • Save lynzrand/713ba99f3a6987b38ceb569758cb8bdf to your computer and use it in GitHub Desktop.

Select an option

Save lynzrand/713ba99f3a6987b38ceb569758cb8bdf to your computer and use it in GitHub Desktop.
Chumsky side-effect demo
[package]
name = "chumsky-demo"
version = "0.1.0"
edition = "2024"
[dependencies]
chumsky = { version = "0.12.0", features = ["pratt"] }
use chumsky::error::Simple;
use chumsky::extra::Full;
use chumsky::inspector::Inspector;
use chumsky::pratt::{infix, left};
use chumsky::prelude::*;
use chumsky::text::{digits, whitespace};
/// A demo state for the parser, similar to how one might use `cstree` in practice
struct DemoState {
n_tokens: usize,
}
impl<'src> Inspector<'src, &'src str> for DemoState {
type Checkpoint = usize;
fn on_token(&mut self, _token: &char) {
self.n_tokens += 1
}
fn on_save<'parse>(
&self,
_cursor: &chumsky::input::Cursor<'src, 'parse, &'src str>,
) -> Self::Checkpoint {
self.n_tokens
}
fn on_rewind<'parse>(
&mut self,
marker: &chumsky::input::Checkpoint<'src, 'parse, &'src str, Self::Checkpoint>,
) {
self.n_tokens = *marker.inspector()
}
}
impl DemoState {
fn ckpt(&self) -> usize {
self.n_tokens
}
fn create_node(&mut self, ckpt: usize) {
println!("Want to create node at {ckpt}..{}", self.n_tokens)
}
}
type Extra<'a> = Full<Simple<'a, char>, DemoState, ()>;
/// Create a checkpoint at the current position for later use
fn ckpt<'a>() -> impl Parser<'a, &'a str, usize, Extra<'a>> {
custom(|iref| {
let st: &mut DemoState = iref.state(); // suprisingly, rustc can't infer this state type
Ok(st.ckpt())
})
}
fn expr<'a>() -> impl Parser<'a, &'a str, (), Extra<'a>> {
let number = digits(10);
let prim_expr = ckpt().then_ignore(number).then_ignore(whitespace());
// The input to pratt parsing is the leftmost checkpoint of the leftmost
// expression, to be used later to create CST node from spans
let pratt = prim_expr.pratt((
// Infix `+`
infix(
left(10),
just('+').then_ignore(whitespace()),
|l, _c, _r, e| {
let st: &mut DemoState = e.state();
st.create_node(l);
l
},
),
// Infix `*`
infix(
left(20),
just('*').then_ignore(whitespace()),
|l, _c, _r, e| {
let st: &mut DemoState = e.state();
st.create_node(l);
l
},
),
));
pratt.ignored() // <------- replace me!
// pratt.map(drop)
}
fn main() {
let mut st = DemoState { n_tokens: 0 };
expr().parse_with_state("1 + 2 * 3 + 4", &mut st).unwrap()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment