#[derive(Debug)]
#[non_exhaustive]
pub struct Support {
pub ast_interp: bool,
pub stack: bool,
pub mir: MirSupport,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct MirSupport {
pub stack: bool,
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Target {
AstInterp,
Stack,
Mir(MirTarget),
}
#[derive(Debug)]
#[non_exhaustive]
pub enum MirTarget {
Stack,
}
impl Target {
pub fn features(&self) -> Features {
match self {
Target::AstInterp | Target::Stack => Features {
basic: true,
keep_high: true,
keep_low: false,
filters: false,
explosion: false,
},
Target::Mir(MirTarget::Stack) => Features {
basic: true,
keep_high: true,
keep_low: true,
filters: false,
explosion: true,
},
}
}
pub fn supports(&self, features: Features) -> bool {
let supported = self.features();
(!features.basic || supported.basic)
&& (!features.keep_high || supported.keep_high)
&& (!features.keep_low || supported.keep_low)
&& (!features.filters || supported.filters)
&& (!features.explosion || supported.explosion)
}
}
#[derive(Default, Debug, Copy, Clone)]
#[non_exhaustive]
pub struct Features {
pub basic: bool,
pub keep_high: bool,
pub keep_low: bool,
pub filters: bool,
pub explosion: bool,
}
impl Features {
pub fn supported_by(&self) -> Support {
Support {
ast_interp: Target::AstInterp.supports(*self),
stack: Target::Stack.supports(*self),
mir: MirSupport {
stack: Target::Mir(MirTarget::Stack).supports(*self),
},
}
}
pub fn of(expression: &crate::parse::Program) -> Self {
use crate::for_;
use crate::parse::Term;
let mut features = Features::default();
for_! [ (term, _ancestors) in expression.postorder() => {
match term {
Term::Constant(_) | Term::DiceRoll(_, _) | Term::Add(_, _) |
Term::Subtract(_, _) | Term::UnarySubtract(_) |
Term::UnaryAdd(_) => features.basic = true,
Term::KeepHigh(_, _) => features.keep_high = true,
Term::KeepLow(_, _) => features.keep_low = true,
Term::Explode(_) => features.explosion = true,
}
}];
features
}
}
pub mod type_level {
#[allow(non_snake_case)]
pub mod Target {
mod seal {
pub trait Sealed {}
pub trait Target: Sealed + Into<crate::backend_support::Target> {
fn reify() -> crate::backend_support::Target;
}
}
pub use seal::Target;
macro_rules! decl_backends {
(@[$m:ident [$($b:ident => $c:ident),*]]) => {
#[allow(non_snake_case)]
pub mod $m {
use super::seal::Target;
$(use crate::backend_support:: $c;
pub struct $b;
impl super::seal::Sealed for $b {}
impl From<$b> for crate::backend_support::Target {
fn from($b: $b) -> Self {
crate::backend_support::Target :: $m ( $c :: $b )
}
}
impl Target for $b {
fn reify() -> crate::backend_support::Target {
$b.into()
}
})*
}
};
(@[$t:ident]) => {
pub struct $t;
impl seal::Sealed for $t {}
impl From<$t> for crate::backend_support::Target {
fn from($t: $t) -> Self {
crate::backend_support::Target:: $t
}
}
impl Target for $t {
fn reify() -> crate::backend_support::Target {
$t.into()
}
}
};
($( $a:tt $([$($b:ident => $c:ident),*])? ),*) => {
$(decl_backends! {@[
$a $([$($b => $c),*])?
]})*
};
}
decl_backends![AstInterp, Stack, Mir[Stack => MirTarget]];
}
}
pub use type_level::Target as TyTarget;
pub struct UnsupportedFeature;
#[derive(::derive_more::Deref)]
pub struct FeatureCheckedProgram<'a, Target> {
#[deref]
program: &'a crate::parse::Program,
_target: ::core::marker::PhantomData<Target>,
}
impl<'a, T: TyTarget::Target> FeatureCheckedProgram<'a, T> {
pub fn check(program: &'a crate::parse::Program) -> Result<Self, UnsupportedFeature> {
if T::reify().supports(Features::of(program)) {
Ok(Self {
program,
_target: ::core::marker::PhantomData,
})
} else {
Err(UnsupportedFeature)
}
}
}