use reblessive::Stk;
use self::token_buffer::TokenBuffer;
use crate::gql::GqlParserSettings;
use crate::gql::ast::{GqlQuery, Ident};
use crate::gql::lexer::Lexer;
use crate::gql::token::{Span, Token, TokenKind};
use crate::syn::error::{SyntaxError, bail};
mod expr;
pub(crate) mod mac;
mod mutation;
mod pattern;
mod stmt;
mod token_buffer;
#[cfg(test)]
mod test;
use mac::unexpected;
pub type ParseResult<T> = Result<T, SyntaxError>;
pub struct Parser<'a> {
lexer: Lexer<'a>,
last_span: Span,
token_buffer: TokenBuffer<4>,
settings: GqlParserSettings,
}
impl<'a> Parser<'a> {
pub fn new_with_settings(source: &'a str, settings: GqlParserSettings) -> Self {
Parser {
lexer: Lexer::new(source),
last_span: Span::empty(),
token_buffer: TokenBuffer::new(),
settings,
}
}
#[expect(clippy::should_implement_trait)]
pub fn next(&mut self) -> Token {
let res = self.token_buffer.pop().unwrap_or_else(|| self.lexer.next_token());
self.last_span = res.span;
res
}
pub fn pop_peek(&mut self) -> Token {
let res = self.token_buffer.pop().expect("token buffer is non-empty");
self.last_span = res.span;
res
}
pub fn peek(&mut self) -> Token {
let Some(x) = self.token_buffer.first() else {
let res = self.lexer.next_token();
self.token_buffer.push(res);
return res;
};
x
}
pub fn peek_kind(&mut self) -> TokenKind {
self.peek().kind
}
fn peek_token_at(&mut self, at: u8) -> Token {
for _ in self.token_buffer.len()..=at {
let r = self.lexer.next_token();
self.token_buffer.push(r);
}
self.token_buffer.at(at).expect("token exists at index")
}
pub fn peek1(&mut self) -> Token {
self.peek_token_at(1)
}
pub fn peek2(&mut self) -> Token {
self.peek_token_at(2)
}
pub fn recent_span(&mut self) -> Span {
self.token_buffer.first().map(|x| x.span).unwrap_or(self.last_span)
}
pub fn last_span(&mut self) -> Span {
self.last_span
}
pub fn eat(&mut self, token: TokenKind) -> bool {
let peek = self.peek();
if token == peek.kind {
self.token_buffer.pop();
self.last_span = peek.span;
true
} else {
false
}
}
fn expect_closing_delimiter(&mut self, kind: TokenKind, should_close: Span) -> ParseResult<()> {
let peek = self.peek();
if peek.kind != kind {
match peek.kind {
TokenKind::Invalid => {
unexpected!(self, peek, "a closing delimiter");
}
TokenKind::Eof => {
bail!(
"Unexpected end of file, expected the delimiter `{kind}`",
@peek.span,
@should_close => "expected this delimiter to close"
);
}
x => {
bail!(
"Unexpected token `{x}`, expected the delimiter `{kind}`",
@peek.span,
@should_close => "expected this delimiter to close"
);
}
}
}
self.pop_peek();
Ok(())
}
pub fn span_str(&self, span: Span) -> &'a str {
self.lexer.span_str(span)
}
pub async fn parse_query(&mut self, stk: &mut Stk) -> ParseResult<GqlQuery> {
let program = self.parse_statement(stk).await?;
let token = self.peek();
if !token.is_eof() {
unexpected!(self, token, "the query to end");
}
Ok(GqlQuery {
program,
})
}
fn token_can_be_ident(kind: TokenKind) -> bool {
match kind {
TokenKind::Identifier
| TokenKind::DoubleQuoted {
..
}
| TokenKind::AccentQuoted {
..
} => true,
TokenKind::Keyword(keyword) => keyword.is_non_reserved(),
_ => false,
}
}
fn parse_ident(&mut self) -> ParseResult<Ident> {
let token = self.next();
match token.kind {
TokenKind::Identifier => Ok(Ident {
name: self.span_str(token.span).to_owned(),
span: token.span,
}),
TokenKind::Keyword(keyword) => {
if keyword.is_non_reserved() {
Ok(Ident {
name: self.span_str(token.span).to_owned(),
span: token.span,
})
} else {
bail!(
"`{}` is a reserved word and cannot be used as an identifier",
self.span_str(token.span),
@token.span => "use a `\"…\"` or `` `…` `` delimited identifier instead"
);
}
}
TokenKind::DoubleQuoted {
..
}
| TokenKind::AccentQuoted {
..
} => {
let name = Lexer::unescape_quoted_span(self.span_str(token.span), token.span)?;
Ok(Ident {
name,
span: token.span,
})
}
_ => unexpected!(self, token, "an identifier"),
}
}
}