change return type of parse_* functions from Option to Result

This commit is contained in:
Moritz Hölting 2024-09-19 20:54:39 +02:00
parent 2bc8281f19
commit 0cccee936e
10 changed files with 462 additions and 265 deletions

View File

@ -89,9 +89,7 @@ pub fn parse(
tracing::info!("Parsing the source code at path: {}", path.display()); tracing::info!("Parsing the source code at path: {}", path.display());
let mut parser = Parser::new(&tokens); let mut parser = Parser::new(&tokens);
let program = parser let program = parser.parse_program(handler)?;
.parse_program(handler)
.ok_or_else(|| Error::other("An error occurred while parsing the source code."))?;
if handler.has_received() { if handler.has_received() {
return Err(Error::other( return Err(Error::other(

View File

@ -3,14 +3,31 @@
use std::fmt::Display; use std::fmt::Display;
use crate::{ use crate::{
base::log::{Message, Severity, SourceCodeDisplay}, base::{
log::{Message, Severity, SourceCodeDisplay},
source_file::Span,
},
lexical::token::{KeywordKind, Token}, lexical::token::{KeywordKind, Token},
}; };
/// Result type for parsing operations.
pub type ParseResult<T> = Result<T, Error>;
/// An enumeration containing all kinds of syntactic errors that can occur while parsing the
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
UnexpectedSyntax(#[from] UnexpectedSyntax),
#[error(transparent)]
InvalidArgument(#[from] InvalidArgument),
}
/// Enumeration containing all kinds of syntax that can be failed to parse. /// Enumeration containing all kinds of syntax that can be failed to parse.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum SyntaxKind { pub enum SyntaxKind {
Either(&'static [SyntaxKind]),
Punctuation(char), Punctuation(char),
Keyword(KeywordKind), Keyword(KeywordKind),
Identifier, Identifier,
@ -24,6 +41,43 @@ pub enum SyntaxKind {
ExecuteBlockTail, ExecuteBlockTail,
} }
impl SyntaxKind {
fn expected_binding_str(&self) -> String {
match self {
Self::Either(variants) => {
if variants.is_empty() {
"end of file".to_string()
} else if variants.len() == 1 {
variants[0].expected_binding_str()
} else {
let comma_range = ..variants.len() - 2;
let comma_elements = variants[comma_range]
.iter()
.map(Self::expected_binding_str)
.collect::<Vec<_>>()
.join(", ");
format!(
"{}, or {}",
comma_elements,
variants.last().unwrap().expected_binding_str()
)
}
}
Self::Identifier => "an identifier token".to_string(),
Self::Punctuation(char) => format!("a punctuation token `{char}`"),
Self::Keyword(keyword) => format!("a keyword token `{}`", keyword.as_str()),
Self::Declaration => "a declaration token".to_string(),
Self::Numeric => "a numeric token".to_string(),
Self::StringLiteral => "a string literal".to_string(),
Self::Statement => "a statement syntax".to_string(),
Self::Expression => "an expression syntax".to_string(),
Self::Type => "a type syntax".to_string(),
Self::ExecuteBlock => "an execute block syntax".to_string(),
Self::ExecuteBlockTail => "an execute block tail syntax".to_string(),
}
}
}
/// A syntax/token is expected but found an other invalid token. /// A syntax/token is expected but found an other invalid token.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnexpectedSyntax { pub struct UnexpectedSyntax {
@ -36,19 +90,7 @@ pub struct UnexpectedSyntax {
impl Display for UnexpectedSyntax { impl Display for UnexpectedSyntax {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let expected_binding = match self.expected { let expected_binding = self.expected.expected_binding_str();
SyntaxKind::Identifier => "an identifier token".to_string(),
SyntaxKind::Punctuation(char) => format!("a punctuation token `{char}`"),
SyntaxKind::Keyword(keyword) => format!("a keyword token `{}`", keyword.as_str()),
SyntaxKind::Declaration => "a declaration token".to_string(),
SyntaxKind::Numeric => "a numeric token".to_string(),
SyntaxKind::StringLiteral => "a string literal".to_string(),
SyntaxKind::Statement => "a statement syntax".to_string(),
SyntaxKind::Expression => "an expression syntax".to_string(),
SyntaxKind::Type => "a type syntax".to_string(),
SyntaxKind::ExecuteBlock => "an execute block syntax".to_string(),
SyntaxKind::ExecuteBlockTail => "an execute block tail syntax".to_string(),
};
let found_binding = match self.found.clone() { let found_binding = match self.found.clone() {
Some(Token::Comment(..)) => "a comment token".to_string(), Some(Token::Comment(..)) => "a comment token".to_string(),
Some(Token::DocComment(..)) => "a doc comment token".to_string(), Some(Token::DocComment(..)) => "a doc comment token".to_string(),
@ -83,10 +125,24 @@ impl Display for UnexpectedSyntax {
impl std::error::Error for UnexpectedSyntax {} impl std::error::Error for UnexpectedSyntax {}
/// An enumeration containing all kinds of syntactic errors that can occur while parsing the /// An error that occurred due to an invalid argument.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, thiserror::Error)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(missing_docs)] pub struct InvalidArgument {
pub enum Error { /// The error message.
#[error(transparent)] pub message: String,
UnexpectedSyntax(#[from] UnexpectedSyntax), /// The span of the invalid argument.
pub span: Span,
} }
impl Display for InvalidArgument {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", Message::new(Severity::Error, &self.message))?;
write!(
f,
"\n{}",
SourceCodeDisplay::new(&self.span, Option::<u8>::None)
)
}
}
impl std::error::Error for InvalidArgument {}

View File

@ -11,7 +11,7 @@ use crate::{
}, },
}; };
use super::error::{Error, SyntaxKind, UnexpectedSyntax}; use super::error::{Error, ParseResult, SyntaxKind, UnexpectedSyntax};
/// Represents a parser that reads a token stream and constructs an abstract syntax tree. /// Represents a parser that reads a token stream and constructs an abstract syntax tree.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deref, DerefMut)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deref, DerefMut)]
@ -38,12 +38,15 @@ impl<'a> Parser<'a> {
/// Steps into the [`Delimited`] token stream and parses the content within the delimiters. /// Steps into the [`Delimited`] token stream and parses the content within the delimiters.
/// ///
/// The parser's position must be at the delimited token stream. /// The parser's position must be at the delimited token stream.
///
/// # Errors
/// - If the parser's position is not at the delimited token stream.
pub fn step_into<T>( pub fn step_into<T>(
&mut self, &mut self,
delimiter: Delimiter, delimiter: Delimiter,
f: impl FnOnce(&mut Self) -> Option<T>, f: impl FnOnce(&mut Self) -> ParseResult<T>,
handler: &impl Handler<base::Error>, handler: &impl Handler<base::Error>,
) -> Option<DelimitedTree<T>> { ) -> ParseResult<DelimitedTree<T>> {
self.current_frame.stop_at_significant(); self.current_frame.stop_at_significant();
let raw_token_tree = self let raw_token_tree = self
.current_frame .current_frame
@ -62,7 +65,7 @@ impl<'a> Parser<'a> {
delimited_tree delimited_tree
} }
found => { found => {
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Punctuation(expected), expected: SyntaxKind::Punctuation(expected),
found: Some(match found { found: Some(match found {
TokenTree::Token(token) => token.clone(), TokenTree::Token(token) => token.clone(),
@ -70,18 +73,20 @@ impl<'a> Parser<'a> {
Token::Punctuation(delimited_tree.open.clone()) Token::Punctuation(delimited_tree.open.clone())
} }
}), }),
})); });
handler.receive(err.clone());
return None; return Err(err);
} }
} }
} else { } else {
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Punctuation(expected), expected: SyntaxKind::Punctuation(expected),
found: self.get_reading(None).into_token(), found: self.get_reading(None).into_token(),
})); });
handler.receive(err.clone());
return None; return Err(err);
}; };
// creates a new frame // creates a new frame
@ -99,7 +104,10 @@ impl<'a> Parser<'a> {
let tree = f(self); let tree = f(self);
// pops the current frame off the stack // pops the current frame off the stack
let new_frame = self.stack.pop()?; let new_frame = self
.stack
.pop()
.expect("frame has been pushed on the stack before");
// the current frame must be at the end // the current frame must be at the end
if !self.current_frame.is_exhausted() { if !self.current_frame.is_exhausted() {
@ -111,10 +119,12 @@ impl<'a> Parser<'a> {
.delimiter .delimiter
.closing_char(); .closing_char();
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Punctuation(expected), expected: SyntaxKind::Punctuation(expected),
found: self.peek().into_token(), found: self.peek().into_token(),
})); });
handler.receive(err.clone());
return Err(err);
} }
let close_punctuation = self let close_punctuation = self
@ -128,7 +138,7 @@ impl<'a> Parser<'a> {
// replaces the current frame with the popped one // replaces the current frame with the popped one
self.current_frame = new_frame; self.current_frame = new_frame;
Some(DelimitedTree { Ok(DelimitedTree {
open, open,
tree, tree,
close: close_punctuation, close: close_punctuation,
@ -137,12 +147,15 @@ impl<'a> Parser<'a> {
/// Tries to parse the given function, and if it fails, resets the current index to the /// Tries to parse the given function, and if it fails, resets the current index to the
/// `current_index` before the function call. /// `current_index` before the function call.
pub fn try_parse<T>(&mut self, f: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> { ///
/// # Errors
/// - If the given function returns an error.
pub fn try_parse<T>(&mut self, f: impl FnOnce(&mut Self) -> ParseResult<T>) -> ParseResult<T> {
let current_index = self.current_frame.current_index; let current_index = self.current_frame.current_index;
let result = f(self); let result = f(self);
if result.is_none() { if result.is_err() {
self.current_frame.current_index = current_index; self.current_frame.current_index = current_index;
} }
@ -157,7 +170,7 @@ pub struct DelimitedTree<T> {
pub open: Punctuation, pub open: Punctuation,
/// The tree inside the delimiter. /// The tree inside the delimiter.
pub tree: Option<T>, pub tree: ParseResult<T>,
/// The closing delimiter. /// The closing delimiter.
pub close: Punctuation, pub close: Punctuation,
@ -363,15 +376,19 @@ impl<'a> Frame<'a> {
/// ///
/// # Errors /// # Errors
/// If the next [`Token`] is not an [`Identifier`]. /// If the next [`Token`] is not an [`Identifier`].
pub fn parse_identifier(&mut self, handler: &impl Handler<base::Error>) -> Option<Identifier> { pub fn parse_identifier(
&mut self,
handler: &impl Handler<base::Error>,
) -> ParseResult<Identifier> {
match self.next_significant_token() { match self.next_significant_token() {
Reading::Atomic(Token::Identifier(ident)) => Some(ident), Reading::Atomic(Token::Identifier(ident)) => Ok(ident),
found => { found => {
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Identifier, expected: SyntaxKind::Identifier,
found: found.into_token(), found: found.into_token(),
})); });
None handler.receive(err.clone());
Err(err)
} }
} }
} }
@ -380,15 +397,16 @@ impl<'a> Frame<'a> {
/// ///
/// # Errors /// # Errors
/// If the next [`Token`] is not an [`Identifier`]. /// If the next [`Token`] is not an [`Identifier`].
pub fn parse_numeric(&mut self, handler: &impl Handler<Error>) -> Option<Numeric> { pub fn parse_numeric(&mut self, handler: &impl Handler<Error>) -> ParseResult<Numeric> {
match self.next_significant_token() { match self.next_significant_token() {
Reading::Atomic(Token::Numeric(ident)) => Some(ident), Reading::Atomic(Token::Numeric(ident)) => Ok(ident),
found => { found => {
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Numeric, expected: SyntaxKind::Numeric,
found: found.into_token(), found: found.into_token(),
})); });
None handler.receive(err.clone());
Err(err)
} }
} }
} }
@ -400,15 +418,16 @@ impl<'a> Frame<'a> {
pub fn parse_string_literal( pub fn parse_string_literal(
&mut self, &mut self,
handler: &impl Handler<base::Error>, handler: &impl Handler<base::Error>,
) -> Option<StringLiteral> { ) -> ParseResult<StringLiteral> {
match self.next_significant_token() { match self.next_significant_token() {
Reading::Atomic(Token::StringLiteral(literal)) => Some(literal), Reading::Atomic(Token::StringLiteral(literal)) => Ok(literal),
found => { found => {
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::StringLiteral, expected: SyntaxKind::StringLiteral,
found: found.into_token(), found: found.into_token(),
})); });
None handler.receive(err.clone());
Err(err)
} }
} }
} }
@ -421,17 +440,18 @@ impl<'a> Frame<'a> {
&mut self, &mut self,
expected: KeywordKind, expected: KeywordKind,
handler: &impl Handler<base::Error>, handler: &impl Handler<base::Error>,
) -> Option<Keyword> { ) -> ParseResult<Keyword> {
match self.next_significant_token() { match self.next_significant_token() {
Reading::Atomic(Token::Keyword(keyword_token)) if keyword_token.keyword == expected => { Reading::Atomic(Token::Keyword(keyword_token)) if keyword_token.keyword == expected => {
Some(keyword_token) Ok(keyword_token)
} }
found => { found => {
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Keyword(expected), expected: SyntaxKind::Keyword(expected),
found: found.into_token(), found: found.into_token(),
})); });
None handler.receive(err.clone());
Err(err)
} }
} }
} }
@ -445,7 +465,7 @@ impl<'a> Frame<'a> {
expected: char, expected: char,
skip_insignificant: bool, skip_insignificant: bool,
handler: &impl Handler<base::Error>, handler: &impl Handler<base::Error>,
) -> Option<Punctuation> { ) -> ParseResult<Punctuation> {
match if skip_insignificant { match if skip_insignificant {
self.next_significant_token() self.next_significant_token()
} else { } else {
@ -454,14 +474,15 @@ impl<'a> Frame<'a> {
Reading::Atomic(Token::Punctuation(punctuation_token)) Reading::Atomic(Token::Punctuation(punctuation_token))
if punctuation_token.punctuation == expected => if punctuation_token.punctuation == expected =>
{ {
Some(punctuation_token) Ok(punctuation_token)
} }
found => { found => {
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Punctuation(expected), expected: SyntaxKind::Punctuation(expected),
found: found.into_token(), found: found.into_token(),
})); });
None handler.receive(err.clone());
Err(err)
} }
} }
} }

