blanket_script/parser/
mod.rs

1use expression::parse_expression;
2use nom::{
3    character::complete::{multispace0, multispace1},
4    error::ParseError,
5    sequence::delimited,
6    IResult, Parser,
7};
8use thiserror::Error;
9
10pub(super) mod expression;
11
12pub use expression::{Associativity, BinaryOperator, Expression, StringLiteral, UnaryOperator};
13
14#[derive(Error, Debug, PartialEq)]
15pub enum SyntaxErrorKind {
16    #[error("Empty input")]
17    EmptyInput,
18    #[error("Trailing characters")]
19    TrailingCharacters,
20    #[error("Numeric literals cannot have consecutive underscores")]
21    ConsecutiveUnderscoreInNumericLiteral,
22    #[error("Numeric literals cannot start with an underscore")]
23    NumericLiteralStartsWithUnderscore,
24    #[error("Numeric literals cannot end with an underscore")]
25    NumericLiteralEndsWithUnderscore,
26    #[error("Empty bigint literal")]
27    EmptyBigIntLiteral,
28    #[error("Invalid hexadecimal digit")]
29    InvalidHexDigit,
30    #[error("Missing opening brace in unicode code point escape sequence")]
31    MissingUnicodeCodePointOpeningBrace,
32    #[error("Missing closing brace in unicode code point escape sequence")]
33    MissingUnicodeCodePointClosingBrace,
34    #[error("Invalid unicode code point: {0}")]
35    InvalidUnicodeCodePoint(u32),
36    #[error("Nom error: {0:?}")]
37    NomError(nom::error::ErrorKind),
38}
39
40#[derive(Debug, PartialEq)]
41pub struct SyntaxError<'a> {
42    pub input: &'a str,
43    pub error: SyntaxErrorKind,
44}
45
46impl<'a> nom::error::ParseError<&'a str> for SyntaxError<'a> {
47    fn from_error_kind(input: &'a str, kind: nom::error::ErrorKind) -> Self {
48        SyntaxError {
49            input,
50            error: match kind {
51                nom::error::ErrorKind::Eof => SyntaxErrorKind::EmptyInput,
52                _ => SyntaxErrorKind::NomError(kind),
53            },
54        }
55    }
56
57    fn append(_: &'a str, _: nom::error::ErrorKind, other: Self) -> Self {
58        other
59    }
60}
61
62pub type ParseResult<'a, T> = IResult<&'a str, T, SyntaxError<'a>>;
63
64pub fn parse(input: &str) -> ParseResult<Expression> {
65    if input.is_empty() {
66        return Err(nom::Err::Failure(SyntaxError {
67            input,
68            error: SyntaxErrorKind::EmptyInput,
69        }));
70    }
71    let (remaining, parsed) = parse_expression(input)?;
72    if remaining.is_empty() {
73        return Ok((remaining, parsed));
74    }
75    Err(nom::Err::Failure(SyntaxError {
76        input,
77        error: SyntaxErrorKind::TrailingCharacters,
78    }))
79}
80
81/// Matches a parser surrounded by optional whitespace.
82fn ws0<'a, O, E: ParseError<&'a str>, F>(inner: F) -> impl Parser<&'a str, Output = O, Error = E>
83where
84    F: Parser<&'a str, Output = O, Error = E>,
85{
86    delimited(multispace0, inner, multispace0)
87}
88
89/// Matches a parser surrounded by at least one whitespace on each side.
90fn ws1<'a, O, E: ParseError<&'a str>, F>(inner: F) -> impl Parser<&'a str, Output = O, Error = E>
91where
92    F: Parser<&'a str, Output = O, Error = E>,
93{
94    delimited(multispace1, inner, multispace1)
95}