diff --git a/src/compile/compiler.rs b/src/compile/compiler.rs index cb2d2c2..a3b9812 100644 --- a/src/compile/compiler.rs +++ b/src/compile/compiler.rs @@ -123,5 +123,17 @@ fn compile_statement(statement: &Statement) -> Option { let content = doccomment.content(); Some(Command::Comment(content.to_string())) } + Statement::Grouping(group) => { + let statements = group.block().statements(); + let commands = statements + .iter() + .filter_map(compile_statement) + .collect::>(); + if commands.is_empty() { + None + } else { + Some(Command::Group(commands)) + } + } } } diff --git a/src/lexical/token.rs b/src/lexical/token.rs index 4dcf95d..124ba7c 100644 --- a/src/lexical/token.rs +++ b/src/lexical/token.rs @@ -20,6 +20,7 @@ pub enum KeywordKind { Function, If, Else, + Group, } impl ToString for KeywordKind { @@ -61,6 +62,7 @@ impl KeywordKind { Self::Function => "fn", Self::If => "if", Self::Else => "else", + Self::Group => "group", } } } diff --git a/src/syntax/syntax_tree/statement.rs b/src/syntax/syntax_tree/statement.rs index f6a54d9..179e51f 100644 --- a/src/syntax/syntax_tree/statement.rs +++ b/src/syntax/syntax_tree/statement.rs @@ -26,6 +26,7 @@ use super::expression::ParenthesizedCondition; /// Block /// | LiteralCommand /// | Conditional +/// | Grouping /// | DocComment /// ; /// ``` @@ -35,6 +36,7 @@ pub enum Statement { Block(Block), LiteralCommand(CommandLiteral), Conditional(Conditional), + Grouping(Grouping), DocComment(DocComment), } @@ -44,6 +46,7 @@ impl SourceElement for Statement { Self::Block(block) => block.span(), Self::LiteralCommand(literal_command) => literal_command.span(), Self::Conditional(conditional) => conditional.span(), + Self::Grouping(grouping) => grouping.span(), Self::DocComment(doc_comment) => doc_comment.span(), } } @@ -136,6 +139,40 @@ impl SourceElement for Conditional { } } +/// Syntax Synopsis: +/// +/// ``` ebnf +/// Grouping: +/// 'group' Block +/// ; +/// ```` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] +pub struct Grouping { + /// The `group` keyword. + #[get = "pub"] + group_keyword: Keyword, + /// The block of the conditional. + #[get = "pub"] + block: Block, +} + +impl Grouping { + /// Dissolves the [`Grouping`] into its components. + #[must_use] + pub fn dissolve(self) -> (Keyword, Block) { + (self.group_keyword, self.block) + } +} + +impl SourceElement for Grouping { + fn span(&self) -> Span { + self.group_keyword + .span() + .join(&self.block.span()) + .expect("The span of the grouping is invalid.") + } +} + /// Syntax Synopsis: /// /// ``` ebnf @@ -262,11 +299,27 @@ impl<'a> Parser<'a> { } } + // doc comment Reading::Atomic(Token::DocComment(doc_comment)) => { self.forward(); Some(Statement::DocComment(doc_comment)) } + // grouping statement + Reading::Atomic(Token::Keyword(group_keyword)) + if group_keyword.keyword == KeywordKind::Group => + { + // eat the group keyword + self.forward(); + + let block = self.parse_block(handler)?; + + Some(Statement::Grouping(Grouping { + group_keyword, + block, + })) + } + // other unexpected => { handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {