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