blanket_script/parser/
mod.rs

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