1use crate::{
2 expr::{ModPath, Origin, Sandbox},
3 typ::{TVar, Type},
4 BindId, ModuleInternalView, Scope, SigImplLink, TypeRefSite,
5};
6use ahash::{AHashMap, AHashSet};
7use anyhow::{anyhow, bail, Result};
8use arcstr::ArcStr;
9use combine::stream::position::SourcePosition;
10use compact_str::CompactString;
11use immutable_chunkmap::{map::MapS as Map, set::SetS as Set};
12use netidx::path::Path;
13use parking_lot::Mutex;
14use poolshark::{
15 global::{GPooled, Pool},
16 local::LPooled,
17};
18use std::{fmt, iter, mem, ops::Bound, sync::LazyLock};
19use triomphe::Arc;
20
21pub struct Bind {
22 pub id: BindId,
23 pub export: bool,
24 pub typ: Type,
25 pub doc: Option<ArcStr>,
26 pub scope: ModPath,
27 pub name: CompactString,
28 pub pos: SourcePosition,
31 pub ori: Arc<Origin>,
33}
34
35impl fmt::Debug for Bind {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 write!(f, "Bind {{ id: {:?}, export: {} }}", self.id, self.export,)
38 }
39}
40
41impl Clone for Bind {
42 fn clone(&self) -> Self {
43 Self {
44 id: self.id,
45 scope: self.scope.clone(),
46 name: self.name.clone(),
47 doc: self.doc.clone(),
48 export: self.export,
49 typ: self.typ.clone(),
50 pos: self.pos,
51 ori: self.ori.clone(),
52 }
53 }
54}
55
56#[derive(Debug, Clone)]
57pub struct TypeDef {
58 pub params: Arc<[(TVar, Option<Type>)]>,
59 pub typ: Type,
60 pub doc: Option<ArcStr>,
61 pub pos: SourcePosition,
64 pub ori: Arc<Origin>,
65}
66
67#[derive(Debug)]
78pub struct Lsp {
79 pub type_refs: GPooled<Vec<TypeRefSite>>,
80 pub sig_links: GPooled<Vec<SigImplLink>>,
81 pub module_internals: GPooled<Vec<ModuleInternalView>>,
82}
83
84impl Lsp {
85 pub fn new() -> Self {
87 static TYPE_REF_SITE_POOL: LazyLock<Pool<Vec<TypeRefSite>>> =
88 LazyLock::new(|| Pool::new(64, 65536));
89 static SIG_LINK_POOL: LazyLock<Pool<Vec<SigImplLink>>> =
90 LazyLock::new(|| Pool::new(32, 4096));
91 static MODULE_INTERNAL_VIEW_POOL: LazyLock<Pool<Vec<ModuleInternalView>>> =
92 LazyLock::new(|| Pool::new(32, 4096));
93 Self {
94 type_refs: TYPE_REF_SITE_POOL.take(),
95 sig_links: SIG_LINK_POOL.take(),
96 module_internals: MODULE_INTERNAL_VIEW_POOL.take(),
97 }
98 }
99}
100
101impl Default for Lsp {
102 fn default() -> Self {
103 Self::new()
104 }
105}
106
107#[derive(Clone, Debug, Default)]
108pub struct Env {
109 pub by_id: Map<BindId, Bind>,
110 pub byref_chain: Map<BindId, BindId>,
111 pub binds: Map<ModPath, Map<CompactString, BindId>>,
112 pub used: Map<ModPath, Arc<Vec<ModPath>>>,
113 pub modules: Set<ModPath>,
114 pub typedefs: Map<ModPath, Map<CompactString, TypeDef>>,
115 pub catch: Map<ModPath, BindId>,
116 pub ide_binds: Map<ModPath, Map<CompactString, Bind>>,
123 pub lsp_mode: bool,
127 pub lsp: Option<Arc<Mutex<Lsp>>>,
134}
135
136impl Env {
137 pub(super) fn clear(&mut self) {
138 let Self {
139 by_id,
140 binds,
141 byref_chain,
142 used,
143 modules,
144 typedefs,
145 catch,
146 ide_binds,
147 lsp_mode: _,
148 lsp: _,
149 } = self;
150 *by_id = Map::new();
151 *binds = Map::new();
152 *byref_chain = Map::new();
153 *used = Map::new();
154 *modules = Set::new();
155 *typedefs = Map::new();
156 *catch = Map::new();
157 *ide_binds = Map::new();
158 }
159
160 pub(super) fn restore_lexical_env(&self, other: Self) -> Self {
168 Self {
169 binds: other.binds,
170 used: other.used,
171 modules: other.modules,
172 typedefs: other.typedefs,
173 by_id: self.by_id.clone(),
174 catch: self.catch.clone(),
175 byref_chain: self.byref_chain.clone(),
176 ide_binds: self.ide_binds.clone(),
177 lsp_mode: self.lsp_mode,
178 lsp: self.lsp.clone(),
179 }
180 }
181
182 pub(super) fn restore_lexical_env_mut(&self, other: &mut Self) -> Self {
183 Self {
184 binds: mem::take(&mut other.binds),
185 used: mem::take(&mut other.used),
186 modules: mem::take(&mut other.modules),
187 typedefs: mem::take(&mut other.typedefs),
188 by_id: self.by_id.clone(),
189 catch: self.catch.clone(),
190 ide_binds: self.ide_binds.clone(),
191 byref_chain: self.byref_chain.clone(),
192 lsp_mode: self.lsp_mode,
193 lsp: self.lsp.clone(),
194 }
195 }
196
197 pub fn push_type_ref(&self, site: TypeRefSite) {
200 if let Some(lsp) = &self.lsp {
201 lsp.lock().type_refs.push(site);
202 }
203 }
204
205 pub fn push_sig_link(&self, link: SigImplLink) {
207 if let Some(lsp) = &self.lsp {
208 lsp.lock().sig_links.push(link);
209 }
210 }
211
212 pub fn push_module_internal_view(&self, view: ModuleInternalView) {
215 if let Some(lsp) = &self.lsp {
216 lsp.lock().module_internals.push(view);
217 }
218 }
219
220 pub fn apply_sandbox(&self, spec: &Sandbox) -> Result<Self> {
221 fn get_bind_name(n: &ModPath) -> Result<(&str, &str)> {
222 let dir = Path::dirname(&**n).ok_or_else(|| anyhow!("unknown module {n}"))?;
223 let k = Path::basename(&**n).ok_or_else(|| anyhow!("unknown module {n}"))?;
224 Ok((dir, k))
225 }
226 match spec {
227 Sandbox::Unrestricted => Ok(self.clone()),
228 Sandbox::Blacklist(bl) => {
229 let mut t = self.clone();
230 for n in bl.iter() {
231 if t.modules.remove_cow(n) {
232 t.binds.remove_cow(n);
233 t.typedefs.remove_cow(n);
234 } else {
235 let (dir, k) = get_bind_name(n)?;
236 let vals = t.binds.get_mut_cow(dir).ok_or_else(|| {
237 anyhow!("no value {k} in module {dir} and no module {n}")
238 })?;
239 if let None = vals.remove_cow(&CompactString::from(k)) {
240 bail!("no value {k} in module {dir} and no module {n}")
241 }
242 }
243 }
244 Ok(t)
245 }
246 Sandbox::Whitelist(wl) => {
247 let mut t = self.clone();
248 let mut modules = AHashSet::default();
249 let mut names: AHashMap<_, AHashSet<_>> = AHashMap::default();
250 for w in wl.iter() {
251 if t.modules.contains(w) {
252 modules.insert(w.clone());
253 } else {
254 let (dir, n) = get_bind_name(w)?;
255 let dir = ModPath(Path::from(ArcStr::from(dir)));
256 let n = CompactString::from(n);
257 t.binds.get(&dir).and_then(|v| v.get(&n)).ok_or_else(|| {
258 anyhow!("no value {n} in module {dir} and no module {w}")
259 })?;
260 names.entry(dir).or_default().insert(n);
261 }
262 }
263 t.typedefs = t.typedefs.update_many(
264 t.typedefs.into_iter().map(|(k, v)| (k.clone(), v.clone())),
265 |k, v, _| {
266 if modules.contains(&k) || names.contains_key(&k) {
267 Some((k, v))
268 } else {
269 None
270 }
271 },
272 );
273 t.modules =
274 t.modules.update_many(t.modules.into_iter().cloned(), |k, _| {
275 if modules.contains(&k) || names.contains_key(&k) {
276 Some(k)
277 } else {
278 None
279 }
280 });
281 t.binds = t.binds.update_many(
282 t.binds.into_iter().map(|(k, v)| (k.clone(), v.clone())),
283 |k, v, _| {
284 if modules.contains(&k) {
285 Some((k, v))
286 } else if let Some(names) = names.get(&k) {
287 let v = v.update_many(
288 v.into_iter().map(|(k, v)| (k.clone(), v.clone())),
289 |kn, vn, _| {
290 if names.contains(&kn) {
291 Some((kn, vn))
292 } else {
293 None
294 }
295 },
296 );
297 Some((k, v))
298 } else {
299 None
300 }
301 },
302 );
303 Ok(t)
304 }
305 }
306 }
307
308 pub fn find_visible<T, F: FnMut(&str, &str) -> Option<T>>(
309 &self,
310 scope: &ModPath,
311 name: &ModPath,
312 mut f: F,
313 ) -> Option<T> {
314 let mut buf = CompactString::from("");
315 let name_scope = Path::dirname(&**name);
316 let name = Path::basename(&**name).unwrap_or("");
317 for scope in Path::dirnames(&**scope).rev() {
318 let used = self.used.get(scope);
319 let used = iter::once(scope)
320 .chain(used.iter().flat_map(|s| s.iter().map(|p| &***p)));
321 for scope in used {
322 let scope = name_scope
323 .map(|ns| {
324 buf.clear();
325 buf.push_str(scope);
326 if let Some(Path::SEP) = buf.chars().next_back() {
327 buf.pop();
328 }
329 buf.push_str(ns);
330 buf.as_str()
331 })
332 .unwrap_or(scope);
333 if let Some(res) = f(scope, name) {
334 return Some(res);
335 }
336 }
337 }
338 None
339 }
340
341 pub fn lookup_bind(
342 &self,
343 scope: &ModPath,
344 name: &ModPath,
345 ) -> Option<(&ModPath, &Bind)> {
346 self.find_visible(scope, name, |scope, name| {
347 self.binds.get_full(scope).and_then(|(scope, vars)| {
348 vars.get(name)
349 .and_then(|bid| self.by_id.get(bid).map(|bind| (scope, bind)))
350 })
351 })
352 }
353
354 pub fn lookup_typedef(&self, scope: &ModPath, name: &ModPath) -> Option<&TypeDef> {
355 self.find_visible(scope, name, |scope, name| {
356 self.typedefs.get(scope).and_then(|m| m.get(name))
357 })
358 }
359
360 pub fn lookup_catch(&self, scope: &ModPath) -> Result<BindId> {
362 match Path::dirnames(&scope.0).rev().find_map(|scope| self.catch.get(scope)) {
363 Some(id) => Ok(*id),
364 None => bail!("there is no catch visible in {scope}"),
365 }
366 }
367
368 pub fn lookup_matching(
372 &self,
373 scope: &ModPath,
374 part: &ModPath,
375 ) -> Vec<(CompactString, BindId)> {
376 let mut res = vec![];
377 self.find_visible(scope, part, |scope, part| {
378 if let Some(vars) = self.binds.get(scope) {
379 let r = vars.range::<str, _>((Bound::Included(part), Bound::Unbounded));
380 for (name, bind) in r {
381 if name.starts_with(part) {
382 res.push((name.clone(), *bind));
383 }
384 }
385 }
386 None::<()>
387 });
388 res
389 }
390
391 pub fn lookup_matching_modules(
395 &self,
396 scope: &ModPath,
397 part: &ModPath,
398 ) -> Vec<ModPath> {
399 let mut res = vec![];
400 self.find_visible(scope, part, |scope, part| {
401 let p = ModPath(Path::from(ArcStr::from(scope)).append(part));
402 for m in self.modules.range((Bound::Included(p.clone()), Bound::Unbounded)) {
403 if m.0.starts_with(&*p.0) {
404 if let Some(m) = m.strip_prefix(scope) {
405 if !m.trim().is_empty() {
406 res.push(ModPath(Path::from(ArcStr::from(m))));
407 }
408 }
409 }
410 }
411 None::<()>
412 });
413 res
414 }
415
416 pub fn canonical_modpath(&self, scope: &ModPath, name: &ModPath) -> Option<ModPath> {
417 self.find_visible(scope, name, |scope, name| {
418 let p = ModPath(Path::from(ArcStr::from(scope)).append(name));
419 if self.modules.contains(&p) {
420 Some(p)
421 } else {
422 None
423 }
424 })
425 }
426
427 pub fn deftype(
428 &mut self,
429 scope: &ModPath,
430 name: &str,
431 params: Arc<[(TVar, Option<Type>)]>,
432 typ: Type,
433 doc: Option<ArcStr>,
434 pos: SourcePosition,
435 ori: Arc<Origin>,
436 ) -> Result<()> {
437 if self.typedefs.get(scope).and_then(|m| m.get(name)).is_some() {
438 bail!("{name} is already defined in scope {scope}")
439 }
440 let mut known: LPooled<AHashMap<ArcStr, TVar>> = LPooled::take();
441 let mut declared: LPooled<AHashSet<ArcStr>> = LPooled::take();
442 for (tv, tc) in params.iter() {
443 Type::TVar(tv.clone()).alias_tvars(&mut known);
444 if let Some(tc) = tc {
445 tc.alias_tvars(&mut known);
446 }
447 }
448 typ.alias_tvars(&mut known);
449 for (tv, _) in params.iter() {
450 if !declared.insert(tv.name.clone()) {
451 bail!("duplicate type variable {tv} in definition of {name}");
452 }
453 }
454 for (_, t) in params.iter() {
455 if let Some(t) = t {
456 t.check_tvars_declared(&mut declared)?;
457 }
458 }
459 for dec in declared.iter() {
460 if !known.contains_key(dec) {
461 bail!("unused type parameter {dec} in definition of {name}")
462 }
463 }
464 if self.lsp_mode {
465 typ.record_ide_refs(self, scope);
472 }
473 let defs = self.typedefs.get_or_default_cow(scope.clone());
474 defs.insert_cow(name.into(), TypeDef { params, typ, doc, pos, ori });
475 Ok(())
476 }
477
478 pub fn undeftype(&mut self, scope: &ModPath, name: &str) {
479 if let Some(defs) = self.typedefs.get_mut_cow(scope) {
480 defs.remove_cow(&CompactString::from(name));
481 if defs.len() == 0 {
482 self.typedefs.remove_cow(scope);
483 }
484 }
485 }
486
487 pub fn unbind_scope_subtree(&mut self, scope: &ModPath) -> usize {
498 fn is_under(s: &ModPath, prefix: &ModPath) -> bool {
499 let s: &str = s;
502 let p: &str = prefix;
503 if s == p {
504 return true;
505 }
506 if !s.starts_with(p) {
507 return false;
508 }
509 s.as_bytes().get(p.len()).copied() == Some(b'/')
511 }
512 let mut removed = 0;
513 let bind_scopes: LPooled<Vec<ModPath>> = (&self.binds)
514 .into_iter()
515 .filter(|(s, _)| is_under(s, scope))
516 .map(|(s, _)| s.clone())
517 .collect();
518 for s in &*bind_scopes {
519 if let Some(defs) = self.binds.get(s) {
520 let ids: Vec<BindId> = defs.into_iter().map(|(_, id)| *id).collect();
521 removed += ids.len();
522 for id in &ids {
523 self.by_id.remove_cow(id);
524 }
525 }
526 self.binds.remove_cow(s);
527 self.ide_binds.remove_cow(s);
528 }
529 let type_scopes: LPooled<Vec<ModPath>> = (&self.typedefs)
530 .into_iter()
531 .filter(|(s, _)| is_under(s, scope))
532 .map(|(s, _)| s.clone())
533 .collect();
534 for s in &*type_scopes {
535 if let Some(defs) = self.typedefs.get(s) {
536 removed += defs.len();
537 }
538 self.typedefs.remove_cow(s);
539 }
540 let used_scopes: LPooled<Vec<ModPath>> = (&self.used)
541 .into_iter()
542 .filter(|(s, _)| is_under(s, scope))
543 .map(|(s, _)| s.clone())
544 .collect();
545 for s in &*used_scopes {
546 self.used.remove_cow(s);
547 }
548 let mod_scopes: LPooled<Vec<ModPath>> =
549 (&self.modules).into_iter().filter(|s| is_under(s, scope)).cloned().collect();
550 for s in &*mod_scopes {
551 self.modules.remove_cow(s);
552 }
553 let catch_scopes: LPooled<Vec<ModPath>> = (&self.catch)
554 .into_iter()
555 .filter(|(s, _)| is_under(s, scope))
556 .map(|(s, _)| s.clone())
557 .collect();
558 for s in &*catch_scopes {
559 self.catch.remove_cow(s);
560 }
561 removed
562 }
563
564 pub fn bind_variable(
567 &mut self,
568 scope: &ModPath,
569 name: &str,
570 typ: Type,
571 pos: SourcePosition,
572 ori: Arc<Origin>,
573 ) -> &mut Bind {
574 let binds = self.binds.get_or_default_cow(scope.clone());
575 let mut existing = true;
576 let id = binds.get_or_insert_cow(CompactString::from(name), || {
577 existing = false;
578 BindId::new()
579 });
580 if existing {
581 *id = BindId::new();
582 }
583 let bind = self.by_id.get_or_insert_cow(*id, || Bind {
584 export: true,
585 id: *id,
586 scope: scope.clone(),
587 doc: None,
588 name: CompactString::from(name),
589 typ,
590 pos,
591 ori,
592 });
593 if self.lsp_mode {
594 let ide_clone = bind.clone();
595 let ide_defs = self.ide_binds.get_or_default_cow(scope.clone());
596 ide_defs.insert_cow(CompactString::from(name), ide_clone);
597 }
598 self.by_id.get_mut_cow(id).unwrap()
599 }
600
601 pub fn alias_variable(&mut self, scope: &ModPath, name: &str, id: BindId) {
603 let binds = self.binds.get_or_default_cow(scope.clone());
604 binds.insert_cow(CompactString::from(name), id);
605 }
606
607 pub fn unbind_variable(&mut self, id: BindId) {
608 if let Some(b) = self.by_id.remove_cow(&id) {
609 if let Some(binds) = self.binds.get_mut_cow(&b.scope) {
610 binds.remove_cow(&b.name);
611 if binds.len() == 0 {
612 self.binds.remove_cow(&b.scope);
613 }
614 }
615 }
616 }
617
618 pub fn use_in_scope(&mut self, scope: &Scope, name: &ModPath) -> Result<()> {
619 match self.canonical_modpath(&scope.lexical, name) {
620 None => bail!("use {name}: no such module {name} in scope {}", scope.lexical),
621 Some(_) => {
622 let used = self.used.get_or_default_cow(scope.lexical.clone());
623 Ok(Arc::make_mut(used).push(name.clone()))
624 }
625 }
626 }
627
628 pub fn stop_use_in_scope(&mut self, scope: &Scope, name: &ModPath) {
629 if let Some(used) = self.used.get_mut_cow(&scope.lexical) {
630 Arc::make_mut(used).retain(|n| n != name);
631 if used.is_empty() {
632 self.used.remove_cow(&scope.lexical);
633 }
634 }
635 }
636}