conjure_core/ast/
symbol_table.rs

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
//! The (global) symbol table ([`SymbolTable`]), mapping identifiers (of type [`Name`]) to their
//! definitions.
//!
//! This only contains one type of definition at present, [`DecisionVariable`].

use std::collections::BTreeSet;
use std::fmt::Display;
use std::{cell::RefCell, collections::BTreeMap};

use serde::{Deserialize, Serialize};
use serde_with::serde_as;

use crate::ast::variables::DecisionVariable;

use super::{Domain, ReturnType};
use derivative::Derivative;

/// A reference to an object stored in the [`SymbolTable`].
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Name {
    /// A name given in the input model.
    UserName(String),
    /// A name generated by Conjure-Oxide.
    MachineName(i32),
}

uniplate::derive_unplateable!(Name);

impl Display for Name {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Name::UserName(s) => write!(f, "{}", s),
            Name::MachineName(i) => write!(f, "__{}", i),
        }
    }
}

/// The global symbol table. Maps [`Names`](Name) to their definitions.
///
/// Names in the symbol table are unique, including between different types of object stored in the
/// symbol table. For example, you cannot have a letting and decision variable with the same name.
#[derive(Derivative)]
#[derivative(Hash, PartialEq)]
#[serde_as]
#[derive(Clone, Debug, Eq, Serialize, Deserialize)]
pub struct SymbolTable {
    // doing some information hiding here to allow for future refactoring (in particular adding
    // lettings, removing variables).
    #[serde_as(as = "Vec<(_, _)>")]
    variables: BTreeMap<Name, DecisionVariable>,

    #[derivative(Hash = "ignore")]
    #[derivative(PartialEq = "ignore")]
    next_machine_name: RefCell<i32>,
}

impl SymbolTable {
    /// Creates an empty symbol table.
    pub fn new() -> Self {
        SymbolTable {
            variables: BTreeMap::new(),
            next_machine_name: RefCell::new(0),
        }
    }

    /*****************************/
    /*        get entries        */
    /*****************************/

    /// Returns an iterator over the names in the symbol table.
    ///
    /// Alias of [`names`].
    pub fn keys(&self) -> impl Iterator<Item = &Name> {
        self.names()
    }

    /// Returns an iterator over the names in the symbol table.
    pub fn names(&self) -> impl Iterator<Item = &Name> {
        self.variables.keys()
    }

    /// Returns an iterator over the names and defintions of all decision variables in the symbol
    /// table.
    pub fn iter_var(&self) -> impl Iterator<Item = (&Name, &DecisionVariable)> {
        self.variables.iter()
    }

    /// Returns a reference to the decision variable with the given name.
    ///
    /// Returns `None` if:
    ///
    /// + There is no decision variable with that name.
    ///
    /// + The decision variable with that name has been deleted.
    ///
    /// + The object with that name is not a decision variable.
    pub fn get_var(&self, name: &Name) -> Option<&DecisionVariable> {
        self.variables.get(name)
    }

    /// Returns a mutable reference to the decision variable with the given name.
    ///
    /// Returns `None` if:
    ///
    /// + There is no decision variable with that name.
    ///
    /// + The decision variable with that name has been deleted.
    ///
    /// + The object with that name is not a decision variable.
    pub fn get_var_mut(&mut self, name: &Name) -> Option<&mut DecisionVariable> {
        self.variables.get_mut(name)
    }

    /********************************/
    /*        mutate entries        */
    /********************************/

    /// Adds a decision variable to the symbol table as `name`.
    ///
    /// Returns `None` if there is a decision variable or other object with that name in the symbol
    /// table.
    pub fn add_var(&mut self, name: Name, var: DecisionVariable) -> Option<()> {
        if let std::collections::btree_map::Entry::Vacant(e) = self.variables.entry(name) {
            e.insert(var);
            Some(())
        } else {
            None
        }
    }

    /// Updates a decision variable to the symbol table as `name`, or adds it.
    ///
    /// Returns `None` if `name` refers to an object that is not a decision variable.
    pub fn update_add_var(&mut self, name: Name, var: DecisionVariable) -> Option<()> {
        self.variables.insert(name, var);
        Some(())
    }

    /// Extends the symbol table with the given symbol table, updating the gensym counter if
    /// necessary.
    pub fn extend(&mut self, other: SymbolTable) {
        if other.names().count() > self.names().count() {
            let new_vars = other.names().collect::<BTreeSet<_>>();
            let old_vars = self.names().collect::<BTreeSet<_>>();

            for added_var in new_vars.difference(&old_vars) {
                let mut next_var = self.next_machine_name.borrow_mut();
                match *added_var {
                    Name::UserName(_) => {}
                    Name::MachineName(m) => {
                        if *m >= *next_var {
                            *next_var = *m + 1;
                        }
                    }
                }
            }
        }
        self.variables.extend(other.variables);
    }

    /****************************************/
    /*        get info about symbols        */
    /****************************************/

    /// Gets the domain of `name` if it exists and has a domain.
    pub fn domain_of(&self, name: &Name) -> Option<&Domain> {
        Some(&self.variables.get(name)?.domain)
    }

    /// Gets the domain of `name` as a mutable reference if it exists and has a domain.
    pub fn domain_of_mut(&mut self, name: &Name) -> Option<&mut Domain> {
        Some(&mut self.variables.get_mut(name)?.domain)
    }

    /// Gets the type of `name` if it exists and has a type.
    pub fn type_of(&self, name: &Name) -> Option<ReturnType> {
        match self.domain_of(name)? {
            Domain::BoolDomain => Some(ReturnType::Bool),
            Domain::IntDomain(_) => Some(ReturnType::Int),
        }
    }

    /// Returns an arbitrary variable name that is not in the symbol table.
    pub fn gensym(&self) -> Name {
        let num = *self.next_machine_name.borrow();
        *(self.next_machine_name.borrow_mut()) += 1;
        Name::MachineName(num) // incremented when inserted
    }
}

impl Default for SymbolTable {
    fn default() -> Self {
        Self::new()
    }
}