//! Contains the syntax tree nodes that represent the structure of the source code. use derive_more::derive::From; use expression::Expression; use getset::Getters; use strum::EnumIs; use crate::{ base::{ self, source_file::{SourceElement, Span}, Handler, VoidHandler, }, lexical::{ token::{Identifier, Punctuation, StringLiteral, Token}, token_stream::Delimiter, }, syntax::{parser::Reading, syntax_tree::expression::TemplateStringLiteral}, }; use super::{ error::{ParseResult, SyntaxKind, UnexpectedSyntax}, parser::Parser, }; pub mod declaration; pub mod expression; pub mod program; pub mod statement; /// Represents a syntax tree node with a pattern of syntax tree nodes separated by a separator. /// /// This struct is useful for representing syntax tree nodes that are separated by a separator. /// For example, a comma separated list of expressions such as `1, 2, 3` can be represented by a /// [`ConnectedList`] with the separator being a comma token and the elements being the expressions. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] pub struct ConnectedList { /// The first element of the list. #[get = "pub"] first: Element, /// The rest of the elements of the list. /// /// Each element of the list is a tuple containing the separator and the element. The separator /// is the token/syntax tree node that separates the current element from the prior one. #[get = "pub"] rest: Vec<(Separator, Element)>, /// The trailing separator of the list. #[get = "pub"] trailing_separator: Option, } /// Represents a syntax tree node with a pattern of having [`ConnectedList`] delimited by a pair of /// punctuation like such `(a, b, c)`. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DelimitedList { /// The open punctuation of the list. pub open: Punctuation, /// The list of elements of the list. /// /// If `None` then the list is empty (or immediately closed after the open punctuation). pub list: Option>, /// The close punctuation of the list. pub close: Punctuation, } /// Represents a syntax tree node that can be either a string literal or a template string literal. /// /// Syntax Synopsis: /// ```ebnf /// AnyStringLiteral: StringLiteral | TemplateStringLiteral ; /// ``` #[allow(missing_docs)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, From)] pub enum AnyStringLiteral { StringLiteral(StringLiteral), TemplateStringLiteral(TemplateStringLiteral), } impl SourceElement for AnyStringLiteral { fn span(&self) -> Span { match self { Self::StringLiteral(string_literal) => string_literal.span(), Self::TemplateStringLiteral(template_string_literal) => template_string_literal.span(), } } } /// Represents an Annotation with optional value. /// /// Syntax Synopsis: /// /// ```ebnf /// Annotation: /// '#[' AnnotationAssignment ']' /// ; /// ``` #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] pub struct Annotation { /// The pound sign of the annotation. #[get = "pub"] pound_sign: Punctuation, /// The open bracket of the annotation. #[get = "pub"] open_bracket: Punctuation, /// The assignment inside the annotation. #[get = "pub"] assignment: AnnotationAssignment, /// The close bracket of the annotation. #[get = "pub"] close_bracket: Punctuation, } impl Annotation { /// Dissolves the [`Annotation`] into its components. #[must_use] pub fn dissolve(self) -> (Punctuation, Punctuation, AnnotationAssignment, Punctuation) { ( self.pound_sign, self.open_bracket, self.assignment, self.close_bracket, ) } /// Checks if the annotation has the given identifier. #[must_use] pub fn has_identifier(&self, identifier: &str) -> bool { self.assignment.identifier.span().str() == identifier } } impl SourceElement for Annotation { fn span(&self) -> Span { self.pound_sign .span .join(&self.close_bracket.span()) .unwrap() } } /// Represents a value of an annotation. /// /// Syntax Synopsis: /// /// ```ebnf /// AnnotationValue: /// '=' Expression /// | '(' AnnotationAssignment ( ',' AnnotationAssignment )* ')' /// ; /// ``` #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumIs)] pub enum AnnotationValue { /// A single value assignment. /// /// '=' Expression Single { /// The equal sign of the assignment. equal_sign: Punctuation, /// The value of the assignment. value: Expression, }, /// A multiple value assignment. /// /// '(' [`AnnotationAssignment`] ( ',' [`AnnotationAssignment`] )* ')' Multiple { /// The opening parenthesis of the assignment. opening_parenthesis: Punctuation, /// The list of assignments. list: Box>, /// The closing parenthesis of the assignment. closing_parenthesis: Punctuation, }, } impl SourceElement for AnnotationValue { fn span(&self) -> Span { match self { Self::Single { equal_sign, value } => equal_sign.span().join(&value.span()).unwrap(), Self::Multiple { opening_parenthesis, closing_parenthesis, .. } => opening_parenthesis .span() .join(&closing_parenthesis.span()) .unwrap(), } } } /// Represents an assignment inside an annotation. /// /// Syntax Synopsis: /// /// ```ebnf /// AnnotationAssignment: /// Identifier AnnotationValue /// ; /// ``` #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] pub struct AnnotationAssignment { /// The identifier of the assignment. pub identifier: Identifier, /// The value of the assignment. pub value: Option, } impl SourceElement for AnnotationAssignment { fn span(&self) -> Span { self.identifier .span() .join( &self .value .as_ref() .map_or_else(|| self.identifier.span(), AnnotationValue::span), ) .unwrap() } } impl Parser<'_> { /// Parses a list of elements enclosed by a pair of delimiters, separated by a separator. /// /// The parser position must be at the delimited list of the given delimiter. It will /// consume the whole delimited list and move the next token after the list. /// /// # Errors /// - if the parser position is not at the delimited list of the given delimiter. /// - any error returned by the given parser function. pub fn parse_enclosed_list( &mut self, delimiter: Delimiter, separator: char, mut f: impl FnMut(&mut Self) -> ParseResult, handler: &impl Handler, ) -> ParseResult> { fn skip_to_next_separator(this: &mut Parser, separator: char) -> Option { if let Reading::Atomic(Token::Punctuation(punc)) = this.stop_at(|token| { matches!( token, Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == separator ) }) { this.forward(); Some(punc) } else { None } } let delimited_tree = self.step_into( delimiter, |parser| { let mut first = None; let mut rest = Vec::new(); let mut trailing_separator: Option = None; while !parser.is_exhausted() { let Ok(element) = f(parser) else { skip_to_next_separator(parser, separator); continue; }; // adds new element match (&first, &trailing_separator) { (None, None) => { first = Some(element); } (Some(_), Some(separator)) => { rest.push((separator.clone(), element)); trailing_separator = None; } _ => { unreachable!() } } // expect separator if not exhausted if !parser.is_exhausted() { let Ok(separator) = parser.parse_punctuation(separator, true, handler) else { if let Some(punctuation) = skip_to_next_separator(parser, separator) { trailing_separator = Some(punctuation); } continue; }; trailing_separator = Some(separator); } } Ok(first.map(|first| ConnectedList { first, rest, trailing_separator, })) }, handler, )?; Ok(DelimitedList { open: delimited_tree.open, list: delimited_tree.tree.unwrap(), close: delimited_tree.close, }) } /// Parses a list of elements separated by a separator. /// /// The parser position must be at the connected list of the first element. It will /// consume the whole connected list and move the next token after the list. /// /// # Errors /// - if the parser position is not at the connected list of the given element. /// - any error returned by the given parser function. pub fn parse_connected_list( &mut self, seperator: char, mut f: impl FnMut(&mut Self) -> ParseResult, _handler: &impl Handler, ) -> ParseResult> { let first = f(self)?; let mut rest = Vec::new(); while let Ok(sep) = self.try_parse(|parser| parser.parse_punctuation(seperator, true, &VoidHandler)) { if let Ok(element) = self.try_parse(&mut f) { rest.push((sep, element)); } else { return Ok(ConnectedList { first, rest, trailing_separator: Some(sep), }); } } Ok(ConnectedList { first, rest, trailing_separator: None, }) } /// 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, ) -> ParseResult { match self.stop_at_significant() { Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => { // eat the pound sign self.forward(); // step into the brackets let content = self.step_into( Delimiter::Bracket, |parser| parser.parse_annotation_assignment(handler), handler, )?; Ok(Annotation { pound_sign: punctuation, open_bracket: content.open, assignment: content.tree?, close_bracket: content.close, }) } unexpected => { let err = super::error::Error::UnexpectedSyntax(UnexpectedSyntax { expected: SyntaxKind::Punctuation('#'), found: unexpected.into_token(), }); handler.receive(Box::new(err.clone())); Err(err) } } } fn parse_annotation_assignment( &mut self, handler: &impl Handler, ) -> ParseResult { let identifier = self.parse_identifier(handler)?; match self.stop_at_significant() { Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == '=' => { // eat the equals sign self.forward(); let value = self.parse_expression(handler)?; Ok(AnnotationAssignment { identifier, value: Some(AnnotationValue::Single { equal_sign: punc, value, }), }) } Reading::IntoDelimited(delim) if delim.punctuation == '(' => { let tree = self.step_into( Delimiter::Parenthesis, |p| { p.parse_connected_list( ',', |pp| pp.parse_annotation_assignment(handler), handler, ) }, handler, )?; Ok(AnnotationAssignment { identifier, value: Some(AnnotationValue::Multiple { opening_parenthesis: tree.open, list: Box::new(tree.tree?), closing_parenthesis: tree.close, }), }) } _ => Ok(AnnotationAssignment { identifier, value: None, }), } } } impl SourceElement for ConnectedList { fn span(&self) -> Span { let end = self.trailing_separator.as_ref().map_or_else( || { self.rest .last() .map_or_else(|| self.first.span(), |(_, element)| element.span()) }, SourceElement::span, ); self.first.span().join(&end).unwrap() } } impl ConnectedList { /// Returns an iterator over the elements of the list. pub fn elements(&self) -> impl Iterator { std::iter::once(&self.first).chain(self.rest.iter().map(|(_, element)| element)) } /// Returns an iterator over the elements of the list. pub fn into_elements(self) -> impl Iterator { std::iter::once(self.first).chain(self.rest.into_iter().map(|(_, element)| element)) } /// Gets the number of elements in the list. pub fn len(&self) -> usize { self.rest.len() + 1 } /// Returns `true` if the list is empty. /// /// The function will never return `false`. pub fn is_empty(&self) -> bool { false } }