implement basic while loop
This commit is contained in:
parent
218f488e76
commit
7f276a4139
|
@ -19,7 +19,7 @@ default = ["fs_access", "lua", "shulkerbox", "zip"]
|
||||||
fs_access = ["shulkerbox?/fs_access"]
|
fs_access = ["shulkerbox?/fs_access"]
|
||||||
lua = ["dep:mlua"]
|
lua = ["dep:mlua"]
|
||||||
serde = ["dep:serde", "dep:serde_json", "shulkerbox?/serde"]
|
serde = ["dep:serde", "dep:serde_json", "shulkerbox?/serde"]
|
||||||
shulkerbox = ["dep:shulkerbox", "dep:chksum-md5", "dep:serde_json"]
|
shulkerbox = ["dep:shulkerbox", "dep:chksum-md5", "dep:serde_json", "dep:oxford_join"]
|
||||||
zip = ["shulkerbox?/zip"]
|
zip = ["shulkerbox?/zip"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -36,7 +36,7 @@ pathdiff = "0.2.3"
|
||||||
serde = { version = "1.0.217", features = ["derive"], optional = true }
|
serde = { version = "1.0.217", features = ["derive"], optional = true }
|
||||||
serde_json = { version = "1.0.138", optional = true }
|
serde_json = { version = "1.0.138", optional = true }
|
||||||
# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
|
# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
|
||||||
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "d4689c696a35328c041bcbbfd203abd5818c46d3", default-features = false, optional = true }
|
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "e1bc953b7a1692c65f1ed2c43fa3b0c607df8090", default-features = false, optional = true }
|
||||||
strsim = "0.11.1"
|
strsim = "0.11.1"
|
||||||
strum = { version = "0.27.0", features = ["derive"] }
|
strum = { version = "0.27.0", features = ["derive"] }
|
||||||
thiserror = "2.0.11"
|
thiserror = "2.0.11"
|
||||||
|
@ -57,3 +57,6 @@ required-features = ["shulkerbox"]
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "transpiling"
|
name = "transpiling"
|
||||||
required-features = ["shulkerbox"]
|
required-features = ["shulkerbox"]
|
||||||
|
|
||||||
|
[package.metadata.cargo-feature-combinations]
|
||||||
|
exclude_features = [ "default" ]
|
|
@ -148,6 +148,7 @@ Statement:
|
||||||
| Grouping
|
| Grouping
|
||||||
| DocComment
|
| DocComment
|
||||||
| ExecuteBlock
|
| ExecuteBlock
|
||||||
|
| WhileLoop
|
||||||
| Semicolon
|
| Semicolon
|
||||||
;
|
;
|
||||||
```
|
```
|
||||||
|
@ -215,6 +216,14 @@ Semicolon:
|
||||||
;
|
;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## WhileLoop
|
||||||
|
|
||||||
|
```ebnf
|
||||||
|
WhileLoop:
|
||||||
|
'while' '(' Expression ')' Block
|
||||||
|
;
|
||||||
|
```
|
||||||
|
|
||||||
## FunctionVariableType
|
## FunctionVariableType
|
||||||
|
|
||||||
```ebnf
|
```ebnf
|
||||||
|
|
|
@ -35,6 +35,8 @@ pub enum Error {
|
||||||
UnknownIdentifier(#[from] UnknownIdentifier),
|
UnknownIdentifier(#[from] UnknownIdentifier),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
AssignmentError(#[from] AssignmentError),
|
AssignmentError(#[from] AssignmentError),
|
||||||
|
#[error(transparent)]
|
||||||
|
NeverLoops(#[from] NeverLoops),
|
||||||
#[error("Lua is disabled, but a Lua function was used.")]
|
#[error("Lua is disabled, but a Lua function was used.")]
|
||||||
LuaDisabled,
|
LuaDisabled,
|
||||||
#[error("Other: {0}")]
|
#[error("Other: {0}")]
|
||||||
|
@ -228,3 +230,27 @@ impl Display for InvalidFunctionArguments {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for InvalidFunctionArguments {}
|
impl std::error::Error for InvalidFunctionArguments {}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
|
||||||
|
pub struct NeverLoops {
|
||||||
|
pub reason: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for NeverLoops {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
Message::new(Severity::Error, "Loop never actually loops.")
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"\n{}",
|
||||||
|
SourceCodeDisplay::new(
|
||||||
|
&self.reason,
|
||||||
|
Some("This statement causes the loop to always terminate.")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,13 @@
|
||||||
#![allow(clippy::missing_errors_doc)]
|
#![allow(clippy::missing_errors_doc)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{self, source_file::SourceElement as _, Handler},
|
base::{
|
||||||
|
self,
|
||||||
|
source_file::{SourceElement as _, Span},
|
||||||
|
Handler,
|
||||||
|
},
|
||||||
lexical::token::KeywordKind,
|
lexical::token::KeywordKind,
|
||||||
|
semantic::error::NeverLoops,
|
||||||
syntax::syntax_tree::{
|
syntax::syntax_tree::{
|
||||||
declaration::{Declaration, Function, FunctionVariableType, ImportItems},
|
declaration::{Declaration, Function, FunctionVariableType, ImportItems},
|
||||||
expression::{
|
expression::{
|
||||||
|
@ -18,7 +23,7 @@ use crate::{
|
||||||
ExecuteBlockTail,
|
ExecuteBlockTail,
|
||||||
},
|
},
|
||||||
Assignment, AssignmentDestination, Block, Grouping, Semicolon, SemicolonStatement,
|
Assignment, AssignmentDestination, Block, Grouping, Semicolon, SemicolonStatement,
|
||||||
Statement, VariableDeclaration,
|
Statement, VariableDeclaration, WhileLoop,
|
||||||
},
|
},
|
||||||
AnyStringLiteral,
|
AnyStringLiteral,
|
||||||
},
|
},
|
||||||
|
@ -254,6 +259,7 @@ impl Statement {
|
||||||
let child_scope = SemanticScope::with_parent(scope);
|
let child_scope = SemanticScope::with_parent(scope);
|
||||||
group.analyze_semantics(&child_scope, handler)
|
group.analyze_semantics(&child_scope, handler)
|
||||||
}
|
}
|
||||||
|
Self::WhileLoop(while_loop) => while_loop.analyze_semantics(scope, handler),
|
||||||
Self::Semicolon(sem) => sem.analyze_semantics(scope, handler),
|
Self::Semicolon(sem) => sem.analyze_semantics(scope, handler),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,6 +373,63 @@ impl Grouping {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_contains_unconditional_return(block: &Block) -> Option<Span> {
|
||||||
|
block
|
||||||
|
.statements
|
||||||
|
.iter()
|
||||||
|
.find_map(|statement| match statement {
|
||||||
|
Statement::Semicolon(semicolon) => {
|
||||||
|
let s = semicolon.statement();
|
||||||
|
if s.is_return() {
|
||||||
|
Some(s.span())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::Grouping(group) => block_contains_unconditional_return(group.block()),
|
||||||
|
Statement::ExecuteBlock(ex) => execute_block_contains_unconditional_return(ex),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_block_contains_unconditional_return(ex: &ExecuteBlock) -> Option<Span> {
|
||||||
|
match ex {
|
||||||
|
ExecuteBlock::HeadTail(head, tail) => {
|
||||||
|
if head.is_conditional() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
match tail {
|
||||||
|
ExecuteBlockTail::Block(inner_block) => {
|
||||||
|
block_contains_unconditional_return(inner_block)
|
||||||
|
}
|
||||||
|
ExecuteBlockTail::ExecuteBlock(_, inner_ex) => {
|
||||||
|
execute_block_contains_unconditional_return(inner_ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExecuteBlock::IfElse(..) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WhileLoop {
|
||||||
|
fn analyze_semantics(
|
||||||
|
&self,
|
||||||
|
scope: &SemanticScope,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> Result<(), error::Error> {
|
||||||
|
self.condition().analyze_semantics(scope, handler)?;
|
||||||
|
|
||||||
|
if let Some(reason) = block_contains_unconditional_return(self.block()) {
|
||||||
|
let err = error::Error::NeverLoops(NeverLoops { reason });
|
||||||
|
handler.receive(err.clone());
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.block().analyze_semantics(scope, handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Semicolon {
|
impl Semicolon {
|
||||||
fn analyze_semantics(
|
fn analyze_semantics(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -23,7 +23,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
syntax::{
|
syntax::{
|
||||||
error::{Error, InvalidAnnotation, ParseResult, SyntaxKind, UnexpectedSyntax},
|
error::{Error, InvalidAnnotation, ParseResult, SyntaxKind, UnexpectedSyntax},
|
||||||
parser::{Parser, Reading},
|
parser::{DelimitedTree, Parser, Reading},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ use super::{expression::Expression, Annotation, AnyStringLiteral};
|
||||||
/// | Grouping
|
/// | Grouping
|
||||||
/// | DocComment
|
/// | DocComment
|
||||||
/// | ExecuteBlock
|
/// | ExecuteBlock
|
||||||
|
/// | WhileLoop
|
||||||
/// | Semicolon
|
/// | Semicolon
|
||||||
/// ;
|
/// ;
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -55,6 +56,7 @@ pub enum Statement {
|
||||||
ExecuteBlock(ExecuteBlock),
|
ExecuteBlock(ExecuteBlock),
|
||||||
Grouping(Grouping),
|
Grouping(Grouping),
|
||||||
DocComment(DocComment),
|
DocComment(DocComment),
|
||||||
|
WhileLoop(WhileLoop),
|
||||||
Semicolon(Semicolon),
|
Semicolon(Semicolon),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +68,7 @@ impl SourceElement for Statement {
|
||||||
Self::ExecuteBlock(execute_block) => execute_block.span(),
|
Self::ExecuteBlock(execute_block) => execute_block.span(),
|
||||||
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::WhileLoop(while_loop) => while_loop.span(),
|
||||||
Self::Semicolon(semi) => semi.span(),
|
Self::Semicolon(semi) => semi.span(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,7 +186,7 @@ pub struct Grouping {
|
||||||
/// The `group` keyword.
|
/// The `group` keyword.
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
group_keyword: Keyword,
|
group_keyword: Keyword,
|
||||||
/// The block of the conditional.
|
/// The block of the grouping.
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
block: Block,
|
block: Block,
|
||||||
}
|
}
|
||||||
|
@ -205,6 +208,58 @@ impl SourceElement for Grouping {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a while loop in the syntax tree.
|
||||||
|
///
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ```ebnf
|
||||||
|
/// WhileLoop:
|
||||||
|
/// 'while' '(' Expression ')' Block
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||||
|
pub struct WhileLoop {
|
||||||
|
/// The `while` keyword.
|
||||||
|
#[get = "pub"]
|
||||||
|
while_keyword: Keyword,
|
||||||
|
/// The opening parenthesis
|
||||||
|
#[get = "pub"]
|
||||||
|
open_paren: Punctuation,
|
||||||
|
/// The condition expression
|
||||||
|
#[get = "pub"]
|
||||||
|
condition: Expression,
|
||||||
|
/// The closing parenthesis
|
||||||
|
#[get = "pub"]
|
||||||
|
close_paren: Punctuation,
|
||||||
|
/// The block of the loop.
|
||||||
|
#[get = "pub"]
|
||||||
|
block: Block,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for WhileLoop {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.while_keyword
|
||||||
|
.span
|
||||||
|
.join(&self.block.span())
|
||||||
|
.expect("spans in same file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WhileLoop {
|
||||||
|
/// Dissolves the [`WhileLoop`] into its components.
|
||||||
|
#[must_use]
|
||||||
|
pub fn dissolve(self) -> (Keyword, Punctuation, Expression, Punctuation, Block) {
|
||||||
|
(
|
||||||
|
self.while_keyword,
|
||||||
|
self.open_paren,
|
||||||
|
self.condition,
|
||||||
|
self.close_paren,
|
||||||
|
self.block,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a statement that ends with a semicolon in the syntax tree.
|
/// Represents a statement that ends with a semicolon in the syntax tree.
|
||||||
///
|
///
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
|
@ -920,11 +975,54 @@ impl Parser<'_> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// while loop
|
||||||
|
Reading::Atomic(Token::Keyword(while_keyword))
|
||||||
|
if while_keyword.keyword == KeywordKind::While =>
|
||||||
|
{
|
||||||
|
self.parse_while_loop(handler).map(Statement::WhileLoop)
|
||||||
|
}
|
||||||
|
|
||||||
// semicolon statement
|
// semicolon statement
|
||||||
_ => self.parse_semicolon(handler).map(Statement::Semicolon),
|
_ => self.parse_semicolon(handler).map(Statement::Semicolon),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a [`WhileLoop`].
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - if the parser is not at a while loop
|
||||||
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
|
pub fn parse_while_loop(
|
||||||
|
&mut self,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> ParseResult<WhileLoop> {
|
||||||
|
let while_keyword = self.parse_keyword(KeywordKind::While, handler)?;
|
||||||
|
|
||||||
|
self.stop_at_significant();
|
||||||
|
|
||||||
|
let DelimitedTree {
|
||||||
|
open: open_paren,
|
||||||
|
tree,
|
||||||
|
close: close_paren,
|
||||||
|
} = self.step_into(
|
||||||
|
Delimiter::Parenthesis,
|
||||||
|
|p| p.parse_expression(handler),
|
||||||
|
handler,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let condition = tree?;
|
||||||
|
|
||||||
|
let block = self.parse_block(handler)?;
|
||||||
|
|
||||||
|
Ok(WhileLoop {
|
||||||
|
while_keyword,
|
||||||
|
open_paren,
|
||||||
|
condition,
|
||||||
|
close_paren,
|
||||||
|
block,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses a [`Semicolon`].
|
/// Parses a [`Semicolon`].
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use getset::Getters;
|
use getset::Getters;
|
||||||
|
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
use oxford_join::OxfordJoin as _;
|
use oxford_join::OxfordJoin as _;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -47,6 +49,8 @@ pub enum TranspileError {
|
||||||
InvalidArgument(#[from] InvalidArgument),
|
InvalidArgument(#[from] InvalidArgument),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
NotComptime(#[from] NotComptime),
|
NotComptime(#[from] NotComptime),
|
||||||
|
#[error(transparent)]
|
||||||
|
InfiniteLoop(#[from] InfiniteLoop),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of a transpilation operation.
|
/// The result of a transpilation operation.
|
||||||
|
@ -263,7 +267,11 @@ impl Display for UnknownIdentifier {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| format!("`{s}`"))
|
.map(|s| format!("`{s}`"))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
let inner = inner.oxford_or();
|
let inner = inner.oxford_or();
|
||||||
|
#[cfg(not(feature = "shulkerbox"))]
|
||||||
|
let inner = std::borrow::Cow::<str>::Owned(inner.join(", "));
|
||||||
|
|
||||||
Some(message + &inner + "?")
|
Some(message + &inner + "?")
|
||||||
};
|
};
|
||||||
|
@ -436,3 +444,29 @@ impl Display for NotComptime {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for NotComptime {}
|
impl std::error::Error for NotComptime {}
|
||||||
|
|
||||||
|
/// An error that occurs when a loop never terminates.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
|
||||||
|
pub struct InfiniteLoop {
|
||||||
|
/// The condition making it not terminate.
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for InfiniteLoop {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
Message::new(Severity::Error, "Loop never terminates.")
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"\n{}",
|
||||||
|
SourceCodeDisplay::new(
|
||||||
|
&self.span,
|
||||||
|
Some("You may want to use a separate function with the `#[tick]` annotation.")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use shulkerbox::datapack::{self, Command, Datapack, Execute, Group};
|
use shulkerbox::datapack::{self, Command, Datapack, Execute, Group, While as WhileCmd};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{
|
base::{
|
||||||
|
@ -22,13 +22,13 @@ use crate::{
|
||||||
program::{Namespace, ProgramFile},
|
program::{Namespace, ProgramFile},
|
||||||
statement::{
|
statement::{
|
||||||
execute_block::{Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockTail},
|
execute_block::{Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockTail},
|
||||||
ReturnStatement, SemicolonStatement, Statement,
|
Block, ReturnStatement, SemicolonStatement, Statement,
|
||||||
},
|
},
|
||||||
AnnotationAssignment,
|
AnnotationAssignment,
|
||||||
},
|
},
|
||||||
transpile::{
|
transpile::{
|
||||||
conversions::ShulkerboxMacroStringMap,
|
conversions::ShulkerboxMacroStringMap,
|
||||||
error::IllegalAnnotationContent,
|
error::{IllegalAnnotationContent, InfiniteLoop},
|
||||||
expression::DataLocation,
|
expression::DataLocation,
|
||||||
util::{MacroString, MacroStringPart},
|
util::{MacroString, MacroStringPart},
|
||||||
variables::FunctionVariableDataType,
|
variables::FunctionVariableDataType,
|
||||||
|
@ -365,6 +365,33 @@ impl Transpiler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn transpile_block(
|
||||||
|
&mut self,
|
||||||
|
block: &Block,
|
||||||
|
program_identifier: &str,
|
||||||
|
scope: &Arc<Scope>,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> TranspileResult<Vec<Command>> {
|
||||||
|
let child_scope = Scope::with_parent(scope.clone());
|
||||||
|
let statements = block.statements();
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
let commands = statements
|
||||||
|
.iter()
|
||||||
|
.flat_map(|statement| {
|
||||||
|
self.transpile_statement(statement, program_identifier, &child_scope, handler)
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
errors.push(err);
|
||||||
|
Vec::new()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return Err(errors.remove(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(commands)
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn transpile_statement(
|
pub(super) fn transpile_statement(
|
||||||
&mut self,
|
&mut self,
|
||||||
statement: &Statement,
|
statement: &Statement,
|
||||||
|
@ -393,33 +420,46 @@ impl Transpiler {
|
||||||
Ok(vec![Command::Comment(content.to_string())])
|
Ok(vec![Command::Comment(content.to_string())])
|
||||||
}
|
}
|
||||||
Statement::Grouping(group) => {
|
Statement::Grouping(group) => {
|
||||||
let child_scope = Scope::with_parent(scope.clone());
|
let commands =
|
||||||
let statements = group.block().statements();
|
self.transpile_block(group.block(), program_identifier, scope, handler)?;
|
||||||
let mut errors = Vec::new();
|
|
||||||
let commands = statements
|
|
||||||
.iter()
|
|
||||||
.flat_map(|statement| {
|
|
||||||
self.transpile_statement(
|
|
||||||
statement,
|
|
||||||
program_identifier,
|
|
||||||
&child_scope,
|
|
||||||
handler,
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
errors.push(err);
|
|
||||||
Vec::new()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
if !errors.is_empty() {
|
|
||||||
return Err(errors.remove(0));
|
|
||||||
}
|
|
||||||
if commands.is_empty() {
|
if commands.is_empty() {
|
||||||
Ok(Vec::new())
|
Ok(Vec::new())
|
||||||
} else {
|
} else {
|
||||||
Ok(vec![Command::Group(Group::new(commands))])
|
Ok(vec![Command::Group(Group::new(commands))])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Statement::WhileLoop(while_loop) => {
|
||||||
|
let (mut condition_commands, prepare_variables, condition) =
|
||||||
|
self.transpile_expression_as_condition(while_loop.condition(), scope, handler)?;
|
||||||
|
|
||||||
|
match condition {
|
||||||
|
ExtendedCondition::Comptime(false) => Ok(Vec::new()),
|
||||||
|
ExtendedCondition::Comptime(true) => {
|
||||||
|
let err = TranspileError::InfiniteLoop(InfiniteLoop {
|
||||||
|
span: while_loop.condition().span(),
|
||||||
|
});
|
||||||
|
handler.receive(Box::new(err.clone()));
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
ExtendedCondition::Runtime(condition) => {
|
||||||
|
let loop_commands = self.transpile_block(
|
||||||
|
while_loop.block(),
|
||||||
|
program_identifier,
|
||||||
|
scope,
|
||||||
|
handler,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
condition_commands
|
||||||
|
.push(Command::While(WhileCmd::new(condition, loop_commands)));
|
||||||
|
|
||||||
|
self.transpile_commands_with_variable_macros(
|
||||||
|
condition_commands,
|
||||||
|
prepare_variables,
|
||||||
|
handler,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Statement::Semicolon(semi) => match semi.statement() {
|
Statement::Semicolon(semi) => match semi.statement() {
|
||||||
SemicolonStatement::Expression(expr) => match expr {
|
SemicolonStatement::Expression(expr) => match expr {
|
||||||
Expression::Primary(Primary::FunctionCall(func)) => {
|
Expression::Primary(Primary::FunctionCall(func)) => {
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
//! Utility methods for transpiling
|
//! Utility methods for transpiling
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
use std::{collections::BTreeMap, sync::Arc};
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
use shulkerbox::prelude::Command;
|
use shulkerbox::prelude::Command;
|
||||||
|
@ -347,6 +350,7 @@ impl TemplateStringLiteral {
|
||||||
) -> TranspileResult<MacroString> {
|
) -> TranspileResult<MacroString> {
|
||||||
if self.contains_expression() {
|
if self.contains_expression() {
|
||||||
let mut prepare_variables = BTreeMap::new();
|
let mut prepare_variables = BTreeMap::new();
|
||||||
|
let mut prepare_variables_reverse = HashMap::<DataLocation, String>::new();
|
||||||
|
|
||||||
let parts = self
|
let parts = self
|
||||||
.parts()
|
.parts()
|
||||||
|
@ -400,10 +404,25 @@ impl TemplateStringLiteral {
|
||||||
path: path.to_owned(),
|
path: path.to_owned(),
|
||||||
r#type: StorageType::Boolean,
|
r#type: StorageType::Boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let macro_name = if let Some(prev_macro_name) =
|
||||||
|
prepare_variables_reverse.get(&data_location)
|
||||||
|
{
|
||||||
|
prev_macro_name.to_string()
|
||||||
|
} else {
|
||||||
prepare_variables.insert(
|
prepare_variables.insert(
|
||||||
macro_name.clone(),
|
macro_name.clone(),
|
||||||
(data_location, Vec::new(), expression.span()),
|
(
|
||||||
|
data_location.clone(),
|
||||||
|
Vec::new(),
|
||||||
|
expression.span(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
prepare_variables_reverse
|
||||||
|
.insert(data_location, macro_name.clone());
|
||||||
|
|
||||||
|
macro_name
|
||||||
|
};
|
||||||
|
|
||||||
Ok(vec![MacroStringPart::MacroUsage(macro_name)])
|
Ok(vec![MacroStringPart::MacroUsage(macro_name)])
|
||||||
}
|
}
|
||||||
|
@ -421,10 +440,24 @@ impl TemplateStringLiteral {
|
||||||
objective: objective.to_owned(),
|
objective: objective.to_owned(),
|
||||||
target: target.to_owned(),
|
target: target.to_owned(),
|
||||||
};
|
};
|
||||||
|
let macro_name = if let Some(prev_macro_name) =
|
||||||
|
prepare_variables_reverse.get(&data_location)
|
||||||
|
{
|
||||||
|
prev_macro_name.to_string()
|
||||||
|
} else {
|
||||||
prepare_variables.insert(
|
prepare_variables.insert(
|
||||||
macro_name.clone(),
|
macro_name.clone(),
|
||||||
(data_location, Vec::new(), expression.span()),
|
(
|
||||||
|
data_location.clone(),
|
||||||
|
Vec::new(),
|
||||||
|
expression.span(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
prepare_variables_reverse
|
||||||
|
.insert(data_location, macro_name.clone());
|
||||||
|
|
||||||
|
macro_name
|
||||||
|
};
|
||||||
|
|
||||||
Ok(vec![MacroStringPart::MacroUsage(macro_name)])
|
Ok(vec![MacroStringPart::MacroUsage(macro_name)])
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue