juniper/parser/
parser.rs

1use std::fmt;
2
3use compact_str::{CompactString, format_compact};
4use derive_more::with_trait::{Display, Error};
5
6use crate::parser::{Lexer, LexerError, Spanning, Token};
7
8/// Error while parsing a GraphQL query
9#[derive(Clone, Debug, Display, Eq, Error, PartialEq)]
10pub enum ParseError {
11    /// An unexpected token occurred in the source
12    // TODO: Previously was `Token<'a>`.
13    //       Revisit on `graphql-parser` integration.
14    #[display("Unexpected \"{_0}\"")]
15    UnexpectedToken(#[error(not(source))] CompactString),
16
17    /// The input source abruptly ended
18    #[display("Unexpected end of input")]
19    UnexpectedEndOfFile,
20
21    /// An error during tokenization occurred
22    LexerError(LexerError),
23
24    /// A scalar of unexpected type occurred in the source
25    ExpectedScalarError(#[error(not(source))] &'static str),
26}
27
28impl ParseError {
29    /// Creates a [`ParseError::UnexpectedToken`] out of the provided [`Token`].
30    #[must_use]
31    pub fn unexpected_token(token: Token<'_>) -> Self {
32        Self::UnexpectedToken(format_compact!("{token}"))
33    }
34}
35
36#[doc(hidden)]
37pub type ParseResult<T> = Result<Spanning<T>, Spanning<ParseError>>;
38
39#[doc(hidden)]
40pub type UnlocatedParseResult<T> = Result<T, Spanning<ParseError>>;
41
42#[doc(hidden)]
43pub type OptionParseResult<T> = Result<Option<Spanning<T>>, Spanning<ParseError>>;
44
45#[doc(hidden)]
46#[derive(Debug)]
47pub struct Parser<'a> {
48    tokens: Vec<Spanning<Token<'a>>>,
49}
50
51impl<'a> Parser<'a> {
52    #[doc(hidden)]
53    pub fn new(lexer: &mut Lexer<'a>) -> Result<Parser<'a>, Spanning<LexerError>> {
54        let mut tokens = Vec::new();
55
56        for res in lexer {
57            match res {
58                Ok(s) => tokens.push(s),
59                Err(e) => return Err(e),
60            }
61        }
62
63        Ok(Parser { tokens })
64    }
65
66    #[doc(hidden)]
67    pub fn peek(&self) -> &Spanning<Token<'a>> {
68        &self.tokens[0]
69    }
70
71    #[doc(hidden)]
72    pub fn next_token(&mut self) -> ParseResult<Token<'a>> {
73        if self.tokens.len() == 1 {
74            Err(Spanning::new(
75                self.peek().span,
76                ParseError::UnexpectedEndOfFile,
77            ))
78        } else {
79            Ok(self.tokens.remove(0))
80        }
81    }
82
83    #[doc(hidden)]
84    pub fn expect(&mut self, expected: &Token) -> ParseResult<Token<'a>> {
85        if &self.peek().item != expected {
86            Err(self.next_token()?.map(ParseError::unexpected_token))
87        } else {
88            self.next_token()
89        }
90    }
91
92    #[doc(hidden)]
93    pub fn skip(
94        &mut self,
95        expected: &Token,
96    ) -> Result<Option<Spanning<Token<'a>>>, Spanning<ParseError>> {
97        if &self.peek().item == expected {
98            Ok(Some(self.next_token()?))
99        } else if self.peek().item == Token::EndOfFile {
100            Err(Spanning::zero_width(
101                &self.peek().span.start,
102                ParseError::UnexpectedEndOfFile,
103            ))
104        } else {
105            Ok(None)
106        }
107    }
108
109    #[doc(hidden)]
110    pub fn delimited_list<T, F>(
111        &mut self,
112        opening: &Token,
113        parser: F,
114        closing: &Token,
115    ) -> ParseResult<Vec<Spanning<T>>>
116    where
117        T: fmt::Debug,
118        F: Fn(&mut Parser<'a>) -> ParseResult<T>,
119    {
120        let start_pos = &self.expect(opening)?.span.start;
121        let mut items = Vec::new();
122
123        loop {
124            if let Some(Spanning { span, .. }) = self.skip(closing)? {
125                return Ok(Spanning::start_end(start_pos, &span.end, items));
126            }
127
128            items.push(parser(self)?);
129        }
130    }
131
132    #[doc(hidden)]
133    pub fn delimited_nonempty_list<T, F>(
134        &mut self,
135        opening: &Token,
136        parser: F,
137        closing: &Token,
138    ) -> ParseResult<Vec<Spanning<T>>>
139    where
140        T: fmt::Debug,
141        F: Fn(&mut Parser<'a>) -> ParseResult<T>,
142    {
143        let start_pos = &self.expect(opening)?.span.start;
144        let mut items = Vec::new();
145
146        loop {
147            items.push(parser(self)?);
148
149            if let Some(end_spanning) = self.skip(closing)? {
150                return Ok(Spanning::start_end(start_pos, &end_spanning.end(), items));
151            }
152        }
153    }
154
155    #[doc(hidden)]
156    pub fn unlocated_delimited_nonempty_list<T, F>(
157        &mut self,
158        opening: &Token,
159        parser: F,
160        closing: &Token,
161    ) -> ParseResult<Vec<T>>
162    where
163        T: fmt::Debug,
164        F: Fn(&mut Parser<'a>) -> UnlocatedParseResult<T>,
165    {
166        let start_pos = &self.expect(opening)?.span.start;
167        let mut items = Vec::new();
168
169        loop {
170            items.push(parser(self)?);
171
172            if let Some(end_spanning) = self.skip(closing)? {
173                return Ok(Spanning::start_end(start_pos, &end_spanning.end(), items));
174            }
175        }
176    }
177
178    #[doc(hidden)]
179    pub fn expect_name(&mut self) -> ParseResult<&'a str> {
180        match *self.peek() {
181            Spanning {
182                item: Token::Name(_),
183                ..
184            } => Ok(self.next_token()?.map(|token| {
185                if let Token::Name(name) = token {
186                    name
187                } else {
188                    panic!("Internal parse error in `expect_name`");
189                }
190            })),
191            Spanning {
192                item: Token::EndOfFile,
193                ..
194            } => Err(Spanning::new(
195                self.peek().span,
196                ParseError::UnexpectedEndOfFile,
197            )),
198            _ => Err(self.next_token()?.map(ParseError::unexpected_token)),
199        }
200    }
201}