microcad_lang/resolve/
symbolize.rs

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