use crate::ast;
use crate::interner::StrInterner;
use crate::span::{SpanId, SpanManager};
use crate::token::{Number, STokenKind, Token, TokenKind};
mod error;
mod expr;
pub use error::{ExpectedThing, ParseError};
pub struct Parser<'a> {
str_interner: &'a StrInterner,
span_mgr: &'a mut SpanManager,
curr_token: Token,
rem_tokens: std::vec::IntoIter<Token>,
expected_things: Vec<ExpectedThing>,
}
impl<'a> Parser<'a> {
pub fn new(
str_interner: &'a StrInterner,
span_mgr: &'a mut SpanManager,
tokens: Vec<Token>,
) -> Self {
let mut token_iter = tokens.into_iter();
let first_token = token_iter.next().expect("passed an empty token slice");
Self {
str_interner,
span_mgr,
curr_token: first_token,
rem_tokens: token_iter,
expected_things: Vec::new(),
}
}
fn next_token(&mut self) -> Token {
let next_token = self.rem_tokens.next().unwrap();
let token = std::mem::replace(&mut self.curr_token, next_token);
self.expected_things.clear();
token
}
#[cold]
#[must_use]
fn report_expected(&mut self) -> ParseError {
ParseError::Expected {
span: self.curr_token.span,
expected: self.expected_things.drain(..).collect(),
instead: self.curr_token.kind.clone(),
}
}
#[inline]
fn eat_eof(&mut self, add_to_expected: bool) -> bool {
if matches!(self.curr_token.kind, TokenKind::EndOfFile) {
assert!(self.rem_tokens.as_slice().is_empty());
self.expected_things.clear();
true
} else {
if add_to_expected {
self.expected_things.push(ExpectedThing::EndOfFile);
}
false
}
}
#[inline]
fn eat_simple(&mut self, kind: STokenKind, add_to_expected: bool) -> Option<SpanId> {
if matches!(self.curr_token.kind, TokenKind::Simple(k) if k == kind) {
let span = self.curr_token.span;
self.next_token();
Some(span)
} else {
if add_to_expected {
self.expected_things.push(ExpectedThing::Simple(kind));
}
None
}
}
#[inline]
fn expect_simple(
&mut self,
kind: STokenKind,
add_to_expected: bool,
) -> Result<SpanId, ParseError> {
if let Some(span) = self.eat_simple(kind, add_to_expected) {
Ok(span)
} else {
Err(self.report_expected())
}
}
#[inline]
fn eat_ident(&mut self, add_to_expected: bool) -> Option<ast::Ident> {
if let TokenKind::Ident(ref value) = self.curr_token.kind {
let value = value.clone();
let span = self.curr_token.span;
self.next_token();
Some(ast::Ident { value, span })
} else {
if add_to_expected {
self.expected_things.push(ExpectedThing::Ident);
}
None
}
}
#[inline]
fn expect_ident(&mut self, add_to_expected: bool) -> Result<ast::Ident, ParseError> {
if let Some(ident) = self.eat_ident(add_to_expected) {
Ok(ident)
} else {
Err(self.report_expected())
}
}
#[inline]
fn eat_number(&mut self, add_to_expected: bool) -> Option<(Number, SpanId)> {
if let TokenKind::Number(_) = self.curr_token.kind {
let span = self.curr_token.span;
let TokenKind::Number(n) = self.next_token().kind else {
unreachable!();
};
Some((n, span))
} else {
if add_to_expected {
self.expected_things.push(ExpectedThing::Number);
}
None
}
}
#[inline]
fn eat_string(&mut self, add_to_expected: bool) -> Option<(Box<str>, SpanId)> {
if let TokenKind::String(_) = self.curr_token.kind {
let span = self.curr_token.span;
let TokenKind::String(s) = self.next_token().kind else {
unreachable!();
};
Some((s, span))
} else {
if add_to_expected {
self.expected_things.push(ExpectedThing::String);
}
None
}
}
#[inline]
fn eat_text_block(&mut self, add_to_expected: bool) -> Option<(Box<str>, SpanId)> {
if let TokenKind::TextBlock(_) = self.curr_token.kind {
let span = self.curr_token.span;
let TokenKind::TextBlock(s) = self.next_token().kind else {
unreachable!();
};
Some((s, span))
} else {
if add_to_expected {
self.expected_things.push(ExpectedThing::TextBlock);
}
None
}
}
#[inline]
fn eat_visibility(&mut self, add_to_expected: bool) -> Option<ast::Visibility> {
if self
.eat_simple(STokenKind::Colon, add_to_expected)
.is_some()
{
Some(ast::Visibility::Default)
} else if self
.eat_simple(STokenKind::ColonColon, add_to_expected)
.is_some()
{
Some(ast::Visibility::Hidden)
} else if self
.eat_simple(STokenKind::ColonColonColon, add_to_expected)
.is_some()
{
Some(ast::Visibility::ForceVisible)
} else {
None
}
}
#[inline]
fn eat_plus_visibility(&mut self, add_to_expected: bool) -> Option<(bool, ast::Visibility)> {
if self
.eat_simple(STokenKind::Colon, add_to_expected)
.is_some()
{
Some((false, ast::Visibility::Default))
} else if self
.eat_simple(STokenKind::ColonColon, add_to_expected)
.is_some()
{
Some((false, ast::Visibility::Hidden))
} else if self
.eat_simple(STokenKind::ColonColonColon, add_to_expected)
.is_some()
{
Some((false, ast::Visibility::ForceVisible))
} else if self
.eat_simple(STokenKind::PlusColon, add_to_expected)
.is_some()
{
Some((true, ast::Visibility::Default))
} else if self
.eat_simple(STokenKind::PlusColonColon, add_to_expected)
.is_some()
{
Some((true, ast::Visibility::Hidden))
} else if self
.eat_simple(STokenKind::PlusColonColonColon, add_to_expected)
.is_some()
{
Some((true, ast::Visibility::ForceVisible))
} else {
None
}
}
#[inline]
fn peek_simple(&self, skind: STokenKind, i: usize) -> bool {
if i == 0 {
matches!(self.curr_token.kind, TokenKind::Simple(k) if k == skind)
} else if let Some(Token { kind, .. }) = self.rem_tokens.as_slice().get(i - 1) {
matches!(*kind, TokenKind::Simple(k) if k == skind)
} else {
false
}
}
#[inline]
fn peek_ident(&self, i: usize) -> bool {
if i == 0 {
matches!(self.curr_token.kind, TokenKind::Ident(_))
} else {
matches!(
self.rem_tokens.as_slice().get(i - 1),
Some(Token {
kind: TokenKind::Ident(_),
..
})
)
}
}
#[inline]
fn build_binary_expr(
&mut self,
lhs: ast::Expr,
op: ast::BinaryOp,
rhs: ast::Expr,
) -> ast::Expr {
let span = self.span_mgr.make_surrounding_span(lhs.span, rhs.span);
ast::Expr {
kind: ast::ExprKind::Binary(Box::new(lhs), op, Box::new(rhs)),
span,
}
}
pub fn parse_root_expr(mut self) -> Result<ast::Expr, ParseError> {
let expr = self.parse_expr()?;
if self.eat_eof(true) {
Ok(expr)
} else {
Err(self.report_expected())
}
}
}