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()
}
}