1use crate::{
2 expr::{Arg, ModPath, Sandbox},
3 typ::{FnType, TVar, Type},
4 BindId, InitFn, LambdaId, Rt, Scope, UserEvent,
5};
6use anyhow::{anyhow, bail, Result};
7use arcstr::ArcStr;
8use compact_str::CompactString;
9use fxhash::{FxHashMap, FxHashSet};
10use immutable_chunkmap::{map::MapS as Map, set::SetS as Set};
11use netidx::path::Path;
12use poolshark::local::LPooled;
13use std::{fmt, iter, mem, ops::Bound, sync::Weak};
14use triomphe::Arc;
15
16pub struct LambdaDef<R: Rt, E: UserEvent> {
17 pub id: LambdaId,
18 pub env: Env<R, E>,
19 pub scope: Scope,
20 pub argspec: Arc<[Arg]>,
21 pub typ: Arc<FnType>,
22 pub init: InitFn<R, E>,
23}
24
25impl<R: Rt, E: UserEvent> fmt::Debug for LambdaDef<R, E> {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 write!(f, "LambdaDef({:?})", self.id)
28 }
29}
30
31pub struct Bind {
32 pub id: BindId,
33 pub export: bool,
34 pub typ: Type,
35 pub doc: Option<ArcStr>,
36 pub scope: ModPath,
37 pub name: CompactString,
38}
39
40impl fmt::Debug for Bind {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 write!(f, "Bind {{ id: {:?}, export: {} }}", self.id, self.export,)
43 }
44}
45
46impl Clone for Bind {
47 fn clone(&self) -> Self {
48 Self {
49 id: self.id,
50 scope: self.scope.clone(),
51 name: self.name.clone(),
52 doc: self.doc.clone(),
53 export: self.export,
54 typ: self.typ.clone(),
55 }
56 }
57}
58
59#[derive(Debug, Clone)]
60pub struct TypeDef {
61 pub params: Arc<[(TVar, Option<Type>)]>,
62 pub typ: Type,
63 pub doc: Option<ArcStr>,
64}
65
66#[derive(Debug)]
67pub struct Env<R: Rt, E: UserEvent> {
68 pub by_id: Map<BindId, Bind>,
69 pub lambdas: Map<LambdaId, Weak<LambdaDef<R, E>>>,
70 pub byref_chain: Map<BindId, BindId>,
71 pub binds: Map<ModPath, Map<CompactString, BindId>>,
72 pub used: Map<ModPath, Arc<Vec<ModPath>>>,
73 pub modules: Set<ModPath>,
74 pub typedefs: Map<ModPath, Map<CompactString, TypeDef>>,
75 pub catch: Map<ModPath, BindId>,
76}
77
78impl<R: Rt, E: UserEvent> Clone for Env<R, E> {
79 fn clone(&self) -> Self {
80 Self {
81 by_id: self.by_id.clone(),
82 binds: self.binds.clone(),
83 byref_chain: self.byref_chain.clone(),
84 used: self.used.clone(),
85 modules: self.modules.clone(),
86 typedefs: self.typedefs.clone(),
87 lambdas: self.lambdas.clone(),
88 catch: self.catch.clone(),
89 }
90 }
91}
92
93impl<R: Rt, E: UserEvent> Env<R, E> {
94 pub(super) fn new() -> Self {
95 Self {
96 by_id: Map::new(),
97 binds: Map::new(),
98 byref_chain: Map::new(),
99 used: Map::new(),
100 modules: Set::new(),
101 typedefs: Map::new(),
102 lambdas: Map::new(),
103 catch: Map::new(),
104 }
105 }
106
107 pub(super) fn clear(&mut self) {
108 let Self { by_id, binds, byref_chain, used, modules, typedefs, lambdas, catch } =
109 self;
110 *by_id = Map::new();
111 *binds = Map::new();
112 *byref_chain = Map::new();
113 *used = Map::new();
114 *modules = Set::new();
115 *typedefs = Map::new();
116 *lambdas = Map::new();
117 *catch = Map::new();
118 }
119
120 pub(super) fn restore_lexical_env(&self, other: Self) -> Self {
124 Self {
125 binds: other.binds,
126 used: other.used,
127 modules: other.modules,
128 typedefs: other.typedefs,
129 by_id: self.by_id.clone(),
130 lambdas: self.lambdas.clone(),
131 catch: self.catch.clone(),
132 byref_chain: self.byref_chain.clone(),
133 }
134 }
135
136 pub(super) fn restore_lexical_env_mut(&self, other: &mut Self) -> Self {
137 Self {
138 binds: mem::take(&mut other.binds),
139 used: mem::take(&mut other.used),
140 modules: mem::take(&mut other.modules),
141 typedefs: mem::take(&mut other.typedefs),
142 by_id: self.by_id.clone(),
143 lambdas: self.lambdas.clone(),
144 catch: self.catch.clone(),
145 byref_chain: self.byref_chain.clone(),
146 }
147 }
148
149 pub fn apply_sandbox(&self, spec: &Sandbox) -> Result<Self> {
150 fn get_bind_name(n: &ModPath) -> Result<(&str, &str)> {
151 let dir = Path::dirname(&**n).ok_or_else(|| anyhow!("unknown module {n}"))?;
152 let k = Path::basename(&**n).ok_or_else(|| anyhow!("unknown module {n}"))?;
153 Ok((dir, k))
154 }
155 match spec {
156 Sandbox::Unrestricted => Ok(self.clone()),
157 Sandbox::Blacklist(bl) => {
158 let mut t = self.clone();
159 for n in bl.iter() {
160 if t.modules.remove_cow(n) {
161 t.binds.remove_cow(n);
162 t.typedefs.remove_cow(n);
163 } else {
164 let (dir, k) = get_bind_name(n)?;
165 let vals = t.binds.get_mut_cow(dir).ok_or_else(|| {
166 anyhow!("no value {k} in module {dir} and no module {n}")
167 })?;
168 if let None = vals.remove_cow(&CompactString::from(k)) {
169 bail!("no value {k} in module {dir} and no module {n}")
170 }
171 }
172 }
173 Ok(t)
174 }
175 Sandbox::Whitelist(wl) => {
176 let mut t = self.clone();
177 let mut modules = FxHashSet::default();
178 let mut names: FxHashMap<_, FxHashSet<_>> = FxHashMap::default();
179 for w in wl.iter() {
180 if t.modules.contains(w) {
181 modules.insert(w.clone());
182 } else {
183 let (dir, n) = get_bind_name(w)?;
184 let dir = ModPath(Path::from(ArcStr::from(dir)));
185 let n = CompactString::from(n);
186 t.binds.get(&dir).and_then(|v| v.get(&n)).ok_or_else(|| {
187 anyhow!("no value {n} in module {dir} and no module {w}")
188 })?;
189 names.entry(dir).or_default().insert(n);
190 }
191 }
192 t.typedefs = t.typedefs.update_many(
193 t.typedefs.into_iter().map(|(k, v)| (k.clone(), v.clone())),
194 |k, v, _| {
195 if modules.contains(&k) || names.contains_key(&k) {
196 Some((k, v))
197 } else {
198 None
199 }
200 },
201 );
202 t.modules =
203 t.modules.update_many(t.modules.into_iter().cloned(), |k, _| {
204 if modules.contains(&k) || names.contains_key(&k) {
205 Some(k)
206 } else {
207 None
208 }
209 });
210 t.binds = t.binds.update_many(
211 t.binds.into_iter().map(|(k, v)| (k.clone(), v.clone())),
212 |k, v, _| {
213 if modules.contains(&k) {
214 Some((k, v))
215 } else if let Some(names) = names.get(&k) {
216 let v = v.update_many(
217 v.into_iter().map(|(k, v)| (k.clone(), v.clone())),
218 |kn, vn, _| {
219 if names.contains(&kn) {
220 Some((kn, vn))
221 } else {
222 None
223 }
224 },
225 );
226 Some((k, v))
227 } else {
228 None
229 }
230 },
231 );
232 Ok(t)
233 }
234 }
235 }
236
237 pub fn find_visible<T, F: FnMut(&str, &str) -> Option<T>>(
238 &self,
239 scope: &ModPath,
240 name: &ModPath,
241 mut f: F,
242 ) -> Option<T> {
243 let mut buf = CompactString::from("");
244 let name_scope = Path::dirname(&**name);
245 let name = Path::basename(&**name).unwrap_or("");
246 for scope in Path::dirnames(&**scope).rev() {
247 let used = self.used.get(scope);
248 let used = iter::once(scope)
249 .chain(used.iter().flat_map(|s| s.iter().map(|p| &***p)));
250 for scope in used {
251 let scope = name_scope
252 .map(|ns| {
253 buf.clear();
254 buf.push_str(scope);
255 if let Some(Path::SEP) = buf.chars().next_back() {
256 buf.pop();
257 }
258 buf.push_str(ns);
259 buf.as_str()
260 })
261 .unwrap_or(scope);
262 if let Some(res) = f(scope, name) {
263 return Some(res);
264 }
265 }
266 }
267 None
268 }
269
270 pub fn lookup_bind(
271 &self,
272 scope: &ModPath,
273 name: &ModPath,
274 ) -> Option<(&ModPath, &Bind)> {
275 self.find_visible(scope, name, |scope, name| {
276 self.binds.get_full(scope).and_then(|(scope, vars)| {
277 vars.get(name)
278 .and_then(|bid| self.by_id.get(bid).map(|bind| (scope, bind)))
279 })
280 })
281 }
282
283 pub fn lookup_typedef(&self, scope: &ModPath, name: &ModPath) -> Option<&TypeDef> {
284 self.find_visible(scope, name, |scope, name| {
285 self.typedefs.get(scope).and_then(|m| m.get(name))
286 })
287 }
288
289 pub fn lookup_catch(&self, scope: &ModPath) -> Result<BindId> {
291 match Path::dirnames(&scope.0).rev().find_map(|scope| self.catch.get(scope)) {
292 Some(id) => Ok(*id),
293 None => bail!("there is no catch visible in {scope}"),
294 }
295 }
296
297 pub fn lookup_matching(
301 &self,
302 scope: &ModPath,
303 part: &ModPath,
304 ) -> Vec<(CompactString, BindId)> {
305 let mut res = vec![];
306 self.find_visible(scope, part, |scope, part| {
307 if let Some(vars) = self.binds.get(scope) {
308 let r = vars.range::<str, _>((Bound::Included(part), Bound::Unbounded));
309 for (name, bind) in r {
310 if name.starts_with(part) {
311 res.push((name.clone(), *bind));
312 }
313 }
314 }
315 None::<()>
316 });
317 res
318 }
319
320 pub fn lookup_matching_modules(
324 &self,
325 scope: &ModPath,
326 part: &ModPath,
327 ) -> Vec<ModPath> {
328 let mut res = vec![];
329 self.find_visible(scope, part, |scope, part| {
330 let p = ModPath(Path::from(ArcStr::from(scope)).append(part));
331 for m in self.modules.range((Bound::Included(p.clone()), Bound::Unbounded)) {
332 if m.0.starts_with(&*p.0) {
333 if let Some(m) = m.strip_prefix(scope) {
334 if !m.trim().is_empty() {
335 res.push(ModPath(Path::from(ArcStr::from(m))));
336 }
337 }
338 }
339 }
340 None::<()>
341 });
342 res
343 }
344
345 pub fn canonical_modpath(&self, scope: &ModPath, name: &ModPath) -> Option<ModPath> {
346 self.find_visible(scope, name, |scope, name| {
347 let p = ModPath(Path::from(ArcStr::from(scope)).append(name));
348 if self.modules.contains(&p) {
349 Some(p)
350 } else {
351 None
352 }
353 })
354 }
355
356 pub fn deftype(
357 &mut self,
358 scope: &ModPath,
359 name: &str,
360 params: Arc<[(TVar, Option<Type>)]>,
361 typ: Type,
362 doc: Option<ArcStr>,
363 ) -> Result<()> {
364 let defs = self.typedefs.get_or_default_cow(scope.clone());
365 if defs.get(name).is_some() {
366 bail!("{name} is already defined in scope {scope}")
367 } else {
368 let mut known: LPooled<FxHashMap<ArcStr, TVar>> = LPooled::take();
369 let mut declared: LPooled<FxHashSet<ArcStr>> = LPooled::take();
370 for (tv, tc) in params.iter() {
371 Type::TVar(tv.clone()).alias_tvars(&mut known);
372 if let Some(tc) = tc {
373 tc.alias_tvars(&mut known);
374 }
375 }
376 typ.alias_tvars(&mut known);
377 for (tv, _) in params.iter() {
378 if !declared.insert(tv.name.clone()) {
379 bail!("duplicate type variable {tv} in definition of {name}");
380 }
381 }
382 typ.check_tvars_declared(&mut declared)?;
383 for (_, t) in params.iter() {
384 if let Some(t) = t {
385 t.check_tvars_declared(&mut declared)?;
386 }
387 }
388 for dec in declared.iter() {
389 if !known.contains_key(dec) {
390 bail!("unused type parameter {dec} in definition of {name}")
391 }
392 }
393 defs.insert_cow(name.into(), TypeDef { params, typ, doc });
394 Ok(())
395 }
396 }
397
398 pub fn undeftype(&mut self, scope: &ModPath, name: &str) {
399 if let Some(defs) = self.typedefs.get_mut_cow(scope) {
400 defs.remove_cow(&CompactString::from(name));
401 if defs.len() == 0 {
402 self.typedefs.remove_cow(scope);
403 }
404 }
405 }
406
407 pub fn bind_variable(&mut self, scope: &ModPath, name: &str, typ: Type) -> &mut Bind {
410 let binds = self.binds.get_or_default_cow(scope.clone());
411 let mut existing = true;
412 let id = binds.get_or_insert_cow(CompactString::from(name), || {
413 existing = false;
414 BindId::new()
415 });
416 if existing {
417 *id = BindId::new();
418 }
419 self.by_id.get_or_insert_cow(*id, || Bind {
420 export: true,
421 id: *id,
422 scope: scope.clone(),
423 doc: None,
424 name: CompactString::from(name),
425 typ,
426 })
427 }
428
429 pub fn alias_variable(&mut self, scope: &ModPath, name: &str, id: BindId) {
431 let binds = self.binds.get_or_default_cow(scope.clone());
432 binds.insert_cow(CompactString::from(name), id);
433 }
434
435 pub fn unbind_variable(&mut self, id: BindId) {
436 if let Some(b) = self.by_id.remove_cow(&id) {
437 if let Some(binds) = self.binds.get_mut_cow(&b.scope) {
438 binds.remove_cow(&b.name);
439 if binds.len() == 0 {
440 self.binds.remove_cow(&b.scope);
441 }
442 }
443 }
444 }
445
446 pub fn use_in_scope(&mut self, scope: &Scope, name: &ModPath) -> Result<()> {
447 match self.canonical_modpath(&scope.lexical, name) {
448 None => bail!("use {name}: no such module {name}"),
449 Some(_) => {
450 let used = self.used.get_or_default_cow(scope.lexical.clone());
451 Ok(Arc::make_mut(used).push(name.clone()))
452 }
453 }
454 }
455
456 pub fn stop_use_in_scope(&mut self, scope: &Scope, name: &ModPath) {
457 if let Some(used) = self.used.get_mut_cow(&scope.lexical) {
458 Arc::make_mut(used).retain(|n| n != name);
459 if used.is_empty() {
460 self.used.remove_cow(&scope.lexical);
461 }
462 }
463 }
464}