Add Binder helper for binding variables and then unbinding them afterwards

This commit is contained in:
Olivier 'reivilibre' 2023-11-21 23:16:55 +00:00
parent 3b369200aa
commit cbfdece2cb

View File

@ -110,6 +110,41 @@ impl Clone for Value {
}
}
pub(crate) struct Binder {
variables_to_unbind: Vec<String>,
}
impl Binder {
pub fn new() -> Self {
Binder {
variables_to_unbind: Vec::new(),
}
}
pub fn bind(
&mut self,
variables: &mut BTreeMap<String, Value>,
binding: &Binding,
value: Value,
) {
// TODO duplicated code
match binding {
Binding::Variable(var_name) => {
let var_name = String::from(var_name as &str);
variables.insert(var_name.clone(), value);
self.variables_to_unbind.push(var_name);
}
Binding::Ignore => {}
}
}
pub fn unbind(self, variables: &mut BTreeMap<String, Value>) {
for var_name in self.variables_to_unbind {
variables.remove(&var_name);
}
}
}
impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpreter<'a, O, LS> {
#[async_recursion]
pub async fn run_steps(
@ -197,25 +232,10 @@ impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpret
self.run_steps(scope_idx, empty_steps).await?;
} else {
for val in list {
// TODO duplicated code
match binding {
Binding::Variable(var) => {
self.scopes[scope_idx]
.variables
.insert(String::from(var as &str), val);
}
Binding::Ignore => {}
}
let mut binder = Binder::new();
binder.bind(&mut self.scopes[scope_idx].variables, &binding, val);
self.run_steps(scope_idx, body_steps).await?;
}
// TODO duplicated code
match binding {
Binding::Variable(var) => {
self.scopes[scope_idx].variables.remove(var as &str);
}
Binding::Ignore => {}
binder.unbind(&mut self.scopes[scope_idx].variables);
}
}
}
@ -228,25 +248,14 @@ impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpret
// TODO(performance) I'd like to remove this clone!
// Can possibly do so with a Yoke or something...
let val = list.get(idx).expect("checked iter").clone_value();
// TODO duplicated code
match binding {
Binding::Variable(var) => {
self.scopes[scope_idx].variables.insert(
String::from(var as &str),
Value::from_reflect(val),
);
}
Binding::Ignore => {}
}
let mut binder = Binder::new();
binder.bind(
&mut self.scopes[scope_idx].variables,
&binding,
Value::from_reflect(val),
);
self.run_steps(scope_idx, body_steps).await?;
}
// TODO duplicated code
match binding {
Binding::Variable(var) => {
self.scopes[scope_idx].variables.remove(var as &str);
}
Binding::Ignore => {}
binder.unbind(&mut self.scopes[scope_idx].variables);
}
}
}
@ -273,6 +282,8 @@ impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpret
let matchable_evaled =
self.evaluate_expression(scope_idx, matchable, &step.locator)?;
let mut binder = Binder::new();
for (arm_binding, arm_steps) in arms {
// if this arm's binding matches, then bind the variable...
match arm_binding {
@ -331,21 +342,11 @@ impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpret
for (piece, field) in
pieces.iter().zip(reflenum.iter_fields())
{
// TODO duplicated code. Should probably make some 'Binder' tool that also makes it easy to unbind afterwards!
match piece {
Binding::Variable(var) => {
self.scopes[scope_idx].variables.insert(
String::from(var as &str),
// TODO would be nice to avoid this clone!
Value::from_reflect(
field.value().clone_value(),
),
);
}
Binding::Ignore => {
// nop.
}
}
binder.bind(
&mut self.scopes[scope_idx].variables,
piece,
Value::from_reflect(field.value().clone_value()),
);
}
}
_ => {
@ -371,24 +372,7 @@ impl<'a, O: OutputSystem + Send, LS: LocalisationSystem + Sync + Send> Interpret
self.run_steps(scope_idx, arm_steps).await?;
// and then unbind the variables.
match arm_binding {
MatchBinding::TupleVariant { pieces, .. } => {
for piece in pieces {
// TODO duplicated code. Should probably make some 'Binder' tool that also makes it easy to unbind afterwards!
match piece {
Binding::Variable(var) => {
self.scopes[scope_idx].variables.remove(var as &str);
}
Binding::Ignore => {
// nop.
}
}
}
}
MatchBinding::UnitVariant { .. } | MatchBinding::Ignore => {
// no variables to unbind
}
};
binder.unbind(&mut self.scopes[scope_idx].variables);
// (don't fall through to other arms since we matched this one)
break;