Created
February 3, 2026 02:19
-
-
Save lynzrand/713ba99f3a6987b38ceb569758cb8bdf to your computer and use it in GitHub Desktop.
Chumsky side-effect demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [package] | |
| name = "chumsky-demo" | |
| version = "0.1.0" | |
| edition = "2024" | |
| [dependencies] | |
| chumsky = { version = "0.12.0", features = ["pratt"] } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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