Skip to content

Instantly share code, notes, and snippets.

@jess-sol
Created September 17, 2023 01:03
Show Gist options
  • Select an option

  • Save jess-sol/f4eb75eea4eebac11cadce7c38413834 to your computer and use it in GitHub Desktop.

Select an option

Save jess-sol/f4eb75eea4eebac11cadce7c38413834 to your computer and use it in GitHub Desktop.
Contextual parsing of bare strings in Chumsky
use std::collections::BTreeMap;
use chumsky::{error::Error, prelude::*};
fn main() {
assert_eq!(parser().parse("hello!").into_result(), Ok(Value::String("hello!".to_string())));
assert_eq!(
parser().parse("{a: b, c: {d: e}}").into_result(),
Ok(Value::Map(BTreeMap::from([
(Value::String("a".to_string()), Value::String("b".to_string())),
(
Value::String("c".to_string()),
Value::Map(BTreeMap::from([(
Value::String("d".to_string()),
Value::String("e".to_string())
)]))
),
])))
);
}
#[derive(Clone, Debug, Default)]
struct ParsingCtx {
_indent: Option<usize>,
in_key: bool,
in_flow: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Value {
String(String),
Number(i64),
Map(BTreeMap<Value, Value>),
}
fn parser<'a>() -> impl Parser<'a, &'a str, Value> {
let end_of_node = custom(|inp| {
let ctx: &ParsingCtx = inp.ctx();
match inp.peek() {
None => Ok(()),
Some(',' | '}') if ctx.in_flow => Ok(()),
Some(':') if ctx.in_key => Ok(()),
found => Err(Error::<&str>::expected_found(
[],
found.map(Into::into),
inp.span_since(inp.offset()),
)),
}
});
recursive(|chain| {
choice((
// Parse map
just('{')
.padded()
.ignore_then(
map_ctx::<_, _, _, extra::Context<ParsingCtx>, _, _>(
|ctx| ParsingCtx { in_key: true, ..*ctx },
chain.clone(),
)
.then_ignore(just(':').padded())
.then(map_ctx::<_, _, _, extra::Context<ParsingCtx>, _, _>(
|ctx| ParsingCtx { in_flow: true, ..*ctx },
chain,
))
.separated_by(just(',').padded())
.allow_trailing()
.collect::<BTreeMap<Value, Value>>(),
)
.then_ignore(just('}').padded())
.map(Value::Map),
// Parse int
text::int(10).repeated().at_least(1).slice().then_ignore(end_of_node).try_map(
|x: &str, _| x.parse().map(Value::Number).map_err(|_| EmptyErr::default()),
),
// Parse string
any()
.and_is(end_of_node.not())
.repeated()
.at_least(1)
.collect::<String>()
.then_ignore(end_of_node)
.map(Value::String),
))
})
.then_ignore(end())
.with_ctx(ParsingCtx::default())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment