From cbfdece2cbdf0dd9acdda4b6a4877ee814b8d580 Mon Sep 17 00:00:00 2001 From: Olivier Date: Tue, 21 Nov 2023 23:16:55 +0000 Subject: [PATCH] Add Binder helper for binding variables and then unbinding them afterwards --- hornbeam_interpreter/src/engine.rs | 122 +++++++++++++---------------- 1 file changed, 53 insertions(+), 69 deletions(-) diff --git a/hornbeam_interpreter/src/engine.rs b/hornbeam_interpreter/src/engine.rs index f7f1934..d7b10dc 100644 --- a/hornbeam_interpreter/src/engine.rs +++ b/hornbeam_interpreter/src/engine.rs @@ -110,6 +110,41 @@ impl Clone for Value { } } +pub(crate) struct Binder { + variables_to_unbind: Vec, +} + +impl Binder { + pub fn new() -> Self { + Binder { + variables_to_unbind: Vec::new(), + } + } + + pub fn bind( + &mut self, + variables: &mut BTreeMap, + 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) { + 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;