1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! WIP use-item importer. This performs eager import resolution on the AST
//!
//! # TODOs:
//! - [ ] Resolve imports using a graph traversal rather than linear iteration
//! - [ ] Separate imported items from natively declared items
//! - [ ] Separate the use-import pass from the project
//! - [ ] Report errors in a meaningful way
//! - [ ] Lazy import resolution using graph-edge traversal during name lookup?
//!     - It doesn't seem to me like the imports in a given scope *can change*.

#![allow(unused)]
use std::fmt::format;

use cl_ast::*;

use crate::{
    definition::{Def, DefKind},
    key::DefID,
    node::NodeSource,
    project::Project,
};

type UseResult = Result<(), String>;

impl<'a> Project<'a> {
    pub fn resolve_imports(&mut self) -> UseResult {
        for id in self.pool.key_iter() {
            self.visit_def(id)?;
        }
        Ok(())
    }

    pub fn visit_def(&mut self, id: DefID) -> UseResult {
        let Def { kind, node, module } = &self.pool[id];
        if let (DefKind::Use(parent), Some(NodeSource::Use(u))) = (kind, node.kind) {
            println!("Importing use item {u}");
            self.visit_use(u, *parent);
        }
        Ok(())
    }

    pub fn visit_use(&mut self, u: &'a Use, parent: DefID) -> UseResult {
        let Use { absolute, tree } = u;

        self.visit_use_tree(tree, parent, if *absolute { self.root } else { parent })
    }

    pub fn visit_use_tree(&mut self, tree: &'a UseTree, parent: DefID, c: DefID) -> UseResult {
        match tree {
            UseTree::Tree(trees) => {
                for tree in trees {
                    self.visit_use_tree(tree, parent, c)?;
                }
            }
            UseTree::Path(part, rest) => {
                let c = self.evaluate(part, c)?;
                self.visit_use_tree(rest, parent, c)?;
            }

            UseTree::Name(name) => self.visit_use_leaf(name, parent, c)?,
            UseTree::Alias(from, to) => {
                self.visit_use_alias(from, to, parent, c)?
            }
            UseTree::Glob => self.visit_use_glob(parent, c)?,
        }
        Ok(())
    }

    pub fn visit_use_path(&mut self) -> UseResult {
        Ok(())
    }

    pub fn visit_use_leaf(&mut self, name: &'a Sym, parent: DefID, c: DefID) -> UseResult {
        self.visit_use_alias(name, name, parent, c)
    }

    pub fn visit_use_alias(
        &mut self,
        from: &Sym,
        name: &Sym,
        parent: DefID,
        c: DefID,
    ) -> UseResult {
        let mut imported = false;
        let c_mod = &self[c].module;
        let (tid, vid) = (
            c_mod.types.get(from).copied(),
            c_mod.values.get(from).copied(),
        );
        let parent = &mut self[parent].module;

        if let Some(tid) = tid {
            parent.types.insert(*name, tid);
            imported = true;
        }

        if let Some(vid) = vid {
            parent.values.insert(*name, vid);
            imported = true;
        }

        if imported {
            Ok(())
        } else {
            Err(format!("Identifier {name} not found in module {c}"))
        }
    }

    pub fn visit_use_glob(&mut self, parent: DefID, c: DefID) -> UseResult {
        // Loop over all the items in c, and add them as items in the parent
        if parent == c {
            return Ok(());
        }
        let [parent, c] = self
            .pool
            .get_many_mut([parent, c])
            .expect("parent and c are not the same");

        for (k, v) in &c.module.types {
            parent.module.types.entry(*k).or_insert(*v);
        }
        for (k, v) in &c.module.values {
            parent.module.values.entry(*k).or_insert(*v);
        }
        Ok(())
    }
}