conjure_cp_essence_parser/parser/
util.rs1use std::collections::BTreeMap;
2use std::sync::{Mutex, OnceLock};
3
4use tree_sitter::{Node, Parser, Tree};
5use tree_sitter_essence::LANGUAGE;
6
7use super::traversal::WalkDFS;
8use crate::diagnostics::diagnostics_api::SymbolKind;
9use crate::diagnostics::source_map::{HoverInfo, SourceMap, SpanId, span_with_hover};
10use crate::errors::RecoverableParseError;
11use conjure_cp_core::ast::{Name, SymbolTablePtr};
12
13pub struct ParseContext<'a> {
15 pub source_code: &'a str,
16 pub root: &'a Node<'a>,
17 pub symbols: Option<SymbolTablePtr>,
18 pub errors: &'a mut Vec<RecoverableParseError>,
19 pub source_map: &'a mut SourceMap,
20 pub decl_spans: &'a mut BTreeMap<Name, SpanId>,
21 pub typechecking_context: TypecheckingContext,
23 pub inner_typechecking_context: TypecheckingContext,
25}
26
27impl<'a> ParseContext<'a> {
28 pub fn new(
29 source_code: &'a str,
30 root: &'a Node<'a>,
31 symbols: Option<SymbolTablePtr>,
32 errors: &'a mut Vec<RecoverableParseError>,
33 source_map: &'a mut SourceMap,
34 decl_spans: &'a mut BTreeMap<Name, SpanId>,
35 ) -> Self {
36 Self {
37 source_code,
38 root,
39 symbols,
40 errors,
41 source_map,
42 decl_spans,
43 typechecking_context: TypecheckingContext::Unknown,
44 inner_typechecking_context: TypecheckingContext::Unknown,
45 }
46 }
47
48 pub fn record_error(&mut self, error: RecoverableParseError) {
49 self.errors.push(error);
50 }
51
52 pub fn with_new_symbols(&mut self, symbols: Option<SymbolTablePtr>) -> ParseContext<'_> {
54 ParseContext {
55 source_code: self.source_code,
56 root: self.root,
57 symbols,
58 errors: self.errors,
59 source_map: self.source_map,
60 decl_spans: self.decl_spans,
61 typechecking_context: self.typechecking_context,
62 inner_typechecking_context: self.inner_typechecking_context,
63 }
64 }
65
66 pub fn save_decl_span(&mut self, name: Name, span_id: SpanId) {
67 self.decl_spans.insert(name, span_id);
68 }
69
70 pub fn lookup_decl_span(&self, name: &Name) -> Option<SpanId> {
71 self.decl_spans.get(name).copied()
72 }
73
74 pub fn lookup_decl_line(&self, name: &Name) -> Option<u32> {
75 let span_id = self.lookup_decl_span(name)?;
76 let span = self.source_map.spans.get(span_id as usize)?;
77 Some(span.start_point.line + 1)
78 }
79
80 pub fn add_span_and_doc_hover(
82 &mut self,
83 node: &tree_sitter::Node,
84 doc_key: &str, kind: SymbolKind,
86 ty: Option<String>,
87 decl_span: Option<u32>,
88 ) {
89 let hover = HoverInfo {
90 description: String::new(),
91 doc_key: Some(normalise_documentation_key(doc_key)),
92 kind: Some(kind),
93 ty,
94 decl_span,
95 };
96 span_with_hover(node, self.source_code, self.source_map, hover);
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub enum TypecheckingContext {
103 Boolean,
104 Arithmetic,
105 Set,
106 SetOrMatrix,
107 MSet,
108 Matrix,
109 Tuple,
110 Record,
111 Partition,
112 Permutation,
113 Sequence,
114 Function,
115 Relation,
116 Unknown,
118}
119
120pub fn get_tree(src: &str) -> Option<(Tree, String)> {
125 let mut parser = Parser::new();
126 parser.set_language(&LANGUAGE.into()).unwrap();
127
128 parser.parse(src, None).and_then(|tree| {
129 let root = tree.root_node();
130 if root.is_error() {
131 return None;
132 }
133 Some((tree, src.to_string()))
134 })
135}
136
137pub fn get_expr_tree(src: &str) -> Option<(Tree, String)> {
142 let mut parser = Parser::new();
143 parser.set_language(&LANGUAGE.into()).unwrap();
144
145 parser.parse(src, None).and_then(|tree| {
146 let root = tree.root_node();
147 if root.is_error() {
148 return None;
149 }
150
151 let children: Vec<_> = named_children(&root).collect();
152 let first_child = children.first()?;
153
154 if first_child.is_error() {
160 if src.starts_with("_FRAGMENT_EXPRESSION") {
161 None
162 } else {
163 get_expr_tree(&format!("_FRAGMENT_EXPRESSION {src}"))
164 }
165 } else {
166 Some((tree, src.to_string()))
167 }
168 })
169}
170
171pub fn named_children<'a>(node: &'a Node<'a>) -> impl Iterator<Item = Node<'a>> + 'a {
173 (0..node.named_child_count())
174 .filter_map(|i| u32::try_from(i).ok().and_then(|i| node.named_child(i)))
175}
176
177pub fn node_is_expression(node: &Node) -> bool {
178 matches!(
179 node.kind(),
180 "bool_expr" | "arithmetic_expr" | "comparison_expr" | "annotation_expr" | "atom"
181 )
182}
183
184pub fn query_toplevel<'a>(
186 node: &'a Node<'a>,
187 predicate: &'a dyn Fn(&Node<'a>) -> bool,
188) -> impl Iterator<Item = Node<'a>> + 'a {
189 WalkDFS::with_retract(node, predicate).filter(|n| n.is_named() && predicate(n))
190}
191
192pub fn get_metavars<'a>(node: &'a Node<'a>, src: &'a str) -> impl Iterator<Item = String> + 'a {
194 query_toplevel(node, &|n| n.kind() == "metavar").filter_map(|child| {
195 child
196 .named_child(0)
197 .map(|name| src[name.start_byte()..name.end_byte()].to_string())
198 })
199}
200
201pub fn get_documentation(name: &str) -> Option<String> {
205 static DOCUMENTATION_CACHE: OnceLock<Mutex<BTreeMap<String, Option<String>>>> = OnceLock::new();
206
207 let base = normalise_documentation_key(name);
208 let cache = DOCUMENTATION_CACHE.get_or_init(|| Mutex::new(BTreeMap::new()));
209
210 if let Some(cached) = cache.lock().ok()?.get(&base).cloned() {
211 return cached;
212 }
213
214 let url =
216 format!("https://raw.githubusercontent.com/conjure-cp/conjure/main/docs/bits/{base}.md");
217
218 let output = std::process::Command::new("curl")
219 .args(["-fsSL", &url])
220 .output()
221 .ok();
222
223 let documentation = output
224 .filter(|output| output.status.success())
225 .and_then(|output| String::from_utf8(output.stdout).ok());
226
227 if let Ok(mut cache) = cache.lock() {
228 cache.insert(base, documentation.clone());
229 }
230
231 documentation
232}
233
234fn normalise_documentation_key(name: &str) -> String {
235 name.strip_suffix(".md").unwrap_or(name).to_string()
236}
237
238mod test {
239 #[allow(unused)]
240 use super::*;
241
242 #[test]
243 fn test_get_metavars() {
244 let src = "such that &x = y";
245 let (tree, _) = get_tree(src).unwrap();
246 let root = tree.root_node();
247 let metavars = get_metavars(&root, src).collect::<Vec<_>>();
248 assert_eq!(metavars, vec!["x"]);
249 }
250}