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    resolve::*,
12    symbol::{Symbol, SymbolDef, SymbolMap},
13    syntax::*,
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 SourceFile {
24    /// Create symbol from definition.
25    pub fn symbolize(
26        &self,
27        visibility: 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 ModuleDefinition {
43    fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<Symbol> {
44        let symbol = if let Some(body) = &self.body {
45            let symbol = Symbol::new(SymbolDef::Module(self.clone().into()), Some(parent.clone()));
46            symbol.set_children(body.symbolize(&symbol, context)?);
47            symbol
48        } else if let Some(parent_path) = parent.source_path() {
49            let mut symbol =
50                context.symbolize_file(self.visibility.clone(), parent_path, self.id_ref())?;
51            symbol.set_parent(parent.clone());
52            symbol
53        } else {
54            todo!("no top-level source file")
55        };
56        Ok(symbol)
57    }
58}
59
60impl Symbolize<SymbolMap> for StatementList {
61    fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<SymbolMap> {
62        let mut symbols = SymbolMap::default();
63        // Iterate over all statement fetch definitions
64        for statement in &self.0 {
65            if let Some((id, symbol)) = statement.symbolize(parent, context)? {
66                if let Some(alt) = symbols.insert(id.clone(), symbol) {
67                    context.error(
68                        &id,
69                        ResolveError::AmbiguousId {
70                            first: alt.id(),
71                            ambiguous: id.clone(),
72                        },
73                    )?;
74                }
75            }
76        }
77        Ok(symbols)
78    }
79}
80
81impl Symbolize<Option<(Identifier, Symbol)>> for Statement {
82    fn symbolize(
83        &self,
84        parent: &Symbol,
85        context: &mut ResolveContext,
86    ) -> ResolveResult<Option<(Identifier, Symbol)>> {
87        match self {
88            Statement::Workbench(wd) => Ok(Some((wd.id(), wd.symbolize(parent, context)?))),
89            Statement::Module(md) => Ok(Some((md.id(), md.symbolize(parent, context)?))),
90            Statement::Function(fd) => Ok(Some((fd.id(), fd.symbolize(parent, context)?))),
91            Statement::Use(us) => us.symbolize(parent, context),
92            Statement::Assignment(a) => Ok(a
93                .symbolize(parent, context)?
94                .map(|symbol| (a.assignment.id(), symbol))),
95            // Not producing any symbols
96            Statement::Init(_)
97            | Statement::Return(_)
98            | Statement::If(_)
99            | Statement::InnerAttribute(_)
100            | Statement::InnerDocComment(_)
101            | Statement::Expression(_) => Ok(None),
102        }
103    }
104}
105
106impl Symbolize<Symbol> for Rc<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<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 AssignmentStatement {
123    fn symbolize(
124        &self,
125        parent: &Symbol,
126        _context: &mut ResolveContext,
127    ) -> ResolveResult<Option<Symbol>> {
128        let symbol = match (&self.assignment.visibility, self.assignment.qualifier()) {
129            // properties do not have a visibility
130            (_, Qualifier::Prop) => {
131                if !parent.can_prop() {
132                    None
133                } else {
134                    Some(None)
135                }
136            }
137            // constants will be symbols (`pub` shall equal `pub const`)
138            (_, Qualifier::Const) | (Visibility::Public, Qualifier::Value) => {
139                if !parent.can_const() {
140                    None
141                } else {
142                    log::trace!(
143                        "Declaring private const expression: {}",
144                        self.assignment.id_ref()
145                    );
146                    Some(Some(Symbol::new(
147                        SymbolDef::Assignment(self.assignment.clone()),
148                        Some(parent.clone()),
149                    )))
150                }
151            }
152            // value go on stack
153            (Visibility::Private | Visibility::PrivateUse(_), Qualifier::Value) => {
154                if self.assignment.visibility == Visibility::Private && !parent.can_value() {
155                    None
156                } else {
157                    Some(None)
158                }
159            }
160            (Visibility::Deleted, _) => unreachable!(),
161        };
162
163        match symbol {
164            Some(symbol) => Ok(symbol),
165            None => Ok(None),
166        }
167    }
168}
169
170impl Symbolize<SymbolMap> for Body {
171    fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<SymbolMap> {
172        self.statements.symbolize(parent, context)
173    }
174}
175
176impl Symbolize<Option<(Identifier, Symbol)>> for UseStatement {
177    fn symbolize(
178        &self,
179        parent: &Symbol,
180        _: &mut ResolveContext,
181    ) -> ResolveResult<Option<(Identifier, Symbol)>> {
182        match &self.decl {
183            UseDeclaration::Use(name) => {
184                let identifier = name.last().expect("Identifier");
185                Ok(Some((
186                    Identifier::unique(),
187                    Symbol::new(
188                        SymbolDef::Alias(self.visibility.clone(), identifier.clone(), name.clone()),
189                        Some(parent.clone()),
190                    ),
191                )))
192            }
193            UseDeclaration::UseAll(name) => Ok(Some((
194                Identifier::unique(),
195                Symbol::new(
196                    SymbolDef::UseAll(self.visibility.clone(), name.clone()),
197                    Some(parent.clone()),
198                ),
199            ))),
200            UseDeclaration::UseAs(name, alias) => Ok(Some((
201                Identifier::unique(),
202                Symbol::new(
203                    SymbolDef::Alias(self.visibility.clone(), alias.clone(), name.clone()),
204                    Some(parent.clone()),
205                ),
206            ))),
207        }
208    }
209}