Skip to main content

nifty_interp/machine/
carry.rs

1//! Which values survive a reload: a state keeps its value when the new program still declares
2//! it with the same name and a type whose values are laid out the same way.
3
4use nifty_check::hir::{Program, StateDef, StructId, Type};
5
6use super::Machine;
7use crate::value::Value;
8
9/// The value `state`, declared by the program `next`, keeps from `previous`, if any.
10pub(super) fn kept_value(previous: &Machine, state: &StateDef, next: &Program) -> Option<Value> {
11    let program = &previous.program;
12    let index = program
13        .states
14        .iter()
15        .position(|old| old.name == state.name)?;
16    let mut types = SameTypes {
17        old: program,
18        new: next,
19        assumed: Vec::new(),
20    };
21    types
22        .same(&program.states[index].ty, &state.ty)
23        .then(|| previous.states.borrow()[index].clone())
24}
25
26/// Compares types across two programs, where struct ids differ but names and fields decide.
27struct SameTypes<'a> {
28    old: &'a Program,
29    new: &'a Program,
30    /// Struct pairs being compared further up, taken as equal so recursive structs end.
31    assumed: Vec<(StructId, StructId)>,
32}
33
34impl SameTypes<'_> {
35    fn same(&mut self, old: &Type, new: &Type) -> bool {
36        match (old, new) {
37            (Type::Int, Type::Int)
38            | (Type::Float, Type::Float)
39            | (Type::Bool, Type::Bool)
40            | (Type::String, Type::String) => true,
41            (Type::List(old), Type::List(new)) => self.same(old, new),
42            (Type::Struct(old), Type::Struct(new)) => self.same_struct(*old, *new),
43            _ => false,
44        }
45    }
46
47    fn same_struct(&mut self, old_id: StructId, new_id: StructId) -> bool {
48        if self.assumed.contains(&(old_id, new_id)) {
49            return true;
50        }
51        let (old, new) = (self.old, self.new);
52        let old = &old.structs[old_id.0];
53        let new = &new.structs[new_id.0];
54        if old.name != new.name || old.fields.len() != new.fields.len() {
55            return false;
56        }
57        self.assumed.push((old_id, new_id));
58        let same = old
59            .fields
60            .iter()
61            .zip(&new.fields)
62            .all(|(old, new)| old.name == new.name && self.same(&old.ty, &new.ty));
63        self.assumed.pop();
64        same
65    }
66}