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());
|
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(
|
||||||
|
|
|
@ -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 {}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)?;
|
let b = parser.parse_punctuation('&', false, &VoidHandler)?;
|
||||||
Some(ConditionalBinaryOperator::LogicalAnd(punc, b))
|
Ok(ConditionalBinaryOperator::LogicalAnd(punc, b))
|
||||||
}
|
}
|
||||||
'|' => {
|
'|' => {
|
||||||
let b = parser.parse_punctuation('|', false, &VoidHandler)?;
|
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,
|
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);
|
||||||
Some(Declaration::Function(function))
|
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,18 +388,30 @@ 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() {
|
///
|
||||||
|
/// # 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
|
// eat the function keyword
|
||||||
self.forward();
|
self.forward();
|
||||||
|
|
||||||
|
@ -419,8 +427,8 @@ impl<'a> Parser<'a> {
|
||||||
// parse the block
|
// parse the block
|
||||||
let block = self.parse_block(handler)?;
|
let block = self.parse_block(handler)?;
|
||||||
|
|
||||||
Some(Function {
|
Ok(Function {
|
||||||
public_keyword: None,
|
public_keyword: pub_keyword.ok(),
|
||||||
annotations: Vec::new(),
|
annotations: Vec::new(),
|
||||||
function_keyword,
|
function_keyword,
|
||||||
identifier,
|
identifier,
|
||||||
|
@ -429,12 +437,15 @@ impl<'a> Parser<'a> {
|
||||||
close_paren: delimited_tree.close,
|
close_paren: delimited_tree.close,
|
||||||
block,
|
block,
|
||||||
})
|
})
|
||||||
} else {
|
}
|
||||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
unexpected => {
|
||||||
|
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||||
expected: SyntaxKind::Keyword(KeywordKind::Function),
|
expected: SyntaxKind::Keyword(KeywordKind::Function),
|
||||||
found: self.peek().into_token(),
|
found: unexpected.into_token(),
|
||||||
}));
|
});
|
||||||
None
|
handler.receive(err.clone());
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,8 +180,8 @@ 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,
|
||||||
',',
|
',',
|
||||||
|
@ -178,15 +189,22 @@ impl<'a> Parser<'a> {
|
||||||
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 {
|
}
|
||||||
|
unexpected => {
|
||||||
// insert parser for regular identifier here
|
// 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
|
// 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
|
|
|
@ -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,
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue