change return type of parse_* functions from Option to Result
This commit is contained in:
parent
2bc8281f19
commit
0cccee936e
|
@ -89,9 +89,7 @@ pub fn parse(
|
|||
tracing::info!("Parsing the source code at path: {}", path.display());
|
||||
|
||||
let mut parser = Parser::new(&tokens);
|
||||
let program = parser
|
||||
.parse_program(handler)
|
||||
.ok_or_else(|| Error::other("An error occurred while parsing the source code."))?;
|
||||
let program = parser.parse_program(handler)?;
|
||||
|
||||
if handler.has_received() {
|
||||
return Err(Error::other(
|
||||
|
|
|
@ -3,14 +3,31 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use crate::{
|
||||
base::log::{Message, Severity, SourceCodeDisplay},
|
||||
base::{
|
||||
log::{Message, Severity, SourceCodeDisplay},
|
||||
source_file::Span,
|
||||
},
|
||||
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.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum SyntaxKind {
|
||||
Either(&'static [SyntaxKind]),
|
||||
Punctuation(char),
|
||||
Keyword(KeywordKind),
|
||||
Identifier,
|
||||
|
@ -24,6 +41,43 @@ pub enum SyntaxKind {
|
|||
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.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct UnexpectedSyntax {
|
||||
|
@ -36,19 +90,7 @@ pub struct UnexpectedSyntax {
|
|||
|
||||
impl Display for UnexpectedSyntax {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let expected_binding = match self.expected {
|
||||
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 expected_binding = self.expected.expected_binding_str();
|
||||
let found_binding = match self.found.clone() {
|
||||
Some(Token::Comment(..)) => "a 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 {}
|
||||
|
||||
/// 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),
|
||||
/// An error that occurred due to an invalid argument.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct InvalidArgument {
|
||||
/// The error message.
|
||||
pub message: String,
|
||||
/// 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 {}
|
||||
|
|
|
@ -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.
|
||||
#[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.
|
||||
///
|
||||
/// 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>(
|
||||
&mut self,
|
||||
delimiter: Delimiter,
|
||||
f: impl FnOnce(&mut Self) -> Option<T>,
|
||||
f: impl FnOnce(&mut Self) -> ParseResult<T>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<DelimitedTree<T>> {
|
||||
) -> ParseResult<DelimitedTree<T>> {
|
||||
self.current_frame.stop_at_significant();
|
||||
let raw_token_tree = self
|
||||
.current_frame
|
||||
|
@ -62,7 +65,7 @@ impl<'a> Parser<'a> {
|
|||
delimited_tree
|
||||
}
|
||||
found => {
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Punctuation(expected),
|
||||
found: Some(match found {
|
||||
TokenTree::Token(token) => token.clone(),
|
||||
|
@ -70,18 +73,20 @@ impl<'a> Parser<'a> {
|
|||
Token::Punctuation(delimited_tree.open.clone())
|
||||
}
|
||||
}),
|
||||
}));
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
||||
return None;
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Punctuation(expected),
|
||||
found: self.get_reading(None).into_token(),
|
||||
}));
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
||||
return None;
|
||||
return Err(err);
|
||||
};
|
||||
|
||||
// creates a new frame
|
||||
|
@ -99,7 +104,10 @@ impl<'a> Parser<'a> {
|
|||
let tree = f(self);
|
||||
|
||||
// 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
|
||||
if !self.current_frame.is_exhausted() {
|
||||
|
@ -111,10 +119,12 @@ impl<'a> Parser<'a> {
|
|||
.delimiter
|
||||
.closing_char();
|
||||
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Punctuation(expected),
|
||||
found: self.peek().into_token(),
|
||||
}));
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let close_punctuation = self
|
||||
|
@ -128,7 +138,7 @@ impl<'a> Parser<'a> {
|
|||
// replaces the current frame with the popped one
|
||||
self.current_frame = new_frame;
|
||||
|
||||
Some(DelimitedTree {
|
||||
Ok(DelimitedTree {
|
||||
open,
|
||||
tree,
|
||||
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
|
||||
/// `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 result = f(self);
|
||||
|
||||
if result.is_none() {
|
||||
if result.is_err() {
|
||||
self.current_frame.current_index = current_index;
|
||||
}
|
||||
|
||||
|
@ -157,7 +170,7 @@ pub struct DelimitedTree<T> {
|
|||
pub open: Punctuation,
|
||||
|
||||
/// The tree inside the delimiter.
|
||||
pub tree: Option<T>,
|
||||
pub tree: ParseResult<T>,
|
||||
|
||||
/// The closing delimiter.
|
||||
pub close: Punctuation,
|
||||
|
@ -363,15 +376,19 @@ impl<'a> Frame<'a> {
|
|||
///
|
||||
/// # Errors
|
||||
/// 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() {
|
||||
Reading::Atomic(Token::Identifier(ident)) => Some(ident),
|
||||
Reading::Atomic(Token::Identifier(ident)) => Ok(ident),
|
||||
found => {
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Identifier,
|
||||
found: found.into_token(),
|
||||
}));
|
||||
None
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -380,15 +397,16 @@ impl<'a> Frame<'a> {
|
|||
///
|
||||
/// # Errors
|
||||
/// 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() {
|
||||
Reading::Atomic(Token::Numeric(ident)) => Some(ident),
|
||||
Reading::Atomic(Token::Numeric(ident)) => Ok(ident),
|
||||
found => {
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Numeric,
|
||||
found: found.into_token(),
|
||||
}));
|
||||
None
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -400,15 +418,16 @@ impl<'a> Frame<'a> {
|
|||
pub fn parse_string_literal(
|
||||
&mut self,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<StringLiteral> {
|
||||
) -> ParseResult<StringLiteral> {
|
||||
match self.next_significant_token() {
|
||||
Reading::Atomic(Token::StringLiteral(literal)) => Some(literal),
|
||||
Reading::Atomic(Token::StringLiteral(literal)) => Ok(literal),
|
||||
found => {
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::StringLiteral,
|
||||
found: found.into_token(),
|
||||
}));
|
||||
None
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -421,17 +440,18 @@ impl<'a> Frame<'a> {
|
|||
&mut self,
|
||||
expected: KeywordKind,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<Keyword> {
|
||||
) -> ParseResult<Keyword> {
|
||||
match self.next_significant_token() {
|
||||
Reading::Atomic(Token::Keyword(keyword_token)) if keyword_token.keyword == expected => {
|
||||
Some(keyword_token)
|
||||
Ok(keyword_token)
|
||||
}
|
||||
found => {
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Keyword(expected),
|
||||
found: found.into_token(),
|
||||
}));
|
||||
None
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -445,7 +465,7 @@ impl<'a> Frame<'a> {
|
|||
expected: char,
|
||||
skip_insignificant: bool,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<Punctuation> {
|
||||
) -> ParseResult<Punctuation> {
|
||||
match if skip_insignificant {
|
||||
self.next_significant_token()
|
||||
} else {
|
||||
|
@ -454,14 +474,15 @@ impl<'a> Frame<'a> {
|
|||
Reading::Atomic(Token::Punctuation(punctuation_token))
|
||||
if punctuation_token.punctuation == expected =>
|
||||
{
|
||||
Some(punctuation_token)
|
||||
Ok(punctuation_token)
|
||||
}
|
||||
found => {
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Punctuation(expected),
|
||||
found: found.into_token(),
|
||||
}));
|
||||
None
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! Syntax tree nodes for conditions.
|
||||
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
|
||||
use std::{cmp::Ordering, collections::VecDeque};
|
||||
|
||||
use enum_as_inner::EnumAsInner;
|
||||
|
@ -9,14 +11,14 @@ use crate::{
|
|||
base::{
|
||||
self,
|
||||
source_file::{SourceElement, Span},
|
||||
VoidHandler, Handler,
|
||||
Handler, VoidHandler,
|
||||
},
|
||||
lexical::{
|
||||
token::{Punctuation, StringLiteral, Token},
|
||||
token_stream::Delimiter,
|
||||
},
|
||||
syntax::{
|
||||
error::{Error, SyntaxKind, UnexpectedSyntax},
|
||||
error::{Error, ParseResult, SyntaxKind, UnexpectedSyntax},
|
||||
parser::{Parser, Reading},
|
||||
},
|
||||
};
|
||||
|
@ -241,12 +243,20 @@ impl SourceElement for Condition {
|
|||
|
||||
impl<'a> Parser<'a> {
|
||||
/// 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 expressions = VecDeque::new();
|
||||
|
||||
// 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((
|
||||
binary_operator,
|
||||
Some(Condition::Primary(self.parse_primary_condition(handler)?)),
|
||||
|
@ -300,14 +310,14 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Some(lhs)
|
||||
Ok(lhs)
|
||||
}
|
||||
|
||||
/// Parses a [`PrimaryCondition`].
|
||||
pub fn parse_primary_condition(
|
||||
&mut self,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<PrimaryCondition> {
|
||||
) -> ParseResult<PrimaryCondition> {
|
||||
match self.stop_at_significant() {
|
||||
// prefixed expression
|
||||
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)?);
|
||||
|
||||
Some(PrimaryCondition::Prefix(ConditionalPrefix {
|
||||
Ok(PrimaryCondition::Prefix(ConditionalPrefix {
|
||||
operator,
|
||||
operand,
|
||||
}))
|
||||
|
@ -330,7 +340,7 @@ impl<'a> Parser<'a> {
|
|||
// string literal
|
||||
Reading::Atomic(Token::StringLiteral(literal)) => {
|
||||
self.forward();
|
||||
Some(PrimaryCondition::StringLiteral(literal))
|
||||
Ok(PrimaryCondition::StringLiteral(literal))
|
||||
}
|
||||
|
||||
// parenthesized condition
|
||||
|
@ -342,12 +352,17 @@ impl<'a> Parser<'a> {
|
|||
// make progress
|
||||
self.forward();
|
||||
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Expression,
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Either(&[
|
||||
SyntaxKind::Punctuation('!'),
|
||||
SyntaxKind::StringLiteral,
|
||||
SyntaxKind::Punctuation('('),
|
||||
]),
|
||||
found: unexpected.into_token(),
|
||||
}));
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
||||
None
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -356,38 +371,59 @@ impl<'a> Parser<'a> {
|
|||
pub fn parse_parenthesized_condition(
|
||||
&mut self,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<ParenthesizedCondition> {
|
||||
) -> ParseResult<ParenthesizedCondition> {
|
||||
let token_tree = self.step_into(
|
||||
Delimiter::Parenthesis,
|
||||
|parser| {
|
||||
let cond = parser.parse_condition(handler)?;
|
||||
parser.stop_at_significant();
|
||||
Some(cond)
|
||||
Ok(cond)
|
||||
},
|
||||
handler,
|
||||
)?;
|
||||
|
||||
Some(ParenthesizedCondition {
|
||||
Ok(ParenthesizedCondition {
|
||||
open_paren: token_tree.open,
|
||||
condition: Box::new(token_tree.tree?),
|
||||
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() {
|
||||
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))
|
||||
Ok(ConditionalBinaryOperator::LogicalAnd(punc, b))
|
||||
}
|
||||
'|' => {
|
||||
let b = parser.parse_punctuation('|', false, &VoidHandler)?;
|
||||
Some(ConditionalBinaryOperator::LogicalOr(punc, b))
|
||||
Ok(ConditionalBinaryOperator::LogicalOr(punc, b))
|
||||
}
|
||||
_ => None,
|
||||
_ => Err(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Either(&[
|
||||
SyntaxKind::Punctuation('&'),
|
||||
SyntaxKind::Punctuation('|'),
|
||||
]),
|
||||
found: Some(token),
|
||||
})),
|
||||
},
|
||||
_ => None,
|
||||
unexpected => Err(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Either(&[
|
||||
SyntaxKind::Punctuation('&'),
|
||||
SyntaxKind::Punctuation('|'),
|
||||
]),
|
||||
found: Some(unexpected),
|
||||
})),
|
||||
},
|
||||
unexpected => Err(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Either(&[
|
||||
SyntaxKind::Punctuation('&'),
|
||||
SyntaxKind::Punctuation('|'),
|
||||
]),
|
||||
found: unexpected.into_token(),
|
||||
})),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
token_stream::Delimiter,
|
||||
},
|
||||
syntax::{
|
||||
error::{Error, SyntaxKind, UnexpectedSyntax},
|
||||
error::{Error, ParseResult, SyntaxKind, UnexpectedSyntax},
|
||||
parser::{Parser, Reading},
|
||||
},
|
||||
};
|
||||
|
@ -227,7 +227,15 @@ impl SourceElement for Import {
|
|||
}
|
||||
|
||||
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() {
|
||||
Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => {
|
||||
// eat the pound sign
|
||||
|
@ -239,36 +247,29 @@ impl<'a> Parser<'a> {
|
|||
|parser| {
|
||||
let identifier = parser.parse_identifier(handler)?;
|
||||
|
||||
let value = if let Reading::Atomic(Token::Punctuation(punctuation)) =
|
||||
parser.stop_at_significant()
|
||||
let value = match parser.stop_at_significant() {
|
||||
Reading::Atomic(Token::Punctuation(punc))
|
||||
if punc.punctuation == '=' =>
|
||||
{
|
||||
if punctuation.punctuation == '=' {
|
||||
// eat the equals sign
|
||||
parser.forward();
|
||||
|
||||
// parse the string literal
|
||||
let string_literal = parser
|
||||
.next_significant_token()
|
||||
.into_token()?
|
||||
.into_string_literal()
|
||||
.ok()?;
|
||||
let string_literal = parser.parse_string_literal(handler)?;
|
||||
|
||||
Some((punctuation, string_literal))
|
||||
} else {
|
||||
None
|
||||
Some((punc, string_literal))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Some((identifier, value))
|
||||
Ok((identifier, value))
|
||||
},
|
||||
handler,
|
||||
)?;
|
||||
|
||||
let (identifier, value) = content.tree?;
|
||||
|
||||
Some(Annotation {
|
||||
Ok(Annotation {
|
||||
pound_sign: punctuation,
|
||||
open_bracket: content.open,
|
||||
identifier,
|
||||
|
@ -276,7 +277,14 @@ impl<'a> Parser<'a> {
|
|||
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(
|
||||
&mut self,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<Declaration> {
|
||||
) -> ParseResult<Declaration> {
|
||||
match self.stop_at_significant() {
|
||||
Reading::Atomic(Token::Keyword(function_keyword))
|
||||
if function_keyword.keyword == KeywordKind::Function =>
|
||||
|
@ -293,22 +301,17 @@ impl<'a> Parser<'a> {
|
|||
|
||||
tracing::trace!("Parsed function '{:?}'", function.identifier.span.str());
|
||||
|
||||
Some(Declaration::Function(function))
|
||||
Ok(Declaration::Function(function))
|
||||
}
|
||||
|
||||
Reading::Atomic(Token::Keyword(pub_keyword))
|
||||
if pub_keyword.keyword == KeywordKind::Pub =>
|
||||
{
|
||||
// eat the public keyword
|
||||
self.forward();
|
||||
|
||||
// parse the function keyword
|
||||
let function = self.parse_function(handler)?;
|
||||
|
||||
Some(Declaration::Function(Function {
|
||||
public_keyword: Some(pub_keyword),
|
||||
..function
|
||||
}))
|
||||
tracing::trace!("Parsed function '{:?}'", function.identifier.span.str());
|
||||
|
||||
Ok(Declaration::Function(function))
|
||||
}
|
||||
|
||||
// parse annotations
|
||||
|
@ -316,23 +319,15 @@ impl<'a> Parser<'a> {
|
|||
// parse the annotation
|
||||
let mut annotations = Vec::new();
|
||||
|
||||
while let Some(annotation) =
|
||||
self.try_parse(|parser| parser.parse_annotation(handler))
|
||||
while let Ok(annotation) =
|
||||
self.try_parse(|parser| parser.parse_annotation(&VoidHandler))
|
||||
{
|
||||
annotations.push(annotation);
|
||||
}
|
||||
|
||||
self.parse_declaration(handler).and_then(|declaration| {
|
||||
if let Declaration::Function(mut function) = declaration {
|
||||
self.parse_function(handler).map(|mut function| {
|
||||
function.annotations.extend(annotations);
|
||||
Some(Declaration::Function(function))
|
||||
} else {
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Keyword(KeywordKind::Function),
|
||||
found: None,
|
||||
}));
|
||||
None
|
||||
}
|
||||
Declaration::Function(function)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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)?;
|
||||
|
||||
tracing::trace!("Parsed import from '{:?}'", module.str_content());
|
||||
|
||||
Some(Declaration::Import(Import {
|
||||
Ok(Declaration::Import(Import {
|
||||
from_keyword,
|
||||
module,
|
||||
import_keyword,
|
||||
|
@ -379,12 +374,13 @@ impl<'a> Parser<'a> {
|
|||
semicolon,
|
||||
}))
|
||||
} else {
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Identifier,
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Punctuation('*'),
|
||||
found: self.stop_at_significant().into_token(),
|
||||
}));
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
||||
None
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,18 +388,30 @@ impl<'a> Parser<'a> {
|
|||
// make progress
|
||||
self.forward();
|
||||
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Declaration,
|
||||
found: unexpected.into_token(),
|
||||
}));
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
||||
None
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_function(&mut self, handler: &impl Handler<base::Error>) -> Option<Function> {
|
||||
if let Reading::Atomic(Token::Keyword(function_keyword)) = self.stop_at_significant() {
|
||||
/// Parses a function.
|
||||
///
|
||||
/// # Errors
|
||||
/// - 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));
|
||||
|
||||
match self.stop_at_significant() {
|
||||
Reading::Atomic(Token::Keyword(function_keyword))
|
||||
if function_keyword.keyword == KeywordKind::Function =>
|
||||
{
|
||||
// eat the function keyword
|
||||
self.forward();
|
||||
|
||||
|
@ -419,8 +427,8 @@ impl<'a> Parser<'a> {
|
|||
// parse the block
|
||||
let block = self.parse_block(handler)?;
|
||||
|
||||
Some(Function {
|
||||
public_keyword: None,
|
||||
Ok(Function {
|
||||
public_keyword: pub_keyword.ok(),
|
||||
annotations: Vec::new(),
|
||||
function_keyword,
|
||||
identifier,
|
||||
|
@ -429,12 +437,15 @@ impl<'a> Parser<'a> {
|
|||
close_paren: delimited_tree.close,
|
||||
block,
|
||||
})
|
||||
} else {
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
}
|
||||
unexpected => {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Keyword(KeywordKind::Function),
|
||||
found: self.peek().into_token(),
|
||||
}));
|
||||
None
|
||||
found: unexpected.into_token(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@ use crate::{
|
|||
token_stream::Delimiter,
|
||||
},
|
||||
syntax::{
|
||||
error::{Error, UnexpectedSyntax},
|
||||
self,
|
||||
error::{Error, ParseResult, UnexpectedSyntax},
|
||||
parser::{Parser, Reading},
|
||||
},
|
||||
};
|
||||
|
@ -156,12 +157,22 @@ impl LuaCode {
|
|||
|
||||
impl<'a> Parser<'a> {
|
||||
/// 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`]
|
||||
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() {
|
||||
// identifier expression
|
||||
Reading::Atomic(Token::Identifier(identifier)) => {
|
||||
|
@ -169,8 +180,8 @@ impl<'a> Parser<'a> {
|
|||
self.forward();
|
||||
|
||||
// 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(
|
||||
Delimiter::Parenthesis,
|
||||
',',
|
||||
|
@ -178,15 +189,22 @@ impl<'a> Parser<'a> {
|
|||
handler,
|
||||
)?;
|
||||
|
||||
Some(Primary::FunctionCall(FunctionCall {
|
||||
Ok(Primary::FunctionCall(FunctionCall {
|
||||
identifier,
|
||||
left_parenthesis: token_tree.open,
|
||||
right_parenthesis: token_tree.close,
|
||||
arguments: token_tree.list,
|
||||
}))
|
||||
} else {
|
||||
}
|
||||
unexpected => {
|
||||
// insert parser for regular identifier here
|
||||
None
|
||||
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
|
||||
self.forward();
|
||||
|
||||
Some(Primary::StringLiteral(literal))
|
||||
Ok(Primary::StringLiteral(literal))
|
||||
}
|
||||
|
||||
// lua code expression
|
||||
|
@ -212,9 +230,16 @@ impl<'a> Parser<'a> {
|
|||
|parser| match parser.next_significant_token() {
|
||||
Reading::Atomic(Token::Identifier(identifier)) => {
|
||||
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,
|
||||
)?;
|
||||
|
@ -241,12 +266,12 @@ impl<'a> Parser<'a> {
|
|||
})
|
||||
.expect("Invalid lua code span");
|
||||
|
||||
Some(combined.str().trim().to_owned())
|
||||
Ok(combined.str().trim().to_owned())
|
||||
},
|
||||
handler,
|
||||
)?;
|
||||
|
||||
Some(Primary::Lua(Box::new(LuaCode {
|
||||
Ok(Primary::Lua(Box::new(LuaCode {
|
||||
lua_keyword,
|
||||
left_parenthesis: variables.open,
|
||||
variables: variables.list,
|
||||
|
@ -261,12 +286,13 @@ impl<'a> Parser<'a> {
|
|||
// make progress
|
||||
self.forward();
|
||||
|
||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: crate::syntax::error::SyntaxKind::Expression,
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: syntax::error::SyntaxKind::Expression,
|
||||
found: unexpected.into_token(),
|
||||
}));
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
||||
None
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
syntax::parser::Reading,
|
||||
};
|
||||
|
||||
use super::parser::Parser;
|
||||
use super::{error::ParseResult, parser::Parser};
|
||||
|
||||
pub mod condition;
|
||||
pub mod declaration;
|
||||
|
@ -76,9 +76,9 @@ impl<'a> Parser<'a> {
|
|||
&mut self,
|
||||
delimiter: Delimiter,
|
||||
separator: char,
|
||||
mut f: impl FnMut(&mut Self) -> Option<T>,
|
||||
mut f: impl FnMut(&mut Self) -> ParseResult<T>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<DelimitedList<T>> {
|
||||
) -> ParseResult<DelimitedList<T>> {
|
||||
fn skip_to_next_separator(this: &mut Parser, separator: char) -> Option<Punctuation> {
|
||||
if let Reading::Atomic(Token::Punctuation(punc)) = this.stop_at(|token| {
|
||||
matches!(
|
||||
|
@ -101,7 +101,7 @@ impl<'a> Parser<'a> {
|
|||
let mut trailing_separator: Option<Punctuation> = None;
|
||||
|
||||
while !parser.is_exhausted() {
|
||||
let Some(element) = f(parser) else {
|
||||
let Ok(element) = f(parser) else {
|
||||
skip_to_next_separator(parser, separator);
|
||||
continue;
|
||||
};
|
||||
|
@ -122,7 +122,7 @@ impl<'a> Parser<'a> {
|
|||
|
||||
// expect separator if not exhausted
|
||||
if !parser.is_exhausted() {
|
||||
let Some(separator) = parser.parse_punctuation(separator, true, handler)
|
||||
let Ok(separator) = parser.parse_punctuation(separator, true, handler)
|
||||
else {
|
||||
if let Some(punctuation) = skip_to_next_separator(parser, separator) {
|
||||
trailing_separator = Some(punctuation);
|
||||
|
@ -135,7 +135,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Some(first.map(|first| ConnectedList {
|
||||
Ok(first.map(|first| ConnectedList {
|
||||
first,
|
||||
rest,
|
||||
trailing_separator,
|
||||
|
@ -144,7 +144,7 @@ impl<'a> Parser<'a> {
|
|||
handler,
|
||||
)?;
|
||||
|
||||
Some(DelimitedList {
|
||||
Ok(DelimitedList {
|
||||
open: delimited_tree.open,
|
||||
list: delimited_tree.tree.unwrap(),
|
||||
close: delimited_tree.close,
|
||||
|
@ -162,20 +162,20 @@ impl<'a> Parser<'a> {
|
|||
pub fn parse_connected_list<T>(
|
||||
&mut self,
|
||||
seperator: char,
|
||||
mut f: impl FnMut(&mut Self) -> Option<T>,
|
||||
mut f: impl FnMut(&mut Self) -> ParseResult<T>,
|
||||
_handler: &impl Handler<base::Error>,
|
||||
) -> Option<ConnectedList<T, Punctuation>> {
|
||||
) -> ParseResult<ConnectedList<T, Punctuation>> {
|
||||
let first = f(self)?;
|
||||
|
||||
let mut rest = Vec::new();
|
||||
|
||||
while let Some(sep) =
|
||||
while let Ok(sep) =
|
||||
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));
|
||||
} else {
|
||||
return Some(ConnectedList {
|
||||
return Ok(ConnectedList {
|
||||
first,
|
||||
rest,
|
||||
trailing_separator: Some(sep),
|
||||
|
@ -183,7 +183,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Some(ConnectedList {
|
||||
Ok(ConnectedList {
|
||||
first,
|
||||
rest,
|
||||
trailing_separator: None,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! The program node of the syntax tree.
|
||||
|
||||
use getset::Getters;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
base::{
|
||||
|
@ -11,7 +12,7 @@ use crate::{
|
|||
lexical::token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
||||
syntax::{
|
||||
self,
|
||||
error::{SyntaxKind, UnexpectedSyntax},
|
||||
error::{InvalidArgument, ParseResult, SyntaxKind, UnexpectedSyntax},
|
||||
parser::{Parser, Reading},
|
||||
},
|
||||
};
|
||||
|
@ -70,18 +71,34 @@ impl Namespace {
|
|||
}
|
||||
|
||||
/// 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_-.";
|
||||
|
||||
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> {
|
||||
/// Parses a [`ProgramFile`].
|
||||
#[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");
|
||||
|
||||
let namespace = match self.stop_at_significant() {
|
||||
|
@ -92,23 +109,37 @@ impl<'a> Parser<'a> {
|
|||
self.forward();
|
||||
|
||||
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)?;
|
||||
|
||||
Some(Namespace {
|
||||
Ok(Namespace {
|
||||
namespace_keyword,
|
||||
namespace_name,
|
||||
semicolon,
|
||||
})
|
||||
}
|
||||
unexpected => {
|
||||
handler.receive(syntax::error::Error::from(UnexpectedSyntax {
|
||||
let err = syntax::error::Error::from(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Keyword(KeywordKind::Namespace),
|
||||
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);
|
||||
|
||||
#[allow(clippy::option_if_let_else)]
|
||||
if let Some(x) = result {
|
||||
if let Ok(x) = result {
|
||||
declarations.push(x);
|
||||
} else {
|
||||
self.stop_at(|reading| {
|
||||
|
@ -137,7 +168,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Some(ProgramFile {
|
||||
Ok(ProgramFile {
|
||||
namespace,
|
||||
declarations,
|
||||
})
|
||||
|
|
|
@ -15,7 +15,10 @@ use crate::{
|
|||
token::{CommandLiteral, DocComment, Keyword, KeywordKind, Punctuation, Token},
|
||||
token_stream::Delimiter,
|
||||
},
|
||||
syntax::parser::{Parser, Reading},
|
||||
syntax::{
|
||||
error::ParseResult,
|
||||
parser::{Parser, Reading},
|
||||
},
|
||||
};
|
||||
|
||||
use self::execute_block::ExecuteBlock;
|
||||
|
@ -209,7 +212,10 @@ impl Semicolon {
|
|||
|
||||
impl<'a> Parser<'a> {
|
||||
/// 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(
|
||||
Delimiter::Brace,
|
||||
|parser| {
|
||||
|
@ -217,7 +223,7 @@ impl<'a> Parser<'a> {
|
|||
|
||||
while !parser.is_exhausted() {
|
||||
parser.parse_statement(handler).map_or_else(
|
||||
|| {
|
||||
|_| {
|
||||
// error recovery
|
||||
parser.stop_at(|reading| matches!(
|
||||
reading,
|
||||
|
@ -234,12 +240,12 @@ impl<'a> Parser<'a> {
|
|||
);
|
||||
}
|
||||
|
||||
Some(statements)
|
||||
Ok(statements)
|
||||
},
|
||||
handler,
|
||||
)?;
|
||||
|
||||
Some(Block {
|
||||
Ok(Block {
|
||||
open_brace: token_tree.open,
|
||||
statements: token_tree.tree?,
|
||||
close_brace: token_tree.close,
|
||||
|
@ -248,19 +254,22 @@ impl<'a> Parser<'a> {
|
|||
|
||||
/// Parses a [`Statement`].
|
||||
#[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() {
|
||||
// variable declaration
|
||||
Reading::Atomic(Token::CommandLiteral(command)) => {
|
||||
self.forward();
|
||||
tracing::trace!("Parsed literal command '{}'", command.clean_command());
|
||||
Some(Statement::LiteralCommand(command))
|
||||
Ok(Statement::LiteralCommand(command))
|
||||
}
|
||||
// block statement
|
||||
Reading::IntoDelimited(open_brace) if open_brace.punctuation == '{' => {
|
||||
let block = self.parse_block(handler)?;
|
||||
|
||||
Some(Statement::Block(block))
|
||||
Ok(Statement::Block(block))
|
||||
}
|
||||
|
||||
// execute block
|
||||
|
@ -274,7 +283,7 @@ impl<'a> Parser<'a> {
|
|||
// doc comment
|
||||
Reading::Atomic(Token::DocComment(doc_comment)) => {
|
||||
self.forward();
|
||||
Some(Statement::DocComment(doc_comment))
|
||||
Ok(Statement::DocComment(doc_comment))
|
||||
}
|
||||
|
||||
// grouping statement
|
||||
|
@ -288,7 +297,7 @@ impl<'a> Parser<'a> {
|
|||
|
||||
tracing::trace!("Parsed group command");
|
||||
|
||||
Some(Statement::Grouping(Grouping {
|
||||
Ok(Statement::Grouping(Grouping {
|
||||
group_keyword,
|
||||
block,
|
||||
}))
|
||||
|
@ -306,7 +315,7 @@ impl<'a> Parser<'a> {
|
|||
|
||||
tracing::trace!("Parsed run statement: {:?}", expression);
|
||||
|
||||
Some(Statement::Run(Run {
|
||||
Ok(Statement::Run(Run {
|
||||
run_keyword,
|
||||
expression,
|
||||
semicolon,
|
||||
|
@ -320,7 +329,7 @@ impl<'a> Parser<'a> {
|
|||
|
||||
tracing::trace!("Parsed semicolon statement: {:?}", expression);
|
||||
|
||||
Some(Statement::Semicolon(Semicolon {
|
||||
Ok(Statement::Semicolon(Semicolon {
|
||||
expression,
|
||||
semicolon,
|
||||
}))
|
||||
|
|
|
@ -8,15 +8,14 @@ use crate::{
|
|||
base::{
|
||||
self,
|
||||
source_file::{SourceElement, Span},
|
||||
VoidHandler, Handler,
|
||||
Handler, VoidHandler,
|
||||
},
|
||||
lexical::{
|
||||
token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
||||
token_stream::Delimiter,
|
||||
},
|
||||
syntax::{
|
||||
self,
|
||||
error::{SyntaxKind, UnexpectedSyntax},
|
||||
error::{Error, ParseResult, SyntaxKind, UnexpectedSyntax},
|
||||
parser::{DelimitedTree, Parser, Reading},
|
||||
syntax_tree::condition::ParenthesizedCondition,
|
||||
},
|
||||
|
@ -711,10 +710,14 @@ impl Summon {
|
|||
|
||||
impl<'a> Parser<'a> {
|
||||
/// 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(
|
||||
&mut self,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<ExecuteBlock> {
|
||||
) -> ParseResult<ExecuteBlock> {
|
||||
match self.stop_at_significant() {
|
||||
Reading::Atomic(Token::Keyword(if_keyword))
|
||||
if if_keyword.keyword == KeywordKind::If =>
|
||||
|
@ -739,14 +742,17 @@ impl<'a> Parser<'a> {
|
|||
// eat the else keyword
|
||||
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,
|
||||
Else {
|
||||
else_keyword,
|
||||
|
@ -755,11 +761,11 @@ impl<'a> Parser<'a> {
|
|||
))
|
||||
});
|
||||
|
||||
if let Some((block, else_tail)) = else_tail {
|
||||
Some(ExecuteBlock::IfElse(conditional, block, else_tail))
|
||||
if let Ok((block, else_tail)) = else_tail {
|
||||
Ok(ExecuteBlock::IfElse(conditional, block, else_tail))
|
||||
} else {
|
||||
let tail = self.parse_execute_block_tail(handler)?;
|
||||
Some(ExecuteBlock::HeadTail(
|
||||
Ok(ExecuteBlock::HeadTail(
|
||||
ExecuteBlockHead::Conditional(conditional),
|
||||
tail,
|
||||
))
|
||||
|
@ -777,11 +783,12 @@ impl<'a> Parser<'a> {
|
|||
handler,
|
||||
),
|
||||
unexpected => {
|
||||
handler.receive(syntax::error::Error::from(UnexpectedSyntax {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Punctuation('('),
|
||||
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)?;
|
||||
|
||||
Some(ExecuteBlock::HeadTail(head, tail))
|
||||
Ok(ExecuteBlock::HeadTail(head, tail))
|
||||
}
|
||||
|
||||
// unexpected
|
||||
unexpected => {
|
||||
handler.receive(syntax::error::Error::from(UnexpectedSyntax {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::ExecuteBlock,
|
||||
found: unexpected.into_token(),
|
||||
}));
|
||||
None
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -806,7 +814,7 @@ impl<'a> Parser<'a> {
|
|||
fn parse_execute_block_tail(
|
||||
&mut self,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<ExecuteBlockTail> {
|
||||
) -> ParseResult<ExecuteBlockTail> {
|
||||
match self.stop_at_significant() {
|
||||
// nested execute block
|
||||
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)?;
|
||||
|
||||
Some(ExecuteBlockTail::ExecuteBlock(
|
||||
Ok(ExecuteBlockTail::ExecuteBlock(
|
||||
punc,
|
||||
Box::new(execute_block),
|
||||
))
|
||||
|
@ -825,15 +833,16 @@ impl<'a> Parser<'a> {
|
|||
Reading::IntoDelimited(punc) if punc.punctuation == '{' => {
|
||||
let block = self.parse_block(handler)?;
|
||||
|
||||
Some(ExecuteBlockTail::Block(block))
|
||||
Ok(ExecuteBlockTail::Block(block))
|
||||
}
|
||||
|
||||
unexpected => {
|
||||
handler.receive(syntax::error::Error::from(UnexpectedSyntax {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::ExecuteBlockTail,
|
||||
found: unexpected.into_token(),
|
||||
}));
|
||||
None
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -842,8 +851,8 @@ impl<'a> Parser<'a> {
|
|||
fn head_from_keyword(
|
||||
keyword: Keyword,
|
||||
argument: DelimitedTree<StringLiteral>,
|
||||
) -> Option<ExecuteBlockHead> {
|
||||
Some(match keyword.keyword {
|
||||
) -> ParseResult<ExecuteBlockHead> {
|
||||
Ok(match keyword.keyword {
|
||||
KeywordKind::Align => Align {
|
||||
align_keyword: keyword,
|
||||
open_paren: argument.open,
|
||||
|
|
Loading…
Reference in New Issue