View File

@ -1,5 +1,7 @@
//! Syntax tree nodes for conditions. //! Syntax tree nodes for conditions.
#![allow(clippy::missing_errors_doc)]
use std::{cmp::Ordering, collections::VecDeque}; use std::{cmp::Ordering, collections::VecDeque};
use enum_as_inner::EnumAsInner; use enum_as_inner::EnumAsInner;
@ -9,14 +11,14 @@ use crate::{
base::{ base::{
self, self,
source_file::{SourceElement, Span}, source_file::{SourceElement, Span},
VoidHandler, Handler, Handler, VoidHandler,
}, },
lexical::{ lexical::{
token::{Punctuation, StringLiteral, Token}, token::{Punctuation, StringLiteral, Token},
token_stream::Delimiter, token_stream::Delimiter,
}, },
syntax::{ syntax::{
error::{Error, SyntaxKind, UnexpectedSyntax}, error::{Error, ParseResult, SyntaxKind, UnexpectedSyntax},
parser::{Parser, Reading}, parser::{Parser, Reading},
}, },
}; };
@ -241,12 +243,20 @@ impl SourceElement for Condition {
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
/// Parses a [`Condition`]. /// Parses a [`Condition`].
pub fn parse_condition(&mut self, handler: &impl Handler<base::Error>) -> Option<Condition> { ///
/// # Precedence of the operators
/// 1. `!`
/// 2. `&&`
/// 3. `||`
pub fn parse_condition(
&mut self,
handler: &impl Handler<base::Error>,
) -> ParseResult<Condition> {
let mut lhs = Condition::Primary(self.parse_primary_condition(handler)?); let mut lhs = Condition::Primary(self.parse_primary_condition(handler)?);
let mut expressions = VecDeque::new(); let mut expressions = VecDeque::new();
// Parses a list of binary operators and expressions // Parses a list of binary operators and expressions
while let Some(binary_operator) = self.try_parse_conditional_binary_operator() { while let Ok(binary_operator) = self.try_parse_conditional_binary_operator() {
expressions.push_back(( expressions.push_back((
binary_operator, binary_operator,
Some(Condition::Primary(self.parse_primary_condition(handler)?)), Some(Condition::Primary(self.parse_primary_condition(handler)?)),
@ -300,14 +310,14 @@ impl<'a> Parser<'a> {
} }
} }
Some(lhs) Ok(lhs)
} }
/// Parses a [`PrimaryCondition`]. /// Parses a [`PrimaryCondition`].
pub fn parse_primary_condition( pub fn parse_primary_condition(
&mut self, &mut self,
handler: &impl Handler<base::Error>, handler: &impl Handler<base::Error>,
) -> Option<PrimaryCondition> { ) -> ParseResult<PrimaryCondition> {
match self.stop_at_significant() { match self.stop_at_significant() {
// prefixed expression // prefixed expression
Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == '!' => { Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == '!' => {
@ -321,7 +331,7 @@ impl<'a> Parser<'a> {
let operand = Box::new(self.parse_primary_condition(handler)?); let operand = Box::new(self.parse_primary_condition(handler)?);
Some(PrimaryCondition::Prefix(ConditionalPrefix { Ok(PrimaryCondition::Prefix(ConditionalPrefix {
operator, operator,
operand, operand,
})) }))
@ -330,7 +340,7 @@ impl<'a> Parser<'a> {
// string literal // string literal
Reading::Atomic(Token::StringLiteral(literal)) => { Reading::Atomic(Token::StringLiteral(literal)) => {
self.forward(); self.forward();
Some(PrimaryCondition::StringLiteral(literal)) Ok(PrimaryCondition::StringLiteral(literal))
} }
// parenthesized condition // parenthesized condition
@ -342,12 +352,17 @@ impl<'a> Parser<'a> {
// make progress // make progress
self.forward(); self.forward();
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Expression, expected: SyntaxKind::Either(&[
SyntaxKind::Punctuation('!'),
SyntaxKind::StringLiteral,
SyntaxKind::Punctuation('('),
]),
found: unexpected.into_token(), found: unexpected.into_token(),
})); });
handler.receive(err.clone());
None Err(err)
} }
} }
} }
@ -356,38 +371,59 @@ impl<'a> Parser<'a> {
pub fn parse_parenthesized_condition( pub fn parse_parenthesized_condition(
&mut self, &mut self,
handler: &impl Handler<base::Error>, handler: &impl Handler<base::Error>,
) -> Option<ParenthesizedCondition> { ) -> ParseResult<ParenthesizedCondition> {
let token_tree = self.step_into( let token_tree = self.step_into(
Delimiter::Parenthesis, Delimiter::Parenthesis,
|parser| { |parser| {
let cond = parser.parse_condition(handler)?; let cond = parser.parse_condition(handler)?;
parser.stop_at_significant(); parser.stop_at_significant();
Some(cond) Ok(cond)
}, },
handler, handler,
)?; )?;
Some(ParenthesizedCondition { Ok(ParenthesizedCondition {
open_paren: token_tree.open, open_paren: token_tree.open,
condition: Box::new(token_tree.tree?), condition: Box::new(token_tree.tree?),
close_paren: token_tree.close, close_paren: token_tree.close,
}) })
} }
fn try_parse_conditional_binary_operator(&mut self) -> Option<ConditionalBinaryOperator> { fn try_parse_conditional_binary_operator(&mut self) -> ParseResult<ConditionalBinaryOperator> {
self.try_parse(|parser| match parser.next_significant_token() { self.try_parse(|parser| match parser.next_significant_token() {
Reading::Atomic(Token::Punctuation(punc)) => match punc.punctuation { Reading::Atomic(token) => match token.clone() {
'&' => { Token::Punctuation(punc) => match punc.punctuation {
let b = parser.parse_punctuation('&', false, &VoidHandler)?; '&' => {
Some(ConditionalBinaryOperator::LogicalAnd(punc, b)) let b = parser.parse_punctuation('&', false, &VoidHandler)?;
} Ok(ConditionalBinaryOperator::LogicalAnd(punc, b))
'|' => { }
let b = parser.parse_punctuation('|', false, &VoidHandler)?; '|' => {
Some(ConditionalBinaryOperator::LogicalOr(punc, b)) let b = parser.parse_punctuation('|', false, &VoidHandler)?;
} Ok(ConditionalBinaryOperator::LogicalOr(punc, b))
_ => None, }
_ => Err(Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Either(&[
SyntaxKind::Punctuation('&'),
SyntaxKind::Punctuation('|'),
]),
found: Some(token),
})),
},
unexpected => Err(Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Either(&[
SyntaxKind::Punctuation('&'),
SyntaxKind::Punctuation('|'),
]),
found: Some(unexpected),
})),
}, },
_ => None, unexpected => Err(Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Either(&[
SyntaxKind::Punctuation('&'),
SyntaxKind::Punctuation('|'),
]),
found: unexpected.into_token(),
})),
}) })
} }
} }

