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).map(|s: &str| s.parse().unwrap())
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 .map(|s: &str| s.parse().unwrap())
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 .map(|s: &str| s.parse().unwrap())
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()
167 .separated_by(just(',').padded())
168 .collect::<Vec<_>>()
169 .delimited_by(just('['), just(']'))
170 }
171}
172
173impl<'src, A, B> Parse<'src> for (A, B)
174where
175 A: Parse<'src> + 'src,
176 B: Parse<'src> + 'src,
177{
178 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
179 A::parser()
180 .padded()
181 .then_ignore(just(',').padded())
182 .then(B::parser().padded())
183 .delimited_by(just('('), just(')'))
184 }
185}
186
187pub fn ident<'src>() -> impl Parser<'src, &'src str, String, E<'src>> + Clone {
193 token_text(|c| c != '@')
194}
195
196pub fn bare_token<'src>() -> impl Parser<'src, &'src str, String, E<'src>> + Clone {
202 token_text(|_| true)
203}
204
205fn token_text<'src>(
206 additional_filter: impl Fn(char) -> bool + Clone + 'src,
207) -> impl Parser<'src, &'src str, String, E<'src>> + Clone {
208 any()
209 .filter(move |c: &char| {
210 !c.is_whitespace()
211 && !matches!(
212 *c,
213 ',' | ';' | '(' | ')' | '{' | '}' | '[' | ']' | '"' | '\'' | '`'
214 )
215 && additional_filter(*c)
216 })
217 .repeated()
218 .at_least(1)
219 .collect::<String>()
220}