Skip to main content

microcad_lang/resolve/
symbolize.rs

1// Copyright © 2025-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Symbolizing of syntax definitions.
5
6use 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    /// Create symbol from definition.
18    fn symbolize(&self, _parent: &Symbol, _context: &mut ResolveContext) -> ResolveResult<T> {
19        unreachable!()
20    }
21}
22
23impl ir::Source {
24    /// Create symbol from definition.
25    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        // Iterate over all statement fetch definitions
66        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            // Not producing any symbols
100            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            // properties do not have a visibility
132            (_, ir::Qualifier::Prop) => {
133                if !parent.can_prop() {
134                    None
135                } else {
136                    Some(None)
137                }
138            }
139            // constants will be symbols (`pub` shall equal `pub const`)
140            (_, 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            // value go on stack
155            (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}