View File

@ -15,7 +15,7 @@ use crate::{
token_stream::Delimiter, token_stream::Delimiter,
}, },
syntax::{ syntax::{
error::{Error, SyntaxKind, UnexpectedSyntax}, error::{Error, ParseResult, SyntaxKind, UnexpectedSyntax},
parser::{Parser, Reading}, parser::{Parser, Reading},
}, },
}; };
@ -227,7 +227,15 @@ impl SourceElement for Import {
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
pub fn parse_annotation(&mut self, handler: &impl Handler<base::Error>) -> Option<Annotation> { /// Parses an annotation.
///
/// # Errors
/// - if the parser position is not at an annotation.
/// - if the parsing of the annotation fails
pub fn parse_annotation(
&mut self,
handler: &impl Handler<base::Error>,
) -> ParseResult<Annotation> {
match self.stop_at_significant() { match self.stop_at_significant() {
Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => { Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => {
// eat the pound sign // eat the pound sign
@ -239,36 +247,29 @@ impl<'a> Parser<'a> {
|parser| { |parser| {
let identifier = parser.parse_identifier(handler)?; let identifier = parser.parse_identifier(handler)?;
let value = if let Reading::Atomic(Token::Punctuation(punctuation)) = let value = match parser.stop_at_significant() {
parser.stop_at_significant() Reading::Atomic(Token::Punctuation(punc))
{ if punc.punctuation == '=' =>
if punctuation.punctuation == '=' { {
// eat the equals sign // eat the equals sign
parser.forward(); parser.forward();
// parse the string literal // parse the string literal
let string_literal = parser let string_literal = parser.parse_string_literal(handler)?;
.next_significant_token()
.into_token()?
.into_string_literal()
.ok()?;
Some((punctuation, string_literal)) Some((punc, string_literal))
} else {
None
} }
} else { _ => None,
None
}; };
Some((identifier, value)) Ok((identifier, value))
}, },
handler, handler,
)?; )?;
let (identifier, value) = content.tree?; let (identifier, value) = content.tree?;
Some(Annotation { Ok(Annotation {
pound_sign: punctuation, pound_sign: punctuation,
open_bracket: content.open, open_bracket: content.open,
identifier, identifier,
@ -276,7 +277,14 @@ impl<'a> Parser<'a> {
close_bracket: content.close, close_bracket: content.close,
}) })
} }
_ => None, unexpected => {
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Punctuation('#'),
found: unexpected.into_token(),
});
handler.receive(err.clone());
Err(err)
}
} }
} }
@ -284,7 +292,7 @@ impl<'a> Parser<'a> {
pub fn parse_declaration( pub fn parse_declaration(
&mut self, &mut self,
handler: &impl Handler<base::Error>, handler: &impl Handler<base::Error>,
) -> Option<Declaration> { ) -> ParseResult<Declaration> {
match self.stop_at_significant() { match self.stop_at_significant() {
Reading::Atomic(Token::Keyword(function_keyword)) Reading::Atomic(Token::Keyword(function_keyword))
if function_keyword.keyword == KeywordKind::Function => if function_keyword.keyword == KeywordKind::Function =>
@ -293,22 +301,17 @@ impl<'a> Parser<'a> {
tracing::trace!("Parsed function '{:?}'", function.identifier.span.str()); tracing::trace!("Parsed function '{:?}'", function.identifier.span.str());
Some(Declaration::Function(function)) Ok(Declaration::Function(function))
} }
Reading::Atomic(Token::Keyword(pub_keyword)) Reading::Atomic(Token::Keyword(pub_keyword))
if pub_keyword.keyword == KeywordKind::Pub => if pub_keyword.keyword == KeywordKind::Pub =>
{ {
// eat the public keyword
self.forward();
// parse the function keyword
let function = self.parse_function(handler)?; let function = self.parse_function(handler)?;
Some(Declaration::Function(Function { tracing::trace!("Parsed function '{:?}'", function.identifier.span.str());
public_keyword: Some(pub_keyword),
..function Ok(Declaration::Function(function))
}))
} }
// parse annotations // parse annotations
@ -316,23 +319,15 @@ impl<'a> Parser<'a> {
// parse the annotation // parse the annotation
let mut annotations = Vec::new(); let mut annotations = Vec::new();
while let Some(annotation) = while let Ok(annotation) =
self.try_parse(|parser| parser.parse_annotation(handler)) self.try_parse(|parser| parser.parse_annotation(&VoidHandler))
{ {
annotations.push(annotation); annotations.push(annotation);
} }
self.parse_declaration(handler).and_then(|declaration| { self.parse_function(handler).map(|mut function| {
if let Declaration::Function(mut function) = declaration { function.annotations.extend(annotations);
function.annotations.extend(annotations); Declaration::Function(function)
Some(Declaration::Function(function))
} else {
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Keyword(KeywordKind::Function),
found: None,
}));
None
}
}) })
} }
@ -366,12 +361,12 @@ impl<'a> Parser<'a> {
// } // }
; ;
if let Some(items) = items { if let Ok(items) = items {
let semicolon = self.parse_punctuation(';', true, handler)?; let semicolon = self.parse_punctuation(';', true, handler)?;
tracing::trace!("Parsed import from '{:?}'", module.str_content()); tracing::trace!("Parsed import from '{:?}'", module.str_content());
Some(Declaration::Import(Import { Ok(Declaration::Import(Import {
from_keyword, from_keyword,
module, module,
import_keyword, import_keyword,
@ -379,12 +374,13 @@ impl<'a> Parser<'a> {
semicolon, semicolon,
})) }))
} else { } else {
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Identifier, expected: SyntaxKind::Punctuation('*'),
found: self.stop_at_significant().into_token(), found: self.stop_at_significant().into_token(),
})); });
handler.receive(err.clone());
None Err(err)
} }
} }
@ -392,49 +388,64 @@ impl<'a> Parser<'a> {
// make progress // make progress
self.forward(); self.forward();
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Declaration, expected: SyntaxKind::Declaration,
found: unexpected.into_token(), found: unexpected.into_token(),
})); });
handler.receive(err.clone());
None Err(err)
} }
} }
} }
pub fn parse_function(&mut self, handler: &impl Handler<base::Error>) -> Option<Function> { /// Parses a function.
if let Reading::Atomic(Token::Keyword(function_keyword)) = self.stop_at_significant() { ///
// eat the function keyword /// # Errors
self.forward(); /// - if the parser is not at a function (not at annotation).
/// - if the parsing of the function fails.
pub fn parse_function(&mut self, handler: &impl Handler<base::Error>) -> ParseResult<Function> {
let pub_keyword =
self.try_parse(|parser| parser.parse_keyword(KeywordKind::Pub, &VoidHandler));
// parse the identifier match self.stop_at_significant() {
let identifier = self.parse_identifier(handler)?; Reading::Atomic(Token::Keyword(function_keyword))
let delimited_tree = self.parse_enclosed_list( if function_keyword.keyword == KeywordKind::Function =>
Delimiter::Parenthesis, {
',', // eat the function keyword
|parser: &mut Parser<'_>| parser.parse_identifier(handler), self.forward();
handler,
)?;
// parse the block // parse the identifier
let block = self.parse_block(handler)?; let identifier = self.parse_identifier(handler)?;
let delimited_tree = self.parse_enclosed_list(
Delimiter::Parenthesis,
',',
|parser: &mut Parser<'_>| parser.parse_identifier(handler),
handler,
)?;
Some(Function { // parse the block
public_keyword: None, let block = self.parse_block(handler)?;
annotations: Vec::new(),
function_keyword, Ok(Function {
identifier, public_keyword: pub_keyword.ok(),
open_paren: delimited_tree.open, annotations: Vec::new(),
parameters: delimited_tree.list, function_keyword,
close_paren: delimited_tree.close, identifier,
block, open_paren: delimited_tree.open,
}) parameters: delimited_tree.list,
} else { close_paren: delimited_tree.close,
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { block,
expected: SyntaxKind::Keyword(KeywordKind::Function), })
found: self.peek().into_token(), }
})); unexpected => {
None let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Keyword(KeywordKind::Function),
found: unexpected.into_token(),
});
handler.receive(err.clone());
Err(err)
}
} }
} }
} }

