1use crate::ast::{ExportItem, Item, Program, Span};
8use crate::error::{Result, ShapeError};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum ModuleExportKind {
17 Function,
18 BuiltinFunction,
19 TypeAlias,
20 BuiltinType,
21 Trait,
22 Enum,
23 Annotation,
24 Value,
25}
26
27#[derive(Debug, Clone)]
29pub struct ModuleExportSymbol {
30 pub name: String,
32 pub alias: Option<String>,
34 pub kind: ModuleExportKind,
36 pub span: Span,
38}
39
40pub fn direct_export_target(export_item: &ExportItem) -> Option<(String, ModuleExportKind)> {
49 match export_item {
50 ExportItem::Function(function) => {
51 Some((function.name.clone(), ModuleExportKind::Function))
52 }
53 ExportItem::BuiltinFunction(function) => {
54 Some((function.name.clone(), ModuleExportKind::BuiltinFunction))
55 }
56 ExportItem::BuiltinType(type_decl) => {
57 Some((type_decl.name.clone(), ModuleExportKind::BuiltinType))
58 }
59 ExportItem::TypeAlias(alias) => Some((alias.name.clone(), ModuleExportKind::TypeAlias)),
60 ExportItem::Enum(enum_def) => Some((enum_def.name.clone(), ModuleExportKind::Enum)),
61 ExportItem::Struct(struct_def) => {
62 Some((struct_def.name.clone(), ModuleExportKind::TypeAlias))
63 }
64 ExportItem::Trait(trait_def) => {
65 Some((trait_def.name.clone(), ModuleExportKind::Trait))
66 }
67 ExportItem::Annotation(annotation) => {
68 Some((annotation.name.clone(), ModuleExportKind::Annotation))
69 }
70 ExportItem::ForeignFunction(function) => {
71 Some((function.name.clone(), ModuleExportKind::Function))
72 }
73 ExportItem::Named(_) => None,
74 }
75}
76
77pub fn strip_import_items(items: Vec<Item>) -> Vec<Item> {
87 items
88 .into_iter()
89 .filter(|item| !matches!(item, Item::Import(..)))
90 .collect()
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106enum ScopeSymbolKind {
107 Function,
108 BuiltinFunction,
109 TypeAlias,
110 BuiltinType,
111 Trait,
112 Enum,
113 Annotation,
114 Value,
115 Const,
116}
117
118fn scope_symbol_kind_to_export(kind: ScopeSymbolKind) -> ModuleExportKind {
119 match kind {
120 ScopeSymbolKind::Function => ModuleExportKind::Function,
121 ScopeSymbolKind::BuiltinFunction => ModuleExportKind::BuiltinFunction,
122 ScopeSymbolKind::TypeAlias => ModuleExportKind::TypeAlias,
123 ScopeSymbolKind::BuiltinType => ModuleExportKind::BuiltinType,
124 ScopeSymbolKind::Trait => ModuleExportKind::Trait,
125 ScopeSymbolKind::Enum => ModuleExportKind::Enum,
126 ScopeSymbolKind::Annotation => ModuleExportKind::Annotation,
127 ScopeSymbolKind::Value => ModuleExportKind::Value,
128 ScopeSymbolKind::Const => ModuleExportKind::Value,
129 }
130}
131
132struct ScopeTable {
134 symbols: std::collections::HashMap<String, (ScopeSymbolKind, Span)>,
135}
136
137impl ScopeTable {
138 fn from_program(program: &Program) -> Self {
139 let mut symbols = std::collections::HashMap::new();
140 for item in &program.items {
141 match item {
142 Item::Function(f, span) => {
143 symbols.insert(f.name.clone(), (ScopeSymbolKind::Function, *span));
144 }
145 Item::BuiltinFunctionDecl(f, span) => {
146 symbols.insert(f.name.clone(), (ScopeSymbolKind::BuiltinFunction, *span));
147 }
148 Item::BuiltinTypeDecl(t, span) => {
149 symbols.insert(t.name.clone(), (ScopeSymbolKind::BuiltinType, *span));
150 }
151 Item::TypeAlias(a, span) => {
152 symbols.insert(a.name.clone(), (ScopeSymbolKind::TypeAlias, *span));
153 }
154 Item::Enum(e, span) => {
155 symbols.insert(e.name.clone(), (ScopeSymbolKind::Enum, *span));
156 }
157 Item::StructType(s, span) => {
158 symbols.insert(s.name.clone(), (ScopeSymbolKind::TypeAlias, *span));
159 }
160 Item::Trait(t, span) => {
161 symbols.insert(t.name.clone(), (ScopeSymbolKind::Trait, *span));
162 }
163 Item::VariableDecl(decl, span) => {
164 if let Some(name) = decl.pattern.as_identifier() {
165 let kind = if decl.kind == crate::ast::VarKind::Const {
169 ScopeSymbolKind::Const
170 } else {
171 ScopeSymbolKind::Value
172 };
173 symbols.insert(name.to_string(), (kind, *span));
174 }
175 }
176 Item::Export(export, export_span) => {
177 if let Some(decl) = export.source_decl.as_ref() {
182 if let Some(name) = decl.pattern.as_identifier() {
183 let kind = if decl.kind == crate::ast::VarKind::Const {
184 ScopeSymbolKind::Const
185 } else {
186 ScopeSymbolKind::Value
187 };
188 symbols
189 .entry(name.to_string())
190 .or_insert((kind, *export_span));
191 }
192 }
193 }
194 Item::AnnotationDef(a, span) => {
195 symbols.insert(a.name.clone(), (ScopeSymbolKind::Annotation, *span));
196 }
197 _ => {}
198 }
199 }
200 Self { symbols }
201 }
202
203 fn resolve(&self, name: &str) -> Option<(ScopeSymbolKind, Span)> {
204 self.symbols.get(name).copied()
205 }
206}
207
208pub fn collect_exported_symbols(program: &Program) -> Result<Vec<ModuleExportSymbol>> {
214 let scope = ScopeTable::from_program(program);
215 let mut symbols = Vec::new();
216
217 for item in &program.items {
218 let Item::Export(export, _) = item else {
219 continue;
220 };
221
222 if let Some((name, kind)) = direct_export_target(&export.item) {
224 let span = match &export.item {
225 ExportItem::Function(f) => f.name_span,
226 ExportItem::BuiltinFunction(f) => f.name_span,
227 ExportItem::Annotation(a) => a.name_span,
228 ExportItem::ForeignFunction(f) => f.name_span,
229 _ => scope
230 .resolve(&name)
231 .map(|(_, span)| span)
232 .unwrap_or_default(),
233 };
234 symbols.push(ModuleExportSymbol {
235 name,
236 alias: None,
237 kind,
238 span,
239 });
240 continue;
241 }
242
243 if let ExportItem::Named(specs) = &export.item {
245 for spec in specs {
246 match scope.resolve(&spec.name) {
247 Some((kind, span)) => {
248 if kind == ScopeSymbolKind::Value {
253 return Err(ShapeError::ModuleError {
254 message: format!(
255 "Cannot export variable '{}': variable exports are not yet supported. \
256 Only `const` bindings (plus functions and types) can be exported.",
257 spec.name
258 ),
259 module_path: None,
260 });
261 }
262 symbols.push(ModuleExportSymbol {
263 name: spec.name.clone(),
264 alias: spec.alias.clone(),
265 kind: scope_symbol_kind_to_export(kind),
266 span,
267 });
268 }
269 None => {
270 return Err(ShapeError::ModuleError {
271 message: format!(
272 "Cannot export '{}': not found in module scope",
273 spec.name
274 ),
275 module_path: None,
276 });
277 }
278 }
279 }
280 }
281 }
282
283 Ok(symbols)
284}
285
286pub fn export_kind_description(kind: ModuleExportKind) -> &'static str {
292 match kind {
293 ModuleExportKind::Function => "a function",
294 ModuleExportKind::BuiltinFunction => "a builtin function",
295 ModuleExportKind::TypeAlias => "a type",
296 ModuleExportKind::BuiltinType => "a builtin type",
297 ModuleExportKind::Trait => "a trait",
298 ModuleExportKind::Enum => "an enum",
299 ModuleExportKind::Annotation => "an annotation",
300 ModuleExportKind::Value => "a value",
301 }
302}