use alloc::sync::Arc;
use core::ops::ControlFlow;
use crate::{
BreakReason, Host, MapExecErr, ONE, Stopper, ZERO,
continuation_stack::Continuation,
execution::{ExecutionState, finalize_clock_cycle, finalize_clock_cycle_with_continuation},
mast::{LoopNode, MastForest, MastNodeId},
operation::OperationError,
processor::{Processor, StackInterface},
tracer::Tracer,
};
#[inline(always)]
pub(super) fn start_loop_node<P, H, S, T>(
state: &mut ExecutionState<'_, P, H, S, T>,
loop_node: &LoopNode,
current_node_id: MastNodeId,
current_forest: &Arc<MastForest>,
) -> ControlFlow<BreakReason>
where
P: Processor,
H: Host,
S: Stopper<Processor = P>,
T: Tracer<Processor = P>,
{
state.tracer.start_clock_cycle(
state.processor,
Continuation::StartNode(current_node_id),
state.continuation_stack,
current_forest,
);
state
.processor
.execute_before_enter_decorators(current_node_id, current_forest, state.host)?;
let condition = state.processor.stack().get(0);
if let Err(err) = state.processor.stack_mut().decrement_size().map_exec_err(
current_forest,
current_node_id,
state.host,
) {
return ControlFlow::Break(BreakReason::Err(err));
}
if condition == ONE {
state.continuation_stack.push_finish_loop_entered(current_node_id);
state.continuation_stack.push_start_node(loop_node.body());
finalize_clock_cycle(
state.processor,
state.tracer,
state.stopper,
state.continuation_stack,
current_forest,
)
} else if condition == ZERO {
finalize_clock_cycle_with_continuation(
state.processor,
state.tracer,
state.stopper,
state.continuation_stack,
|| {
Some(Continuation::FinishLoop {
node_id: current_node_id,
was_entered: false,
})
},
current_forest,
)?;
finish_loop_node(state, false, current_node_id, current_forest)
} else {
let err = OperationError::NotBinaryValueLoop { value: condition };
ControlFlow::Break(BreakReason::Err(err.with_context(
current_forest,
current_node_id,
state.host,
)))
}
}
#[inline(always)]
pub(super) fn finish_loop_node<P, H, S, T>(
state: &mut ExecutionState<'_, P, H, S, T>,
loop_was_entered: bool,
current_node_id: MastNodeId,
current_forest: &Arc<MastForest>,
) -> ControlFlow<BreakReason>
where
P: Processor,
H: Host,
S: Stopper<Processor = P>,
T: Tracer<Processor = P>,
{
let condition = if loop_was_entered {
state.processor.stack().get(0)
} else {
ZERO
};
let loop_node = current_forest[current_node_id].unwrap_loop();
if condition == ONE {
state.tracer.start_clock_cycle(
state.processor,
Continuation::FinishLoop {
node_id: current_node_id,
was_entered: true,
},
state.continuation_stack,
current_forest,
);
if let Err(err) = state.processor.stack_mut().decrement_size().map_exec_err(
current_forest,
current_node_id,
state.host,
) {
return ControlFlow::Break(BreakReason::Err(err));
}
state.continuation_stack.push_finish_loop_entered(current_node_id);
state.continuation_stack.push_start_node(loop_node.body());
finalize_clock_cycle(
state.processor,
state.tracer,
state.stopper,
state.continuation_stack,
current_forest,
)
} else if condition == ZERO {
state.tracer.start_clock_cycle(
state.processor,
Continuation::FinishLoop {
node_id: current_node_id,
was_entered: loop_was_entered,
},
state.continuation_stack,
current_forest,
);
if loop_was_entered
&& let Err(err) = state.processor.stack_mut().decrement_size().map_exec_err(
current_forest,
current_node_id,
state.host,
)
{
return ControlFlow::Break(BreakReason::Err(err));
}
finalize_clock_cycle_with_continuation(
state.processor,
state.tracer,
state.stopper,
state.continuation_stack,
|| Some(Continuation::AfterExitDecorators(current_node_id)),
current_forest,
)?;
state
.processor
.execute_after_exit_decorators(current_node_id, current_forest, state.host)
} else {
let err = OperationError::NotBinaryValueLoop { value: condition };
ControlFlow::Break(BreakReason::Err(err.with_context(
current_forest,
current_node_id,
state.host,
)))
}
}