method-ray 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,6 @@ pub struct ScopeId(pub usize);
7
7
 
8
8
  /// Scope kind
9
9
  #[derive(Debug, Clone)]
10
- #[allow(dead_code)]
11
10
  pub enum ScopeKind {
12
11
  TopLevel,
13
12
  Class {
@@ -27,7 +26,6 @@ pub enum ScopeKind {
27
26
 
28
27
  /// Scope information
29
28
  #[derive(Debug, Clone)]
30
- #[allow(dead_code)]
31
29
  pub struct Scope {
32
30
  pub id: ScopeId,
33
31
  pub kind: ScopeKind,
@@ -41,9 +39,11 @@ pub struct Scope {
41
39
 
42
40
  /// Class variables (class scope only)
43
41
  pub class_vars: HashMap<String, VertexId>,
42
+
43
+ /// Constants (simple name → qualified name)
44
+ pub constants: HashMap<String, String>,
44
45
  }
45
46
 
46
- #[allow(dead_code)]
47
47
  impl Scope {
48
48
  pub fn new(id: ScopeId, kind: ScopeKind, parent: Option<ScopeId>) -> Self {
49
49
  Self {
@@ -53,6 +53,7 @@ impl Scope {
53
53
  local_vars: HashMap::new(),
54
54
  instance_vars: HashMap::new(),
55
55
  class_vars: HashMap::new(),
56
+ constants: HashMap::new(),
56
57
  }
57
58
  }
58
59
 
@@ -85,7 +86,12 @@ pub struct ScopeManager {
85
86
  current_scope: ScopeId,
86
87
  }
87
88
 
88
- #[allow(dead_code)]
89
+ impl Default for ScopeManager {
90
+ fn default() -> Self {
91
+ Self::new()
92
+ }
93
+ }
94
+
89
95
  impl ScopeManager {
90
96
  pub fn new() -> Self {
91
97
  let top_level = Scope::new(ScopeId(0), ScopeKind::TopLevel, None);
@@ -135,6 +141,18 @@ impl ScopeManager {
135
141
  self.scopes.get_mut(&self.current_scope).unwrap()
136
142
  }
137
143
 
144
+ /// Walk scopes from current scope up to the top-level, yielding each scope
145
+ fn walk_scopes(&self) -> impl Iterator<Item = &Scope> + '_ {
146
+ let scopes = &self.scopes;
147
+ let mut current = Some(self.current_scope);
148
+ std::iter::from_fn(move || {
149
+ let scope_id = current?;
150
+ let scope = scopes.get(&scope_id)?;
151
+ current = scope.parent;
152
+ Some(scope)
153
+ })
154
+ }
155
+
138
156
  /// Get scope by ID
139
157
  pub fn get_scope(&self, id: ScopeId) -> Option<&Scope> {
140
158
  self.scopes.get(&id)
@@ -147,103 +165,54 @@ impl ScopeManager {
147
165
 
148
166
  /// Lookup variable in current scope or parent scopes
149
167
  pub fn lookup_var(&self, name: &str) -> Option<VertexId> {
150
- let mut current = Some(self.current_scope);
151
-
152
- while let Some(scope_id) = current {
153
- if let Some(scope) = self.scopes.get(&scope_id) {
154
- if let Some(vtx) = scope.get_local_var(name) {
155
- return Some(vtx);
156
- }
157
- current = scope.parent;
158
- } else {
159
- break;
160
- }
161
- }
168
+ self.walk_scopes().find_map(|scope| scope.get_local_var(name))
169
+ }
162
170
 
163
- None
171
+ /// Lookup constant in current scope or parent scopes (simple name → qualified name)
172
+ pub fn lookup_constant(&self, simple_name: &str) -> Option<String> {
173
+ self.walk_scopes()
174
+ .find_map(|scope| scope.constants.get(simple_name).cloned())
164
175
  }
165
176
 
166
177
  /// Lookup instance variable in enclosing class scope
167
178
  pub fn lookup_instance_var(&self, name: &str) -> Option<VertexId> {
168
- let mut current = Some(self.current_scope);
169
-
170
- while let Some(scope_id) = current {
171
- if let Some(scope) = self.scopes.get(&scope_id) {
172
- // Walk up to class scope
173
- match &scope.kind {
174
- ScopeKind::Class { .. } => {
175
- return scope.get_instance_var(name);
176
- }
177
- _ => {
178
- current = scope.parent;
179
- }
180
- }
181
- } else {
182
- break;
183
- }
184
- }
185
-
186
- None
179
+ self.walk_scopes()
180
+ .find(|scope| matches!(&scope.kind, ScopeKind::Class { .. }))
181
+ .and_then(|scope| scope.get_instance_var(name))
187
182
  }
188
183
 
189
184
  /// Set instance variable in enclosing class scope
190
185
  pub fn set_instance_var_in_class(&mut self, name: String, vtx: VertexId) {
191
- let mut current = Some(self.current_scope);
192
-
193
- while let Some(scope_id) = current {
194
- if let Some(scope) = self.scopes.get(&scope_id) {
195
- // Find class scope and set variable
196
- match &scope.kind {
197
- ScopeKind::Class { .. } => {
198
- if let Some(class_scope) = self.scopes.get_mut(&scope_id) {
199
- class_scope.set_instance_var(name, vtx);
200
- }
201
- return;
202
- }
203
- _ => {
204
- current = scope.parent;
205
- }
206
- }
207
- } else {
208
- break;
186
+ let class_scope_id = self.walk_scopes()
187
+ .find(|scope| matches!(&scope.kind, ScopeKind::Class { .. }))
188
+ .map(|scope| scope.id);
189
+ if let Some(scope_id) = class_scope_id {
190
+ if let Some(scope) = self.scopes.get_mut(&scope_id) {
191
+ scope.set_instance_var(name, vtx);
209
192
  }
210
193
  }
211
194
  }
212
195
 
213
196
  /// Get current class name (simple name, not qualified)
214
197
  pub fn current_class_name(&self) -> Option<String> {
215
- let mut current = Some(self.current_scope);
216
-
217
- while let Some(scope_id) = current {
218
- if let Some(scope) = self.scopes.get(&scope_id) {
219
- if let ScopeKind::Class { name, .. } = &scope.kind {
220
- return Some(name.clone());
221
- }
222
- current = scope.parent;
198
+ self.walk_scopes().find_map(|scope| {
199
+ if let ScopeKind::Class { name, .. } = &scope.kind {
200
+ Some(name.clone())
223
201
  } else {
224
- break;
202
+ None
225
203
  }
226
- }
227
-
228
- None
204
+ })
229
205
  }
230
206
 
231
207
  /// Get current module name (simple name, not qualified)
232
208
  pub fn current_module_name(&self) -> Option<String> {
233
- let mut current = Some(self.current_scope);
234
-
235
- while let Some(scope_id) = current {
236
- if let Some(scope) = self.scopes.get(&scope_id) {
237
- if let ScopeKind::Module { name } = &scope.kind {
238
- return Some(name.clone());
239
- }
240
- current = scope.parent;
209
+ self.walk_scopes().find_map(|scope| {
210
+ if let ScopeKind::Module { name } = &scope.kind {
211
+ Some(name.clone())
241
212
  } else {
242
- break;
213
+ None
243
214
  }
244
- }
245
-
246
- None
215
+ })
247
216
  }
248
217
 
249
218
  /// Get current fully qualified name by traversing all parent class/module scopes
@@ -260,36 +229,12 @@ impl ScopeManager {
260
229
  /// ```
261
230
  /// When inside `greet`, this returns `Some("Api::V1::User")`
262
231
  pub fn current_qualified_name(&self) -> Option<String> {
263
- let mut path_segments: Vec<String> = Vec::new();
264
- let mut current = Some(self.current_scope);
265
-
266
- // Traverse from current scope up to top-level, collecting class/module names
267
- while let Some(scope_id) = current {
268
- if let Some(scope) = self.scopes.get(&scope_id) {
269
- match &scope.kind {
270
- ScopeKind::Class { name, .. } => {
271
- // If the name already contains ::, it's a qualified name from AST
272
- // (e.g., `class Api::User` defined at top level)
273
- if name.contains("::") {
274
- path_segments.push(name.clone());
275
- } else {
276
- path_segments.push(name.clone());
277
- }
278
- }
279
- ScopeKind::Module { name } => {
280
- if name.contains("::") {
281
- path_segments.push(name.clone());
282
- } else {
283
- path_segments.push(name.clone());
284
- }
285
- }
286
- _ => {}
287
- }
288
- current = scope.parent;
289
- } else {
290
- break;
291
- }
292
- }
232
+ let mut path_segments: Vec<&str> = self.walk_scopes()
233
+ .filter_map(|scope| match &scope.kind {
234
+ ScopeKind::Class { name, .. } | ScopeKind::Module { name } => Some(name.as_str()),
235
+ _ => None,
236
+ })
237
+ .collect();
293
238
 
294
239
  if path_segments.is_empty() {
295
240
  return None;
@@ -297,76 +242,35 @@ impl ScopeManager {
297
242
 
298
243
  // Reverse to get from outermost to innermost
299
244
  path_segments.reverse();
300
-
301
- // Join all segments, handling cases where segments may already contain ::
302
- let mut result = String::new();
303
- for segment in path_segments {
304
- if !result.is_empty() {
305
- result.push_str("::");
306
- }
307
- result.push_str(&segment);
308
- }
309
-
310
- Some(result)
245
+ Some(path_segments.join("::"))
311
246
  }
312
247
 
313
248
  /// Get return_vertex from the nearest enclosing method scope
314
249
  pub fn current_method_return_vertex(&self) -> Option<VertexId> {
315
- let mut current = Some(self.current_scope);
316
- while let Some(scope_id) = current {
317
- if let Some(scope) = self.scopes.get(&scope_id) {
318
- if let ScopeKind::Method { return_vertex, .. } = &scope.kind {
319
- return *return_vertex;
320
- }
321
- current = scope.parent;
250
+ self.walk_scopes().find_map(|scope| {
251
+ if let ScopeKind::Method { return_vertex, .. } = &scope.kind {
252
+ *return_vertex
322
253
  } else {
323
- break;
254
+ None
324
255
  }
325
- }
326
- None
256
+ })
327
257
  }
328
258
 
329
259
  /// Lookup instance variable in enclosing module scope
330
260
  pub fn lookup_instance_var_in_module(&self, name: &str) -> Option<VertexId> {
331
- let mut current = Some(self.current_scope);
332
-
333
- while let Some(scope_id) = current {
334
- if let Some(scope) = self.scopes.get(&scope_id) {
335
- match &scope.kind {
336
- ScopeKind::Module { .. } => {
337
- return scope.get_instance_var(name);
338
- }
339
- _ => {
340
- current = scope.parent;
341
- }
342
- }
343
- } else {
344
- break;
345
- }
346
- }
347
-
348
- None
261
+ self.walk_scopes()
262
+ .find(|scope| matches!(&scope.kind, ScopeKind::Module { .. }))
263
+ .and_then(|scope| scope.get_instance_var(name))
349
264
  }
350
265
 
351
266
  /// Set instance variable in enclosing module scope
352
267
  pub fn set_instance_var_in_module(&mut self, name: String, vtx: VertexId) {
353
- let mut current = Some(self.current_scope);
354
-
355
- while let Some(scope_id) = current {
356
- if let Some(scope) = self.scopes.get(&scope_id) {
357
- match &scope.kind {
358
- ScopeKind::Module { .. } => {
359
- if let Some(module_scope) = self.scopes.get_mut(&scope_id) {
360
- module_scope.set_instance_var(name, vtx);
361
- }
362
- return;
363
- }
364
- _ => {
365
- current = scope.parent;
366
- }
367
- }
368
- } else {
369
- break;
268
+ let module_scope_id = self.walk_scopes()
269
+ .find(|scope| matches!(&scope.kind, ScopeKind::Module { .. }))
270
+ .map(|scope| scope.id);
271
+ if let Some(scope_id) = module_scope_id {
272
+ if let Some(scope) = self.scopes.get_mut(&scope_id) {
273
+ scope.set_instance_var(name, vtx);
370
274
  }
371
275
  }
372
276
  }
@@ -376,6 +280,13 @@ impl ScopeManager {
376
280
  mod tests {
377
281
  use super::*;
378
282
 
283
+ #[test]
284
+ fn test_scope_manager_default() {
285
+ let sm = ScopeManager::default();
286
+ assert_eq!(sm.current_scope().id, ScopeId(0));
287
+ assert!(matches!(sm.current_scope().kind, ScopeKind::TopLevel));
288
+ }
289
+
379
290
  #[test]
380
291
  fn test_scope_manager_creation() {
381
292
  let sm = ScopeManager::new();
@@ -614,4 +525,72 @@ mod tests {
614
525
  // At top level, no class/module
615
526
  assert_eq!(sm.current_qualified_name(), None);
616
527
  }
528
+
529
+ #[test]
530
+ fn test_constant_registration_and_lookup() {
531
+ let mut sm = ScopeManager::new();
532
+
533
+ // module Api
534
+ sm.current_scope_mut().constants.insert("Api".to_string(), "Api".to_string());
535
+ let api_id = sm.new_scope(ScopeKind::Module { name: "Api".to_string() });
536
+ sm.enter_scope(api_id);
537
+
538
+ // class User (inside Api) — register in parent scope (Api)
539
+ sm.current_scope_mut().constants.insert("User".to_string(), "Api::User".to_string());
540
+ let user_id = sm.new_scope(ScopeKind::Class {
541
+ name: "User".to_string(),
542
+ superclass: None,
543
+ });
544
+ sm.enter_scope(user_id);
545
+
546
+ assert_eq!(sm.lookup_constant("User"), Some("Api::User".to_string()));
547
+ assert_eq!(sm.lookup_constant("Api"), Some("Api".to_string()));
548
+ assert_eq!(sm.lookup_constant("Unknown"), None);
549
+ }
550
+
551
+ #[test]
552
+ fn test_constant_lookup_from_method_scope() {
553
+ let mut sm = ScopeManager::new();
554
+
555
+ sm.current_scope_mut().constants.insert("Api".to_string(), "Api".to_string());
556
+ let api_id = sm.new_scope(ScopeKind::Module { name: "Api".to_string() });
557
+ sm.enter_scope(api_id);
558
+
559
+ sm.current_scope_mut().constants.insert("User".to_string(), "Api::User".to_string());
560
+ let user_id = sm.new_scope(ScopeKind::Class {
561
+ name: "User".to_string(),
562
+ superclass: None,
563
+ });
564
+ sm.enter_scope(user_id);
565
+
566
+ let method_id = sm.new_scope(ScopeKind::Method {
567
+ name: "greet".to_string(),
568
+ receiver_type: None,
569
+ return_vertex: None,
570
+ });
571
+ sm.enter_scope(method_id);
572
+
573
+ // Should find constant by traversing parent scopes from method scope
574
+ assert_eq!(sm.lookup_constant("User"), Some("Api::User".to_string()));
575
+ }
576
+
577
+ #[test]
578
+ fn test_constant_same_name_different_namespaces() {
579
+ let mut sm = ScopeManager::new();
580
+
581
+ // module Api
582
+ let api_id = sm.new_scope(ScopeKind::Module { name: "Api".to_string() });
583
+ sm.enter_scope(api_id);
584
+ sm.current_scope_mut().constants.insert("User".to_string(), "Api::User".to_string());
585
+
586
+ sm.exit_scope();
587
+
588
+ // module Admin
589
+ let admin_id = sm.new_scope(ScopeKind::Module { name: "Admin".to_string() });
590
+ sm.enter_scope(admin_id);
591
+ sm.current_scope_mut().constants.insert("User".to_string(), "Admin::User".to_string());
592
+
593
+ // Inside Admin scope, User should resolve to Admin::User
594
+ assert_eq!(sm.lookup_constant("User"), Some("Admin::User".to_string()));
595
+ }
617
596
  }
@@ -17,7 +17,6 @@ pub struct VertexManager {
17
17
  next_vertex_id: usize,
18
18
  }
19
19
 
20
- #[allow(dead_code)]
21
20
  impl VertexManager {
22
21
  /// Create a new empty vertex manager
23
22
  pub fn new() -> Self {