conjure_core/ast/symbol_table.rs
1//! The symbol table.
2//!
3//! See the item documentation for [`SymbolTable`] for more details.
4
5use crate::bug;
6use crate::representation::{get_repr_rule, Representation};
7
8use super::comprehension::Comprehension;
9use super::serde::{RcRefCellAsId, RcRefCellAsInner};
10use std::cell::RefCell;
11use std::collections::btree_map::Entry;
12use std::collections::BTreeSet;
13use std::collections::{BTreeMap, VecDeque};
14use std::rc::Rc;
15use std::sync::atomic::{AtomicU32, Ordering};
16
17use super::declaration::Declaration;
18use super::serde::{DefaultWithId, HasId, ObjId};
19use super::types::Typeable;
20use itertools::{izip, Itertools as _};
21use serde::{Deserialize, Serialize};
22use serde_with::serde_as;
23use uniplate::Tree;
24use uniplate::{Biplate, Uniplate};
25
26use super::name::Name;
27use super::{Domain, Expression, ReturnType, SubModel};
28use derivative::Derivative;
29
30// Count symbol tables per thread / model.
31//
32// We run tests in parallel and having the id's be per thread keeps them more deterministic in the
33// JSON output. If this were not thread local, ids would be given to symbol tables differently in
34// each test run (depending on how the threads were scheduled). These id changes would result in
35// all the generated tests "changing" each time `ACCEPT=true cargo test` is ran.
36//
37// SAFETY: Symbol tables use Rc<RefCell<<>>, so a model is not thread-safe anyways.
38thread_local! {
39static ID_COUNTER: AtomicU32 = const { AtomicU32::new(0) };
40}
41
42/// The global symbol table, mapping names to their definitions.
43///
44/// Names in the symbol table are unique, including between different types of object stored in the
45/// symbol table. For example, you cannot have a letting and decision variable with the same name.
46///
47/// # Symbol Kinds
48///
49/// The symbol table tracks the following types of symbol:
50///
51/// ## Decision Variables
52///
53/// ```text
54/// find NAME: DOMAIN
55/// ```
56///
57/// See [`DecisionVariable`](super::DecisionVariable).
58///
59/// ## Lettings
60///
61/// Lettings define constants, of which there are two types:
62///
63/// + **Constant values**: `letting val be A`, where A is an [`Expression`].
64///
65/// A can be any integer, boolean, or matrix expression.
66/// A can include references to other lettings, model parameters, and, unlike Savile Row,
67/// decision variables.
68///
69/// + **Constant domains**: `letting Domain be domain D`, where D is a [`Domain`].
70///
71/// D can include references to other lettings and model parameters, and, unlike Savile Row,
72/// decision variables.
73///
74/// Unless otherwise stated, these follow the semantics specified in section 2.2.2 of the Savile
75/// Row manual (version 1.9.1 at time of writing).
76#[derive(Derivative)]
77#[derivative(PartialEq)]
78#[derive(Debug, Eq)]
79#[serde_as]
80#[derive(Serialize, Deserialize)]
81pub struct SymbolTable {
82 #[serde_as(as = "Vec<(_,RcRefCellAsInner)>")]
83 table: BTreeMap<Name, Rc<Declaration>>,
84
85 /// A unique id for this symbol table, for serialisation and debugging.
86 #[derivative(PartialEq = "ignore")] // eq by value not id.
87 id: ObjId,
88
89 #[serde_as(as = "Option<RcRefCellAsId>")]
90 parent: Option<Rc<RefCell<SymbolTable>>>,
91
92 next_machine_name: RefCell<i32>,
93}
94
95impl SymbolTable {
96 /// Creates an empty symbol table.
97 pub fn new() -> SymbolTable {
98 SymbolTable::new_inner(None)
99 }
100
101 /// Creates an empty symbol table with the given parent.
102 pub fn with_parent(parent: Rc<RefCell<SymbolTable>>) -> SymbolTable {
103 SymbolTable::new_inner(Some(parent))
104 }
105
106 fn new_inner(parent: Option<Rc<RefCell<SymbolTable>>>) -> SymbolTable {
107 let id = ID_COUNTER.with(|x| x.fetch_add(1, Ordering::Relaxed));
108 SymbolTable {
109 id,
110 table: BTreeMap::new(),
111 next_machine_name: RefCell::new(0),
112 parent,
113 }
114 }
115
116 /// Looks up the declaration with the given name in the current scope only.
117 ///
118 /// Returns `None` if there is no declaration with that name in the current scope.
119 pub fn lookup_local(&self, name: &Name) -> Option<Rc<Declaration>> {
120 self.table.get(name).cloned()
121 }
122
123 /// Looks up the declaration with the given name, checking all enclosing scopes.
124 ///
125 /// Returns `None` if there is no declaration with that name in scope.
126 pub fn lookup(&self, name: &Name) -> Option<Rc<Declaration>> {
127 self.lookup_local(name).or_else(|| {
128 self.parent
129 .as_ref()
130 .and_then(|parent| (*parent).borrow().lookup(name))
131 })
132 }
133
134 /// Inserts a declaration into the symbol table.
135 ///
136 /// Returns `None` if there is already a symbol with this name in the local scope.
137 pub fn insert(&mut self, declaration: Rc<Declaration>) -> Option<()> {
138 let name = declaration.name().clone();
139 if let Entry::Vacant(e) = self.table.entry(name) {
140 e.insert(declaration);
141 Some(())
142 } else {
143 None
144 }
145 }
146
147 /// Updates or adds a declaration in the immediate local scope.
148 pub fn update_insert(&mut self, declaration: Rc<Declaration>) {
149 let name = declaration.name().clone();
150 self.table.insert(name, declaration);
151 }
152
153 /// Looks up the return type for name if it has one and is in scope.
154 pub fn return_type(&self, name: &Name) -> Option<ReturnType> {
155 self.lookup(name).and_then(|x| x.return_type())
156 }
157
158 /// Looks up the return type for name if has one and is in the local scope.
159 pub fn return_type_local(&self, name: &Name) -> Option<ReturnType> {
160 self.lookup_local(name).and_then(|x| x.return_type())
161 }
162
163 /// Looks up the domain of name if it has one and is in scope.
164 ///
165 /// This method can return domain references: if a ground domain is always required, use
166 /// [`SymbolTable::resolve_domain`].
167 pub fn domain(&self, name: &Name) -> Option<Domain> {
168 // TODO: do not clone here: in the future, we might want to wrap all domains in Rc's to get
169 // clone-on-write behaviour (saving memory in scenarios such as matrix decomposition where
170 // a lot of the domains would be the same).
171
172 if let Name::WithRepresentation(name, _) = name {
173 let decl = self.lookup(name.as_ref())?;
174 decl.domain().cloned()
175 } else {
176 let decl = self.lookup(name)?;
177 decl.domain().cloned()
178 }
179 }
180
181 /// Looks up the domain of name, resolving domain references to ground domains.
182 ///
183 /// See [`SymbolTable::domain`].
184 pub fn resolve_domain(&self, name: &Name) -> Option<Domain> {
185 self.domain(name).map(|domain| domain.resolve(self))
186 }
187
188 /// Iterates over entries in the local symbol table only.
189 pub fn into_iter_local(self) -> LocalIntoIter {
190 LocalIntoIter {
191 inner: self.table.into_iter(),
192 }
193 }
194
195 /// Extends the symbol table with the given symbol table, updating the gensym counter if
196 /// necessary.
197 pub fn extend(&mut self, other: SymbolTable) {
198 if other.table.keys().count() > self.table.keys().count() {
199 let new_vars = other.table.keys().collect::<BTreeSet<_>>();
200 let old_vars = self.table.keys().collect::<BTreeSet<_>>();
201
202 for added_var in new_vars.difference(&old_vars) {
203 let mut next_var = self.next_machine_name.borrow_mut();
204 if let Name::Machine(m) = *added_var {
205 if *m >= *next_var {
206 *next_var = *m + 1;
207 }
208 }
209 }
210 }
211
212 self.table.extend(other.table);
213 }
214
215 /// Returns an arbitrary variable name that is not in the symbol table.
216 pub fn gensym(&self) -> Name {
217 let num = *self.next_machine_name.borrow();
218 *(self.next_machine_name.borrow_mut()) += 1;
219 Name::Machine(num) // incremented when inserted
220 }
221
222 /// Gets the parent of this symbol table as a mutable reference.
223 ///
224 /// This function provides no sanity checks.
225 pub fn parent_mut_unchecked(&mut self) -> &mut Option<Rc<RefCell<SymbolTable>>> {
226 &mut self.parent
227 }
228
229 /// Gets the representation `representation` for `name`.
230 ///
231 /// # Returns
232 ///
233 /// + `None` if `name` does not exist, is not a decision variable, or does not have that representation.
234 pub fn get_representation(
235 &self,
236 name: &Name,
237 representation: &[&str],
238 ) -> Option<Vec<Box<dyn Representation>>> {
239 // TODO: move representation stuff to declaration / variable to avoid cloning? (we have to
240 // move inside of an rc here, so cannot return borrows)
241 //
242 // Also would prevent constant "does exist" "is var" checks.
243 //
244 // The reason it is not there now is because I'm getting serde issues...
245 //
246 // Also might run into issues putting get_or_add into declaration/variable, as that
247 // requires us to mutably borrow both the symbol table, and the variable inside the symbol
248 // table..
249
250 let decl = self.lookup(name)?;
251 let var = decl.as_var()?;
252
253 var.representations
254 .iter()
255 .find(|x| &x.iter().map(|r| r.repr_name()).collect_vec()[..] == representation)
256 .cloned()
257 .clone()
258 }
259
260 /// Gets all initialised representations for `name`.
261 ///
262 /// # Returns
263 ///
264 /// + `None` if `name` does not exist, or is not a decision variable.
265 pub fn representations_for(&self, name: &Name) -> Option<Vec<Vec<Box<dyn Representation>>>> {
266 let decl = self.lookup(name)?;
267 decl.as_var().map(|x| x.representations.clone())
268 }
269
270 /// Gets the representation `representation` for `name`, creating it if it does not exist.
271 ///
272 /// If the representation does not exist, this method initialises the representation in this
273 /// symbol table, adding the representation to `name`, and the declarations for the represented
274 /// variables to the symbol table.
275 ///
276 /// # Usage
277 ///
278 /// Representations for variable references should be selected and created by the
279 /// `select_representation` rule. Therefore, this method should not be used in other rules.
280 /// Consider using [`get_representation`](`SymbolTable::get_representation`) instead.
281 ///
282 /// # Returns
283 ///
284 /// + `None` if `name` does not exist, is not a decision variable, or cannot be given that
285 /// representation.
286 pub fn get_or_add_representation(
287 &mut self,
288 name: &Name,
289 representation: &[&str],
290 ) -> Option<Vec<Box<dyn Representation>>> {
291 let decl = self.lookup(name)?;
292 let var = decl.as_var()?;
293
294 var.representations
295 .iter()
296 .find(|x| &x.iter().map(|r| r.repr_name()).collect_vec()[..] == representation)
297 .cloned()
298 .clone()
299 .or_else(|| {
300 // add represented name declaration to the same scope as the name they are
301 // representing
302 if let Some(decl) = self.lookup_local(name) {
303 let mut decl: Declaration = Rc::unwrap_or_clone(decl);
304 let var = decl.as_var_mut()?;
305
306 // TODO: no idea how to deal with initialising representations for nested things..
307
308 if representation.len() != 1 {
309 bug!("nested representations not implemented")
310 }
311
312 let repr_rule = get_repr_rule(representation[0])?;
313
314 let reprs = vec![repr_rule(name, self)?];
315 for repr in &reprs {
316 repr.declaration_down()
317 .ok()
318 .unwrap()
319 .iter()
320 .for_each(|x| self.update_insert(Rc::new(x.clone())));
321 }
322 var.representations.push(reprs.clone());
323
324 self.update_insert(Rc::new(decl));
325 Some(reprs)
326 } else {
327 None
328
329 // declaration is in some parent table
330 //
331 // FIXME: this is broken. for now, just add representations for variables in
332 // our scope. This is slower, in particular, when we want to represent a
333 // decision variable referenced inside a comprehension, but it is correct
334 // nonetheless.
335 //
336 //
337 // let mut parent_table = self.parent.clone();
338 // let mut decl = None;
339 // while let Some(table) = parent_table.clone() {
340 // let table_ref= (*table).borrow();
341 // if let Some(d) = table_ref.lookup_local(name) {
342 // decl = Some(d);
343 // break;
344 // }
345 // parent_table = table_ref.parent.clone();
346 // }
347
348 // let mut decl: Declaration = Rc::unwrap_or_clone(
349 // decl.expect("declaration should exist: we used it earlier!"),
350 // );
351
352 // let parent_table = parent_table.unwrap();
353 // let mut table = parent_table.borrow_mut();
354
355 // let var = decl.as_var_mut()?;
356
357 // // TODO: no idea how to deal with initialising representations for nested things..
358
359 // if representation.len() != 1 {
360 // bug!("nested representations not implemented")
361 // }
362
363 // let repr_rule = get_repr_rule(representation[0])?;
364
365 // let reprs = vec![repr_rule(name, &table)?];
366 // for repr in &reprs {
367 // repr.declaration_down()
368 // .ok()
369 // .unwrap()
370 // .iter()
371 // .for_each(|x| table.update_insert(Rc::new(x.clone())));
372 // }
373 // var.representations.push(reprs.clone());
374
375 // table.update_insert(Rc::new(decl));
376 // dbg!(table);
377 // Some(reprs)
378 }
379 })
380 }
381}
382
383impl IntoIterator for SymbolTable {
384 type Item = (Name, Rc<Declaration>);
385
386 type IntoIter = IntoIter;
387
388 /// Iterates over symbol table entries in scope.
389 fn into_iter(self) -> Self::IntoIter {
390 IntoIter {
391 inner: self.table.into_iter(),
392 parent: self.parent,
393 }
394 }
395}
396
397/// Iterator over symbol table entries in the current scope only.
398pub struct LocalIntoIter {
399 // iterator over the btreemap
400 inner: std::collections::btree_map::IntoIter<Name, Rc<Declaration>>,
401}
402
403impl Iterator for LocalIntoIter {
404 type Item = (Name, Rc<Declaration>);
405
406 fn next(&mut self) -> Option<Self::Item> {
407 self.inner.next()
408 }
409}
410
411/// Iterator over all symbol table entries in scope.
412pub struct IntoIter {
413 // iterator over the current scopes' btreemap
414 inner: std::collections::btree_map::IntoIter<Name, Rc<Declaration>>,
415
416 // the parent scope
417 parent: Option<Rc<RefCell<SymbolTable>>>,
418}
419
420impl Iterator for IntoIter {
421 type Item = (Name, Rc<Declaration>);
422
423 fn next(&mut self) -> Option<Self::Item> {
424 let mut val = self.inner.next();
425
426 // Go up the tree until we find a parent symbol table with declarations to iterate over.
427 //
428 // Note that the parent symbol table may be empty - this is why this is a loop!
429 while val.is_none() {
430 let parent = self.parent.clone()?;
431 let parent_ref = (*parent).borrow();
432 self.parent = parent_ref.parent.clone();
433 self.inner = parent_ref.table.clone().into_iter();
434
435 val = self.inner.next();
436 }
437
438 val
439 }
440}
441
442impl HasId for SymbolTable {
443 fn id(&self) -> ObjId {
444 self.id
445 }
446}
447
448impl DefaultWithId for SymbolTable {
449 fn default_with_id(id: ObjId) -> Self {
450 Self {
451 table: BTreeMap::new(),
452 id,
453 parent: None,
454 next_machine_name: RefCell::new(0),
455 }
456 }
457}
458
459impl Clone for SymbolTable {
460 fn clone(&self) -> Self {
461 Self {
462 table: self.table.clone(),
463 id: ID_COUNTER.with(|x| x.fetch_add(1, Ordering::Relaxed)),
464 parent: self.parent.clone(),
465 next_machine_name: self.next_machine_name.clone(),
466 }
467 }
468}
469
470impl Default for SymbolTable {
471 fn default() -> Self {
472 Self::new_inner(None)
473 }
474}
475
476impl Uniplate for SymbolTable {
477 fn uniplate(&self) -> (Tree<Self>, Box<dyn Fn(Tree<Self>) -> Self>) {
478 // do not recurse up parents, that would be weird?
479 let self2 = self.clone();
480 (Tree::Zero, Box::new(move |_| self2.clone()))
481 }
482}
483
484impl Biplate<Expression> for SymbolTable {
485 fn biplate(&self) -> (Tree<Expression>, Box<dyn Fn(Tree<Expression>) -> Self>) {
486 let (child_trees, ctxs): (VecDeque<_>, Vec<_>) = self
487 .table
488 .values()
489 .map(|decl| <Declaration as Biplate<Expression>>::biplate(decl.as_ref()))
490 .unzip();
491
492 let tree = Tree::Many(child_trees);
493
494 let self2 = self.clone();
495 let ctx = Box::new(move |tree| {
496 let Tree::Many(exprs) = tree else {
497 panic!("unexpected children structure");
498 };
499
500 let mut self3 = self2.clone();
501 let self3_iter = self3.table.iter_mut();
502 for (ctx, tree, (_, decl)) in izip!(&ctxs, exprs, self3_iter) {
503 // update declaration inside the pointer instead of creating a new one, so all
504 // things referencing this keep referencing this.
505 *decl = Rc::new(ctx(tree));
506 }
507
508 self3
509 });
510
511 (tree, ctx)
512 }
513}
514
515impl Biplate<Comprehension> for SymbolTable {
516 fn biplate(
517 &self,
518 ) -> (
519 Tree<Comprehension>,
520 Box<dyn Fn(Tree<Comprehension>) -> Self>,
521 ) {
522 let (expr_tree, expr_ctx) = <SymbolTable as Biplate<Expression>>::biplate(self);
523 let (comprehension_tree, comprehension_ctx) =
524 <VecDeque<Expression> as Biplate<Comprehension>>::biplate(
525 &expr_tree.into_iter().collect(),
526 );
527
528 let ctx = Box::new(move |x| {
529 expr_ctx(Tree::Many(
530 comprehension_ctx(x).into_iter().map(Tree::One).collect(),
531 ))
532 });
533
534 (comprehension_tree, ctx)
535 }
536}
537
538impl Biplate<SubModel> for SymbolTable {
539 // walk into expressions
540 fn biplate(&self) -> (Tree<SubModel>, Box<dyn Fn(Tree<SubModel>) -> Self>) {
541 let (exprs, exprs_ctx) = <SymbolTable as Biplate<Expression>>::biplate(self);
542 let (submodel_tree, submodel_ctx) =
543 <VecDeque<Expression> as Biplate<SubModel>>::biplate(&exprs.into_iter().collect());
544
545 let ctx = Box::new(move |x| {
546 exprs_ctx(Tree::Many(
547 submodel_ctx(x).into_iter().map(Tree::One).collect(),
548 ))
549 });
550 (submodel_tree, ctx)
551 }
552}