Add 'Run' statement to grammar and syntax tree
This commit is contained in:
parent
c770a54517
commit
9f8b31e2aa
|
@ -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:
|
||||||
|
|
|
@ -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",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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)?;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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}")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue