netidx_bscript/
env.rs

1use crate::{
2    expr::{Arg, ModPath},
3    typ::{FnType, NoRefs, Type},
4    BindId, Ctx, InitFn, LambdaId, UserEvent,
5};
6use anyhow::{bail, Result};
7use arcstr::ArcStr;
8use compact_str::CompactString;
9use immutable_chunkmap::{map::MapS as Map, set::SetS as Set};
10use netidx::path::Path;
11use std::{fmt, iter, ops::Bound, sync::Weak};
12use triomphe::Arc;
13
14pub struct LambdaDef<C: Ctx, E: UserEvent> {
15    pub id: LambdaId,
16    pub env: Env<C, E>,
17    pub scope: ModPath,
18    pub argspec: Arc<[Arg<NoRefs>]>,
19    pub typ: Arc<FnType<NoRefs>>,
20    pub init: InitFn<C, E>,
21}
22
23impl<C: Ctx, E: UserEvent> fmt::Debug for LambdaDef<C, E> {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        write!(f, "LambdaDef({:?})", self.id)
26    }
27}
28
29pub struct Bind {
30    pub id: BindId,
31    pub export: bool,
32    pub typ: Type<NoRefs>,
33    pub doc: Option<ArcStr>,
34    scope: ModPath,
35    name: CompactString,
36}
37
38impl fmt::Debug for Bind {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(f, "Bind {{ id: {:?}, export: {} }}", self.id, self.export,)
41    }
42}
43
44impl Clone for Bind {
45    fn clone(&self) -> Self {
46        Self {
47            id: self.id,
48            scope: self.scope.clone(),
49            name: self.name.clone(),
50            doc: self.doc.clone(),
51            export: self.export,
52            typ: self.typ.clone(),
53        }
54    }
55}
56
57pub struct Env<C: Ctx, E: UserEvent> {
58    pub by_id: Map<BindId, Bind>,
59    pub lambdas: Map<LambdaId, Weak<LambdaDef<C, E>>>,
60    pub binds: Map<ModPath, Map<CompactString, BindId>>,
61    pub used: Map<ModPath, Arc<Vec<ModPath>>>,
62    pub modules: Set<ModPath>,
63    pub typedefs: Map<ModPath, Map<CompactString, Type<NoRefs>>>,
64}
65
66impl<C: Ctx, E: UserEvent> Clone for Env<C, E> {
67    fn clone(&self) -> Self {
68        Self {
69            by_id: self.by_id.clone(),
70            binds: self.binds.clone(),
71            used: self.used.clone(),
72            modules: self.modules.clone(),
73            typedefs: self.typedefs.clone(),
74            lambdas: self.lambdas.clone(),
75        }
76    }
77}
78
79impl<C: Ctx, E: UserEvent> Env<C, E> {
80    pub(super) fn new() -> Self {
81        Self {
82            by_id: Map::new(),
83            binds: Map::new(),
84            used: Map::new(),
85            modules: Set::new(),
86            typedefs: Map::new(),
87            lambdas: Map::new(),
88        }
89    }
90
91    pub(super) fn clear(&mut self) {
92        let Self { by_id, binds, used, modules, typedefs, lambdas } = self;
93        *by_id = Map::new();
94        *binds = Map::new();
95        *used = Map::new();
96        *modules = Set::new();
97        *typedefs = Map::new();
98        *lambdas = Map::new();
99    }
100
101    // restore the lexical environment to the state it was in at the
102    // snapshot `other`, but leave the bind and type environment
103    // alone.
104    pub(super) fn restore_lexical_env(&self, other: &Self) -> Self {
105        Self {
106            binds: other.binds.clone(),
107            used: other.used.clone(),
108            modules: other.modules.clone(),
109            typedefs: other.typedefs.clone(),
110            by_id: self.by_id.clone(),
111            lambdas: self.lambdas.clone(),
112        }
113    }
114
115    // merge two lexical environments, with the `orig` environment
116    // taking prescidence in case of conflicts. The type and binding
117    // environment is not altered
118    pub(super) fn merge_lexical(&self, orig: &Self) -> Self {
119        let Self { by_id: _, lambdas: _, binds, used, modules, typedefs } = self;
120        let binds = binds.update_many(
121            orig.binds.into_iter().map(|(s, m)| (s.clone(), m)),
122            |k, v, kv| match kv {
123                None => Some((k, v.clone())),
124                Some((_, m)) => {
125                    let v = m.update_many(
126                        v.into_iter().map(|(k, v)| (k.clone(), *v)),
127                        |k, v, _| Some((k, v)),
128                    );
129                    Some((k, v))
130                }
131            },
132        );
133        let used = used.update_many(
134            orig.used.into_iter().map(|(k, v)| (k.clone(), v.clone())),
135            |k, v, _| Some((k, v)),
136        );
137        let modules = modules.union(&orig.modules);
138        let typedefs = typedefs.update_many(
139            orig.typedefs.into_iter().map(|(k, v)| (k.clone(), v)),
140            |k, v, kv| match kv {
141                None => Some((k, v.clone())),
142                Some((_, m)) => {
143                    let v = m.update_many(
144                        v.into_iter().map(|(k, v)| (k.clone(), v.clone())),
145                        |k, v, _| Some((k, v)),
146                    );
147                    Some((k, v))
148                }
149            },
150        );
151        Self {
152            binds,
153            used,
154            modules,
155            typedefs,
156            by_id: self.by_id.clone(),
157            lambdas: self.lambdas.clone(),
158        }
159    }
160
161    pub fn find_visible<R, F: FnMut(&str, &str) -> Option<R>>(
162        &self,
163        scope: &ModPath,
164        name: &ModPath,
165        mut f: F,
166    ) -> Option<R> {
167        let mut buf = CompactString::from("");
168        let name_scope = Path::dirname(&**name);
169        let name = Path::basename(&**name).unwrap_or("");
170        for scope in Path::dirnames(&**scope).rev() {
171            let used = self.used.get(scope);
172            let used = iter::once(scope)
173                .chain(used.iter().flat_map(|s| s.iter().map(|p| &***p)));
174            for scope in used {
175                let scope = name_scope
176                    .map(|ns| {
177                        buf.clear();
178                        buf.push_str(scope);
179                        if let Some(Path::SEP) = buf.chars().next_back() {
180                            buf.pop();
181                        }
182                        buf.push_str(ns);
183                        buf.as_str()
184                    })
185                    .unwrap_or(scope);
186                if let Some(res) = f(scope, name) {
187                    return Some(res);
188                }
189            }
190        }
191        None
192    }
193
194    pub fn lookup_bind(
195        &self,
196        scope: &ModPath,
197        name: &ModPath,
198    ) -> Option<(&ModPath, &Bind)> {
199        self.find_visible(scope, name, |scope, name| {
200            self.binds.get_full(scope).and_then(|(scope, vars)| {
201                vars.get(name)
202                    .and_then(|bid| self.by_id.get(bid).map(|bind| (scope, bind)))
203            })
204        })
205    }
206
207    /// lookup binds in scope that match the specified partial
208    /// name. This is intended to be used for IDEs and interactive
209    /// shells, and is not used by the compiler.
210    pub fn lookup_matching(
211        &self,
212        scope: &ModPath,
213        part: &ModPath,
214    ) -> Vec<(CompactString, BindId)> {
215        let mut res = vec![];
216        self.find_visible(scope, part, |scope, part| {
217            if let Some(vars) = self.binds.get(scope) {
218                let r = vars.range::<str, _>((Bound::Included(part), Bound::Unbounded));
219                for (name, bind) in r {
220                    if name.starts_with(part) {
221                        res.push((name.clone(), *bind));
222                    }
223                }
224            }
225            None::<()>
226        });
227        res
228    }
229
230    /// lookup modules in scope that match the specified partial
231    /// name. This is intended to be used for IDEs and interactive
232    /// shells, and is not used by the compiler.
233    pub fn lookup_matching_modules(
234        &self,
235        scope: &ModPath,
236        part: &ModPath,
237    ) -> Vec<ModPath> {
238        let mut res = vec![];
239        self.find_visible(scope, part, |scope, part| {
240            let p = ModPath(Path::from(ArcStr::from(scope)).append(part));
241            for m in self.modules.range((Bound::Included(p.clone()), Bound::Unbounded)) {
242                if m.0.starts_with(&*p.0) {
243                    if let Some(m) = m.strip_prefix(scope) {
244                        if !m.trim().is_empty() {
245                            res.push(ModPath(Path::from(ArcStr::from(m))));
246                        }
247                    }
248                }
249            }
250            None::<()>
251        });
252        res
253    }
254
255    pub fn deftype(
256        &mut self,
257        scope: &ModPath,
258        name: &str,
259        typ: Type<NoRefs>,
260    ) -> Result<()> {
261        let defs = self.typedefs.get_or_default_cow(scope.clone());
262        if defs.get(name).is_some() {
263            bail!("{name} is already defined in scope {scope}")
264        } else {
265            defs.insert_cow(name.into(), typ);
266            Ok(())
267        }
268    }
269
270    pub fn undeftype(&mut self, scope: &ModPath, name: &str) {
271        if let Some(defs) = self.typedefs.get_mut_cow(scope) {
272            defs.remove_cow(&CompactString::from(name));
273            if defs.len() == 0 {
274                self.typedefs.remove_cow(scope);
275            }
276        }
277    }
278
279    // create a new binding. If an existing bind exists in the same
280    // scope shadow it.
281    pub fn bind_variable(
282        &mut self,
283        scope: &ModPath,
284        name: &str,
285        typ: Type<NoRefs>,
286    ) -> &mut Bind {
287        let binds = self.binds.get_or_default_cow(scope.clone());
288        let mut existing = true;
289        let id = binds.get_or_insert_cow(CompactString::from(name), || {
290            existing = false;
291            BindId::new()
292        });
293        if existing {
294            *id = BindId::new();
295        }
296        self.by_id.get_or_insert_cow(*id, || Bind {
297            export: true,
298            id: *id,
299            scope: scope.clone(),
300            doc: None,
301            name: CompactString::from(name),
302            typ,
303        })
304    }
305
306    pub fn unbind_variable(&mut self, id: BindId) {
307        if let Some(b) = self.by_id.remove_cow(&id) {
308            if let Some(binds) = self.binds.get_mut_cow(&b.scope) {
309                binds.remove_cow(&b.name);
310                if binds.len() == 0 {
311                    self.binds.remove_cow(&b.scope);
312                }
313            }
314        }
315    }
316}