1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
//! TypeRegistry - Maps type names to their defining modules
//!
//! This module provides a centralized registry that tracks where each type is defined.
//! This enables the code generator to produce correct import paths without guessing.
//!
//! Example:
//! - Type "Vec2" is defined in "math/vec2.wj" → module path "math::vec2"
//! - Type "Camera2D" is defined in "rendering/camera2d.wj" → module path "rendering::camera2d"
use crate::parser::ast::Item;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
/// Maps type names and function names to their defining module paths
#[derive(Debug, Clone, Default)]
pub struct TypeRegistry {
/// Map: TypeName -> ModulePath
/// Example: "Vec2" -> "math::vec2"
types: HashMap<String, String>,
/// Map: FunctionName -> ModulePath
/// Example: "check_validity" -> "validator"
functions: HashMap<String, String>,
/// Map: TypeName -> FilePath (for debugging)
/// Example: "Vec2" -> "src/math/vec2.wj"
file_paths: HashMap<String, PathBuf>,
}
impl TypeRegistry {
pub fn new() -> Self {
Self {
types: HashMap::new(),
functions: HashMap::new(),
file_paths: HashMap::new(),
}
}
/// Register a type with its module path
pub fn register_type(&mut self, type_name: String, module_path: String, file_path: PathBuf) {
self.types.insert(type_name.clone(), module_path);
self.file_paths.insert(type_name, file_path);
}
/// Register a function with its module path
pub fn register_function(&mut self, function_name: String, module_path: String) {
self.functions.insert(function_name, module_path);
}
/// Look up the module path for a type
/// Returns None if the type is not in the registry
pub fn lookup_type(&self, type_name: &str) -> Option<&str> {
self.types.get(type_name).map(|s| s.as_str())
}
/// Look up the module path for a function
/// Returns None if the function is not in the registry
pub fn lookup_function(&self, function_name: &str) -> Option<&str> {
self.functions.get(function_name).map(|s| s.as_str())
}
/// Look up either type or function (tries type first, then function)
pub fn lookup(&self, name: &str) -> Option<&str> {
self.lookup_type(name)
.or_else(|| self.lookup_function(name))
}
/// Get the file path where a type is defined (for debugging)
pub fn get_file_path(&self, type_name: &str) -> Option<&Path> {
self.file_paths.get(type_name).map(|p| p.as_path())
}
/// Scan a single .wj file and register all types defined in it
pub fn scan_file(&mut self, file_path: &Path, ast: &[Item]) -> Result<(), String> {
// Compute module path from file path
// Example: "src/math/vec2.wj" -> "math::vec2"
let module_path = Self::file_path_to_module_path(file_path)?;
// Extract all type and function definitions from the AST
for item in ast {
match item {
Item::Struct { decl, .. } => {
self.register_type(
decl.name.clone(),
module_path.clone(),
file_path.to_path_buf(),
);
}
Item::Enum { decl, .. } => {
self.register_type(
decl.name.clone(),
module_path.clone(),
file_path.to_path_buf(),
);
}
Item::Trait { decl, .. } => {
self.register_type(
decl.name.clone(),
module_path.clone(),
file_path.to_path_buf(),
);
}
Item::Function { decl, .. }
// Register top-level functions
if decl.is_pub => {
self.register_function(decl.name.clone(), module_path.clone());
}
Item::TypeAlias { name, is_pub, .. }
if *is_pub => {
self.register_type(
name.clone(),
module_path.clone(),
file_path.to_path_buf(),
);
}
Item::Mod { items, .. } => {
// For inline modules, types and functions are defined in the parent module
// Example: mod utils { struct Helper {} } -> Helper is in current module
for nested_item in items {
match nested_item {
Item::Struct { decl, .. } => {
self.register_type(
decl.name.clone(),
module_path.clone(),
file_path.to_path_buf(),
);
}
Item::Enum { decl, .. } => {
self.register_type(
decl.name.clone(),
module_path.clone(),
file_path.to_path_buf(),
);
}
Item::Trait { decl, .. } => {
self.register_type(
decl.name.clone(),
module_path.clone(),
file_path.to_path_buf(),
);
}
Item::Function { decl, .. }
if decl.is_pub => {
self.register_function(decl.name.clone(), module_path.clone());
}
Item::TypeAlias { name, is_pub, .. }
if *is_pub => {
self.register_type(
name.clone(),
module_path.clone(),
file_path.to_path_buf(),
);
}
_ => {}
}
}
}
_ => {}
}
}
Ok(())
}
/// Convert a file path to a module path
/// Since all generated files are FLAT in src/generated/, we only use the filename
/// Example: "src/math/vec2.wj" -> "vec2"
/// Example: "src/utils/validator.wj" -> "validator"
fn file_path_to_module_path(file_path: &Path) -> Result<String, String> {
// Extract just the filename without extension
let filename = file_path
.file_stem()
.and_then(|s| s.to_str())
.ok_or_else(|| format!("Invalid file path: {:?}", file_path))?;
Ok(filename.to_string())
}
/// Generate a correct use statement for a type
/// Example: "Vec2" in context "rendering" -> "use super::vec2::Vec2"
pub fn generate_use_statement(&self, type_name: &str, current_module: &str) -> Option<String> {
let module_path = self.lookup(type_name)?;
// If the type is in the same module, no import needed
if module_path == current_module {
return None;
}
// Generate relative import using super::
// All generated files are in src/generated/, so we use super:: to reference siblings
let use_path = if module_path.contains("::") {
// Nested module: math::vec2 -> super::math::vec2
format!("use super::{}::{};", module_path, type_name)
} else {
// Top-level module: vec2 -> super::vec2
format!("use super::{}::{};", module_path, type_name)
};
Some(use_path)
}
/// Get all registered types (for debugging)
pub fn all_types(&self) -> Vec<&str> {
self.types.keys().map(|s| s.as_str()).collect()
}
/// Get all registered functions (for debugging)
pub fn all_functions(&self) -> Vec<&str> {
self.functions.keys().map(|s| s.as_str()).collect()
}
/// Get total count of registered types and functions
pub fn total_count(&self) -> usize {
self.types.len() + self.functions.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
// Future: May need Item, StructDecl, TypeParam for generic testing
#[test]
fn test_file_path_to_module_path() {
// Since all generated files are flat, only the filename matters
assert_eq!(
TypeRegistry::file_path_to_module_path(Path::new("src/math/vec2.wj")).unwrap(),
"vec2"
);
assert_eq!(
TypeRegistry::file_path_to_module_path(Path::new("math/vec2.wj")).unwrap(),
"vec2"
);
assert_eq!(
TypeRegistry::file_path_to_module_path(Path::new("src/rendering/camera2d.wj")).unwrap(),
"camera2d"
);
}
#[test]
fn test_register_and_lookup() {
let mut registry = TypeRegistry::new();
registry.register_type(
"Vec2".to_string(),
"vec2".to_string(),
PathBuf::from("src/math/vec2.wj"),
);
registry.register_function("check_validity".to_string(), "validator".to_string());
assert_eq!(registry.lookup_type("Vec2"), Some("vec2"));
assert_eq!(registry.lookup_type("Vec3"), None);
assert_eq!(
registry.lookup_function("check_validity"),
Some("validator")
);
assert_eq!(registry.lookup("Vec2"), Some("vec2"));
assert_eq!(registry.lookup("check_validity"), Some("validator"));
}
#[test]
fn test_generate_use_statement() {
let mut registry = TypeRegistry::new();
registry.register_type(
"Vec2".to_string(),
"vec2".to_string(),
PathBuf::from("src/math/vec2.wj"),
);
registry.register_type(
"Camera2D".to_string(),
"camera2d".to_string(),
PathBuf::from("src/rendering/camera2d.wj"),
);
// Use Vec2 from camera2d module -> generates import
assert_eq!(
registry.generate_use_statement("Vec2", "camera2d"),
Some("use super::vec2::Vec2;".to_string())
);
// Use Camera2D in camera2d module -> no import needed
assert_eq!(
registry.generate_use_statement("Camera2D", "camera2d"),
None
);
}
}