1use std::rc::Rc;
7
8use microcad_lang_base::PushDiag;
9
10use crate::{
11 lower::ir,
12 resolve::*,
13 symbol::{Symbol, SymbolDef, SymbolMap},
14};
15
16pub(super) trait Symbolize<T = Option<Symbol>> {
17 fn symbolize(&self, _parent: &Symbol, _context: &mut ResolveContext) -> ResolveResult<T> {
19 unreachable!()
20 }
21}
22
23impl ir::Source {
24 pub fn symbolize(
26 &self,
27 visibility: ir::Visibility,
28 context: &mut ResolveContext,
29 ) -> ResolveResult<Symbol> {
30 let symbol = Symbol::new_with_visibility(
31 visibility,
32 SymbolDef::SourceFile(self.clone().into()),
33 None,
34 );
35 symbol.set_children(self.statements.symbolize(&symbol, context)?);
36 log::trace!("Granting {}", self.name);
37 self.grant(&mut GrantContext::new(context))?;
38 Ok(symbol)
39 }
40}
41
42impl Symbolize<Symbol> for ir::ModuleDefinition {
43 fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<Symbol> {
44 use crate::Identifiable;
45
46 let symbol = if let Some(body) = &self.body {
47 let symbol = Symbol::new(SymbolDef::Module(self.clone().into()), Some(parent.clone()));
48 symbol.set_children(body.symbolize(&symbol, context)?);
49 symbol
50 } else if let Some(parent_path) = parent.source_path() {
51 let mut symbol =
52 context.symbolize_file(self.visibility.clone(), parent_path, self.id_ref())?;
53 symbol.set_parent(parent.clone());
54 symbol
55 } else {
56 todo!("no top-level source file")
57 };
58 Ok(symbol)
59 }
60}
61
62impl Symbolize<SymbolMap> for ir::StatementList {
63 fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<SymbolMap> {
64 let mut symbols = SymbolMap::default();
65 for statement in &self.0 {
67 if let Some((id, symbol)) = statement.symbolize(parent, context)? {
68 if let Some(alt) = symbols.insert(id.clone(), symbol) {
69 context.error(
70 &id,
71 ResolveError::AmbiguousId {
72 first: alt.id(),
73 ambiguous: id.clone(),
74 },
75 )?;
76 }
77 }
78 }
79 Ok(symbols)
80 }
81}
82
83impl Symbolize<Option<(ir::Identifier, Symbol)>> for ir::Statement {
84 fn symbolize(
85 &self,
86 parent: &Symbol,
87 context: &mut ResolveContext,
88 ) -> ResolveResult<Option<(ir::Identifier, Symbol)>> {
89 use crate::Identifiable;
90 use ir::Statement::*;
91 match self {
92 Workbench(wd) => Ok(Some((wd.id(), wd.symbolize(parent, context)?))),
93 Module(md) => Ok(Some((md.id(), md.symbolize(parent, context)?))),
94 Function(fd) => Ok(Some((fd.id(), fd.symbolize(parent, context)?))),
95 Use(us) => us.symbolize(parent, context),
96 Assignment(a) => Ok(a
97 .symbolize(parent, context)?
98 .map(|symbol| (a.assignment.id(), symbol))),
99 Init(_) | Return(_) | If(_) | InnerAttribute(_) | InnerDocComment(_)
101 | Expression(_) => Ok(None),
102 }
103 }
104}
105
106impl Symbolize<Symbol> for Rc<ir::WorkbenchDefinition> {
107 fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<Symbol> {
108 let symbol = Symbol::new(SymbolDef::Workbench(self.clone()), Some(parent.clone()));
109 symbol.set_children(self.body.symbolize(&symbol, context)?);
110 Ok(symbol)
111 }
112}
113
114impl Symbolize<Symbol> for Rc<ir::FunctionDefinition> {
115 fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<Symbol> {
116 let symbol = Symbol::new(SymbolDef::Function(self.clone()), Some(parent.clone()));
117 symbol.set_children(self.body.symbolize(&symbol, context)?);
118 Ok(symbol)
119 }
120}
121
122impl Symbolize for ir::AssignmentStatement {
123 fn symbolize(
124 &self,
125 parent: &Symbol,
126 _context: &mut ResolveContext,
127 ) -> ResolveResult<Option<Symbol>> {
128 use crate::Identifiable;
129
130 let symbol = match (&self.assignment.visibility, self.assignment.qualifier()) {
131 (_, ir::Qualifier::Prop) => {
133 if !parent.can_prop() {
134 None
135 } else {
136 Some(None)
137 }
138 }
139 (_, ir::Qualifier::Const) | (ir::Visibility::Public, ir::Qualifier::Value) => {
141 if !parent.can_const() {
142 None
143 } else {
144 log::trace!(
145 "Declaring private const expression: {}",
146 self.assignment.id_ref()
147 );
148 Some(Some(Symbol::new(
149 SymbolDef::Assignment(self.assignment.clone()),
150 Some(parent.clone()),
151 )))
152 }
153 }
154 (ir::Visibility::Private | ir::Visibility::PrivateUse(_), ir::Qualifier::Value) => {
156 if self.assignment.visibility == ir::Visibility::Private && !parent.can_value() {
157 None
158 } else {
159 Some(None)
160 }
161 }
162 (ir::Visibility::Deleted, _) => unreachable!(),
163 };
164
165 match symbol {
166 Some(symbol) => Ok(symbol),
167 None => Ok(None),
168 }
169 }
170}
171
172impl Symbolize<SymbolMap> for ir::Body {
173 fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<SymbolMap> {
174 self.statements.symbolize(parent, context)
175 }
176}
177
178impl Symbolize<Option<(ir::Identifier, Symbol)>> for ir::UseStatement {
179 fn symbolize(
180 &self,
181 parent: &Symbol,
182 _: &mut ResolveContext,
183 ) -> ResolveResult<Option<(ir::Identifier, Symbol)>> {
184 match &self.decl {
185 ir::UseDeclaration::Use(name) => {
186 let identifier = name.last().expect("Identifier");
187 Ok(Some((
188 ir::Identifier::unique(),
189 Symbol::new(
190 SymbolDef::Alias(self.visibility.clone(), identifier.clone(), name.clone()),
191 Some(parent.clone()),
192 ),
193 )))
194 }
195 ir::UseDeclaration::UseAll(name) => Ok(Some((
196 ir::Identifier::unique(),
197 Symbol::new(
198 SymbolDef::UseAll(self.visibility.clone(), name.clone()),
199 Some(parent.clone()),
200 ),
201 ))),
202 ir::UseDeclaration::UseAs(name, alias) => Ok(Some((
203 ir::Identifier::unique(),
204 Symbol::new(
205 SymbolDef::Alias(self.visibility.clone(), alias.clone(), name.clone()),
206 Some(parent.clone()),
207 ),
208 ))),
209 }
210 }
211}