1use nom::{
10 branch::alt,
11 bytes::complete::{tag, take, take_until, take_while, take_while1, take_while_m_n},
12 character::complete::char as tag_char,
13 combinator::{cut, map, map_res, not, opt, peek, recognize},
14 multi::{many0, separated_list0, separated_list1},
15 sequence::{delimited, preceded, separated_pair, terminated, tuple},
16};
17
18use arithmetic_parser::{with_span, ErrorKind as ParseErrorKind, InputSpan, NomResult, Spanned};
19
20mod conversion;
21#[cfg(test)]
22mod tests;
23
24pub use self::conversion::AstConversionError;
25pub(crate) use self::conversion::AstConversionState;
26
27#[derive(Debug, Clone, PartialEq)]
57#[non_exhaustive]
58pub enum TypeAst<'a> {
59 Some,
62 Any,
64 Dyn(TypeConstraintsAst<'a>),
66 Ident,
68 Param,
70 Function(Box<FunctionAst<'a>>),
72 FunctionWithConstraints {
74 constraints: Spanned<'a, ConstraintsAst<'a>>,
76 function: Box<Spanned<'a, FunctionAst<'a>>>,
78 },
79 Tuple(TupleAst<'a>),
81 Slice(SliceAst<'a>),
83 Object(ObjectAst<'a>),
85}
86
87impl<'a> TypeAst<'a> {
88 pub fn parse(input: InputSpan<'a>) -> NomResult<'a, Spanned<'a, Self>> {
90 with_span(type_definition)(input)
91 }
92}
93
94pub type SpannedTypeAst<'a> = Spanned<'a, TypeAst<'a>>;
96
97#[derive(Debug, Clone, PartialEq)]
99pub struct TupleAst<'a> {
100 pub start: Vec<SpannedTypeAst<'a>>,
103 pub middle: Option<Spanned<'a, SliceAst<'a>>>,
105 pub end: Vec<SpannedTypeAst<'a>>,
108}
109
110#[derive(Debug, Clone, PartialEq)]
112pub struct SliceAst<'a> {
113 pub element: Box<SpannedTypeAst<'a>>,
115 pub length: Spanned<'a, TupleLenAst>,
117}
118
119#[derive(Debug, Clone, PartialEq)]
143#[non_exhaustive]
144pub struct FunctionAst<'a> {
145 pub args: Spanned<'a, TupleAst<'a>>,
147 pub return_type: SpannedTypeAst<'a>,
149}
150
151impl<'a> FunctionAst<'a> {
152 pub fn parse(input: InputSpan<'a>) -> NomResult<'a, Self> {
154 fn_definition(input)
155 }
156}
157
158#[derive(Debug, Clone, PartialEq)]
160#[non_exhaustive]
161pub enum TupleLenAst {
162 Some,
164 Dynamic,
167 Ident,
169}
170
171#[derive(Debug, Clone, PartialEq)]
173#[non_exhaustive]
174pub struct ConstraintsAst<'a> {
175 pub static_lengths: Vec<Spanned<'a>>,
177 pub type_params: Vec<(Spanned<'a>, TypeConstraintsAst<'a>)>,
179}
180
181#[derive(Debug, Default, Clone, PartialEq)]
183#[non_exhaustive]
184pub struct TypeConstraintsAst<'a> {
185 pub object: Option<ObjectAst<'a>>,
187 pub terms: Vec<Spanned<'a>>,
189}
190
191#[derive(Debug, Clone, PartialEq)]
193#[non_exhaustive]
194pub struct ObjectAst<'a> {
195 pub fields: Vec<(Spanned<'a>, SpannedTypeAst<'a>)>,
197}
198
199fn ws(input: InputSpan<'_>) -> NomResult<'_, InputSpan<'_>> {
201 fn narrow_ws(input: InputSpan<'_>) -> NomResult<'_, InputSpan<'_>> {
202 take_while1(|c: char| c.is_ascii_whitespace())(input)
203 }
204
205 fn long_comment_body(input: InputSpan<'_>) -> NomResult<'_, InputSpan<'_>> {
206 cut(take_until("*/"))(input)
207 }
208
209 let comment = preceded(tag("//"), take_while(|c: char| c != '\n'));
210 let long_comment = delimited(tag("/*"), long_comment_body, tag("*/"));
211 let ws_line = alt((narrow_ws, comment, long_comment));
212 recognize(many0(ws_line))(input)
213}
214
215fn comma_sep(input: InputSpan<'_>) -> NomResult<'_, char> {
217 delimited(ws, tag_char(','), ws)(input)
218}
219
220fn ident(input: InputSpan<'_>) -> NomResult<'_, Spanned<'_>> {
221 preceded(
222 peek(take_while_m_n(1, 1, |c: char| {
223 c.is_ascii_alphabetic() || c == '_'
224 })),
225 map(
226 take_while1(|c: char| c.is_ascii_alphanumeric() || c == '_'),
227 Spanned::from,
228 ),
229 )(input)
230}
231
232fn not_keyword(input: InputSpan<'_>) -> NomResult<'_, Spanned<'_>> {
233 map_res(ident, |ident| {
234 if *ident.fragment() == "as" {
235 Err(ParseErrorKind::Type(anyhow::anyhow!(
236 "`as` is a reserved keyword"
237 )))
238 } else {
239 Ok(ident)
240 }
241 })(input)
242}
243
244fn type_param_ident(input: InputSpan<'_>) -> NomResult<'_, Spanned<'_>> {
245 preceded(tag_char('\''), ident)(input)
246}
247
248fn comma_separated_types(input: InputSpan<'_>) -> NomResult<'_, Vec<SpannedTypeAst<'_>>> {
249 separated_list0(delimited(ws, tag_char(','), ws), with_span(type_definition))(input)
250}
251
252fn tuple_middle(input: InputSpan<'_>) -> NomResult<'_, Spanned<'_, SliceAst<'_>>> {
253 preceded(terminated(tag("..."), ws), with_span(slice_definition))(input)
254}
255
256type TupleTailAst<'a> = (Spanned<'a, SliceAst<'a>>, Vec<SpannedTypeAst<'a>>);
257
258fn tuple_tail(input: InputSpan<'_>) -> NomResult<'_, TupleTailAst<'_>> {
259 tuple((
260 tuple_middle,
261 map(
262 opt(preceded(comma_sep, comma_separated_types)),
263 Option::unwrap_or_default,
264 ),
265 ))(input)
266}
267
268fn tuple_definition(input: InputSpan<'_>) -> NomResult<'_, TupleAst<'_>> {
269 let maybe_comma = opt(comma_sep);
270
271 let main_parser = alt((
272 map(tuple_tail, |(middle, end)| TupleAst {
273 start: Vec::new(),
274 middle: Some(middle),
275 end,
276 }),
277 map(
278 tuple((comma_separated_types, opt(preceded(comma_sep, tuple_tail)))),
279 |(start, maybe_tail)| {
280 if let Some((middle, end)) = maybe_tail {
281 TupleAst {
282 start,
283 middle: Some(middle),
284 end,
285 }
286 } else {
287 TupleAst {
288 start,
289 middle: None,
290 end: Vec::new(),
291 }
292 }
293 },
294 ),
295 ));
296
297 preceded(
298 terminated(tag_char('('), ws),
299 cut(terminated(
301 main_parser,
302 tuple((maybe_comma, ws, tag_char(')'))),
303 )),
304 )(input)
305}
306
307fn tuple_len(input: InputSpan<'_>) -> NomResult<'_, Spanned<'_, TupleLenAst>> {
308 let semicolon = tuple((ws, tag_char(';'), ws));
309 let empty = map(take(0_usize), Spanned::from);
310 map(alt((preceded(semicolon, not_keyword), empty)), |id| {
311 id.map_extra(|()| match *id.fragment() {
312 "_" => TupleLenAst::Some,
313 "" => TupleLenAst::Dynamic,
314 _ => TupleLenAst::Ident,
315 })
316 })(input)
317}
318
319fn slice_definition(input: InputSpan<'_>) -> NomResult<'_, SliceAst<'_>> {
320 preceded(
321 terminated(tag_char('['), ws),
322 cut(terminated(
324 map(
325 tuple((with_span(type_definition), tuple_len)),
326 |(element, length)| SliceAst {
327 element: Box::new(element),
328 length,
329 },
330 ),
331 tuple((ws, tag_char(']'))),
332 )),
333 )(input)
334}
335
336fn object(input: InputSpan<'_>) -> NomResult<'_, ObjectAst<'_>> {
337 let colon = tuple((ws, tag_char(':'), ws));
338 let object_field = separated_pair(ident, colon, with_span(type_definition));
339 let object_body = terminated(separated_list1(comma_sep, object_field), opt(comma_sep));
340 let object = preceded(
341 terminated(tag_char('{'), ws),
342 cut(terminated(object_body, tuple((ws, tag_char('}'))))),
343 );
344 map(object, |fields| ObjectAst { fields })(input)
345}
346
347fn constraint_sep(input: InputSpan<'_>) -> NomResult<'_, ()> {
348 map(tuple((ws, tag_char('+'), ws)), drop)(input)
349}
350
351fn simple_type_bounds(input: InputSpan<'_>) -> NomResult<'_, TypeConstraintsAst<'_>> {
352 map(separated_list1(constraint_sep, not_keyword), |terms| {
353 TypeConstraintsAst {
354 object: None,
355 terms,
356 }
357 })(input)
358}
359
360fn type_bounds(input: InputSpan<'_>) -> NomResult<'_, TypeConstraintsAst<'_>> {
361 alt((
362 map(
363 tuple((
364 object,
365 opt(preceded(
366 constraint_sep,
367 separated_list1(constraint_sep, not_keyword),
368 )),
369 )),
370 |(object, terms)| TypeConstraintsAst {
371 object: Some(object),
372 terms: terms.unwrap_or_default(),
373 },
374 ),
375 simple_type_bounds,
376 ))(input)
377}
378
379fn type_params(input: InputSpan<'_>) -> NomResult<'_, Vec<(Spanned<'_>, TypeConstraintsAst<'_>)>> {
380 let type_bounds = preceded(tuple((ws, tag_char(':'), ws)), type_bounds);
381 let type_param = tuple((type_param_ident, type_bounds));
382 separated_list1(comma_sep, type_param)(input)
383}
384
385fn constraints(input: InputSpan<'_>) -> NomResult<'_, ConstraintsAst<'_>> {
387 let semicolon = tuple((ws, tag_char(';'), ws));
388
389 let len_params = preceded(
390 terminated(tag("len!"), ws),
391 separated_list1(comma_sep, not_keyword),
392 );
393
394 let params_parser = alt((
395 map(
396 tuple((len_params, opt(preceded(semicolon, type_params)))),
397 |(static_lengths, type_params)| (static_lengths, type_params.unwrap_or_default()),
398 ),
399 map(type_params, |type_params| (vec![], type_params)),
400 ));
401
402 let constraints_parser = tuple((
403 terminated(tag("for"), ws),
404 terminated(tag_char('<'), ws),
405 cut(terminated(params_parser, tuple((ws, tag_char('>'))))),
406 ));
407
408 map(
409 constraints_parser,
410 |(_, _, (static_lengths, type_params))| ConstraintsAst {
411 static_lengths,
412 type_params,
413 },
414 )(input)
415}
416
417fn return_type(input: InputSpan<'_>) -> NomResult<'_, SpannedTypeAst<'_>> {
418 preceded(tuple((ws, tag("->"), ws)), cut(with_span(type_definition)))(input)
419}
420
421fn fn_or_tuple(input: InputSpan<'_>) -> NomResult<'_, TypeAst<'_>> {
422 map(
423 tuple((with_span(tuple_definition), opt(return_type))),
424 |(args, return_type)| {
425 if let Some(return_type) = return_type {
426 TypeAst::Function(Box::new(FunctionAst { args, return_type }))
427 } else {
428 TypeAst::Tuple(args.extra)
429 }
430 },
431 )(input)
432}
433
434fn fn_definition(input: InputSpan<'_>) -> NomResult<'_, FunctionAst<'_>> {
435 map(
436 tuple((with_span(tuple_definition), return_type)),
437 |(args, return_type)| FunctionAst { args, return_type },
438 )(input)
439}
440
441fn fn_definition_with_constraints(input: InputSpan<'_>) -> NomResult<'_, TypeAst<'_>> {
442 map(
443 tuple((with_span(constraints), ws, cut(with_span(fn_definition)))),
444 |(constraints, _, function)| TypeAst::FunctionWithConstraints {
445 constraints,
446 function: Box::new(function),
447 },
448 )(input)
449}
450
451fn not_ident_char(input: InputSpan<'_>) -> NomResult<'_, ()> {
452 peek(not(take_while_m_n(1, 1, |c: char| {
453 c.is_ascii_alphanumeric() || c == '_'
454 })))(input)
455}
456
457fn any_type(input: InputSpan<'_>) -> NomResult<'_, ()> {
458 terminated(map(tag("any"), drop), not_ident_char)(input)
459}
460
461fn dyn_type(input: InputSpan<'_>) -> NomResult<'_, TypeConstraintsAst<'_>> {
462 map(
463 preceded(
464 terminated(tag("dyn"), not_ident_char),
465 opt(preceded(ws, type_bounds)),
466 ),
467 Option::unwrap_or_default,
468 )(input)
469}
470
471fn free_ident(input: InputSpan<'_>) -> NomResult<'_, TypeAst<'_>> {
472 map(not_keyword, |id| match *id.fragment() {
473 "_" => TypeAst::Some,
474 _ => TypeAst::Ident,
475 })(input)
476}
477
478fn type_definition(input: InputSpan<'_>) -> NomResult<'_, TypeAst<'_>> {
479 alt((
480 fn_or_tuple,
481 fn_definition_with_constraints,
482 map(type_param_ident, |_| TypeAst::Param),
483 map(slice_definition, TypeAst::Slice),
484 map(object, TypeAst::Object),
485 map(dyn_type, TypeAst::Dyn),
486 map(any_type, |()| TypeAst::Any),
487 free_ident,
488 ))(input)
489}