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            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 => {
178                context.error(
179                    self,
180                    ResolveError::DeclNotAllowed(self.assignment.id.clone(), parent.full_name()),
181                )?;
182                Ok(None)
183            }
184        }
185    }
186}
187
188impl Symbolize<SymbolMap> for Body {
189    fn symbolize(&self, parent: &Symbol, context: &mut ResolveContext) -> ResolveResult<SymbolMap> {
190        self.statements
191            .grant(parent, context)?
192            .symbolize(parent, context)
193    }
194}
195
196impl Symbolize<Option<(Identifier, Symbol)>> for UseStatement {
197    fn symbolize(
198        &self,
199        parent: &Symbol,
200        _: &mut ResolveContext,
201    ) -> ResolveResult<Option<(Identifier, Symbol)>> {
202        if !parent.is_module() {
203            return Ok(None);
204        }
205        match &self.decl {
206            UseDeclaration::Use(name) => {
207                let identifier = name.last().expect("Identifier");
208                Ok(Some((
209                    Identifier::unique(),
210                    Symbol::new(
211                        SymbolDef::Alias(self.visibility.clone(), identifier.clone(), name.clone()),
212                        Some(parent.clone()),
213                    ),
214                )))
215            }
216            UseDeclaration::UseAll(name) => Ok(Some((
217                Identifier::unique(),
218                Symbol::new(
219                    SymbolDef::UseAll(self.visibility.clone(), name.clone()),
220                    Some(parent.clone()),
221                ),
222            ))),
223            UseDeclaration::UseAs(name, alias) => Ok(Some((
224                Identifier::unique(),
225                Symbol::new(
226                    SymbolDef::Alias(self.visibility.clone(), alias.clone(), name.clone()),
227                    Some(parent.clone()),
228                ),
229            ))),
230        }
231    }
232}