View File

@ -14,7 +14,8 @@ use crate::{
token_stream::Delimiter, token_stream::Delimiter,
}, },
syntax::{ syntax::{
error::{Error, UnexpectedSyntax}, self,
error::{Error, ParseResult, UnexpectedSyntax},
parser::{Parser, Reading}, parser::{Parser, Reading},
}, },
}; };
@ -156,12 +157,22 @@ impl LuaCode {
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
/// Parses an [`Expression`] /// Parses an [`Expression`]
pub fn parse_expression(&mut self, handler: &impl Handler<base::Error>) -> Option<Expression> { ///
Some(Expression::Primary(self.parse_primary(handler)?)) /// # Errors
/// - If the parser is not at a primary expression.
pub fn parse_expression(
&mut self,
handler: &impl Handler<base::Error>,
) -> ParseResult<Expression> {
self.parse_primary(handler).map(Expression::Primary)
} }
/// Parses an [`Primary`] /// Parses an [`Primary`]
pub fn parse_primary(&mut self, handler: &impl Handler<base::Error>) -> Option<Primary> { ///
/// # Errors
/// - If the parser is not at a primary expression.
/// - If the parser is not at a valid primary expression.
pub fn parse_primary(&mut self, handler: &impl Handler<base::Error>) -> ParseResult<Primary> {
match self.stop_at_significant() { match self.stop_at_significant() {
// identifier expression // identifier expression
Reading::Atomic(Token::Identifier(identifier)) => { Reading::Atomic(Token::Identifier(identifier)) => {
@ -169,24 +180,31 @@ impl<'a> Parser<'a> {
self.forward(); self.forward();
// function call // function call
if matches!(self.stop_at_significant(), Reading::IntoDelimited(punc) if punc.punctuation == '(') match self.stop_at_significant() {
{ Reading::IntoDelimited(punc) if punc.punctuation == '(' => {
let token_tree = self.parse_enclosed_list( let token_tree = self.parse_enclosed_list(
Delimiter::Parenthesis, Delimiter::Parenthesis,
',', ',',
|parser| parser.parse_expression(handler).map(Box::new), |parser| parser.parse_expression(handler).map(Box::new),
handler, handler,
)?; )?;
Some(Primary::FunctionCall(FunctionCall { Ok(Primary::FunctionCall(FunctionCall {
identifier, identifier,
left_parenthesis: token_tree.open, left_parenthesis: token_tree.open,
right_parenthesis: token_tree.close, right_parenthesis: token_tree.close,
arguments: token_tree.list, arguments: token_tree.list,
})) }))
} else { }
// insert parser for regular identifier here unexpected => {
None // insert parser for regular identifier here
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: syntax::error::SyntaxKind::Punctuation('('),
found: unexpected.into_token(),
});
handler.receive(err.clone());
Err(err)
}
} }
} }
@ -195,7 +213,7 @@ impl<'a> Parser<'a> {
// eat the string literal // eat the string literal
self.forward(); self.forward();
Some(Primary::StringLiteral(literal)) Ok(Primary::StringLiteral(literal))
} }
// lua code expression // lua code expression
@ -212,9 +230,16 @@ impl<'a> Parser<'a> {
|parser| match parser.next_significant_token() { |parser| match parser.next_significant_token() {
Reading::Atomic(Token::Identifier(identifier)) => { Reading::Atomic(Token::Identifier(identifier)) => {
parser.forward(); parser.forward();
Some(identifier) Ok(identifier)
}
unexpected => {
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: syntax::error::SyntaxKind::Identifier,
found: unexpected.into_token(),
});
handler.receive(err.clone());
Err(err)
} }
_ => None,
}, },
handler, handler,
)?; )?;
@ -241,12 +266,12 @@ impl<'a> Parser<'a> {
}) })
.expect("Invalid lua code span"); .expect("Invalid lua code span");
Some(combined.str().trim().to_owned()) Ok(combined.str().trim().to_owned())
}, },
handler, handler,
)?; )?;
Some(Primary::Lua(Box::new(LuaCode { Ok(Primary::Lua(Box::new(LuaCode {
lua_keyword, lua_keyword,
left_parenthesis: variables.open, left_parenthesis: variables.open,
variables: variables.list, variables: variables.list,
@ -261,12 +286,13 @@ impl<'a> Parser<'a> {
// make progress // make progress
self.forward(); self.forward();
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: crate::syntax::error::SyntaxKind::Expression, expected: syntax::error::SyntaxKind::Expression,
found: unexpected.into_token(), found: unexpected.into_token(),
})); });
handler.receive(err.clone());
None Err(err)
} }
} }
} }

