use bitflags::bitflags;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
bitflags! {
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StepFlags: u8 {
const SKIP_IF_UNIT = 0b_0000_0001;
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Step {
Index {
operand: u16,
flags: StepFlags,
pos: rhai::Position,
bracket: rhai::Position,
},
Property {
name: u32,
getter: u32,
setter: u32,
flags: StepFlags,
pos: rhai::Position,
},
Method {
name: u32,
argc: u8,
operand: u16,
flags: StepFlags,
pos: rhai::Position,
},
}
impl Step {
#[must_use]
pub fn position_slots(&self) -> u32 {
match self {
Step::Index { .. } => 2,
Step::Property { .. } | Step::Method { .. } => 1,
}
}
#[cfg(feature = "internals")]
pub fn disassemble(&self, program: &crate::grain::Program) -> String {
match self {
Step::Index { operand, flags, .. } => {
format!(
"{}[{operand}]",
if flags.contains(StepFlags::SKIP_IF_UNIT) {
"?"
} else {
""
}
)
}
Step::Property { name, flags, .. } => {
format!(
".{}{}",
if flags.contains(StepFlags::SKIP_IF_UNIT) {
"?"
} else {
""
},
program.name(*name).unwrap()
)
}
Step::Method {
name,
argc,
operand,
flags,
..
} if *argc > 0 => format!(
".{}{}({argc} args from {operand})",
if flags.contains(StepFlags::SKIP_IF_UNIT) {
"?"
} else {
""
},
program.name(*name).unwrap()
),
Step::Method { name, flags, .. } => format!(
".{}{}()",
if flags.contains(StepFlags::SKIP_IF_UNIT) {
"?"
} else {
""
},
program.name(*name).unwrap()
),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Tail {
Read,
Assign {
op: Option<u32>,
},
}
impl Tail {
#[cfg(feature = "internals")]
pub fn disassemble(&self, program: &crate::grain::Program) -> String {
match self {
Tail::Read => String::new(),
Tail::Assign { op } => match op {
Some(op) => format!("{}", program.assign_op(*op).unwrap().disassemble(program)),
None => "=".to_string(),
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Root {
Local {
slot: u16,
name: u32,
},
Named {
name: u32,
pos: rhai::Position,
},
This {
pos: rhai::Position,
},
Temporary,
}
impl Root {
#[must_use]
pub fn position_slots(&self) -> u32 {
match self {
Root::Named { .. } | Root::This { .. } => 1,
Root::Local { .. } | Root::Temporary => 0,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Chain {
pub root: Root,
pub steps: Vec<Step>,
pub tail: Tail,
pub operands: u16,
}
impl Chain {
#[must_use]
pub fn assigns(&self) -> bool {
matches!(self.tail, Tail::Assign { .. })
}
#[must_use]
pub fn roots_on_stack(&self) -> bool {
match self.root {
Root::Local { .. } | Root::Named { .. } | Root::This { .. } => false,
Root::Temporary => true,
}
}
#[must_use]
pub fn consumes(&self) -> usize {
self.operands as usize + usize::from(self.roots_on_stack()) + usize::from(self.assigns())
}
#[must_use]
pub fn position_slots(&self) -> u32 {
self.root.position_slots() + self.steps.iter().map(Step::position_slots).sum::<u32>()
}
#[must_use]
pub fn step_slot(&self, index: usize) -> Option<u32> {
(index < self.steps.len()).then(|| {
self.root.position_slots()
+ self.steps[..index]
.iter()
.map(Step::position_slots)
.sum::<u32>()
})
}
pub fn positions(&self) -> impl Iterator<Item = rhai::Position> + '_ {
let root = match self.root {
Root::Named { pos, .. } | Root::This { pos } => Some(pos),
Root::Local { .. } | Root::Temporary => None,
};
root.into_iter().chain(
self.steps
.iter()
.flat_map(|step| match step {
Step::Index { pos, bracket, .. } => [Some(*pos), Some(*bracket)],
Step::Property { pos, .. } | Step::Method { pos, .. } => [Some(*pos), None],
})
.flatten(),
)
}
pub fn positions_mut(&mut self) -> impl Iterator<Item = &mut rhai::Position> {
let Self { root, steps, .. } = self;
let root = match root {
Root::Named { pos, .. } | Root::This { pos } => Some(pos),
Root::Local { .. } | Root::Temporary => None,
};
root.into_iter().chain(
steps
.iter_mut()
.flat_map(|step| match step {
Step::Index { pos, bracket, .. } => [Some(pos), Some(bracket)],
Step::Property { pos, .. } | Step::Method { pos, .. } => [Some(pos), None],
})
.flatten(),
)
}
#[cfg(feature = "internals")]
pub fn disassemble(&self, program: &crate::grain::Program) -> String {
format!(
"{:?} {} {} {}",
self.root,
match self.root {
Root::Named { name, .. } | Root::Local { name, .. } => program.name(name).unwrap(),
Root::This { .. } => "this",
Root::Temporary => "<temp value>",
},
self.steps
.iter()
.map(|step| step.disassemble(program))
.collect::<Vec<_>>()
.join(" "),
self.tail.disassemble(program)
)
}
}