Skip to content

Commit 1aa978f

Browse files
committed
Added chumsky example
1 parent 5f8b711 commit 1aa978f

File tree

4 files changed

+211
-0
lines changed

4 files changed

+211
-0
lines changed

test_cases/Cargo.lock

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

test_cases/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ edition = "2021"
88
[dependencies]
99
trybuild = "1"
1010
diesel = {version = "2.0.0-rc.0", default-features = false, features = ["sqlite", "postgres", "mysql"]}
11+
chumsky = "0.8.0"

test_cases/tests/chumsky/json.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use chumsky::prelude::*;
2+
use std::collections::HashMap;
3+
4+
#[derive(Clone, Debug)]
5+
enum Json {
6+
Invalid,
7+
Null,
8+
Bool(bool),
9+
Str(String),
10+
Num(f64),
11+
Array(Vec<Json>),
12+
Object(HashMap<String, Json>),
13+
}
14+
15+
fn parser() -> impl Parser<char, Json, Error = Simple<char>> {
16+
recursive(|value| {
17+
let frac = just('.').chain(text::digits(10));
18+
19+
let exp = just('e')
20+
.or(just('E'))
21+
.chain(just('+').or(just('-')).or_not())
22+
.chain(text::digits(10));
23+
24+
let number = just('-')
25+
.or_not()
26+
.chain(text::int(10))
27+
.chain(frac.or_not().flatten())
28+
.chain::<char, _, _>(exp.or_not().flatten())
29+
.collect::<String>()
30+
.from_str()
31+
.unwrapped()
32+
.labelled("number");
33+
34+
let escape = just('\\').ignore_then(
35+
just('\\')
36+
.or(just('/'))
37+
.or(just('"'))
38+
.or(just('b').to('\x08'))
39+
.or(just('f').to('\x0C'))
40+
.or(just('n').to('\n'))
41+
.or(just('r').to('\r'))
42+
.or(just('t').to('\t'))
43+
.or(just('u').ignore_then(
44+
filter(|c: &char| c.is_digit(16))
45+
.repeated()
46+
.exactly(4)
47+
.collect::<String>()
48+
.validate(|digits, span, emit| {
49+
char::from_u32(u32::from_str_radix(&digits, 16).unwrap())
50+
.unwrap_or_else(|| {
51+
emit(Simple::custom(span, "invalid unicode character"));
52+
'\u{FFFD}' // unicode replacement character
53+
})
54+
}),
55+
)),
56+
);
57+
58+
let string = just('"')
59+
.ignore_then(filter(|c| *c != '\\' && *c != '"').or(escape).repeated())
60+
.then_ignore(just('"'))
61+
.collect::<String>()
62+
.labelled("string");
63+
64+
let array = value
65+
.clone()
66+
.chain(just(',').ignore_then(value.clone()).repeated())
67+
.or_not()
68+
.flatten()
69+
.delimited_by(just('['), just(']'))
70+
.map(Json::Array)
71+
.labelled("array");
72+
73+
let member = string.clone().then_ignore(just(':').padded()).then(value);
74+
let object = member
75+
.clone()
76+
.chain(just(',').padded().ignore_then(member).repeated())
77+
.or_not()
78+
.flatten()
79+
.padded()
80+
.delimited_by(just('{'), just('}'))
81+
.collect::<HashMap<String, Json>>()
82+
.map(Json::Object)
83+
.labelled("object");
84+
85+
just("null")
86+
.to(Json::Null)
87+
.labelled("null")
88+
.or(just("true").to(Json::Bool(true)).labelled("true"))
89+
.or(just("false").to(Json::Bool(false)).labelled("false"))
90+
.or(number.map(Json::Num))
91+
// SWAP THESE LINES
92+
.or(string)
93+
//.or(string.map(Json::Str))
94+
.or(array)
95+
.or(object)
96+
.recover_with(nested_delimiters('{', '}', [('[', ']')], |_| Json::Invalid))
97+
.recover_with(nested_delimiters('[', ']', [('{', '}')], |_| Json::Invalid))
98+
.recover_with(skip_then_retry_until(['}', ']']))
99+
.padded()
100+
})
101+
.then_ignore(end().recover_with(skip_then_retry_until([])))
102+
}
103+
104+
fn main() {
105+
parser()
106+
.parse("{ \"a\": 5, b: \"hello, world!\"}")
107+
.unwrap();
108+
}

test_cases/tests/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ fn diesel() {
77
t.compile_fail("tests/diesel/*.rs");
88
}
99

10+
#[test]
11+
fn chumsky() {
12+
let t = trybuild::TestCases::new();
13+
t.compile_fail("tests/chumsky/*.rs");
14+
}

0 commit comments

Comments
 (0)