View File

@ -15,7 +15,7 @@ use crate::{
syntax::parser::Reading, syntax::parser::Reading,
}; };
use super::parser::Parser; use super::{error::ParseResult, parser::Parser};
pub mod condition; pub mod condition;
pub mod declaration; pub mod declaration;
@ -76,9 +76,9 @@ impl<'a> Parser<'a> {
&mut self, &mut self,
delimiter: Delimiter, delimiter: Delimiter,
separator: char, separator: char,
mut f: impl FnMut(&mut Self) -> Option<T>, mut f: impl FnMut(&mut Self) -> ParseResult<T>,
handler: &impl Handler<base::Error>, handler: &impl Handler<base::Error>,
) -> Option<DelimitedList<T>> { ) -> ParseResult<DelimitedList<T>> {
fn skip_to_next_separator(this: &mut Parser, separator: char) -> Option<Punctuation> { fn skip_to_next_separator(this: &mut Parser, separator: char) -> Option<Punctuation> {
if let Reading::Atomic(Token::Punctuation(punc)) = this.stop_at(|token| { if let Reading::Atomic(Token::Punctuation(punc)) = this.stop_at(|token| {
matches!( matches!(
@ -101,7 +101,7 @@ impl<'a> Parser<'a> {
let mut trailing_separator: Option<Punctuation> = None; let mut trailing_separator: Option<Punctuation> = None;
while !parser.is_exhausted() { while !parser.is_exhausted() {
let Some(element) = f(parser) else { let Ok(element) = f(parser) else {
skip_to_next_separator(parser, separator); skip_to_next_separator(parser, separator);
continue; continue;
}; };
@ -122,7 +122,7 @@ impl<'a> Parser<'a> {
// expect separator if not exhausted // expect separator if not exhausted
if !parser.is_exhausted() { if !parser.is_exhausted() {
let Some(separator) = parser.parse_punctuation(separator, true, handler) let Ok(separator) = parser.parse_punctuation(separator, true, handler)
else { else {
if let Some(punctuation) = skip_to_next_separator(parser, separator) { if let Some(punctuation) = skip_to_next_separator(parser, separator) {
trailing_separator = Some(punctuation); trailing_separator = Some(punctuation);
@ -135,7 +135,7 @@ impl<'a> Parser<'a> {
} }
} }
Some(first.map(|first| ConnectedList { Ok(first.map(|first| ConnectedList {
first, first,
rest, rest,
trailing_separator, trailing_separator,
@ -144,7 +144,7 @@ impl<'a> Parser<'a> {
handler, handler,
)?; )?;
Some(DelimitedList { Ok(DelimitedList {
open: delimited_tree.open, open: delimited_tree.open,
list: delimited_tree.tree.unwrap(), list: delimited_tree.tree.unwrap(),
close: delimited_tree.close, close: delimited_tree.close,
@ -162,20 +162,20 @@ impl<'a> Parser<'a> {
pub fn parse_connected_list<T>( pub fn parse_connected_list<T>(
&mut self, &mut self,
seperator: char, seperator: char,
mut f: impl FnMut(&mut Self) -> Option<T>, mut f: impl FnMut(&mut Self) -> ParseResult<T>,
_handler: &impl Handler<base::Error>, _handler: &impl Handler<base::Error>,
) -> Option<ConnectedList<T, Punctuation>> { ) -> ParseResult<ConnectedList<T, Punctuation>> {
let first = f(self)?; let first = f(self)?;
let mut rest = Vec::new(); let mut rest = Vec::new();
while let Some(sep) = while let Ok(sep) =
self.try_parse(|parser| parser.parse_punctuation(seperator, true, &VoidHandler)) self.try_parse(|parser| parser.parse_punctuation(seperator, true, &VoidHandler))
{ {
if let Some(element) = self.try_parse(&mut f) { if let Ok(element) = self.try_parse(&mut f) {
rest.push((sep, element)); rest.push((sep, element));
} else { } else {
return Some(ConnectedList { return Ok(ConnectedList {
first, first,
rest, rest,
trailing_separator: Some(sep), trailing_separator: Some(sep),
@ -183,7 +183,7 @@ impl<'a> Parser<'a> {
} }
} }
Some(ConnectedList { Ok(ConnectedList {
first, first,
rest, rest,
trailing_separator: None, trailing_separator: None,

View File

@ -1,6 +1,7 @@
//! The program node of the syntax tree. //! The program node of the syntax tree.
use getset::Getters; use getset::Getters;
use itertools::Itertools;
use crate::{ use crate::{
base::{ base::{
@ -11,7 +12,7 @@ use crate::{
lexical::token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token}, lexical::token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
syntax::{ syntax::{
self, self,
error::{SyntaxKind, UnexpectedSyntax}, error::{InvalidArgument, ParseResult, SyntaxKind, UnexpectedSyntax},
parser::{Parser, Reading}, parser::{Parser, Reading},
}, },
}; };
@ -70,18 +71,34 @@ impl Namespace {
} }
/// Validates a namespace string. /// Validates a namespace string.
#[must_use] ///
pub fn validate_str(namespace: &str) -> bool { /// # Errors
/// - If the namespace contains invalid characters.
pub fn validate_str(namespace: &str) -> Result<(), String> {
const VALID_CHARS: &str = "0123456789abcdefghijklmnopqrstuvwxyz_-."; const VALID_CHARS: &str = "0123456789abcdefghijklmnopqrstuvwxyz_-.";
namespace.chars().all(|c| VALID_CHARS.contains(c)) let invalid_chars = namespace
.chars()
.filter(|c| !VALID_CHARS.contains(*c))
.sorted()
.unique()
.collect::<String>();
if invalid_chars.is_empty() {
Ok(())
} else {
Err(invalid_chars)
}
} }
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
/// Parses a [`ProgramFile`]. /// Parses a [`ProgramFile`].
#[tracing::instrument(level = "debug", skip_all)] #[tracing::instrument(level = "debug", skip_all)]
pub fn parse_program(&mut self, handler: &impl Handler<base::Error>) -> Option<ProgramFile> { pub fn parse_program(
&mut self,
handler: &impl Handler<base::Error>,
) -> ParseResult<ProgramFile> {
tracing::debug!("Parsing program"); tracing::debug!("Parsing program");
let namespace = match self.stop_at_significant() { let namespace = match self.stop_at_significant() {
@ -92,23 +109,37 @@ impl<'a> Parser<'a> {
self.forward(); self.forward();
let namespace_name = self.parse_string_literal(handler).and_then(|name| { let namespace_name = self.parse_string_literal(handler).and_then(|name| {
Namespace::validate_str(name.str_content().as_ref()).then_some(name) Namespace::validate_str(name.str_content().as_ref())
.map(|()| name.clone())
.map_err(|invalid| {
let err = syntax::error::Error::InvalidArgument(InvalidArgument {
message: format!(
"Invalid characters in namespace '{}'. The following characters are not allowed in namespace definitions: '{}'",
name.str_content(),
invalid
),
span: name.span(),
});
handler.receive(err.clone());
err
})
})?; })?;
let semicolon = self.parse_punctuation(';', true, handler)?; let semicolon = self.parse_punctuation(';', true, handler)?;
Some(Namespace { Ok(Namespace {
namespace_keyword, namespace_keyword,
namespace_name, namespace_name,
semicolon, semicolon,
}) })
} }
unexpected => { unexpected => {
handler.receive(syntax::error::Error::from(UnexpectedSyntax { let err = syntax::error::Error::from(UnexpectedSyntax {
expected: SyntaxKind::Keyword(KeywordKind::Namespace), expected: SyntaxKind::Keyword(KeywordKind::Namespace),
found: unexpected.into_token(), found: unexpected.into_token(),
})); });
None handler.receive(err.clone());
Err(err)
} }
}?; }?;
@ -123,7 +154,7 @@ impl<'a> Parser<'a> {
let result = self.parse_declaration(handler); let result = self.parse_declaration(handler);
#[allow(clippy::option_if_let_else)] #[allow(clippy::option_if_let_else)]
if let Some(x) = result { if let Ok(x) = result {
declarations.push(x); declarations.push(x);
} else { } else {
self.stop_at(|reading| { self.stop_at(|reading| {
@ -137,7 +168,7 @@ impl<'a> Parser<'a> {
} }
} }
Some(ProgramFile { Ok(ProgramFile {
namespace, namespace,
declarations, declarations,
}) })

View File

@ -15,7 +15,10 @@ use crate::{
token::{CommandLiteral, DocComment, Keyword, KeywordKind, Punctuation, Token}, token::{CommandLiteral, DocComment, Keyword, KeywordKind, Punctuation, Token},
token_stream::Delimiter, token_stream::Delimiter,
}, },
syntax::parser::{Parser, Reading}, syntax::{
error::ParseResult,
parser::{Parser, Reading},
},
}; };
use self::execute_block::ExecuteBlock; use self::execute_block::ExecuteBlock;
@ -209,7 +212,10 @@ impl Semicolon {
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
/// Parses a [`Block`]. /// Parses a [`Block`].
pub fn parse_block(&mut self, handler: &impl Handler<base::Error>) -> Option<Block> { ///
/// # Errors
/// - if the parser is not at a block.
pub fn parse_block(&mut self, handler: &impl Handler<base::Error>) -> ParseResult<Block> {
let token_tree = self.step_into( let token_tree = self.step_into(
Delimiter::Brace, Delimiter::Brace,
|parser| { |parser| {
@ -217,7 +223,7 @@ impl<'a> Parser<'a> {
while !parser.is_exhausted() { while !parser.is_exhausted() {
parser.parse_statement(handler).map_or_else( parser.parse_statement(handler).map_or_else(
|| { |_| {
// error recovery // error recovery
parser.stop_at(|reading| matches!( parser.stop_at(|reading| matches!(
reading, reading,
@ -234,12 +240,12 @@ impl<'a> Parser<'a> {
); );
} }
Some(statements) Ok(statements)
}, },
handler, handler,
)?; )?;
Some(Block { Ok(Block {
open_brace: token_tree.open, open_brace: token_tree.open,
statements: token_tree.tree?, statements: token_tree.tree?,
close_brace: token_tree.close, close_brace: token_tree.close,
@ -248,19 +254,22 @@ impl<'a> Parser<'a> {
/// Parses a [`Statement`]. /// Parses a [`Statement`].
#[tracing::instrument(level = "trace", skip_all)] #[tracing::instrument(level = "trace", skip_all)]
pub fn parse_statement(&mut self, handler: &impl Handler<base::Error>) -> Option<Statement> { pub fn parse_statement(
&mut self,
handler: &impl Handler<base::Error>,
) -> ParseResult<Statement> {
match self.stop_at_significant() { match self.stop_at_significant() {
// variable declaration // variable declaration
Reading::Atomic(Token::CommandLiteral(command)) => { Reading::Atomic(Token::CommandLiteral(command)) => {
self.forward(); self.forward();
tracing::trace!("Parsed literal command '{}'", command.clean_command()); tracing::trace!("Parsed literal command '{}'", command.clean_command());
Some(Statement::LiteralCommand(command)) Ok(Statement::LiteralCommand(command))
} }
// block statement // block statement
Reading::IntoDelimited(open_brace) if open_brace.punctuation == '{' => { Reading::IntoDelimited(open_brace) if open_brace.punctuation == '{' => {
let block = self.parse_block(handler)?; let block = self.parse_block(handler)?;
Some(Statement::Block(block)) Ok(Statement::Block(block))
} }
// execute block // execute block
@ -274,7 +283,7 @@ impl<'a> Parser<'a> {
// doc comment // doc comment
Reading::Atomic(Token::DocComment(doc_comment)) => { Reading::Atomic(Token::DocComment(doc_comment)) => {
self.forward(); self.forward();
Some(Statement::DocComment(doc_comment)) Ok(Statement::DocComment(doc_comment))
} }
// grouping statement // grouping statement
@ -288,7 +297,7 @@ impl<'a> Parser<'a> {
tracing::trace!("Parsed group command"); tracing::trace!("Parsed group command");
Some(Statement::Grouping(Grouping { Ok(Statement::Grouping(Grouping {
group_keyword, group_keyword,
block, block,
})) }))
@ -306,7 +315,7 @@ impl<'a> Parser<'a> {
tracing::trace!("Parsed run statement: {:?}", expression); tracing::trace!("Parsed run statement: {:?}", expression);
Some(Statement::Run(Run { Ok(Statement::Run(Run {
run_keyword, run_keyword,
expression, expression,
semicolon, semicolon,
@ -320,7 +329,7 @@ impl<'a> Parser<'a> {
tracing::trace!("Parsed semicolon statement: {:?}", expression); tracing::trace!("Parsed semicolon statement: {:?}", expression);
Some(Statement::Semicolon(Semicolon { Ok(Statement::Semicolon(Semicolon {
expression, expression,
semicolon, semicolon,
})) }))

View File

@ -8,15 +8,14 @@ use crate::{
base::{ base::{
self, self,
source_file::{SourceElement, Span}, source_file::{SourceElement, Span},
VoidHandler, Handler, Handler, VoidHandler,
}, },
lexical::{ lexical::{
token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token}, token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
token_stream::Delimiter, token_stream::Delimiter,
}, },
syntax::{ syntax::{
self, error::{Error, ParseResult, SyntaxKind, UnexpectedSyntax},
error::{SyntaxKind, UnexpectedSyntax},
parser::{DelimitedTree, Parser, Reading}, parser::{DelimitedTree, Parser, Reading},
syntax_tree::condition::ParenthesizedCondition, syntax_tree::condition::ParenthesizedCondition,
}, },
@ -711,10 +710,14 @@ impl Summon {
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
/// Parses an [`ExecuteBlock`]. /// Parses an [`ExecuteBlock`].
///
/// # Errors
/// - if not at the start of an execute block statement.
/// - if the parsing of the execute block statement fails.
pub fn parse_execute_block_statement( pub fn parse_execute_block_statement(
&mut self, &mut self,
handler: &impl Handler<base::Error>, handler: &impl Handler<base::Error>,
) -> Option<ExecuteBlock> { ) -> ParseResult<ExecuteBlock> {
match self.stop_at_significant() { match self.stop_at_significant() {
Reading::Atomic(Token::Keyword(if_keyword)) Reading::Atomic(Token::Keyword(if_keyword))
if if_keyword.keyword == KeywordKind::If => if if_keyword.keyword == KeywordKind::If =>
@ -739,14 +742,17 @@ impl<'a> Parser<'a> {
// eat the else keyword // eat the else keyword
parser.forward(); parser.forward();
let else_block = parser.parse_block(handler)?; let else_block = parser.parse_block(&VoidHandler)?;
Some((else_keyword, else_block)) Ok((else_keyword, else_block))
} }
_ => None, unexpected => Err(UnexpectedSyntax {
expected: SyntaxKind::Keyword(KeywordKind::Else),
found: unexpected.into_token(),
}),
}?; }?;
Some(( Ok((
block, block,
Else { Else {
else_keyword, else_keyword,
@ -755,11 +761,11 @@ impl<'a> Parser<'a> {
)) ))
}); });
if let Some((block, else_tail)) = else_tail { if let Ok((block, else_tail)) = else_tail {
Some(ExecuteBlock::IfElse(conditional, block, else_tail)) Ok(ExecuteBlock::IfElse(conditional, block, else_tail))
} else { } else {
let tail = self.parse_execute_block_tail(handler)?; let tail = self.parse_execute_block_tail(handler)?;
Some(ExecuteBlock::HeadTail( Ok(ExecuteBlock::HeadTail(
ExecuteBlockHead::Conditional(conditional), ExecuteBlockHead::Conditional(conditional),
tail, tail,
)) ))
@ -777,11 +783,12 @@ impl<'a> Parser<'a> {
handler, handler,
), ),
unexpected => { unexpected => {
handler.receive(syntax::error::Error::from(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Punctuation('('), expected: SyntaxKind::Punctuation('('),
found: unexpected.into_token(), found: unexpected.into_token(),
})); });
None handler.receive(err.clone());
Err(err)
} }
}?; }?;
@ -789,16 +796,17 @@ impl<'a> Parser<'a> {
let head = head_from_keyword(keyword, argument)?; let head = head_from_keyword(keyword, argument)?;
Some(ExecuteBlock::HeadTail(head, tail)) Ok(ExecuteBlock::HeadTail(head, tail))
} }
// unexpected // unexpected
unexpected => { unexpected => {
handler.receive(syntax::error::Error::from(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::ExecuteBlock, expected: SyntaxKind::ExecuteBlock,
found: unexpected.into_token(), found: unexpected.into_token(),
})); });
None handler.receive(err.clone());
Err(err)
} }
} }
} }
@ -806,7 +814,7 @@ impl<'a> Parser<'a> {
fn parse_execute_block_tail( fn parse_execute_block_tail(
&mut self, &mut self,
handler: &impl Handler<base::Error>, handler: &impl Handler<base::Error>,
) -> Option<ExecuteBlockTail> { ) -> ParseResult<ExecuteBlockTail> {
match self.stop_at_significant() { match self.stop_at_significant() {
// nested execute block // nested execute block
Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == ',' => { Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == ',' => {
@ -815,7 +823,7 @@ impl<'a> Parser<'a> {
let execute_block = self.parse_execute_block_statement(handler)?; let execute_block = self.parse_execute_block_statement(handler)?;
Some(ExecuteBlockTail::ExecuteBlock( Ok(ExecuteBlockTail::ExecuteBlock(
punc, punc,
Box::new(execute_block), Box::new(execute_block),
)) ))
@ -825,15 +833,16 @@ impl<'a> Parser<'a> {
Reading::IntoDelimited(punc) if punc.punctuation == '{' => { Reading::IntoDelimited(punc) if punc.punctuation == '{' => {
let block = self.parse_block(handler)?; let block = self.parse_block(handler)?;
Some(ExecuteBlockTail::Block(block)) Ok(ExecuteBlockTail::Block(block))
} }
unexpected => { unexpected => {
handler.receive(syntax::error::Error::from(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::ExecuteBlockTail, expected: SyntaxKind::ExecuteBlockTail,
found: unexpected.into_token(), found: unexpected.into_token(),
})); });
None handler.receive(err.clone());
Err(err)
} }
} }
} }
@ -842,8 +851,8 @@ impl<'a> Parser<'a> {
fn head_from_keyword( fn head_from_keyword(
keyword: Keyword, keyword: Keyword,
argument: DelimitedTree<StringLiteral>, argument: DelimitedTree<StringLiteral>,
) -> Option<ExecuteBlockHead> { ) -> ParseResult<ExecuteBlockHead> {
Some(match keyword.keyword { Ok(match keyword.keyword {
KeywordKind::Align => Align { KeywordKind::Align => Align {
align_keyword: keyword, align_keyword: keyword,
open_paren: argument.open, open_paren: argument.open,