use core::num::NonZeroU8;
use midenc_hir::TraceTarget;
use super::{Action, Operand, SolverContext, Stack, ValueOrAlias};
mod copy_all;
mod linear;
mod linear_stack_window;
mod move_down_and_swap;
mod move_up_and_swap;
mod swap_and_move_up;
mod two_args;
pub use self::{
copy_all::CopyAll, linear::Linear, linear_stack_window::LinearStackWindow,
move_down_and_swap::MoveDownAndSwap, move_up_and_swap::MoveUpAndSwap,
swap_and_move_up::SwapAndMoveUp, two_args::TwoArgs,
};
#[derive(Debug)]
pub enum TacticError {
PreconditionFailed,
NotApplicable,
}
pub type TacticResult = Result<(), TacticError>;
pub trait Tactic {
fn name(&self) -> &'static str {
let name = core::any::type_name::<Self>();
match name.find(|c: char| c.is_ascii_uppercase()) {
None => name,
Some(index) => name.split_at(index).1,
}
}
fn cost(&self, _context: &SolverContext) -> usize {
1
}
fn apply(&mut self, builder: &mut SolutionBuilder) -> TacticResult;
}
#[derive(Debug, Clone)]
pub struct SolutionBuilder<'a> {
context: &'a SolverContext,
pending: Stack,
actions: Vec<Action>,
}
impl<'a> SolutionBuilder<'a> {
#[doc(hidden)]
pub fn new(context: &'a SolverContext) -> Self {
Self {
context,
pending: context.stack().clone(),
actions: vec![],
}
}
pub fn arity(&self) -> usize {
self.context.arity()
}
pub fn requires_copies(&self) -> bool {
!self.context.copies().is_empty()
}
pub fn num_copies(&self) -> usize {
self.context.copies().len()
}
pub fn requires_strict_solution(&self) -> bool {
self.context.is_strict()
}
#[inline(always)]
pub fn context(&self) -> &'a SolverContext {
self.context
}
#[inline]
pub fn trace_target(&self) -> &TraceTarget {
&self.context.options().trace_target
}
#[inline(always)]
pub fn stack(&self) -> &Stack {
&self.pending
}
pub fn take(&mut self) -> Vec<Action> {
let actions = core::mem::take(&mut self.actions);
self.pending.reset_to(self.context.stack());
actions
}
pub fn discard(&mut self) {
self.actions.clear();
self.pending.reset_to(self.context.stack());
}
pub fn is_valid(&self) -> bool {
self.context.is_solved(&self.pending)
}
pub fn get_expected(&self, index: u8) -> Option<ValueOrAlias> {
self.context.expected().get(index as usize).copied()
}
#[track_caller]
pub fn unwrap_expected(&self, index: u8) -> ValueOrAlias {
match self.get_expected(index) {
Some(value) => value,
None => panic!(
"expected operand {index} does not exist: there are only {} expected operands",
self.context.arity()
),
}
}
#[allow(unused)]
pub fn get_current(&self, index: u8) -> Option<ValueOrAlias> {
self.pending.get(index as usize).copied()
}
#[track_caller]
pub fn unwrap_current(&self, index: u8) -> ValueOrAlias {
self.pending.get(index as usize).copied().unwrap_or_else(|| {
panic!(
"operand {index} does not exist: the stack contains only {} operands",
self.pending.len()
)
})
}
pub fn get_expected_position(&self, value: &ValueOrAlias) -> Option<u8> {
self.context.expected().position(value).map(|index| index as u8)
}
#[track_caller]
pub fn unwrap_expected_position(&self, value: &ValueOrAlias) -> u8 {
match self.get_expected_position(value) {
Some(pos) => pos,
None => panic!("value {value:?} is not an expected operand"),
}
}
#[inline]
pub fn get_current_position(&self, value: &ValueOrAlias) -> Option<u8> {
self.pending.position(value).map(|index| index as u8)
}
pub fn get_current_position_skip(&self, start_index: u8, value: &ValueOrAlias) -> Option<u8> {
self.pending.position_skip(start_index as usize, value).map(|index| index as u8)
}
#[track_caller]
pub fn unwrap_current_position(&self, value: &ValueOrAlias) -> u8 {
match self.get_current_position(value) {
Some(pos) => pos,
None => panic!("value {value:?} not found on operand stack"),
}
}
pub fn is_expected(&self, index: u8) -> bool {
self.get_expected(index)
.map(|v| v == self.pending[index as usize])
.unwrap_or(false)
}
pub fn dup(&mut self, index: u8, alias_id: NonZeroU8) {
self.pending.dup(index as usize, alias_id);
self.actions.push(Action::Copy(index));
}
pub fn swap(&mut self, index: u8) {
self.pending.swap(index as usize);
self.actions.push(Action::Swap(index));
}
#[track_caller]
pub fn movup(&mut self, index: u8) {
assert_ne!(index, 0);
if index == 1 {
self.swap(index);
} else {
self.pending.movup(index as usize);
self.actions.push(Action::MoveUp(index));
}
}
#[track_caller]
pub fn movdn(&mut self, index: u8) {
assert_ne!(index, 0);
if index == 1 {
self.swap(index);
} else {
self.pending.movdn(index as usize);
self.actions.push(Action::MoveDown(index));
}
}
pub fn evict(&mut self) {
self.evict_from(0)
}
#[inline]
pub fn evict_from(&mut self, index: u8) {
if index > 0 {
self.movup(index);
}
self.movdn(self.context.arity() as u8);
}
}