1use thiserror::Error;
9
10use crate::arena::Handle;
11
12use super::Filter;
13use super::ImplicationFilter;
14use super::NoFilter;
15
16#[derive(Clone, Debug, Default, Eq, PartialEq)]
17#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
18#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
19pub struct StackGraph {
20 pub files: Files,
21 pub nodes: Nodes,
22 pub edges: Edges,
23}
24
25#[derive(Debug, Error, Eq, PartialEq)]
26pub enum Error {
27 #[error("failed to load file `{0}`")]
28 FileNotFound(String),
29 #[error("duplicate file `{0}`")]
30 FileAlreadyPresent(String),
31 #[error("node `{0}` is an invalid node")]
32 InvalidGlobalNodeID(u32),
33 #[error("variable `{0}` is an invalid stack variable")]
34 InvalidStackVariable(u32),
35 #[error("failed to locate node `{0}` in graph")]
36 NodeNotFound(NodeID),
37}
38
39impl StackGraph {
40 pub fn from_graph<'a>(graph: &crate::graph::StackGraph) -> Self {
41 Self::from_graph_filter(graph, &NoFilter)
42 }
43
44 pub fn from_graph_filter<'a>(graph: &crate::graph::StackGraph, filter: &'a dyn Filter) -> Self {
45 let filter = ImplicationFilter(filter);
46 let files = graph.filter_files(&filter);
47 let nodes = graph.filter_nodes(&filter);
48 let edges = graph.filter_edges(&filter);
49 Self {
50 files,
51 nodes,
52 edges,
53 }
54 }
55
56 pub fn load_into(&self, graph: &mut crate::graph::StackGraph) -> Result<(), Error> {
57 self.load_files(graph)?;
58 self.load_nodes(graph)?;
59 self.load_edges(graph)?;
60 Ok(())
61 }
62
63 fn load_files(&self, graph: &mut crate::graph::StackGraph) -> Result<(), Error> {
64 for file in self.files.data.iter() {
65 graph
66 .add_file(&file)
67 .map_err(|_| Error::FileAlreadyPresent(file.to_owned()))?;
68 }
69
70 Ok(())
71 }
72
73 fn load_nodes(&self, graph: &mut crate::graph::StackGraph) -> Result<(), Error> {
74 for node in &self.nodes.data {
75 let handle = match node {
76 Node::DropScopes { id, .. } => {
77 let node_id = id.to_node_id(graph)?;
78 graph.add_drop_scopes_node(node_id)
79 }
80 Node::PopScopedSymbol {
81 id,
82 symbol,
83 is_definition,
84 ..
85 } => {
86 let node_id = id.to_node_id(graph)?;
87 let symbol_handle = graph.add_symbol(&symbol);
88 graph.add_pop_scoped_symbol_node(node_id, symbol_handle, *is_definition)
89 }
90 Node::PopSymbol {
91 id,
92 symbol,
93 is_definition,
94 ..
95 } => {
96 let node_id = id.to_node_id(graph)?;
97 let symbol_handle = graph.add_symbol(&symbol);
98 graph.add_pop_symbol_node(node_id, symbol_handle, *is_definition)
99 }
100 Node::PushScopedSymbol {
101 id,
102 symbol,
103 scope,
104 is_reference,
105 ..
106 } => {
107 let node_id = id.to_node_id(graph)?;
108 let scope_id = scope.to_node_id(graph)?;
109 let symbol_handle = graph.add_symbol(&symbol);
110 graph.add_push_scoped_symbol_node(
111 node_id,
112 symbol_handle,
113 scope_id,
114 *is_reference,
115 )
116 }
117 Node::PushSymbol {
118 id,
119 symbol,
120 is_reference,
121 ..
122 } => {
123 let node_id = id.to_node_id(graph)?;
124 let symbol_handle = graph.add_symbol(&symbol);
125 graph.add_push_symbol_node(node_id, symbol_handle, *is_reference)
126 }
127 Node::Scope {
128 id, is_exported, ..
129 } => {
130 let node_id = id.to_node_id(graph)?;
131 graph.add_scope_node(node_id, *is_exported)
132 }
133 Node::JumpToScope { .. } | Node::Root { .. } => None,
134 };
135
136 if let Some(handle) = handle {
137 if let Some(source_info) = node.source_info() {
139 *graph.source_info_mut(handle) = crate::graph::SourceInfo {
140 span: source_info.span.clone(),
141 syntax_type: source_info
142 .syntax_type
143 .as_ref()
144 .map(|st| graph.add_string(&st))
145 .into(),
146 ..Default::default()
147 };
148 }
149
150 if let Some(debug_info) = node.debug_info() {
152 *graph.node_debug_info_mut(handle) = debug_info.data.iter().fold(
153 crate::graph::DebugInfo::default(),
154 |mut info, entry| {
155 let key = graph.add_string(&entry.key);
156 let value = graph.add_string(&entry.value);
157 info.add(key, value);
158 info
159 },
160 );
161 }
162 }
163 }
164 Ok(())
165 }
166
167 fn load_edges(&self, graph: &mut crate::graph::StackGraph) -> Result<(), Error> {
168 for Edge {
170 source,
171 sink,
172 precedence,
173 debug_info,
174 } in &self.edges.data
175 {
176 let source_id = source.to_node_id(graph)?;
177 let sink_id = sink.to_node_id(graph)?;
178
179 let source_handle = graph
180 .node_for_id(source_id)
181 .ok_or(Error::InvalidGlobalNodeID(source.local_id))?;
182 let sink_handle = graph
183 .node_for_id(sink_id)
184 .ok_or(Error::InvalidGlobalNodeID(sink.local_id))?;
185
186 graph.add_edge(source_handle, sink_handle, *precedence);
187
188 if let Some(debug_info) = debug_info {
190 *graph.edge_debug_info_mut(source_handle, sink_handle) = debug_info
191 .data
192 .iter()
193 .fold(crate::graph::DebugInfo::default(), |mut info, entry| {
194 let key = graph.add_string(&entry.key);
195 let value = graph.add_string(&entry.value);
196 info.add(key, value);
197 info
198 });
199 }
200 }
201 Ok(())
202 }
203}
204
205#[derive(Clone, Debug, Default, Eq, PartialEq)]
206#[cfg_attr(
207 feature = "serde",
208 derive(serde::Deserialize, serde::Serialize),
209 serde(transparent)
210)]
211#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
212pub struct Files {
213 pub data: Vec<String>,
214}
215
216#[derive(Clone, Debug, Default, Eq, PartialEq)]
217#[cfg_attr(
218 feature = "serde",
219 derive(serde::Deserialize, serde::Serialize),
220 serde(transparent)
221)]
222#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
223pub struct Nodes {
224 pub data: Vec<Node>,
225}
226
227#[derive(Clone, Debug, Eq, PartialEq)]
228#[cfg_attr(
229 feature = "serde",
230 serde_with::skip_serializing_none, derive(serde::Deserialize, serde::Serialize),
232 serde(tag = "type", rename_all = "snake_case"),
233)]
234#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
235pub enum Node {
236 DropScopes {
237 id: NodeID,
238 source_info: Option<SourceInfo>,
239 debug_info: Option<DebugInfo>,
240 },
241
242 JumpToScope {
243 id: NodeID,
244 source_info: Option<SourceInfo>,
245 debug_info: Option<DebugInfo>,
246 },
247
248 PopScopedSymbol {
249 id: NodeID,
250 symbol: String,
251 is_definition: bool,
252 source_info: Option<SourceInfo>,
253 debug_info: Option<DebugInfo>,
254 },
255
256 PopSymbol {
257 id: NodeID,
258 symbol: String,
259 is_definition: bool,
260 source_info: Option<SourceInfo>,
261 debug_info: Option<DebugInfo>,
262 },
263
264 PushScopedSymbol {
265 id: NodeID,
266 symbol: String,
267 scope: NodeID,
268 is_reference: bool,
269 source_info: Option<SourceInfo>,
270 debug_info: Option<DebugInfo>,
271 },
272
273 PushSymbol {
274 id: NodeID,
275 symbol: String,
276 is_reference: bool,
277 source_info: Option<SourceInfo>,
278 debug_info: Option<DebugInfo>,
279 },
280
281 Root {
282 id: NodeID,
283 source_info: Option<SourceInfo>,
284 debug_info: Option<DebugInfo>,
285 },
286
287 Scope {
288 id: NodeID,
289 is_exported: bool,
290 source_info: Option<SourceInfo>,
291 debug_info: Option<DebugInfo>,
292 },
293}
294
295impl Node {
296 fn source_info(&self) -> Option<&SourceInfo> {
297 match self {
298 Self::DropScopes { source_info, .. } => source_info,
299 Self::JumpToScope { source_info, .. } => source_info,
300 Self::PopScopedSymbol { source_info, .. } => source_info,
301 Self::PopSymbol { source_info, .. } => source_info,
302 Self::PushScopedSymbol { source_info, .. } => source_info,
303 Self::PushSymbol { source_info, .. } => source_info,
304 Self::Root { source_info, .. } => source_info,
305 Self::Scope { source_info, .. } => source_info,
306 }
307 .as_ref()
308 }
309
310 fn debug_info(&self) -> Option<&DebugInfo> {
311 match self {
312 Self::DropScopes { debug_info, .. } => debug_info,
313 Self::JumpToScope { debug_info, .. } => debug_info,
314 Self::PopScopedSymbol { debug_info, .. } => debug_info,
315 Self::PopSymbol { debug_info, .. } => debug_info,
316 Self::PushScopedSymbol { debug_info, .. } => debug_info,
317 Self::PushSymbol { debug_info, .. } => debug_info,
318 Self::Root { debug_info, .. } => debug_info,
319 Self::Scope { debug_info, .. } => debug_info,
320 }
321 .as_ref()
322 }
323}
324
325#[derive(Clone, Debug, Eq, PartialEq)]
326#[cfg_attr(
327 feature = "serde",
328 serde_with::skip_serializing_none, derive(serde::Deserialize, serde::Serialize),
330)]
331#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
332pub struct SourceInfo {
333 pub span: lsp_positions::Span,
334 pub syntax_type: Option<String>,
335}
336
337#[derive(Clone, Debug, Eq, PartialEq)]
338#[cfg_attr(
339 feature = "serde",
340 derive(serde::Deserialize, serde::Serialize),
341 serde(transparent)
342)]
343#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
344pub struct DebugInfo {
345 pub data: Vec<DebugEntry>,
346}
347
348#[derive(Clone, Debug, Eq, PartialEq)]
349#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
350#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
351pub struct DebugEntry {
352 pub key: String,
353 pub value: String,
354}
355
356#[derive(Clone, Debug, Eq, PartialEq)]
357#[cfg_attr(
358 feature = "serde",
359 serde_with::skip_serializing_none, derive(serde::Deserialize, serde::Serialize),
361)]
362#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
363pub struct NodeID {
364 pub file: Option<String>,
365 pub local_id: u32,
366}
367
368impl NodeID {
369 pub fn from_node_id(graph: &crate::graph::StackGraph, value: crate::graph::NodeID) -> Self {
370 Self {
371 file: value.file().map(|f| graph[f].to_string()),
372 local_id: value.local_id(),
373 }
374 }
375
376 pub fn to_node_id(
377 &self,
378 graph: &crate::graph::StackGraph,
379 ) -> Result<crate::graph::NodeID, Error> {
380 if let Some(file) = &self.file {
381 let file = graph
382 .get_file(file)
383 .ok_or_else(|| Error::FileNotFound(file.clone()))?;
384 Ok(crate::graph::NodeID::new_in_file(file, self.local_id))
385 } else if self.local_id == crate::graph::JUMP_TO_NODE_ID {
386 Ok(crate::graph::NodeID::jump_to())
387 } else if self.local_id == crate::graph::ROOT_NODE_ID {
388 Ok(crate::graph::NodeID::root())
389 } else {
390 Err(Error::InvalidGlobalNodeID(self.local_id))
391 }
392 }
393
394 pub fn from_node(graph: &crate::graph::StackGraph, handle: Handle<crate::graph::Node>) -> Self {
395 Self::from_node_id(graph, graph[handle].id())
396 }
397
398 pub fn to_node(
399 &self,
400 graph: &mut crate::graph::StackGraph,
401 ) -> Result<Handle<crate::graph::Node>, Error> {
402 let value = self.to_node_id(graph)?;
403 Ok(graph
404 .node_for_id(value)
405 .ok_or_else(|| Error::NodeNotFound(self.clone()))?)
406 }
407}
408
409impl std::fmt::Display for NodeID {
410 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
411 if let Some(file) = &self.file {
412 write!(f, "{}:", file)?;
413 }
414 write!(f, "{}", self.local_id)
415 }
416}
417
418#[derive(Clone, Debug, Default, Eq, PartialEq)]
419#[cfg_attr(
420 feature = "serde",
421 derive(serde::Deserialize, serde::Serialize),
422 serde(transparent)
423)]
424#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
425pub struct Edges {
426 pub data: Vec<Edge>,
427}
428
429#[derive(Clone, Debug, Eq, PartialEq)]
430#[cfg_attr(
431 feature = "serde",
432 serde_with::skip_serializing_none, derive(serde::Deserialize, serde::Serialize),
434)]
435#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
436pub struct Edge {
437 pub source: NodeID,
438 pub sink: NodeID,
439 pub precedence: i32,
440 pub debug_info: Option<DebugInfo>,
441}
442
443impl crate::graph::StackGraph {
444 pub fn to_serializable(&self) -> StackGraph {
445 self.to_serializable_filter(&NoFilter)
446 }
447
448 pub fn to_serializable_filter<'a>(&self, f: &'a dyn Filter) -> StackGraph {
449 crate::serde::StackGraph::from_graph_filter(self, f)
450 }
451
452 fn filter_files<'a>(&self, filter: &'a dyn Filter) -> Files {
453 Files {
454 data: self
455 .iter_files()
456 .filter(|f| filter.include_file(self, f))
457 .map(|f| self[f].name().to_owned())
458 .collect::<Vec<_>>(),
459 }
460 }
461
462 fn filter_node<'a>(&self, _filter: &'a dyn Filter, id: crate::graph::NodeID) -> NodeID {
463 let file = id.file().map(|idx| self[idx].name().to_owned());
464 let local_id = id.local_id();
465 NodeID { file, local_id }
466 }
467
468 fn filter_source_info<'a>(
469 &self,
470 _filter: &'a dyn Filter,
471 handle: Handle<crate::graph::Node>,
472 ) -> Option<SourceInfo> {
473 self.source_info(handle).map(|info| SourceInfo {
474 span: info.span.clone(),
475 syntax_type: info.syntax_type.into_option().map(|ty| self[ty].to_owned()),
476 })
477 }
478
479 fn filter_node_debug_info<'a>(
480 &self,
481 _filter: &'a dyn Filter,
482 handle: Handle<crate::graph::Node>,
483 ) -> Option<DebugInfo> {
484 self.node_debug_info(handle).map(|info| DebugInfo {
485 data: info
486 .iter()
487 .map(|entry| DebugEntry {
488 key: self[entry.key].to_owned(),
489 value: self[entry.value].to_owned(),
490 })
491 .collect(),
492 })
493 }
494
495 fn filter_nodes<'a>(&self, filter: &'a dyn Filter) -> Nodes {
496 Nodes {
497 data: self
498 .iter_nodes()
499 .filter(|n| filter.include_node(self, &n))
500 .map(|handle| {
501 let node = &self[handle];
502 let id = self.filter_node(filter, node.id());
503 let source_info = self.filter_source_info(filter, handle);
504 let debug_info = self.filter_node_debug_info(filter, handle);
505
506 match node {
507 crate::graph::Node::DropScopes(_node) => Node::DropScopes {
508 id,
509 source_info,
510 debug_info,
511 },
512 crate::graph::Node::JumpTo(_node) => Node::JumpToScope {
513 id,
514 source_info,
515 debug_info,
516 },
517 crate::graph::Node::PopScopedSymbol(node) => Node::PopScopedSymbol {
518 id,
519 symbol: self[node.symbol].to_owned(),
520 is_definition: node.is_definition,
521 source_info,
522 debug_info,
523 },
524 crate::graph::Node::PopSymbol(node) => Node::PopSymbol {
525 id,
526 symbol: self[node.symbol].to_owned(),
527 is_definition: node.is_definition,
528 source_info,
529 debug_info,
530 },
531 crate::graph::Node::PushScopedSymbol(node) => Node::PushScopedSymbol {
532 id,
533 symbol: self[node.symbol].to_owned(),
534 scope: self.filter_node(filter, node.scope),
535 is_reference: node.is_reference,
536 source_info,
537 debug_info,
538 },
539 crate::graph::Node::PushSymbol(node) => Node::PushSymbol {
540 id,
541 symbol: self[node.symbol].to_owned(),
542 is_reference: node.is_reference,
543 source_info,
544 debug_info,
545 },
546 crate::graph::Node::Root(_node) => Node::Root {
547 id,
548 source_info,
549 debug_info,
550 },
551 crate::graph::Node::Scope(node) => Node::Scope {
552 id,
553 is_exported: node.is_exported,
554 source_info,
555 debug_info,
556 },
557 }
558 })
559 .collect::<Vec<_>>(),
560 }
561 }
562
563 fn filter_edges<'a>(&self, filter: &'a dyn Filter) -> Edges {
564 Edges {
565 data: self
566 .iter_nodes()
567 .map(|source| {
568 self.outgoing_edges(source)
569 .filter(|e| filter.include_edge(self, &e.source, &e.sink))
570 .map(|e| Edge {
571 source: self.filter_node(filter, self[e.source].id()),
572 sink: self.filter_node(filter, self[e.sink].id()),
573 precedence: e.precedence,
574 debug_info: self.filter_edge_debug_info(filter, e.source, e.sink),
575 })
576 })
577 .flatten()
578 .collect::<Vec<_>>(),
579 }
580 }
581
582 fn filter_edge_debug_info<'a>(
583 &self,
584 _filter: &'a dyn Filter,
585 source_handle: Handle<crate::graph::Node>,
586 sink_handle: Handle<crate::graph::Node>,
587 ) -> Option<DebugInfo> {
588 self.edge_debug_info(source_handle, sink_handle)
589 .map(|info| DebugInfo {
590 data: info
591 .iter()
592 .map(|entry| DebugEntry {
593 key: self[entry.key].to_owned(),
594 value: self[entry.value].to_owned(),
595 })
596 .collect(),
597 })
598 }
599}