implement comptime while loops
This commit is contained in:
parent
e367920922
commit
389d791ac1
|
@ -25,7 +25,7 @@ impl Block {
|
||||||
{
|
{
|
||||||
let err = TranspileError::AssignmentError(AssignmentError {
|
let err = TranspileError::AssignmentError(AssignmentError {
|
||||||
identifier: ident.span.clone(),
|
identifier: ident.span.clone(),
|
||||||
message: "cannot assign to a compile-time variable in a conditional execute block"
|
message: "cannot assign to a compile-time variable declared before a runtime conditional block"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
});
|
});
|
||||||
handler.receive(Box::new(err.clone()));
|
handler.receive(Box::new(err.clone()));
|
||||||
|
|
|
@ -49,8 +49,6 @@ 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.
|
||||||
|
@ -444,29 +442,3 @@ 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.")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2139,6 +2139,14 @@ impl Transpiler {
|
||||||
scope: &Arc<super::Scope>,
|
scope: &Arc<super::Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<(Vec<Command>, ShulkerboxMacroStringMap, ExtendedCondition)> {
|
) -> TranspileResult<(Vec<Command>, ShulkerboxMacroStringMap, ExtendedCondition)> {
|
||||||
|
if let Ok(ComptimeValue::Boolean(val)) = binary.comptime_eval(scope, handler) {
|
||||||
|
return Ok((
|
||||||
|
Vec::new(),
|
||||||
|
BTreeMap::new(),
|
||||||
|
ExtendedCondition::Comptime(val),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
match binary.operator() {
|
match binary.operator() {
|
||||||
BinaryOperator::Equal(..)
|
BinaryOperator::Equal(..)
|
||||||
| BinaryOperator::NotEqual(..)
|
| BinaryOperator::NotEqual(..)
|
||||||
|
|
|
@ -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},
|
||||||
Block, ReturnStatement, SemicolonStatement, Statement,
|
Block, ReturnStatement, SemicolonStatement, Statement, WhileLoop,
|
||||||
},
|
},
|
||||||
AnnotationAssignment,
|
AnnotationAssignment,
|
||||||
},
|
},
|
||||||
transpile::{
|
transpile::{
|
||||||
conversions::ShulkerboxMacroStringMap,
|
conversions::ShulkerboxMacroStringMap,
|
||||||
error::{IllegalAnnotationContent, InfiniteLoop},
|
error::IllegalAnnotationContent,
|
||||||
expression::DataLocation,
|
expression::DataLocation,
|
||||||
util::{MacroString, MacroStringPart},
|
util::{MacroString, MacroStringPart},
|
||||||
variables::FunctionVariableDataType,
|
variables::FunctionVariableDataType,
|
||||||
|
@ -42,6 +42,8 @@ use super::{
|
||||||
FunctionData, TranspileAnnotationValue, TranspiledFunctionArguments,
|
FunctionData, TranspileAnnotationValue, TranspiledFunctionArguments,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const LOOP_LIMIT: usize = 4_096;
|
||||||
|
|
||||||
/// A transpiler for `Shulkerscript`.
|
/// A transpiler for `Shulkerscript`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Transpiler {
|
pub struct Transpiler {
|
||||||
|
@ -429,37 +431,9 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::WhileLoop(while_loop) => {
|
Statement::WhileLoop(while_loop) => {
|
||||||
let (mut condition_commands, prepare_variables, condition) =
|
self.transpile_while_loop(while_loop, program_identifier, scope, handler)
|
||||||
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)) => {
|
||||||
|
@ -1422,6 +1396,60 @@ impl Transpiler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transpile_while_loop(
|
||||||
|
&mut self,
|
||||||
|
while_loop: &WhileLoop,
|
||||||
|
program_identifier: &str,
|
||||||
|
scope: &Arc<Scope>,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> TranspileResult<Vec<Command>> {
|
||||||
|
let mut cmds = Vec::new();
|
||||||
|
|
||||||
|
for _ in 0..LOOP_LIMIT {
|
||||||
|
let (mut condition_commands, prepare_variables, condition) =
|
||||||
|
self.transpile_expression_as_condition(while_loop.condition(), scope, handler)?;
|
||||||
|
|
||||||
|
match condition {
|
||||||
|
ExtendedCondition::Comptime(false) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ExtendedCondition::Comptime(true) => {
|
||||||
|
let loop_commands = self.transpile_block(
|
||||||
|
while_loop.block(),
|
||||||
|
program_identifier,
|
||||||
|
scope,
|
||||||
|
handler,
|
||||||
|
)?;
|
||||||
|
cmds.extend(condition_commands.into_iter().chain(loop_commands));
|
||||||
|
}
|
||||||
|
ExtendedCondition::Runtime(condition) => {
|
||||||
|
// TODO: allow comptime assignments when wrapped in comptime checks
|
||||||
|
while_loop
|
||||||
|
.block()
|
||||||
|
.check_no_comptime_assignments(scope, handler)?;
|
||||||
|
let loop_commands = self.transpile_block(
|
||||||
|
while_loop.block(),
|
||||||
|
program_identifier,
|
||||||
|
scope,
|
||||||
|
handler,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
condition_commands
|
||||||
|
.push(Command::While(WhileCmd::new(condition, loop_commands)));
|
||||||
|
|
||||||
|
cmds.extend(self.transpile_commands_with_variable_macros(
|
||||||
|
condition_commands,
|
||||||
|
prepare_variables,
|
||||||
|
handler,
|
||||||
|
)?);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(cmds)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn transpile_commands_with_variable_macros(
|
pub(crate) fn transpile_commands_with_variable_macros(
|
||||||
&mut self,
|
&mut self,
|
||||||
cmds: Vec<Command>,
|
cmds: Vec<Command>,
|
||||||
|
|
Loading…
Reference in New Issue