Skip to content

Commit 64b66ea

Browse files
authored
Merge pull request #9 from metaborg/name-resolution
Name resolution
2 parents 54ac24d + 2f29bdc commit 64b66ea

File tree

28 files changed

+2288
-142
lines changed

28 files changed

+2288
-142
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ Cargo.lock
1515
*.pdb
1616

1717
.vscode/
18+
.idea/
1819

1920
*.dot

scopegraphs-lib/Cargo.toml

+9-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9-
9+
log = "0.4.20"
10+
prust-lib = {git="https://github.com/jdonszelmann/prust", branch = "main"}
11+
syn = { version = "2.0.29", features = ["full", "extra-traits"] }
12+
quote = { version = "1.0.33" , optional = true}
13+
scopegraphs-regular-expressions = {path = "../scopegraphs-regular-expressions"}
14+
scopegraphs-macros = {path = "../scopegraphs-macros"}
1015

1116
[dev-dependencies]
12-
scopegraphs = {path = "../scopegraphs"}
17+
scopegraphs = {path = "../scopegraphs"}
18+
env_logger = "0.10.1"
19+
ctor = "0.2.5"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use crate::{completeness::Completeness, Scope, ScopeGraph};
2+
use std::{collections::HashSet, hash::Hash};
3+
4+
pub(super) struct CriticalEdgeSet<LABEL> {
5+
open_edges: Vec<HashSet<LABEL>>,
6+
}
7+
8+
impl<LABEL> Default for CriticalEdgeSet<LABEL> {
9+
fn default() -> Self {
10+
Self {
11+
open_edges: Default::default(),
12+
}
13+
}
14+
}
15+
16+
impl<LABEL> CriticalEdgeSet<LABEL> {
17+
pub(super) fn init_scope(&mut self, edges: HashSet<LABEL>) {
18+
self.open_edges.push(edges)
19+
}
20+
}
21+
22+
impl<LABEL: Hash + Eq> CriticalEdgeSet<LABEL> {
23+
pub fn is_open(&self, scope: Scope, lbl: &LABEL) -> bool {
24+
self.open_edges[scope.0].contains(lbl)
25+
}
26+
27+
pub(super) fn close(&mut self, scope: Scope, lbl: &LABEL) -> bool {
28+
self.open_edges[scope.0].remove(lbl)
29+
}
30+
}
31+
32+
/// Sub-trait of [`Completeness`] that uses _critical edges_ (scope-label pairs) to manage completeness.
33+
///
34+
/// Provides utility function to create scopes with particular open edges.
35+
///
36+
/// Should not be called externally, but only from utility function on [`super::ScopeGraph`].
37+
pub trait CriticalEdgeBasedCompleteness<LABEL, DATA>: Completeness<LABEL, DATA> {
38+
fn init_scope_with(&mut self, open_edges: HashSet<LABEL>);
39+
}
40+
41+
/// Error returned when attempting to add an edge with a label that is already closed in that scope.
42+
#[derive(Debug)]
43+
pub struct EdgeClosedError<LABEL> {
44+
pub scope: Scope,
45+
pub label: LABEL,
46+
}
47+
48+
/// Value returned when a query cannot yet be computed because some edge it depends on is still closed.
49+
#[derive(Debug, Copy, Clone)]
50+
pub struct Delay<LABEL> {
51+
pub scope: Scope,
52+
pub label: LABEL,
53+
}
54+
55+
pub(crate) type EdgesOrDelay<EDGES, LABEL> = Result<EDGES, Delay<LABEL>>;
56+
57+
impl<LABEL: Hash + Eq, DATA, CMPL> ScopeGraph<LABEL, DATA, CMPL>
58+
where
59+
CMPL: CriticalEdgeBasedCompleteness<LABEL, DATA>,
60+
{
61+
/// Adds a new scope with some open edges.
62+
pub fn add_scope_with<I>(&mut self, data: DATA, open_edges: I) -> Scope
63+
where
64+
I: IntoIterator<Item = LABEL>,
65+
{
66+
let scope = self.inner_scope_graph.add_scope(data);
67+
self.completeness
68+
.borrow_mut()
69+
.init_scope_with(HashSet::from_iter(open_edges.into_iter()));
70+
scope
71+
}
72+
73+
/// Adds a new scope with no open edges.
74+
pub fn add_scope_closed(&mut self, data: DATA) -> Scope {
75+
let scope = self.inner_scope_graph.add_scope(data);
76+
self.completeness
77+
.borrow_mut()
78+
.init_scope_with(HashSet::new());
79+
scope
80+
}
81+
}
82+
83+
impl<LABEL: Hash + Eq, DATA, CMPL> ScopeGraph<LABEL, DATA, CMPL>
84+
where
85+
DATA: Default,
86+
CMPL: CriticalEdgeBasedCompleteness<LABEL, DATA>,
87+
{
88+
/// Adds a new scope with some open edges and default data.
89+
pub fn add_scope_default_with<I>(&mut self, open_edges: I) -> Scope
90+
where
91+
I: IntoIterator<Item = LABEL>,
92+
{
93+
self.add_scope_with(DATA::default(), open_edges)
94+
}
95+
96+
/// Adds a new scope with no open edges and default data.
97+
pub fn add_scope_default_closed(&mut self) -> Scope {
98+
self.add_scope_with(DATA::default(), HashSet::new())
99+
}
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use crate::{
2+
completeness::private::Sealed,
3+
completeness::{
4+
Completeness, CriticalEdgeBasedCompleteness, CriticalEdgeSet, Delay, EdgeClosedError,
5+
EdgesOrDelay,
6+
},
7+
label::Label,
8+
InnerScopeGraph, Scope, ScopeGraph,
9+
};
10+
use std::{collections::HashSet, hash::Hash};
11+
12+
/// Critical-edge based [`Completeness`] implementation.
13+
///
14+
/// Unlike [`ImplicitClose`], this implementation shifts responsibility of closing edges to the
15+
/// _type checker writer_. I.e., they have to insert `sg.close(scope, label)` statements at the
16+
/// appropriate positions in the code.
17+
///
18+
/// Returns [`EdgeClosedError`] when an edge is added to a scope in which the label is already
19+
/// closed (by an explicit close of the type checker writer).
20+
///
21+
/// Returns [`Delay`] when edges are retrieved (e.g. during query resolution) for an edge that is
22+
/// not yet closed.
23+
pub struct ExplicitClose<LABEL> {
24+
critical_edges: CriticalEdgeSet<LABEL>,
25+
}
26+
27+
impl<LABEL> Default for ExplicitClose<LABEL> {
28+
fn default() -> Self {
29+
ExplicitClose {
30+
critical_edges: CriticalEdgeSet::default(),
31+
}
32+
}
33+
}
34+
35+
impl<LABEL> Sealed for ExplicitClose<LABEL> {}
36+
37+
impl<LABEL: Hash + Eq + Label, DATA> Completeness<LABEL, DATA> for ExplicitClose<LABEL> {
38+
fn cmpl_new_scope(&mut self, _: &InnerScopeGraph<LABEL, DATA>, _: Scope) {
39+
<ExplicitClose<LABEL> as CriticalEdgeBasedCompleteness<LABEL, DATA>>::init_scope_with(
40+
self,
41+
LABEL::iter().collect(), // init with all labels: programmer is responsible for closing edges
42+
)
43+
}
44+
45+
fn cmpl_new_complete_scope(&mut self, _: &InnerScopeGraph<LABEL, DATA>, _: Scope) {
46+
<ExplicitClose<LABEL> as CriticalEdgeBasedCompleteness<LABEL, DATA>>::init_scope_with(
47+
self,
48+
HashSet::new(), // init with empty label set to prevent extension
49+
)
50+
}
51+
52+
type NewEdgeResult = Result<(), EdgeClosedError<LABEL>>;
53+
54+
fn cmpl_new_edge(
55+
&mut self,
56+
inner_scope_graph: &mut InnerScopeGraph<LABEL, DATA>,
57+
src: Scope,
58+
lbl: LABEL,
59+
dst: Scope,
60+
) -> Self::NewEdgeResult {
61+
if self.critical_edges.is_open(src, &lbl) {
62+
inner_scope_graph.add_edge(src, lbl, dst);
63+
Ok(())
64+
} else {
65+
Err(EdgeClosedError {
66+
scope: src,
67+
label: lbl,
68+
})
69+
}
70+
}
71+
72+
type GetEdgesResult = EdgesOrDelay<Vec<Scope>, LABEL>;
73+
74+
fn cmpl_get_edges(
75+
&mut self,
76+
inner_scope_graph: &InnerScopeGraph<LABEL, DATA>,
77+
src: Scope,
78+
lbl: LABEL,
79+
) -> Self::GetEdgesResult {
80+
if self.critical_edges.is_open(src, &lbl) {
81+
Err(Delay {
82+
scope: src,
83+
label: lbl,
84+
})
85+
} else {
86+
Ok(inner_scope_graph.get_edges(src, lbl))
87+
}
88+
}
89+
}
90+
91+
impl<LABEL: Hash + Eq + Label, DATA> CriticalEdgeBasedCompleteness<LABEL, DATA>
92+
for ExplicitClose<LABEL>
93+
{
94+
fn init_scope_with(&mut self, open_labels: HashSet<LABEL>) {
95+
self.critical_edges.init_scope(open_labels)
96+
}
97+
}
98+
99+
impl<LABEL: Hash + Eq> ExplicitClose<LABEL> {
100+
fn close(&mut self, scope: Scope, label: &LABEL) {
101+
self.critical_edges.close(scope, label);
102+
}
103+
}
104+
105+
impl<LABEL: Hash + Eq, DATA> ScopeGraph<LABEL, DATA, ExplicitClose<LABEL>> {
106+
/// Closes an edge, (i.e., prohibit future new
107+
///
108+
/// For example, the following program will return an error.
109+
/// ```
110+
/// # use scopegraphs_lib::completeness::ExplicitClose;
111+
/// # use scopegraphs_lib::ScopeGraph;
112+
/// # use scopegraphs_macros::Label;
113+
/// # #[derive(Eq, Hash, PartialEq, Label)] enum Lbl { Def }
114+
/// # use Lbl::*;
115+
/// let mut sg = ScopeGraph::<Lbl, usize, _>::new(ExplicitClose::default());
116+
///
117+
/// let s1 = sg.add_scope_with(0, [Def]);
118+
/// let s2 = sg.add_scope_closed(42);
119+
///
120+
/// sg.close(s1, &Def);
121+
/// sg.add_edge(s1, Def, s2).expect_err("cannot add edge after closing edge");
122+
/// ```
123+
///
124+
/// Closing is required to permit queries to traverse these edges:
125+
/// ```
126+
///
127+
/// # use scopegraphs_lib::completeness::ExplicitClose;
128+
/// # use scopegraphs_lib::ScopeGraph;
129+
/// # use scopegraphs_lib::resolve::{DefaultDataEquiv, DefaultLabelOrder, EdgeOrData, Resolve};
130+
/// # use scopegraphs_macros::{compile_regex, Label};
131+
/// #
132+
/// # #[derive(Eq, Hash, PartialEq, Label, Debug, Copy, Clone)]
133+
/// # enum Lbl { Def }
134+
/// # use Lbl::*;
135+
/// # type LblD = EdgeOrData<Lbl>;
136+
/// #
137+
/// # compile_regex!(type Regex<Lbl> = Def);
138+
/// let mut sg = ScopeGraph::<Lbl, usize, _>::new(ExplicitClose::default());
139+
///
140+
/// let s1 = sg.add_scope_with(0, [Def]);
141+
/// let s2 = sg.add_scope_closed(42);
142+
///
143+
/// // Note: not calling `sg.close(s1, &Def)`
144+
///
145+
/// let query_result = sg.query()
146+
/// .with_path_wellformedness(Regex::new()) // regex: `Def`
147+
/// .with_data_wellformedness(|x: &usize| *x == 42) // match `42`
148+
/// .resolve(s1);
149+
///
150+
/// query_result.expect_err("require s1/Def to be closed");
151+
/// ```
152+
///
153+
/// Closing allows queries to resolve:
154+
/// ```
155+
///
156+
/// # use scopegraphs_lib::completeness::ExplicitClose;
157+
/// # use scopegraphs_lib::ScopeGraph;
158+
/// # use scopegraphs_lib::resolve::{DefaultDataEquiv, DefaultLabelOrder, EdgeOrData, Resolve};
159+
/// # use scopegraphs_macros::{compile_regex, Label};
160+
/// #
161+
/// # #[derive(Eq, Hash, PartialEq, Label, Debug, Copy, Clone)]
162+
/// # enum Lbl { Def }
163+
/// # use Lbl::*;
164+
/// # type LblD = EdgeOrData<Lbl>;
165+
/// #
166+
/// # compile_regex!(type Regex<Lbl> = Def);
167+
/// let mut sg = ScopeGraph::<Lbl, usize, _>::new(ExplicitClose::default());
168+
///
169+
/// let s1 = sg.add_scope_with(0, [Def]);
170+
/// let s2 = sg.add_scope_closed(42);
171+
///
172+
/// // Note: closing the edge *after* creating all edges, *before* doing the query
173+
/// sg.close(s1, &Def);
174+
///
175+
/// let query_result = sg.query()
176+
/// .with_path_wellformedness(Regex::new()) // regex: `Def`
177+
/// .with_data_wellformedness(|x: &usize| *x == 42) // match `42`
178+
/// .resolve(s1);
179+
///
180+
/// query_result.expect("query should return result");
181+
/// ```
182+
pub fn close(&self, scope: Scope, label: &LABEL) {
183+
self.completeness.borrow_mut().close(scope, label)
184+
}
185+
}

0 commit comments

Comments
 (0)