use crate::{tokenizer::*, Token};
use leo_ast::*;
use leo_errors::{emitter::Handler, ParserError, ParserWarning, Result};
use leo_span::{Span, Symbol};
use std::{fmt::Display, mem};
pub(crate) struct ParserContext<'a> {
pub(crate) handler: &'a Handler,
tokens: Vec<SpannedToken>,
pub(crate) token: SpannedToken,
pub(crate) prev_token: SpannedToken,
pub(crate) disallow_struct_construction: bool,
pub(crate) allow_identifier_underscores: bool,
}
const DUMMY_EOF: SpannedToken = SpannedToken { token: Token::Eof, span: Span::dummy() };
impl<'a> ParserContext<'a> {
pub fn new(handler: &'a Handler, mut tokens: Vec<SpannedToken>) -> Self {
tokens.retain(|x| !matches!(x.token, Token::CommentLine(_) | Token::CommentBlock(_)));
tokens.reverse();
let token = SpannedToken::dummy();
let mut p = Self {
handler,
disallow_struct_construction: false,
allow_identifier_underscores: false,
prev_token: token.clone(),
token,
tokens,
};
p.bump();
p
}
pub(crate) fn bump(&mut self) {
if let Token::Eof = self.prev_token.token {
panic!("attempted to bump the parser past EOF (may be stuck in a loop)");
}
let next_token = self.tokens.pop().unwrap_or(SpannedToken { token: Token::Eof, span: self.token.span });
self.prev_token = mem::replace(&mut self.token, next_token);
}
pub(super) fn check(&self, tok: &Token) -> bool {
&self.token.token == tok
}
pub(super) fn check_int(&self) -> bool {
matches!(&self.token.token, Token::Integer(_))
}
pub(super) fn eat(&mut self, token: &Token) -> bool {
self.check(token).then(|| self.bump()).is_some()
}
pub(super) fn look_ahead<'s, R>(&'s self, dist: usize, looker: impl FnOnce(&'s SpannedToken) -> R) -> R {
if dist == 0 {
return looker(&self.token);
}
let idx = match self.tokens.len().checked_sub(dist) {
None => return looker(&DUMMY_EOF),
Some(idx) => idx,
};
looker(self.tokens.get(idx).unwrap_or(&DUMMY_EOF))
}
pub(super) fn emit_err(&self, err: ParserError) {
self.handler.emit_err(err);
}
pub(super) fn emit_warning(&self, warning: ParserWarning) {
self.handler.emit_warning(warning.into());
}
pub(crate) fn has_next(&self) -> bool {
!matches!(self.token.token, Token::Eof)
}
fn mk_ident_prev(&self, name: Symbol) -> Identifier {
let span = self.prev_token.span;
Identifier { name, span }
}
pub(super) fn eat_identifier(&mut self) -> Option<Identifier> {
if let Token::Identifier(name) = self.token.token {
self.bump();
return Some(self.mk_ident_prev(name));
}
None
}
pub(super) fn expect_identifier(&mut self) -> Result<Identifier> {
self.eat_identifier()
.ok_or_else(|| ParserError::unexpected_str(&self.token.token, "identifier", self.token.span).into())
}
pub fn eat_integer(&mut self) -> Result<(PositiveNumber, Span)> {
if let Token::Integer(value) = &self.token.token {
let value = value.clone();
self.bump();
Ok((PositiveNumber { value }, self.prev_token.span))
} else {
Err(ParserError::unexpected(&self.token.token, "integer literal", self.token.span).into())
}
}
pub(super) fn eat_any(&mut self, tokens: &[Token]) -> bool {
tokens.iter().any(|x| self.check(x)).then(|| self.bump()).is_some()
}
pub(super) fn unexpected<T>(&self, expected: impl Display) -> Result<T> {
Err(ParserError::unexpected(&self.token.token, expected, self.token.span).into())
}
pub(super) fn expect(&mut self, token: &Token) -> Result<Span> {
if self.eat(token) { Ok(self.prev_token.span) } else { self.unexpected(token) }
}
pub(super) fn expect_any(&mut self, tokens: &[Token]) -> Result<Span> {
if self.eat_any(tokens) {
Ok(self.prev_token.span)
} else {
self.unexpected(tokens.iter().map(|x| format!("'{x}'")).collect::<Vec<_>>().join(", "))
}
}
pub(super) fn parse_list<T>(
&mut self,
delimiter: Delimiter,
sep: Option<Token>,
mut inner: impl FnMut(&mut Self) -> Result<Option<T>>,
) -> Result<(Vec<T>, bool, Span)> {
let (open, close) = delimiter.open_close_pair();
let mut list = Vec::new();
let mut trailing = false;
let open_span = self.expect(&open)?;
while !self.check(&close) {
if let Some(elem) = inner(self)? {
list.push(elem);
}
if sep.as_ref().filter(|sep| !self.eat(sep)).is_some() {
trailing = false;
break;
}
trailing = true;
}
let span = open_span + self.expect(&close)?;
Ok((list, trailing, span))
}
pub(super) fn parse_paren_comma_list<T>(
&mut self,
f: impl FnMut(&mut Self) -> Result<Option<T>>,
) -> Result<(Vec<T>, bool, Span)> {
self.parse_list(Delimiter::Parenthesis, Some(Token::Comma), f)
}
pub(super) fn peek_is_left_par(&self) -> bool {
matches!(self.token.token, Token::LeftParen)
}
}