use anathema_state::{PendingValue, StateId};
use anathema_store::slab::Key;
use crate::expression::{Kind, ValueExpr};
use crate::{Collection, Value, ValueKind};
#[derive(Debug)]
pub(crate) enum Entry<'parent, 'bp> {
Component { state: StateId, component_attributes: Key },
Collection(&'parent Collection<'bp>),
Index(&'bp str, usize, PendingValue),
WithValue(&'bp str, &'parent Value<'bp>),
Empty,
}
#[derive(Debug)]
pub struct Scope<'parent, 'bp> {
outer: Option<&'parent Scope<'parent, 'bp>>,
parent: Option<&'parent Scope<'parent, 'bp>>,
value: Entry<'parent, 'bp>,
}
impl<'parent, 'bp> Scope<'parent, 'bp> {
fn new(value: Entry<'parent, 'bp>) -> Self {
Self {
parent: None,
outer: None,
value,
}
}
pub fn with_component(state: StateId, attributes: Key, outer: Option<&'parent Scope<'parent, 'bp>>) -> Self {
Self {
outer,
parent: None,
value: Entry::Component {
state,
component_attributes: attributes,
},
}
}
pub fn with_collection(collection: &'parent Collection<'bp>, parent: &'parent Scope<'parent, 'bp>) -> Self {
let value = Entry::Collection(collection);
Self {
outer: None,
parent: Some(parent),
value,
}
}
pub fn with_index(
binding: &'bp str,
index: usize,
parent: &'parent Scope<'parent, 'bp>,
loop_index: PendingValue,
) -> Self {
let value = Entry::Index(binding, index, loop_index);
Self {
value,
parent: Some(parent),
outer: None,
}
}
pub fn with_value(binding: &'bp str, value: &'parent Value<'bp>, parent: &'parent Scope<'parent, 'bp>) -> Self {
let value = Entry::WithValue(binding, value);
Self {
outer: None,
parent: Some(parent),
value,
}
}
pub fn root() -> Self {
Self::empty()
}
pub fn empty() -> Self {
Self::new(Entry::Empty)
}
pub(crate) fn get_state(&self) -> Option<StateId> {
match &self.value {
Entry::Component { state, .. } => Some(*state),
_ => self.parent?.get_state(),
}
}
pub(crate) fn get_attributes(&self) -> Option<Key> {
match &self.value {
Entry::Component {
component_attributes, ..
} => Some(*component_attributes),
_ => self.parent?.get_attributes(),
}
}
pub(crate) fn lookup(&self, key: &str) -> Option<ValueExpr<'bp>> {
match self.value {
Entry::WithValue(ident, val) if ident == key => Some(val.expr.clone()),
Entry::Index(_, _, loop_index) if key == "loop" => Some(ValueExpr::Int(Kind::Dyn(loop_index))),
Entry::Index(binding, index, _) if key == binding => {
match self.parent.expect("the parent can only be a collection").value {
Entry::Collection(collection) => match &collection.0.kind {
ValueKind::List(_) => {
let value_expr = ValueExpr::Index(
collection.0.expr.clone().into(),
ValueExpr::Int(Kind::Static(index as i64)).into(),
);
Some(value_expr)
}
ValueKind::DynList(value) => {
let state = value.as_state()?;
let list = state.as_any_list()?;
let value = list.lookup(index)?;
Some(value.into())
}
&ValueKind::Range(from, to) => (from..to)
.skip(index)
.take(1)
.map(|num| ValueExpr::Int(Kind::Static(num as i64)))
.next(),
_ => unreachable!("none of the other values can be a collection"),
},
_ => unreachable!("the parent scope is always a collection"),
}
}
_ => self.parent?.lookup(key),
}
}
pub fn outer(&self) -> &'parent Scope<'parent, 'bp> {
match self.outer {
Some(scope) => scope,
None => match self.parent {
Some(parent) => parent.outer(),
None => panic!("no outer scope, no parent"),
},
}
}
}
#[cfg(test)]
mod test {
#[test]
fn scope_one() {
}
}