1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
use crate::{
  ast::{ErrorToken, Expression},
  parser::{
    identifier,
    utils::{LocatedSpan, ToRange},
    IResult,
  },
};
use nom::{branch::alt, bytes::complete::take_till1, combinator::map};
use std::ops::Range;

/// Error containing a text span and an error message to display.
#[derive(Debug, PartialEq)]
pub struct Error(pub Range<usize>, pub String);

/// Evaluate `parser` and wrap the result in a `Some(_)`. Otherwise,
/// emit the  provided `error_msg` and return a `None` while allowing
/// parsing to continue.
pub(crate) fn expect<'a, F, E, T>(
  parser: F,
  error_msg: E,
) -> impl Fn(LocatedSpan<'a>) -> IResult<Option<T>>
where
  F: Fn(LocatedSpan<'a>) -> IResult<T>,
  E: ToString,
{
  move |input| match parser(input) {
    Ok((remaining, out)) => Ok((remaining, Some(out))),
    Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => {
      let input = err.input;
      let err = Error(input.to_range(), error_msg.to_string());
      input.extra.report_error(err);
      Ok((input, None))
    }
    Err(err) => Err(err),
  }
}

pub(crate) fn error(input: LocatedSpan) -> IResult<Expression> {
  alt((
    map(identifier, |span: LocatedSpan| {
      span.extra.report_error(Error(
        span.to_range(),
        format!("unexpected `{}`", span.fragment()),
      ));
      Expression::Error(ErrorToken::Identifier(span.to_string()))
    }),
    map(take_till1(|c| c == ')' || c == ' '), |span: LocatedSpan| {
      span.extra.report_error(Error(
        span.to_range(),
        format!("unexpected `{}`", span.fragment()),
      ));
      Expression::Error(ErrorToken::Unexpected)
    }),
  ))(input)
}