mod expr;
mod item;
mod stmt;
mod ty;
use crate::ast::Program;
use crate::error::{FixIt, SyntaxError, V0001, V0002};
use crate::span::{FileId, Span};
use crate::token::{Token, TokenKind};
pub fn parse(tokens: &[Token], file: FileId) -> (Program, Vec<SyntaxError>) {
let mut parser = Parser::new(tokens, file);
let program = parser.parse_program();
(program, parser.errors().to_vec())
}
pub(crate) struct Parser<'a> {
tokens: &'a [Token],
pos: usize,
file: FileId,
errors: Vec<SyntaxError>,
in_condition: bool,
}
impl<'a> Parser<'a> {
pub(crate) fn new(tokens: &'a [Token], file: FileId) -> Self {
Self {
tokens,
pos: 0,
file,
errors: Vec::new(),
in_condition: false,
}
}
pub(crate) fn errors(&self) -> &[SyntaxError] {
&self.errors
}
fn peek(&self) -> Option<&TokenKind> {
self.tokens.get(self.pos).map(|t| &t.kind)
}
fn peek_at(&self, offset: usize) -> Option<&TokenKind> {
self.tokens.get(self.pos + offset).map(|t| &t.kind)
}
fn peek_token(&self) -> Option<&Token> {
self.tokens.get(self.pos)
}
fn bump(&mut self) -> Option<Token> {
let token = self.tokens.get(self.pos).cloned();
if token.is_some() {
self.pos += 1;
}
token
}
fn eof_span(&self) -> Span {
let end = self.tokens.last().map(|t| t.span.end).unwrap_or(0);
Span::new(self.file, end, end)
}
fn current_span(&self) -> Span {
self.peek_token()
.map(|t| t.span)
.unwrap_or_else(|| self.eof_span())
}
fn span_from(&self, from: Span, to: Span) -> Span {
Span::new(self.file, from.start, to.end)
}
fn bump_if(&mut self, kind: &TokenKind) -> bool {
if self.peek() == Some(kind) {
self.bump();
true
} else {
false
}
}
fn without_condition<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
let was_in_condition = self.in_condition;
self.in_condition = false;
let result = f(self);
self.in_condition = was_in_condition;
result
}
fn push_error(&mut self, code: &'static str, span: Span, message: impl Into<String>) {
self.errors.push(SyntaxError::new(span, code, message));
}
fn push_error_with_fix_it(
&mut self,
code: &'static str,
span: Span,
message: impl Into<String>,
fix_it: FixIt,
) {
self.errors
.push(SyntaxError::new(span, code, message).with_fix_it(fix_it));
}
fn expect(&mut self, expected: TokenKind, what: &str) -> Result<Token, ()> {
if self.peek() == Some(&expected) {
Ok(self.bump().expect("peek just confirmed a token is present"))
} else {
let span = self.current_span();
self.push_error(V0002, span, format!("expected {what}"));
Err(())
}
}
fn expect_identifier(&mut self, what: &str) -> Result<crate::ast::Ident, ()> {
match self.peek() {
Some(TokenKind::ReservedKeyword(word)) => {
let message =
format!("`{word}` is a Rust keyword and cannot be used as a name in Varyk");
let span = self.current_span();
self.bump();
self.push_error(V0001, span, message);
Err(())
}
Some(TokenKind::Identifier(_)) => {
let token = self.bump().expect("peek just confirmed a token is present");
let name = match token.kind {
TokenKind::Identifier(name) => name,
_ => unreachable!("matched above"),
};
Ok(crate::ast::Ident {
name,
span: token.span,
})
}
_ => {
let span = self.current_span();
self.push_error(V0002, span, format!("expected {what}"));
Err(())
}
}
}
fn expect_name_identifier(&mut self, what: &str) -> Result<crate::ast::Ident, ()> {
let ident = self.expect_identifier(what)?;
if ident.name == "_" {
self.push_error(V0002, ident.span, "`_` cannot be used as a name here");
return Err(());
}
Ok(ident)
}
}