microcad_lang/resolve/
resolve_context.rs1use microcad_core::hash::HashSet;
7use microcad_lang_base::{
8 Diag, DiagHandler, DiagResult, Diagnostic, GetSourceLocInfoByHash, HashId, PushDiag,
9 ResourceLocation, SourceLocInfo, SrcReferrer, TreeDisplay, TreeState, WriteToFile,
10};
11
12use crate::{lower::ir, resolve::*, symbol::Symbol};
13
14pub struct ResolveContext {
16 pub root: Symbol,
18 pub(crate) sources: Sources,
20 pub diag: DiagHandler,
22}
23
24impl ResolveContext {
25 pub fn create(
27 root: std::rc::Rc<ir::Source>,
28 search_paths: Vec<std::path::PathBuf>,
29 builtin: Option<Symbol>,
30 diag: DiagHandler,
31 ) -> ResolveResult<Self> {
32 let mut context = Self {
33 sources: Sources::load(root.clone(), search_paths)?,
34 diag,
35 root: Symbol::default(),
36 };
37 match context.load(builtin) {
38 Ok(()) => Ok(context),
39 Err(err) => {
40 context.error(&err.src_ref(), err)?;
41 Ok(context)
42 }
43 }
44 }
45
46 fn load(&mut self, builtin: Option<Symbol>) -> ResolveResult<()> {
47 self.symbolize()?;
48 log::trace!("Symbolized Context:\n{self:?}");
49 if let Some(builtin) = builtin {
50 log::trace!("Added builtin library {id}.", id = builtin.id());
51 self.root.add_symbol(builtin)?;
52 }
53 self.resolve()?;
54
55 Ok(())
56 }
57
58 pub(crate) fn symbolize(&mut self) -> ResolveResult<()> {
59 use crate::lower::SingleIdentifier;
60
61 let named_symbols = self
62 .sources
63 .clone()
64 .iter()
65 .map(|source| {
66 match (
67 self.sources
68 .generate_name_from_path(&source.to_file_path().unwrap()),
69 source.symbolize(ir::Visibility::Public, self),
70 ) {
71 (Ok(name), Ok(symbol)) => Ok((name, symbol)),
72 (_, Err(err)) | (Err(err), _) => Err(err),
73 }
74 })
75 .collect::<ResolveResult<Vec<_>>>()?;
76 for (name, symbol) in named_symbols {
77 if let Some(id) = name.single_identifier() {
78 self.root.insert_symbol(id.clone(), symbol)?;
79 } else {
80 unreachable!("name is not an id")
81 }
82 }
83
84 Ok(())
85 }
86
87 fn resolve(&mut self) -> ResolveResult<()> {
88 if let Some(std) = self.root.get_child(&ir::Identifier::no_ref("std")) {
90 std.resolve(self)?;
91 }
92
93 const MAX_PASSES: usize = 3;
95 let mut passes_needed = 0;
96 let mut resolved = false;
97 for _ in 0..MAX_PASSES {
98 self.root
99 .iter()
100 .filter(|child| child.is_resolvable())
101 .map(|child| child.resolve(self))
102 .collect::<Result<Vec<_>, _>>()?;
103 passes_needed += 1;
104 if !self.has_links() {
105 resolved = true;
106 break;
107 }
108 self.diag.clear()
109 }
110
111 if resolved {
112 log::info!("Resolve OK ({passes_needed} passes).");
113 } else {
114 log::info!("Resolve failed after {passes_needed} passes.");
115 }
116 log::debug!("Resolved symbol table:\n{self:?}");
117
118 Ok(())
119 }
120
121 fn has_links(&self) -> bool {
122 self.root
123 .iter()
124 .filter(|symbol| !symbol.is_deleted())
125 .any(|symbol| symbol.has_links())
126 }
127
128 pub fn symbolize_file(
130 &mut self,
131 visibility: ir::Visibility,
132 parent_path: impl AsRef<std::path::Path>,
133 id: &ir::Identifier,
134 ) -> ResolveResult<Symbol> {
135 let mut symbol = self
136 .sources
137 .load_mod_file(parent_path, id)?
138 .symbolize(visibility, self)?;
139 symbol.set_src_ref(id.src_ref());
140 Ok(symbol)
141 }
142}
143
144impl WriteToFile for ResolveContext {}
145
146impl PushDiag for ResolveContext {
147 fn push_diag(&mut self, diag: Diagnostic) -> DiagResult<()> {
148 self.diag.push_diag(diag)
149 }
150}
151
152impl GetSourceLocInfoByHash for ResolveContext {
153 fn get_source_loc_info_by_hash(&'_ self, hash: HashId) -> Option<SourceLocInfo<'_>> {
154 self.sources.get_source_loc_info_by_hash(hash)
155 }
156}
157
158impl Diag for ResolveContext {
159 fn fmt_diagnosis(&self, f: &mut dyn std::fmt::Write) -> std::fmt::Result {
160 self.diag.pretty_print(f, self)
161 }
162
163 fn warning_count(&self) -> u32 {
164 self.diag.warning_count()
165 }
166
167 fn error_count(&self) -> u32 {
168 self.diag.error_count()
169 }
170
171 fn error_lines(&self) -> HashSet<u32> {
172 self.diag.error_lines()
173 }
174
175 fn warning_lines(&self) -> HashSet<u32> {
176 self.diag.warning_lines()
177 }
178}
179
180impl GetSourceByHash for ResolveContext {
181 fn get_by_hash(&self, hash: u64) -> ResolveResult<std::rc::Rc<ir::Source>> {
182 self.sources.get_by_hash(hash)
183 }
184}
185
186impl std::fmt::Debug for ResolveContext {
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 writeln!(f, "Sources:\n")?;
189 write!(f, "{:?}", &self.sources)?;
190 writeln!(f, "\nSymbols:\n")?;
191 self.root.tree_print(f, TreeState::new_debug(1))?;
192 let err_count = self.diag.error_count();
193 if err_count == 0 {
194 writeln!(f, "No errors.")?;
195 } else {
196 writeln!(f, "\n{err_count} error(s):\n")?;
197 self.diag.pretty_print(f, &self.sources)?;
198 }
199
200 Ok(())
201 }
202}
203
204impl std::fmt::Display for ResolveContext {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 writeln!(f, "Resolved symbols:")?;
207 self.root.tree_print(f, TreeState::new_display())?;
208
209 if self.has_errors() {
210 writeln!(
211 f,
212 "{diag}{err} error(s) and {warn} warning(s) so far.",
213 err = self.error_count(),
214 warn = self.warning_count(),
215 diag = self.diagnosis()
216 )?;
217 } else {
218 writeln!(f, "No errors so far.")?;
219 }
220 Ok(())
221 }
222}