1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use super::Value;

pub struct FixedPointStack<Input, Output> {
    entries: Vec<StackEntry<Input, Output>>,
}

impl<Input, Output> Default for FixedPointStack<Input, Output> {
    fn default() -> Self {
        Self {
            entries: Default::default(),
        }
    }
}

struct StackEntry<Input, Output> {
    /// Input.
    input: Input,

    /// Current output, updated during computation as we approach a fixed point.
    output: Output,

    /// Initially false; set to true when the outputs of this rule
    /// are observed while it is being evaluated.
    has_dependents: bool,
}

impl<Input, Output> FixedPointStack<Input, Output>
where
    Input: Value,
    Output: Value,
{
    /// Access the top frame on the stack, which should be for `input`.
    fn top_frame(&mut self, input: &Input) -> &mut StackEntry<Input, Output> {
        let top = self.entries.last_mut().unwrap();
        assert_eq!(top.input, *input);
        top
    }

    /// Search backwards through the stack, looking for the given input.
    ///
    /// If it is found, return `Some` with the current outputs, and mark it
    /// as needing fixed point iteration.
    ///
    /// If not, return `None`.
    ///
    /// The fixed-point mark is returned when the stack is [popped](`Self::pop`) and is used
    /// as part of the fixed point algorithm.
    pub fn search(&mut self, input: &Input) -> Option<Output> {
        for entry in &mut self.entries {
            if entry.input == *input {
                entry.has_dependents = true;
                return Some(entry.output.clone());
            }
        }

        None
    }

    /// Push an entry onto the stack, indicating it is currently being evaluated.
    /// There must not already be an entry for `input`.
    pub fn push(&mut self, input: &Input, output: Output) {
        assert!(self.search(input).is_none());

        self.entries.push(StackEntry {
            input: input.clone(),
            output,
            has_dependents: false,
        });
    }

    /// Add outputs to the top-most stack entry, which must be for `input`.
    /// Returns true if another iteration is needed before reaching a fixed point.
    pub fn update_output(&mut self, input: &Input, output: Output) -> bool {
        let top = self.top_frame(input);
        if top.output == output {
            return false;
        }

        top.output = output;
        top.has_dependents
    }

    /// Pops the top entry from the stack, returning the saved outputs.
    pub fn pop(&mut self, input: &Input) -> Output {
        let top = self.entries.pop().unwrap();
        assert_eq!(top.input, *input);
        top.output
    }
}