1
//! The symbol table.
2
//!
3
//! See the item documentation for [`SymbolTable`] for more details.
4

            
5
use crate::bug;
6
use crate::representation::{Representation, get_repr_rule};
7

            
8
use super::comprehension::Comprehension;
9
use super::serde::RcRefCellAsId;
10
use std::cell::RefCell;
11
use std::collections::BTreeSet;
12
use std::collections::btree_map::Entry;
13
use std::collections::{BTreeMap, VecDeque};
14
use std::hash::{Hash, Hasher};
15
use std::rc::Rc;
16
use std::sync::atomic::{AtomicU32, Ordering};
17

            
18
use super::declaration::{DeclarationPtr, serde::DeclarationPtrFull};
19
use super::serde::{DefaultWithId, HasId, ObjId};
20
use itertools::{Itertools as _, izip};
21
use serde::{Deserialize, Serialize};
22
use serde_with::serde_as;
23
use uniplate::Tree;
24
use uniplate::{Biplate, Uniplate};
25

            
26
use super::name::Name;
27
use super::{DomainPtr, Expression, GroundDomain, Moo, ReturnType, SubModel, Typeable};
28
use 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.
38
thread_local! {
39
static 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)]
81
pub struct SymbolTable {
82
    #[serde_as(as = "Vec<(_,DeclarationPtrFull)>")]
83
    table: BTreeMap<Name, DeclarationPtr>,
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

            
95
impl Hash for SymbolTable {
96
    fn hash<H: Hasher>(&self, state: &mut H) {
97
        self.id.hash(state);
98
    }
99
}
100

            
101
impl SymbolTable {
102
    /// Creates an empty symbol table.
103
    pub fn new() -> SymbolTable {
104
        SymbolTable::new_inner(None)
105
    }
106

            
107
    /// Creates an empty symbol table with the given parent.
108
    pub fn with_parent(parent: Rc<RefCell<SymbolTable>>) -> SymbolTable {
109
        SymbolTable::new_inner(Some(parent))
110
    }
111

            
112
    fn new_inner(parent: Option<Rc<RefCell<SymbolTable>>>) -> SymbolTable {
113
        let id = ID_COUNTER.with(|x| x.fetch_add(1, Ordering::Relaxed));
114
        SymbolTable {
115
            id,
116
            table: BTreeMap::new(),
117
            next_machine_name: RefCell::new(0),
118
            parent,
119
        }
120
    }
121

            
122
    /// Looks up the declaration with the given name in the current scope only.
123
    ///
124
    /// Returns `None` if there is no declaration with that name in the current scope.
125
    pub fn lookup_local(&self, name: &Name) -> Option<DeclarationPtr> {
126
        self.table.get(name).cloned()
127
    }
128

            
129
    /// Looks up the declaration with the given name, checking all enclosing scopes.
130
    ///
131
    /// Returns `None` if there is no declaration with that name in scope.
132
    pub fn lookup(&self, name: &Name) -> Option<DeclarationPtr> {
133
        self.lookup_local(name).or_else(|| {
134
            self.parent
135
                .as_ref()
136
                .and_then(|parent| (*parent).borrow().lookup(name))
137
        })
138
    }
139

            
140
    /// Inserts a declaration into the symbol table.
141
    ///
142
    /// Returns `None` if there is already a symbol with this name in the local scope.
143
    pub fn insert(&mut self, declaration: DeclarationPtr) -> Option<()> {
144
        let name = declaration.name().clone();
145
        if let Entry::Vacant(e) = self.table.entry(name) {
146
            e.insert(declaration);
147
            Some(())
148
        } else {
149
            None
150
        }
151
    }
152

            
153
    /// Updates or adds a declaration in the immediate local scope.
154
    pub fn update_insert(&mut self, declaration: DeclarationPtr) {
155
        let name = declaration.name().clone();
156
        self.table.insert(name, declaration);
157
    }
158

            
159
    /// Looks up the return type for name if it has one and is in scope.
160
    pub fn return_type(&self, name: &Name) -> Option<ReturnType> {
161
        self.lookup(name).map(|x| x.return_type())
162
    }
163

            
164
    /// Looks up the return type for name if has one and is in the local scope.
165
    pub fn return_type_local(&self, name: &Name) -> Option<ReturnType> {
166
        self.lookup_local(name).map(|x| x.return_type())
167
    }
168

            
169
    /// Looks up the domain of name if it has one and is in scope.
170
    ///
171
    /// This method can return domain references: if a ground domain is always required, use
172
    /// [`SymbolTable::resolve_domain`].
173
    pub fn domain(&self, name: &Name) -> Option<DomainPtr> {
174
        if let Name::WithRepresentation(name, _) = name {
175
            self.lookup(name)?.domain()
176
        } else {
177
            self.lookup(name)?.domain()
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<Moo<GroundDomain>> {
185
        self.domain(name)?.resolve()
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
                    && *m >= *next_var
206
                {
207
                    *next_var = *m + 1;
208
                }
209
            }
210
        }
211

            
212
        self.table.extend(other.table);
213
    }
214

            
215
    /// Creates a new variable in this symbol table with a unique name, and returns its
216
    /// declaration.
217
    pub fn gensym(&mut self, domain: &DomainPtr) -> DeclarationPtr {
218
        let num = *self.next_machine_name.borrow();
219
        *(self.next_machine_name.borrow_mut()) += 1;
220
        let decl = DeclarationPtr::new_var(Name::Machine(num), domain.clone());
221
        self.insert(decl.clone());
222
        decl
223
    }
224

            
225
    /// Gets the parent of this symbol table as a mutable reference.
226
    ///
227
    /// This function provides no sanity checks.
228
    pub fn parent_mut_unchecked(&mut self) -> &mut Option<Rc<RefCell<SymbolTable>>> {
229
        &mut self.parent
230
    }
231

            
232
    /// Gets the representation `representation` for `name`.
233
    ///
234
    /// # Returns
235
    ///
236
    /// + `None` if `name` does not exist, is not a decision variable, or does not have that representation.
237
    pub fn get_representation(
238
        &self,
239
        name: &Name,
240
        representation: &[&str],
241
    ) -> Option<Vec<Box<dyn Representation>>> {
242
        // TODO: move representation stuff to declaration / variable to avoid cloning? (we have to
243
        // move inside of an rc here, so cannot return borrows)
244
        //
245
        // Also would prevent constant "does exist" "is var" checks.
246
        //
247
        // The reason it is not there now is because I'm getting serde issues...
248
        //
249
        // Also might run into issues putting get_or_add into declaration/variable, as that
250
        // requires us to mutably borrow both the symbol table, and the variable inside the symbol
251
        // table..
252

            
253
        let decl = self.lookup(name)?;
254
        let var = &decl.as_var()?;
255

            
256
        var.representations
257
            .iter()
258
            .find(|x| &x.iter().map(|r| r.repr_name()).collect_vec()[..] == representation)
259
            .cloned()
260
    }
261

            
262
    /// Gets all initialised representations for `name`.
263
    ///
264
    /// # Returns
265
    ///
266
    /// + `None` if `name` does not exist, or is not a decision variable.
267
    pub fn representations_for(&self, name: &Name) -> Option<Vec<Vec<Box<dyn Representation>>>> {
268
        let decl = self.lookup(name)?;
269
        decl.as_var().map(|x| x.representations.clone())
270
    }
271

            
272
    /// Gets the representation `representation` for `name`, creating it if it does not exist.
273
    ///
274
    /// If the representation does not exist, this method initialises the representation in this
275
    /// symbol table, adding the representation to `name`, and the declarations for the represented
276
    /// variables to the symbol table.
277
    ///
278
    /// # Usage
279
    ///
280
    /// Representations for variable references should be selected and created by the
281
    /// `select_representation` rule. Therefore, this method should not be used in other rules.
282
    /// Consider using [`get_representation`](`SymbolTable::get_representation`) instead.
283
    ///
284
    /// # Returns
285
    ///
286
    /// + `None` if `name` does not exist, is not a decision variable, or cannot be given that
287
    ///   representation.
288
    pub fn get_or_add_representation(
289
        &mut self,
290
        name: &Name,
291
        representation: &[&str],
292
    ) -> Option<Vec<Box<dyn Representation>>> {
293
        // Lookup the declaration reference
294
        let mut decl = self.lookup(name)?;
295

            
296
        if let Some(var) = decl.as_var()
297
            && let Some(existing_reprs) = var
298
                .representations
299
                .iter()
300
                .find(|x| &x.iter().map(|r| r.repr_name()).collect_vec()[..] == representation)
301
                .cloned()
302
        {
303
            return Some(existing_reprs); // Found: return early
304
        }
305
        // Representation not found
306

            
307
        // TODO: nested representations logic...
308
        if representation.len() != 1 {
309
            bug!("nested representations not implemented")
310
        }
311
        let repr_name_str = representation[0];
312
        let repr_init_fn = get_repr_rule(repr_name_str)?;
313

            
314
        let reprs = vec![repr_init_fn(name, self)?];
315

            
316
        // Get mutable access to the variable part
317
        let mut var = decl.as_var_mut()?;
318

            
319
        for repr_instance in &reprs {
320
            repr_instance
321
                .declaration_down()
322
                .ok()?
323
                .into_iter()
324
                .for_each(|x| self.update_insert(x));
325
        }
326

            
327
        var.representations.push(reprs.clone());
328

            
329
        Some(reprs)
330
    }
331
}
332

            
333
impl IntoIterator for SymbolTable {
334
    type Item = (Name, DeclarationPtr);
335

            
336
    type IntoIter = IntoIter;
337

            
338
    /// Iterates over symbol table entries in scope.
339
    fn into_iter(self) -> Self::IntoIter {
340
        IntoIter {
341
            inner: self.table.into_iter(),
342
            parent: self.parent,
343
        }
344
    }
345
}
346

            
347
/// Iterator over symbol table entries in the current scope only.
348
pub struct LocalIntoIter {
349
    // iterator over the btreemap
350
    inner: std::collections::btree_map::IntoIter<Name, DeclarationPtr>,
351
}
352

            
353
impl Iterator for LocalIntoIter {
354
    type Item = (Name, DeclarationPtr);
355

            
356
    fn next(&mut self) -> Option<Self::Item> {
357
        self.inner.next()
358
    }
359
}
360

            
361
/// Iterator over all symbol table entries in scope.
362
pub struct IntoIter {
363
    // iterator over the current scopes' btreemap
364
    inner: std::collections::btree_map::IntoIter<Name, DeclarationPtr>,
365

            
366
    // the parent scope
367
    parent: Option<Rc<RefCell<SymbolTable>>>,
368
}
369

            
370
impl Iterator for IntoIter {
371
    type Item = (Name, DeclarationPtr);
372

            
373
    fn next(&mut self) -> Option<Self::Item> {
374
        let mut val = self.inner.next();
375

            
376
        // Go up the tree until we find a parent symbol table with declarations to iterate over.
377
        //
378
        // Note that the parent symbol table may be empty - this is why this is a loop!
379
        while val.is_none() {
380
            let parent = self.parent.clone()?;
381
            let parent_ref = (*parent).borrow();
382
            self.parent.clone_from(&parent_ref.parent);
383
            self.inner = parent_ref.table.clone().into_iter();
384

            
385
            val = self.inner.next();
386
        }
387

            
388
        val
389
    }
390
}
391

            
392
impl HasId for SymbolTable {
393
    fn id(&self) -> ObjId {
394
        self.id
395
    }
396
}
397

            
398
impl DefaultWithId for SymbolTable {
399
    fn default_with_id(id: ObjId) -> Self {
400
        Self {
401
            table: BTreeMap::new(),
402
            id,
403
            parent: None,
404
            next_machine_name: RefCell::new(0),
405
        }
406
    }
407
}
408

            
409
impl Clone for SymbolTable {
410
    fn clone(&self) -> Self {
411
        Self {
412
            table: self.table.clone(),
413
            id: ID_COUNTER.with(|x| x.fetch_add(1, Ordering::Relaxed)),
414
            parent: self.parent.clone(),
415
            next_machine_name: self.next_machine_name.clone(),
416
        }
417
    }
418
}
419

            
420
impl Default for SymbolTable {
421
    fn default() -> Self {
422
        Self::new_inner(None)
423
    }
424
}
425

            
426
impl Uniplate for SymbolTable {
427
    fn uniplate(&self) -> (Tree<Self>, Box<dyn Fn(Tree<Self>) -> Self>) {
428
        // do not recurse up parents, that would be weird?
429
        let self2 = self.clone();
430
        (Tree::Zero, Box::new(move |_| self2.clone()))
431
    }
432
}
433

            
434
impl Biplate<Expression> for SymbolTable {
435
    fn biplate(&self) -> (Tree<Expression>, Box<dyn Fn(Tree<Expression>) -> Self>) {
436
        let (child_trees, ctxs): (VecDeque<_>, Vec<_>) = self
437
            .table
438
            .values()
439
            .map(Biplate::<Expression>::biplate)
440
            .unzip();
441

            
442
        let tree = Tree::Many(child_trees);
443

            
444
        let self2 = self.clone();
445
        let ctx = Box::new(move |tree| {
446
            let Tree::Many(exprs) = tree else {
447
                panic!("unexpected children structure");
448
            };
449

            
450
            let mut self3 = self2.clone();
451
            let self3_iter = self3.table.iter_mut();
452
            for (ctx, tree, (_, decl)) in izip!(&ctxs, exprs, self3_iter) {
453
                // update declaration inside the pointer instead of creating a new one, so all
454
                // things referencing this keep referencing this.
455
                *decl = ctx(tree)
456
            }
457

            
458
            self3
459
        });
460

            
461
        (tree, ctx)
462
    }
463
}
464

            
465
impl Biplate<Comprehension> for SymbolTable {
466
    fn biplate(
467
        &self,
468
    ) -> (
469
        Tree<Comprehension>,
470
        Box<dyn Fn(Tree<Comprehension>) -> Self>,
471
    ) {
472
        let (expr_tree, expr_ctx) = <SymbolTable as Biplate<Expression>>::biplate(self);
473

            
474
        let (exprs, recons_expr_tree) = expr_tree.list();
475

            
476
        let (comprehension_tree, comprehension_ctx) =
477
            <VecDeque<Expression> as Biplate<Comprehension>>::biplate(&exprs);
478

            
479
        let ctx = Box::new(move |x| {
480
            // 1. turn comprehension tree into a list of expressions
481
            let exprs = comprehension_ctx(x);
482

            
483
            // 2. turn list of expressions into an expression tree
484
            let expr_tree = recons_expr_tree(exprs);
485

            
486
            // 3. turn expression tree into a symbol table
487
            expr_ctx(expr_tree)
488
        });
489

            
490
        (comprehension_tree, ctx)
491
    }
492
}
493

            
494
impl Biplate<SubModel> for SymbolTable {
495
    // walk into expressions
496
    fn biplate(&self) -> (Tree<SubModel>, Box<dyn Fn(Tree<SubModel>) -> Self>) {
497
        let (expr_tree, expr_ctx) = <SymbolTable as Biplate<Expression>>::biplate(self);
498

            
499
        let (exprs, recons_expr_tree) = expr_tree.list();
500

            
501
        let (submodel_tree, submodel_ctx) =
502
            <VecDeque<Expression> as Biplate<SubModel>>::biplate(&exprs);
503

            
504
        let ctx = Box::new(move |x| {
505
            // 1. turn submodel tree into a list of expressions
506
            let exprs = submodel_ctx(x);
507

            
508
            // 2. turn list of expressions into an expression tree
509
            let expr_tree = recons_expr_tree(exprs);
510

            
511
            // 3. turn expression tree into a symbol table
512
            expr_ctx(expr_tree)
513
        });
514
        (submodel_tree, ctx)
515
    }
516
}