Add 'Run' statement to grammar and syntax tree
This commit is contained in:
parent
c770a54517
commit
9f8b31e2aa
|
@ -35,6 +35,8 @@ Statement:
|
|||
| Conditional
|
||||
| Grouping
|
||||
| DocComment
|
||||
| Semicolon
|
||||
| Run
|
||||
;
|
||||
```
|
||||
|
||||
|
@ -43,6 +45,13 @@ Statement:
|
|||
Block: '{' Statement* '}';
|
||||
```
|
||||
|
||||
### Run
|
||||
```ebnf
|
||||
Run:
|
||||
'run' Expression ';'
|
||||
;
|
||||
```
|
||||
|
||||
### Conditional
|
||||
```ebnf
|
||||
Conditional:
|
||||
|
|
|
@ -22,6 +22,7 @@ pub enum KeywordKind {
|
|||
If,
|
||||
Else,
|
||||
Group,
|
||||
Run,
|
||||
}
|
||||
|
||||
impl ToString for KeywordKind {
|
||||
|
@ -64,6 +65,7 @@ impl KeywordKind {
|
|||
Self::If => "if",
|
||||
Self::Else => "else",
|
||||
Self::Group => "group",
|
||||
Self::Run => "run",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
Handler,
|
||||
},
|
||||
lexical::{
|
||||
token::{Identifier, Punctuation, Token},
|
||||
token::{Identifier, Punctuation, StringLiteral, Token},
|
||||
token_stream::Delimiter,
|
||||
},
|
||||
syntax::{
|
||||
|
@ -52,12 +52,14 @@ impl SourceElement for Expression {
|
|||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
|
||||
pub enum Primary {
|
||||
FunctionCall(FunctionCall),
|
||||
StringLiteral(StringLiteral),
|
||||
}
|
||||
|
||||
impl SourceElement for Primary {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
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 => {
|
||||
// make progress
|
||||
self.forward();
|
||||
|
|
|
@ -28,6 +28,8 @@ use super::{condition::ParenthesizedCondition, expression::Expression};
|
|||
/// | Conditional
|
||||
/// | Grouping
|
||||
/// | DocComment
|
||||
/// | Semicolon
|
||||
/// | Run
|
||||
/// ;
|
||||
/// ```
|
||||
#[allow(missing_docs)]
|
||||
|
@ -40,6 +42,7 @@ pub enum Statement {
|
|||
Grouping(Grouping),
|
||||
DocComment(DocComment),
|
||||
Semicolon(Semicolon),
|
||||
Run(Run),
|
||||
}
|
||||
|
||||
impl SourceElement for Statement {
|
||||
|
@ -51,6 +54,7 @@ impl SourceElement for Statement {
|
|||
Self::Grouping(grouping) => grouping.span(),
|
||||
Self::DocComment(doc_comment) => doc_comment.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:
|
||||
///
|
||||
/// ``` ebnf
|
||||
/// Conditional:
|
||||
/// 'if' ParenthizedCondition Block ('else' Block)?
|
||||
/// ;
|
||||
/// ````
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
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
|
||||
_ => {
|
||||
let expression = self.parse_expression(handler)?;
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
//! Errors that can occur during transpilation.
|
||||
|
||||
use crate::{base::source_file::SourceElement, syntax::syntax_tree::expression::Expression};
|
||||
|
||||
/// Errors that can occur during transpilation.
|
||||
#[allow(clippy::module_name_repetitions, missing_docs)]
|
||||
#[derive(Debug, thiserror::Error, Clone)]
|
||||
pub enum TranspileError {
|
||||
#[error("Function {} was called but never declared.", .0)]
|
||||
MissingFunctionDeclaration(String),
|
||||
#[error("Unexpected expression: {}", .0.span().str())]
|
||||
UnexpectedExpression(Expression),
|
||||
}
|
||||
|
||||
/// The result of a transpilation operation.
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
base::{source_file::SourceElement, Handler},
|
||||
syntax::syntax_tree::{
|
||||
declaration::Declaration,
|
||||
expression::{Expression, Primary},
|
||||
expression::{Expression, FunctionCall, Primary},
|
||||
program::Program,
|
||||
statement::{Conditional, Statement},
|
||||
},
|
||||
|
@ -226,6 +226,14 @@ impl Transpiler {
|
|||
Statement::LiteralCommand(literal_command) => {
|
||||
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(_) => {
|
||||
unreachable!("Only literal commands are allowed in functions at this time.")
|
||||
}
|
||||
|
@ -256,14 +264,31 @@ impl Transpiler {
|
|||
Ok(Some(Command::Group(commands)))
|
||||
}
|
||||
}
|
||||
#[allow(clippy::match_wildcard_for_single_variants)]
|
||||
Statement::Semicolon(semi) => match semi.expression() {
|
||||
Expression::Primary(primary) => self
|
||||
.transpile_primary_expression(primary, handler)
|
||||
.map(Some),
|
||||
Expression::Primary(Primary::FunctionCall(func)) => {
|
||||
self.transpile_function_call(func, handler).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(
|
||||
&mut self,
|
||||
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}")))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue