Skip to main content

seahorse_dev/core/compile/namespace/
mod.rs

1// TODO just throwing everything into mod.rs for now, don't want to deal with keeping things clean
2// yet
3use crate::core::{
4    clean::ast as ca,
5    compile::builtin::*,
6    compile::check::*,
7    preprocess::{self as pre, Module},
8    util::*,
9};
10use crate::match1;
11use std::collections::{BTreeMap, VecDeque};
12
13enum Error {
14    ImportNotFound(Vec<String>),
15    CircularImport(Vec<String>),
16    SymbolNotFound(String),
17    AutomaticImportsNotExported(Vec<String>),
18    NotDefType(Vec<String>),
19    NoSuchSymbol(Vec<String>),
20}
21
22impl Error {
23    fn core(self) -> CoreError {
24        match self {
25            Self::ImportNotFound(ext) => {
26                CoreError::make_raw(format!("module \"{}\" not found", path_to_string(&ext)), "")
27            }
28            Self::CircularImport(path) => CoreError::make_raw(
29                "circular import",
30                format!(
31                    "Path: \"{}\".",
32                    path_to_string(&path.into_iter().skip(1).collect::<Vec<_>>())
33                ),
34            ),
35            Self::SymbolNotFound(symbol) => {
36                CoreError::make_raw(format!("symbol \"{}\" not found", symbol), "")
37            }
38            Self::AutomaticImportsNotExported(path) => {
39                CoreError::make_raw(
40                    "automatic import are not exported",
41                    format!(
42                        concat!(
43                            "Help: this is an automatic import, which does not get exported from other modules.\n",
44                            "You still have access to this object, just use its name: {}"
45                        ),
46                        path.last().unwrap()
47                    )
48                )
49            }
50            Self::NotDefType(path) => {
51                CoreError::make_raw(format!("\"{}\" is not a class", path_to_string(&path)), "")
52            }
53            Self::NoSuchSymbol(rel) => {
54                CoreError::make_raw(format!("symbol \"{}\" not found", path_to_string(&rel)), "")
55            }
56        }
57    }
58}
59
60pub fn path_to_string(path: &Vec<String>) -> String {
61    if path.len() == 0 {
62        return "".to_string();
63    }
64
65    let mut path_string = path[0].clone();
66    for part in path.iter().skip(1) {
67        path_string.push('.');
68        path_string.push_str(part.as_str());
69    }
70
71    return path_string;
72}
73
74/// The output of the "namespace" step. Transforms modules into namespaces, which are simply maps
75/// from export name to export item.
76///
77/// An export can be a re-imported item from another module, or a defined object.
78#[derive(Clone, Debug)]
79pub struct NamespaceOutput {
80    pub preprocessed: pre::ModuleRegistry,
81    pub tree: Tree<Namespace>,
82}
83
84/// The (global) namespace for a module.
85pub type Namespace = BTreeMap<String, NamespacedObject>;
86
87impl Tree<Namespace> {
88    /// Get an item given an absolute path.
89    // pub fn get_item<'a>(&'a self, abs: &Vec<String>) -> Option<&'a Item> {
90    //     let mut curr = self;
91
92    //     for (i, part) in abs.iter().enumerate() {
93    //         match curr {
94    //             Self::Node(package) => {
95    //                 curr = package.get(part)?;
96    //             }
97    //             Self::Leaf(namespace) => {
98    //                 if i == abs.len() - 1 {
99    //                     return namespace.get(part).and_then(|export| match export {
100    //                         NamespacedObject::Item(item) => Some(item),
101    //                         _ => None,
102    //                     });
103    //                 }
104    //             }
105    //         }
106    //     }
107
108    //     return None;
109    // }
110
111    /// Advance a relative path as far as possible.
112    ///
113    /// Two paths are returned: the resulting absolute path and the remainder of the relative path,
114    /// which may be needed to index into the resulting item.
115    pub fn advance_path(
116        &self,
117        rel: &Vec<String>,
118        abs: &Vec<String>,
119    ) -> Option<(Vec<String>, Vec<String>)> {
120        self._advance_path(rel.clone().into(), abs)
121    }
122
123    fn _advance_path(
124        &self,
125        mut rel: VecDeque<String>,
126        abs: &Vec<String>,
127    ) -> Option<(Vec<String>, Vec<String>)> {
128        let next = match rel.pop_front() {
129            Some(next) => next,
130            None => {
131                return Some((abs.clone(), vec![]));
132            }
133        };
134
135        match self.get(abs) {
136            Some(Tree::Leaf(namespace)) => match namespace.get(&next) {
137                Some(NamespacedObject::Automatic(..) | NamespacedObject::Item(..)) => {
138                    let mut abs = abs.clone();
139                    abs.push(next);
140                    Some((abs, rel.into()))
141                }
142                Some(NamespacedObject::Import(Located(_, import))) => match import.import_type {
143                    ImportType::Symbol => {
144                        let mut abs = import.path.clone();
145                        rel.push_front(abs.pop().unwrap());
146                        self._advance_path(rel, &abs)
147                    }
148                    ImportType::Module | ImportType::Package => match self.get(&import.path) {
149                        Some(..) => self._advance_path(rel, &import.path),
150                        None => None,
151                    },
152                },
153                None => None,
154            },
155            Some(Tree::Node(package)) => match package.get(&next) {
156                Some(..) => {
157                    let mut abs = abs.clone();
158                    abs.push(next);
159                    self._advance_path(rel, &abs)
160                }
161                None => None,
162            },
163            None => None,
164        }
165    }
166}
167
168/// Module export.
169#[derive(Clone, Debug)]
170pub enum NamespacedObject {
171    Automatic(Builtin),
172    Import(Import),
173    Item(Item),
174}
175
176/// Module import. Just a path that is distinguished between an imported symbol, module, or package.
177/// Guaranteed to exist in the tree.
178pub type Import = Located<ImportObj>;
179#[derive(Clone, Debug)]
180pub struct ImportObj {
181    pub path: Vec<String>,
182    pub import_type: ImportType,
183    pub is_builtin: bool,
184}
185
186#[derive(Clone, Debug)]
187pub enum ImportType {
188    Symbol,
189    Module,
190    Package,
191}
192
193#[derive(Clone, Debug)]
194pub enum Item {
195    Defined(ca::TopLevelStatement),
196    Builtin(Builtin),
197}
198
199/// "Work-in-progress" namespace, used for circular import detection.
200enum Wip {
201    Empty,
202    Pending,
203    Done(Namespace),
204}
205
206impl TryFrom<pre::ModuleRegistry> for NamespaceOutput {
207    type Error = CoreError;
208
209    fn try_from(registry: pre::ModuleRegistry) -> CResult<NamespaceOutput> {
210        let mut wip = registry.tree.clone().map(|module| match module {
211            Module::Python(..) => Wip::Empty,
212            Module::SeahorsePrelude => Wip::Done(prelude::namespace()),
213            Module::SeahorsePyth => Wip::Done(pyth::namespace()),
214        });
215
216        build_namespace(&mut wip, &registry, &registry.origin)?;
217
218        let tree = wip.map(|namespace| match namespace {
219            Wip::Done(namespace) => namespace,
220            _ => panic!(),
221        });
222
223        let registry = NamespaceOutput {
224            preprocessed: registry,
225            tree,
226        };
227
228        return Ok(registry);
229    }
230}
231
232impl Tree<Namespace> {
233    pub fn build_ty(&self, ty_expr: &ca::TyExpression, abs: &Vec<String>) -> CResult<Ty> {
234        let Located(loc, obj) = ty_expr;
235
236        match obj {
237            ca::TyExpressionObj::Generic { base, params } => {
238                let params = params
239                    .iter()
240                    .map(|param| self.build_ty(param, abs))
241                    .collect::<Result<Vec<_>, _>>()?;
242
243                let base = match self.advance_path(base, abs) {
244                    Some((path, rem)) => {
245                        if rem.len() == 0 {
246                            match self.get_leaf_ext(&path).unwrap() {
247                                NamespacedObject::Automatic(builtin) => {
248                                    if !path.starts_with(&abs) {
249                                        Err(Error::AutomaticImportsNotExported(path)
250                                            .core()
251                                            .located(loc.clone()))
252                                    } else {
253                                        Ok(TyName::Builtin(builtin.clone()))
254                                    }
255                                }
256                                NamespacedObject::Item(Item::Defined(def)) => {
257                                    let Located(_, obj) = def;
258                                    match obj {
259                                        ca::TopLevelStatementObj::ClassDef { .. } => {
260                                            Ok(TyName::Defined(path, DefinedType::Struct))
261                                        }
262                                        _ => {
263                                            Err(Error::NotDefType(path).core().located(loc.clone()))
264                                        }
265                                    }
266                                }
267                                NamespacedObject::Item(Item::Builtin(builtin)) => {
268                                    builtin
269                                        .as_instance(&params)
270                                        .map_err(|err| err.located(loc.clone()))?;
271
272                                    Ok(TyName::Builtin(builtin.clone()))
273                                }
274                                _ => panic!(),
275                            }
276                        } else {
277                            Err(Error::NoSuchSymbol(base.clone())
278                                .core()
279                                .located(loc.clone()))
280                        }
281                    }
282                    _ => Err(Error::NoSuchSymbol(base.clone())
283                        .core()
284                        .located(loc.clone())),
285                }?;
286
287                Ok(Ty::Generic(base, params))
288            }
289            ca::TyExpressionObj::Const(n) => Ok(Ty::Const(*n)),
290        }
291        .map_err(|err: Error| err.core().located(loc.clone()))
292    }
293}
294
295// TODO after moving some things around, there's just a bunch of random functions lying around.
296// should find commonalities between them and give them a Context struct or something
297
298/// Recursively build the namespace associated with `path`.
299fn build_namespace(
300    wip: &mut Tree<Wip>,
301    registry: &pre::ModuleRegistry,
302    path: &Vec<String>,
303) -> CResult<()> {
304    let node = wip.get(path).unwrap();
305    match &node {
306        &Tree::Leaf(Wip::Pending) => return Err(Error::CircularImport(path.clone()).core()),
307        &Tree::Leaf(Wip::Done(..)) => return Ok(()),
308        &Tree::Leaf(Wip::Empty) => {
309            *wip.get_mut(path).unwrap() = Tree::Leaf(Wip::Pending);
310
311            let module = match1!(registry.tree.get(path).unwrap(), Tree::Leaf(module) => module);
312            let namespace = match module {
313                pre::Module::Python(module) => build_python_namespace(wip, registry, path, module)?,
314                _ => panic!(),
315            };
316
317            *wip.get_mut(path).unwrap() = Tree::Leaf(Wip::Done(namespace));
318        }
319        // TODO when asked to build the namespace of a package, recursively builds each of its
320        // constituent modules/packages. This is a bit outside of what Python does, needs work
321        &Tree::Node(package) => {
322            // Can't just iterate over keys, since that contains an immutable borrow to wip.
323            let keys = package.keys().map(|key| key.clone()).collect::<Vec<_>>();
324            for key in keys.iter() {
325                let mut path_ = path.clone();
326                path_.push(key.clone());
327                build_namespace(wip, registry, &path_)?;
328            }
329
330            return Ok(());
331        }
332    }
333
334    Ok(())
335}
336
337/// Build the namespace for a Python module at `path`.
338fn build_python_namespace(
339    wip: &mut Tree<Wip>,
340    registry: &pre::ModuleRegistry,
341    path: &Vec<String>,
342    module: &ca::Module,
343) -> CResult<Namespace> {
344    let mut namespace = BTreeMap::new();
345
346    // Automatic imports
347    namespace.append(&mut prelude::namespace());
348    namespace.append(&mut python::namespace());
349
350    for statement in module.statements.iter() {
351        let Located(loc, obj) = statement;
352        match obj {
353            ca::TopLevelStatementObj::Import { symbols } => {
354                for ca::ImportSymbol { symbol, alias } in symbols.iter() {
355                    // TODO i think symbols here can be deep imports (like `import
356                    // seahorse.prelude`)?
357                    let ext = vec![symbol.clone()];
358                    // Safe unwrap - preprocessor has already resolved all imports
359                    let abs = registry.get_abs_path(path, &ext).ok_or(
360                        Error::ImportNotFound(ext.clone())
361                            .core()
362                            .located(loc.clone()),
363                    )?;
364
365                    build_namespace(wip, registry, &abs)?;
366
367                    let obj =
368                        get_import_obj(wip, &abs, None).map_err(|err| err.located(loc.clone()))?;
369                    let name = alias.clone().unwrap_or(symbol.clone());
370                    namespace.insert(name, NamespacedObject::Import(Located(loc.clone(), obj)));
371                }
372            }
373            ca::TopLevelStatementObj::ImportFrom {
374                // TODO relative imports
375                path: ext,
376                symbols,
377                ..
378            } => {
379                let abs = registry.get_abs_path(path, ext).ok_or(
380                    Error::ImportNotFound(ext.clone())
381                        .core()
382                        .located(loc.clone()),
383                )?;
384
385                build_namespace(wip, registry, &abs)?;
386
387                for ca::ImportSymbol { symbol, alias } in symbols.iter() {
388                    if symbol.as_str() == "*" {
389                        let glob = match wip.get(&abs).unwrap() {
390                            Tree::Leaf(Wip::Done(namespace)) => {
391                                namespace
392                                    .iter()
393                                    .filter_map(|(name, object)| {
394                                        // Filter out the automatic imports
395                                        if let NamespacedObject::Automatic(..) = object {
396                                            None
397                                        } else {
398                                            Some(name)
399                                        }
400                                    })
401                                    .collect::<Vec<_>>()
402                            }
403                            Tree::Node(package) => package.keys().collect::<Vec<_>>(),
404                            _ => panic!(),
405                        };
406
407                        for symbol in glob.into_iter() {
408                            let obj = get_import_obj(wip, &abs, Some(symbol))
409                                .map_err(|err| err.located(loc.clone()))?;
410                            namespace.insert(
411                                symbol.clone(),
412                                NamespacedObject::Import(Located(loc.clone(), obj)),
413                            );
414                        }
415                    } else {
416                        let obj = get_import_obj(wip, &abs, Some(symbol))
417                            .map_err(|err| err.located(loc.clone()))?;
418                        let name = alias.clone().unwrap_or(symbol.clone());
419                        namespace.insert(name, NamespacedObject::Import(Located(loc.clone(), obj)));
420                    }
421                }
422            }
423            ca::TopLevelStatementObj::Constant { name, .. }
424            | ca::TopLevelStatementObj::ClassDef { name, .. }
425            | ca::TopLevelStatementObj::FunctionDef(ca::FunctionDef { name, .. }) => {
426                let export =
427                    NamespacedObject::Item(Item::Defined(Located(loc.clone(), obj.clone())));
428                namespace.insert(name.clone(), export);
429            }
430            ca::TopLevelStatementObj::Expression(..) => {}
431        }
432    }
433
434    return Ok(namespace);
435}
436
437/// Finds an imported object given a path within the namespace tree and optional extra symbol.
438///
439/// Requires the caller to make sure that the namespace at `path` has been built already.
440fn get_import_obj(
441    wip: &Tree<Wip>,
442    path: &Vec<String>,
443    symbol: Option<&String>,
444) -> CResult<ImportObj> {
445    match wip.get(path).unwrap() {
446        Tree::Leaf(Wip::Done(namespace)) => match symbol {
447            Some(symbol) => {
448                if let Some(object) = namespace.get(symbol) {
449                    let is_builtin = match object {
450                        NamespacedObject::Item(Item::Defined(..)) => false,
451                        NamespacedObject::Import(Located(
452                            _,
453                            ImportObj {
454                                is_builtin: false, ..
455                            },
456                        )) => false,
457                        _ => true,
458                    };
459
460                    let mut path = path.clone();
461                    path.push(symbol.clone());
462                    Ok(ImportObj {
463                        path,
464                        import_type: ImportType::Symbol,
465                        is_builtin,
466                    })
467                } else {
468                    Err(Error::SymbolNotFound(symbol.clone()).core())
469                }
470            }
471            None => Ok(ImportObj {
472                path: path.clone(),
473                import_type: ImportType::Module,
474                is_builtin: path.starts_with(&["sh".to_string()]),
475            }),
476        },
477        Tree::Node(package) => match symbol {
478            Some(symbol) => match package.get(symbol) {
479                Some(Tree::Leaf(..)) => {
480                    let mut path = path.clone();
481                    path.push(symbol.clone());
482                    let is_builtin = path.starts_with(&["sh".to_string()]);
483                    Ok(ImportObj {
484                        path: path,
485                        import_type: ImportType::Module,
486                        is_builtin,
487                    })
488                }
489                Some(Tree::Node(..)) => {
490                    let mut path = path.clone();
491                    path.push(symbol.clone());
492                    let is_builtin = path.starts_with(&["sh".to_string()]);
493                    Ok(ImportObj {
494                        path: path,
495                        import_type: ImportType::Package,
496                        is_builtin,
497                    })
498                }
499                None => Err(Error::SymbolNotFound(symbol.clone()).core()),
500            },
501            None => Ok(ImportObj {
502                path: path.clone(),
503                import_type: ImportType::Package,
504                is_builtin: path.starts_with(&["sh".to_string()]),
505            }),
506        },
507        _ => panic!(),
508    }
509}
510
511/// Builds the namespace of every module.
512pub fn namespace(registry: pre::ModuleRegistry) -> CResult<NamespaceOutput> {
513    registry.try_into()
514}