Add 'Run' statement to grammar and syntax tree

This commit is contained in:
Moritz Hölting 2024-04-06 15:12:20 +02:00
parent c770a54517
commit 9f8b31e2aa
6 changed files with 115 additions and 21 deletions

View File

@ -35,6 +35,8 @@ Statement:
| Conditional | Conditional
| Grouping | Grouping
| DocComment | DocComment
| Semicolon
| Run
; ;
``` ```
@ -43,6 +45,13 @@ Statement:
Block: '{' Statement* '}'; Block: '{' Statement* '}';
``` ```
### Run
```ebnf
Run:
'run' Expression ';'
;
```
### Conditional ### Conditional
```ebnf ```ebnf
Conditional: Conditional:

View File

@ -22,6 +22,7 @@ pub enum KeywordKind {
If, If,
Else, Else,
Group, Group,
Run,
} }
impl ToString for KeywordKind { impl ToString for KeywordKind {
@ -64,6 +65,7 @@ impl KeywordKind {
Self::If => "if", Self::If => "if",
Self::Else => "else", Self::Else => "else",
Self::Group => "group", Self::Group => "group",
Self::Run => "run",
} }
} }
} }

View File

@ -9,7 +9,7 @@ use crate::{
Handler, Handler,
}, },
lexical::{ lexical::{
token::{Identifier, Punctuation, Token}, token::{Identifier, Punctuation, StringLiteral, Token},
token_stream::Delimiter, token_stream::Delimiter,
}, },
syntax::{ syntax::{
@ -52,12 +52,14 @@ impl SourceElement for Expression {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
pub enum Primary { pub enum Primary {
FunctionCall(FunctionCall), FunctionCall(FunctionCall),
StringLiteral(StringLiteral),
} }
impl SourceElement for Primary { impl SourceElement for Primary {
fn span(&self) -> Span { fn span(&self) -> Span {
match self { match self {
Self::FunctionCall(function_call) => function_call.span(), Self::FunctionCall(function_call) => function_call.span(),
Self::StringLiteral(string_literal) => string_literal.span(),
} }
} }
} }
@ -131,6 +133,14 @@ impl<'a> Parser<'a> {
} }
} }
// string literal expression
Reading::Atomic(Token::StringLiteral(literal)) => {
// eat the string literal
self.forward();
Some(Primary::StringLiteral(literal))
}
unexpected => { unexpected => {
// make progress // make progress
self.forward(); self.forward();

View File

@ -28,6 +28,8 @@ use super::{condition::ParenthesizedCondition, expression::Expression};
/// | Conditional /// | Conditional
/// | Grouping /// | Grouping
/// | DocComment /// | DocComment
/// | Semicolon
/// | Run
/// ; /// ;
/// ``` /// ```
#[allow(missing_docs)] #[allow(missing_docs)]
@ -40,6 +42,7 @@ pub enum Statement {
Grouping(Grouping), Grouping(Grouping),
DocComment(DocComment), DocComment(DocComment),
Semicolon(Semicolon), Semicolon(Semicolon),
Run(Run),
} }
impl SourceElement for Statement { impl SourceElement for Statement {
@ -51,6 +54,7 @@ impl SourceElement for Statement {
Self::Grouping(grouping) => grouping.span(), Self::Grouping(grouping) => grouping.span(),
Self::DocComment(doc_comment) => doc_comment.span(), Self::DocComment(doc_comment) => doc_comment.span(),
Self::Semicolon(semi) => semi.span(), Self::Semicolon(semi) => semi.span(),
Self::Run(run) => run.span(),
} }
} }
} }
@ -93,13 +97,51 @@ impl SourceElement for Block {
} }
} }
/// Syntax Synopsis:
///
/// ``` ebnf
/// Run:
/// 'run' Expression ';'
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct Run {
/// The `run` keyword.
#[get = "pub"]
run_keyword: Keyword,
/// The expression of the run statement.
#[get = "pub"]
expression: Expression,
/// The semicolon of the run statement.
#[get = "pub"]
semicolon: Punctuation,
}
impl SourceElement for Run {
fn span(&self) -> Span {
self.run_keyword
.span()
.join(&self.semicolon.span())
.expect("The span of the run statement is invalid.")
}
}
impl Run {
/// Dissolves the [`Run`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Expression, Punctuation) {
(self.run_keyword, self.expression, self.semicolon)
}
}
/// Syntax Synopsis: /// Syntax Synopsis:
/// ///
/// ``` ebnf /// ``` ebnf
/// Conditional: /// Conditional:
/// 'if' ParenthizedCondition Block ('else' Block)? /// 'if' ParenthizedCondition Block ('else' Block)?
/// ; /// ;
/// ```` /// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct Conditional { pub struct Conditional {
@ -361,6 +403,23 @@ impl<'a> Parser<'a> {
})) }))
} }
// run statement
Reading::Atomic(Token::Keyword(run_keyword))
if run_keyword.keyword == KeywordKind::Run =>
{
// eat the run keyword
self.forward();
let expression = self.parse_expression(handler)?;
let semicolon = self.parse_punctuation(';', true, handler)?;
Some(Statement::Run(Run {
run_keyword,
expression,
semicolon,
}))
}
// semicolon statement // semicolon statement
_ => { _ => {
let expression = self.parse_expression(handler)?; let expression = self.parse_expression(handler)?;

View File

@ -1,11 +1,15 @@
//! Errors that can occur during transpilation. //! Errors that can occur during transpilation.
use crate::{base::source_file::SourceElement, syntax::syntax_tree::expression::Expression};
/// Errors that can occur during transpilation. /// Errors that can occur during transpilation.
#[allow(clippy::module_name_repetitions, missing_docs)] #[allow(clippy::module_name_repetitions, missing_docs)]
#[derive(Debug, thiserror::Error, Clone)] #[derive(Debug, thiserror::Error, Clone)]
pub enum TranspileError { pub enum TranspileError {
#[error("Function {} was called but never declared.", .0)] #[error("Function {} was called but never declared.", .0)]
MissingFunctionDeclaration(String), MissingFunctionDeclaration(String),
#[error("Unexpected expression: {}", .0.span().str())]
UnexpectedExpression(Expression),
} }
/// The result of a transpilation operation. /// The result of a transpilation operation.

View File

@ -9,7 +9,7 @@ use crate::{
base::{source_file::SourceElement, Handler}, base::{source_file::SourceElement, Handler},
syntax::syntax_tree::{ syntax::syntax_tree::{
declaration::Declaration, declaration::Declaration,
expression::{Expression, Primary}, expression::{Expression, FunctionCall, Primary},
program::Program, program::Program,
statement::{Conditional, Statement}, statement::{Conditional, Statement},
}, },
@ -226,6 +226,14 @@ impl Transpiler {
Statement::LiteralCommand(literal_command) => { Statement::LiteralCommand(literal_command) => {
Ok(Some(literal_command.clean_command().into())) Ok(Some(literal_command.clean_command().into()))
} }
Statement::Run(run) => match run.expression() {
Expression::Primary(Primary::FunctionCall(func)) => {
self.transpile_function_call(func, handler).map(Some)
}
Expression::Primary(Primary::StringLiteral(string)) => {
Ok(Some(Command::Raw(string.str_content().to_string())))
}
},
Statement::Block(_) => { Statement::Block(_) => {
unreachable!("Only literal commands are allowed in functions at this time.") unreachable!("Only literal commands are allowed in functions at this time.")
} }
@ -256,14 +264,31 @@ impl Transpiler {
Ok(Some(Command::Group(commands))) Ok(Some(Command::Group(commands)))
} }
} }
#[allow(clippy::match_wildcard_for_single_variants)]
Statement::Semicolon(semi) => match semi.expression() { Statement::Semicolon(semi) => match semi.expression() {
Expression::Primary(primary) => self Expression::Primary(Primary::FunctionCall(func)) => {
.transpile_primary_expression(primary, handler) self.transpile_function_call(func, handler).map(Some)
.map(Some), }
unexpected => {
let error = TranspileError::UnexpectedExpression(unexpected.clone());
handler.receive(error.clone());
Err(error)
}
}, },
} }
} }
fn transpile_function_call(
&mut self,
func: &FunctionCall,
handler: &impl Handler<TranspileError>,
) -> TranspileResult<Command> {
let identifier = func.identifier().span();
let identifier_name = identifier.str();
let location = self.get_or_transpile_function(identifier_name, handler)?;
Ok(Command::Raw(format!("function {location}")))
}
fn transpile_conditional( fn transpile_conditional(
&mut self, &mut self,
cond: &Conditional, cond: &Conditional,
@ -344,19 +369,4 @@ impl Transpiler {
})) }))
} }
} }
fn transpile_primary_expression(
&mut self,
primary: &Primary,
handler: &impl Handler<TranspileError>,
) -> TranspileResult<Command> {
match primary {
Primary::FunctionCall(func) => {
let identifier = func.identifier().span();
let identifier_name = identifier.str();
let location = self.get_or_transpile_function(identifier_name, handler)?;
Ok(Command::Raw(format!("function {location}")))
}
}
}
} }