1use crate::Parse;
5use chumsky::error::Simple;
6use chumsky::extra;
7use chumsky::prelude::*;
8use std::{borrow::Borrow, fmt};
9
10type E<'src> = extra::Err<Simple<'src, char>>;
11
12#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct Ident(pub String);
15
16#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct BareToken(pub String);
19
20#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub struct QuotedString(pub String);
23
24macro_rules! impl_lexical_string {
25 ($ty:ty) => {
26 impl $ty {
27 pub fn as_str(&self) -> &str {
29 &self.0
30 }
31
32 pub fn into_inner(self) -> String {
34 self.0
35 }
36 }
37
38 impl AsRef<str> for $ty {
39 fn as_ref(&self) -> &str {
40 self.as_str()
41 }
42 }
43
44 impl Borrow<str> for $ty {
45 fn borrow(&self) -> &str {
46 self.as_str()
47 }
48 }
49
50 impl fmt::Display for $ty {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 f.write_str(self.as_str())
53 }
54 }
55
56 impl From<$ty> for String {
57 fn from(value: $ty) -> Self {
58 value.into_inner()
59 }
60 }
61 };
62}
63
64impl_lexical_string!(Ident);
65impl_lexical_string!(BareToken);
66impl_lexical_string!(QuotedString);
67
68macro_rules! impl_uint {
69 ($($t:ty),+) => {
70 $(impl<'src> Parse<'src> for $t {
71 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
72 text::int(10).try_map(|s: &str, span| s.parse().map_err(|_| Simple::new(None, span)))
73 }
74 })+
75 };
76}
77impl_uint!(u64, u32, usize);
78
79macro_rules! impl_sint {
80 ($($t:ty),+) => {
81 $(impl<'src> Parse<'src> for $t {
82 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
83 just('-')
84 .or_not()
85 .then(text::int(10))
86 .to_slice()
87 .try_map(|s: &str, span| s.parse().map_err(|_| Simple::new(None, span)))
88 }
89 })+
90 };
91}
92impl_sint!(i64, i32);
93
94macro_rules! impl_float {
95 ($($t:ty),+) => {
96 $(impl<'src> Parse<'src> for $t {
97 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
98 let exp = one_of("eE")
104 .then(one_of("+-").or_not())
105 .then(text::digits(10));
106 just('-')
107 .or_not()
108 .then(text::int(10))
109 .then(just('.').then(text::digits(10)).or_not())
110 .then(exp.or_not())
111 .to_slice()
112 .try_map(|s: &str, span| s.parse().map_err(|_| Simple::new(None, span)))
113 }
114 })+
115 };
116}
117impl_float!(f64, f32);
118
119impl<'src> Parse<'src> for bool {
120 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
121 just("true").to(true).or(just("false").to(false))
122 }
123}
124
125impl<'src> Parse<'src> for Ident {
126 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
127 ident().map(Self)
128 }
129}
130
131impl<'src> Parse<'src> for BareToken {
132 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
133 bare_token().map(Self)
134 }
135}
136
137impl<'src> Parse<'src> for QuotedString {
138 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
139 let escape = just('\\').ignore_then(choice((
140 just('"').to('"'),
141 just('\\').to('\\'),
142 just('n').to('\n'),
143 just('t').to('\t'),
144 just('r').to('\r'),
145 just('0').to('\0'),
146 )));
147 let character = choice((
148 escape,
149 any().and_is(just('"').not()).and_is(just('\\').not()),
150 ));
151
152 character
153 .repeated()
154 .collect::<String>()
155 .delimited_by(just('"'), just('"'))
156 .map(Self)
157 }
158}
159
160impl<'src, T> Parse<'src> for Vec<T>
161where
162 T: Parse<'src> + 'src,
163{
164 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
165 T::parser()
166 .padded_by(trivia())
167 .separated_by(just(',').padded_by(trivia()))
168 .allow_trailing()
169 .collect::<Vec<_>>()
170 .delimited_by(just('[').padded_by(trivia()), just(']').padded_by(trivia()))
171 }
172}
173
174impl<'src, A, B> Parse<'src> for (A, B)
175where
176 A: Parse<'src> + 'src,
177 B: Parse<'src> + 'src,
178{
179 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
180 A::parser()
181 .padded()
182 .then_ignore(just(',').padded())
183 .then(B::parser().padded())
184 .delimited_by(just('('), just(')'))
185 }
186}
187
188pub fn ident<'src>() -> impl Parser<'src, &'src str, String, E<'src>> + Clone {
194 token_text(|c| c != '@')
195}
196
197pub fn bare_token<'src>() -> impl Parser<'src, &'src str, String, E<'src>> + Clone {
203 token_text(|_| true)
204}
205
206fn token_text<'src>(
207 additional_filter: impl Fn(char) -> bool + Clone + 'src,
208) -> impl Parser<'src, &'src str, String, E<'src>> + Clone {
209 any()
210 .filter(move |c: &char| {
211 !c.is_whitespace()
212 && !matches!(
213 *c,
214 ',' | ';' | '(' | ')' | '{' | '}' | '[' | ']' | '"' | '\'' | '`'
215 )
216 && additional_filter(*c)
217 })
218 .repeated()
219 .at_least(1)
220 .collect::<String>()
221}
222
223pub fn trivia<'src>() -> impl Parser<'src, &'src str, (), E<'src>> + Clone {
225 let whitespace = any().filter(|c: &char| c.is_whitespace()).ignored();
226 let comment = just("//")
227 .then(any().and_is(just('\n').not()).repeated())
228 .ignored();
229 whitespace.or(comment).repeated().collect::<()>()
230}
231
232pub fn metadata_key<'src>(key: &'static str) -> impl Parser<'src, &'src str, (), E<'src>> {
234 text::ascii::ident()
235 .map(str::to_owned)
236 .or(QuotedString::parser().map(|s| s.into_inner()))
237 .try_map(move |value, span| {
238 if value == key {
239 Ok(())
240 } else {
241 Err(Simple::new(None, span))
242 }
243 })
244 .padded_by(trivia())
245}
246
247impl<'src> Parse<'src> for String {
248 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
249 QuotedString::parser().map(|s| s.into_inner())
250 }
251}
252
253impl<'src, T: Parse<'src> + 'src> Parse<'src> for Option<T> {
254 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
255 text::keyword("null")
256 .map(|_| None)
257 .or(T::parser().map(Some))
258 }
259}