Skip to main content

microcad_lang/resolve/
symbolize.rs

1// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Symbolizing of syntax definitions.
5
6use crate::{resolve::*, syntax::*};
7
8pub(super) trait Symbolize<T = Option<Symbol>> {
9    /// Create symbol from definition.
10    fn symbolize(&self, _parent: &Symbol, _context: &mut ResolveContext) -> ResolveResult<T> {
11        unreachable!()
12    }
13}
14
15impl SourceFile {
16    /// Create symbol from definition.
17    pub fn symbolize(
18        &self,
19        visibility: Visibility,
20        context: &mut ResolveContext,
21    ) -> ResolveResult<Symbol> {
22        let symbol = Symbol::new_with_visibility(
23            visibility,
24            SymbolDef::SourceFile(self.clone().into()),
25            None,
26        );
27        symbol.set_children(
28            self.statements
29                .grant(&symbol, context)?
30                .symbolize(&symbol, context)?,
31        );
32        Ok(symbol)
33    }
34}
35
36impl Symbolize<Symbol> for ModuleDefinition {
37    fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<Symbol> {
38        let symbol = if let Some(body) = &self.body {
39            let symbol = Symbol::new(SymbolDef::Module(self.clone().into()), Some(parent.clone()));
40            symbol.set_children(body.grant(&symbol, context)?.symbolize(&symbol, context)?);
41            symbol
42        } else if let Some(parent_path) = parent.source_path() {
43            let mut symbol =
44                context.symbolize_file(self.visibility.clone(), parent_path, &self.id)?;
45            symbol.set_parent(parent.clone());
46            symbol
47        } else {
48            todo!("no top-level source file")
49        };
50        Ok(symbol)
51    }
52}
53
54impl Symbolize<SymbolMap> for StatementList {
55    fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<SymbolMap> {
56        let mut symbols = SymbolMap::default();
57
58        // Iterate over all statement fetch definitions
59        for statement in &self.0 {
60            if let Some((id, symbol)) = statement
61                .grant(parent, context)?
62                .symbolize(parent, context)?
63            {
64                symbols.insert(id, symbol);
65            }
66        }
67
68        Ok(symbols)
69    }
70}
71
72impl Symbolize<Option<(Identifier, Symbol)>> for Statement {
73    fn symbolize(
74        &self,
75        parent: &Symbol,
76        context: &mut ResolveContext,
77    ) -> ResolveResult<Option<(Identifier, Symbol)>> {
78        match self {
79            Statement::Workbench(wd) => Ok(Some((
80                wd.id.clone(),
81                wd.grant(parent, context)?.symbolize(parent, context)?,
82            ))),
83            Statement::Module(md) => Ok(Some((
84                md.id.clone(),
85                md.grant(parent, context)?.symbolize(parent, context)?,
86            ))),
87            Statement::Function(fd) => Ok(Some((
88                fd.id.clone(),
89                fd.grant(parent, context)?.symbolize(parent, context)?,
90            ))),
91            Statement::Use(us) => us.grant(parent, context)?.symbolize(parent, context),
92            Statement::Assignment(a) => Ok(a
93                .grant(parent, context)?
94                .symbolize(parent, context)?
95                .map(|symbol| (a.assignment.id.clone(), symbol))),
96            // Not producing any symbols
97            Statement::Init(_)
98            | Statement::Return(_)
99            | Statement::If(_)
100            | Statement::InnerAttribute(_)
101            | Statement::Expression(_) => Ok(None),
102        }
103    }
104}
105
106impl Symbolize<Symbol> for WorkbenchDefinition {
107    fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<Symbol> {
108        let symbol = Symbol::new(
109            SymbolDef::Workbench(self.clone().into()),
110            Some(parent.clone()),
111        );
112        symbol.set_children(
113            self.body
114                .grant(&symbol, context)?
115                .symbolize(&symbol, context)?,
116        );
117        Ok(symbol)
118    }
119}
120
121impl Symbolize<Symbol> for FunctionDefinition {
122    fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<Symbol> {
123        let symbol = Symbol::new(
124            SymbolDef::Function((*self).clone().into()),
125            Some(parent.clone()),
126        );
127        symbol.set_children(
128            self.body
129                .grant(&symbol, context)?
130                .symbolize(&symbol, context)?,
131        );
132
133        Ok(symbol)
134    }
135}
136
137impl Symbolize for AssignmentStatement {
138    fn symbolize(
139        &self,
140        parent: &Symbol,
141        _context: &mut ResolveContext,
142    ) -> ResolveResult<Option<Symbol>> {
143        let symbol = match (&self.assignment.visibility, self.assignment.qualifier()) {
144            // properties do not have a visibility
145            (_, Qualifier::Prop) => {
146                if !parent.can_prop() {
147                    None
148                } else {
149                    Some(None)
150                }
151            }
152            // constants will be symbols (`pub` shall equal `pub const`)
153            (_, Qualifier::Const) | (Visibility::Public, Qualifier::Value) => {
154                if !parent.can_const() {
155                    None
156                } else {
157                    log::trace!("Declaring private const expression: {}", self.assignment.id);
158                    Some(Some(Symbol::new(
159                        SymbolDef::Assignment(self.assignment.clone()),
160                        Some(parent.clone()),
161                    )))
162                }
163            }
164            // value go on stack
165            (Visibility::Private | Visibility::PrivateUse(_), Qualifier::Value) => {
166                if self.assignment.visibility == Visibility::Private && !parent.can_value() {
167                    None
168                } else {
169                    Some(None)
170                }
171            }
172            (Visibility::Deleted, _) => unreachable!(),
173        };
174
175        match symbol {
176            Some(symbol) => Ok(symbol),
177            None => Ok(None),
178        }
179    }
180}
181
182impl Symbolize<SymbolMap> for Body {
183    fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<SymbolMap> {
184        self.statements
185            .grant(parent, context)?
186            .symbolize(parent, context)
187    }
188}
189
190impl Symbolize<Option<(Identifier, Symbol)>> for UseStatement {
191    fn symbolize(
192        &self,
193        parent: &Symbol,
194        _: &mut ResolveContext,
195    ) -> ResolveResult<Option<(Identifier, Symbol)>> {
196        if !parent.is_module() {
197            return Ok(None);
198        }
199        match &self.decl {
200            UseDeclaration::Use(name) => {
201                let identifier = name.last().expect("Identifier");
202                Ok(Some((
203                    Identifier::unique(),
204                    Symbol::new(
205                        SymbolDef::Alias(self.visibility.clone(), identifier.clone(), name.clone()),
206                        Some(parent.clone()),
207                    ),
208                )))
209            }
210            UseDeclaration::UseAll(name) => Ok(Some((
211                Identifier::unique(),
212                Symbol::new(
213                    SymbolDef::UseAll(self.visibility.clone(), name.clone()),
214                    Some(parent.clone()),
215                ),
216            ))),
217            UseDeclaration::UseAs(name, alias) => Ok(Some((
218                Identifier::unique(),
219                Symbol::new(
220                    SymbolDef::Alias(self.visibility.clone(), alias.clone(), name.clone()),
221                    Some(parent.clone()),
222                ),
223            ))),
224        }
225    }
226}