stack_graphs/paths.rs
1// -*- coding: utf-8 -*-
2// ------------------------------------------------------------------------------------------------
3// Copyright © 2021, stack-graphs authors.
4// Licensed under either of Apache License, Version 2.0, or MIT license, at your option.
5// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details.
6// ------------------------------------------------------------------------------------------------
7
8//! Paths represent name bindings in a source language.
9//!
10//! With the set of rules we have for constructing stack graphs, bindings between references and
11//! definitions are represented by paths within the graph. Each edge in the path must leave the
12//! symbol and scopes stacks in a valid state — otherwise we have violated some name binding rule
13//! in the source language. The symbol and scope stacks must be empty at the beginning and end of
14//! the path. The reference's _push symbol_ node "seeds" the symbol stack with the first thing
15//! that we want to look for, and once we (hopefully) reach the definition that reference refers
16//! to, its pop node will remove that symbol from the symbol stack, leaving both stacks empty.
17
18use std::collections::VecDeque;
19
20/// Errors that can occur during the path resolution process.
21#[derive(Debug)]
22pub enum PathResolutionError {
23 /// The path is cyclic, and the cycle is disallowed.
24 DisallowedCycle,
25 /// The path contains a _jump to scope_ node, but there are no scopes on the scope stack to
26 /// jump to.
27 EmptyScopeStack,
28 /// The path contains a _pop symbol_ or _pop scoped symbol_ node, but there are no symbols on
29 /// the symbol stack to pop off.
30 EmptySymbolStack,
31 /// The partial path contains multiple references to a scope stack variable, and those
32 /// references can't unify on a single scope stack.
33 IncompatibleScopeStackVariables,
34 /// The partial path contains multiple references to a symbol stack variable, and those
35 /// references can't unify on a single symbol stack.
36 IncompatibleSymbolStackVariables,
37 /// The partial path contains edges from multiple files.
38 IncorrectFile,
39 /// The path contains a _pop symbol_ or _pop scoped symbol_ node, but the symbol at the top of
40 /// the symbol stack does not match.
41 IncorrectPoppedSymbol,
42 /// The path contains an edge whose source node does not match the sink node of the preceding
43 /// edge.
44 IncorrectSourceNode,
45 /// The path contains a _pop scoped symbol_ node, but the symbol at the top of the symbol stack
46 /// does not have an attached scope list to pop off.
47 MissingAttachedScopeList,
48 /// The path's scope stack does not satisfy the partial path's scope stack precondition.
49 ScopeStackUnsatisfied,
50 /// The path's symbol stack does not satisfy the partial path's symbol stack precondition.
51 SymbolStackUnsatisfied,
52 /// The partial path's postcondition references a symbol stack variable that isn't present in
53 /// the precondition.
54 UnboundSymbolStackVariable,
55 /// The partial path's postcondition references a scope stack variable that isn't present in
56 /// the precondition.
57 UnboundScopeStackVariable,
58 /// The path contains a _pop symbol_ node, but the symbol at the top of the symbol stack has an
59 /// attached scope list that we weren't expecting.
60 UnexpectedAttachedScopeList,
61 /// A _push scoped symbol_ node referes to an exported scope node that doesn't exist.
62 UnknownAttachedScope,
63}
64
65/// A collection that can be used to receive the results of the [`Path::extend`][] method.
66///
67/// Note: There's an [open issue][std-extend] to add these methods to std's `Extend` trait. If
68/// that gets merged, we can drop this trait and use the std one instead.
69///
70/// [std-extend]: https://github.com/rust-lang/rust/issues/72631
71pub trait Extend<T> {
72 /// Reserve space for `additional` elements in the collection.
73 fn reserve(&mut self, additional: usize);
74 /// Add a new element to the collection.
75 fn push(&mut self, item: T);
76}
77
78impl<T> Extend<T> for Vec<T> {
79 fn reserve(&mut self, additional: usize) {
80 self.reserve(additional);
81 }
82
83 fn push(&mut self, item: T) {
84 self.push(item);
85 }
86}
87
88impl<T> Extend<T> for VecDeque<T> {
89 fn reserve(&mut self, additional: usize) {
90 self.reserve(additional);
91 }
92
93 fn push(&mut self, item: T) {
94 self.push_back(item);
95 }
96}