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