method-ray 0.1.2 → 0.1.4

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.
@@ -8,13 +8,13 @@ use crate::env::{GlobalEnv, LocalEnv};
8
8
  use crate::graph::{ChangeSet, VertexId};
9
9
  use ruby_prism::Node;
10
10
 
11
- use super::definitions::{exit_scope, extract_class_name, install_class, install_method};
12
- use super::dispatch::{
13
- dispatch_needs_child, dispatch_simple, finish_ivar_write, finish_local_var_write,
14
- finish_method_call, DispatchResult, NeedsChildKind,
15
- };
11
+ use super::blocks::process_block_node;
12
+ use super::conditionals::{process_case_node, process_if_node, process_unless_node};
13
+ use super::definitions::{process_class_node, process_def_node, process_module_node};
14
+ use super::dispatch::{dispatch_needs_child, dispatch_simple, process_needs_child, DispatchResult};
15
+ use super::literals::install_literal_node;
16
16
 
17
- /// Build graph from AST
17
+ /// Build graph from AST (public API wrapper)
18
18
  pub struct AstInstaller<'a> {
19
19
  genv: &'a mut GlobalEnv,
20
20
  lenv: &'a mut LocalEnv,
@@ -32,195 +32,77 @@ impl<'a> AstInstaller<'a> {
32
32
  }
33
33
  }
34
34
 
35
- /// Install node (returns Vertex ID)
36
35
  pub fn install_node(&mut self, node: &Node) -> Option<VertexId> {
37
- // Class definition
38
- if let Some(class_node) = node.as_class_node() {
39
- return self.install_class_node(&class_node);
40
- }
41
-
42
- // Method definition
43
- if let Some(def_node) = node.as_def_node() {
44
- return self.install_def_node(&def_node);
45
- }
46
-
47
- // Try simple dispatch first (no child processing needed)
48
- match dispatch_simple(self.genv, self.lenv, node) {
49
- DispatchResult::Vertex(vtx) => return Some(vtx),
50
- DispatchResult::NotHandled => {}
51
- }
52
-
53
- // Check if node needs child processing
54
- if let Some(kind) = dispatch_needs_child(node, self.source) {
55
- return self.process_needs_child(kind);
56
- }
57
-
58
- None
36
+ install_node(self.genv, self.lenv, &mut self.changes, self.source, node)
59
37
  }
60
38
 
61
- /// Process nodes that need child evaluation first
62
- fn process_needs_child(&mut self, kind: NeedsChildKind) -> Option<VertexId> {
63
- match kind {
64
- NeedsChildKind::IvarWrite { ivar_name, value } => {
65
- let value_vtx = self.install_node(&value)?;
66
- Some(finish_ivar_write(self.genv, ivar_name, value_vtx))
67
- }
68
- NeedsChildKind::LocalVarWrite { var_name, value } => {
69
- let value_vtx = self.install_node(&value)?;
70
- Some(finish_local_var_write(
71
- self.genv,
72
- self.lenv,
73
- &mut self.changes,
74
- var_name,
75
- value_vtx,
76
- ))
77
- }
78
- NeedsChildKind::MethodCall {
79
- receiver,
80
- method_name,
81
- location,
82
- } => {
83
- let recv_vtx = self.install_node(&receiver)?;
84
- Some(finish_method_call(
85
- self.genv,
86
- recv_vtx,
87
- method_name,
88
- location,
89
- ))
90
- }
91
- }
92
- }
93
-
94
- /// Install class definition
95
- fn install_class_node(&mut self, class_node: &ruby_prism::ClassNode) -> Option<VertexId> {
96
- let class_name = extract_class_name(class_node);
97
- install_class(self.genv, class_name);
98
-
99
- if let Some(body) = class_node.body() {
100
- if let Some(statements) = body.as_statements_node() {
101
- self.install_statements(&statements);
102
- }
103
- }
104
-
105
- exit_scope(self.genv);
106
- None
107
- }
108
-
109
- /// Install method definition
110
- fn install_def_node(&mut self, def_node: &ruby_prism::DefNode) -> Option<VertexId> {
111
- let method_name = String::from_utf8_lossy(def_node.name().as_slice()).to_string();
112
- install_method(self.genv, method_name);
113
-
114
- if let Some(body) = def_node.body() {
115
- if let Some(statements) = body.as_statements_node() {
116
- self.install_statements(&statements);
117
- }
118
- }
119
-
120
- exit_scope(self.genv);
121
- None
122
- }
123
-
124
- /// Process multiple statements
125
- fn install_statements(&mut self, statements: &ruby_prism::StatementsNode) {
126
- for stmt in &statements.body() {
127
- self.install_node(&stmt);
128
- }
129
- }
130
-
131
- /// Finish installation (apply changes and execute Boxes)
132
39
  pub fn finish(self) {
133
40
  self.genv.apply_changes(self.changes);
134
41
  self.genv.run_all();
135
42
  }
136
43
  }
137
44
 
138
- #[cfg(test)]
139
- mod tests {
140
- use super::*;
141
- use crate::parser::parse_ruby_source;
142
- use crate::types::Type;
143
-
144
- #[test]
145
- fn test_install_literal() {
146
- let source = r#"x = "hello""#;
147
- let parse_result = parse_ruby_source(source, "test.rb".to_string()).unwrap();
148
-
149
- let mut genv = GlobalEnv::new();
150
- let mut lenv = LocalEnv::new();
151
- let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
152
-
153
- let root = parse_result.node();
154
- if let Some(program_node) = root.as_program_node() {
155
- let statements = program_node.statements();
156
- for stmt in &statements.body() {
157
- installer.install_node(&stmt);
158
- }
159
- }
160
-
161
- installer.finish();
45
+ /// Install node (returns Vertex ID)
46
+ pub(crate) fn install_node(
47
+ genv: &mut GlobalEnv,
48
+ lenv: &mut LocalEnv,
49
+ changes: &mut ChangeSet,
50
+ source: &str,
51
+ node: &Node,
52
+ ) -> Option<VertexId> {
53
+ if let Some(class_node) = node.as_class_node() {
54
+ return process_class_node(genv, lenv, changes, source, &class_node);
55
+ }
162
56
 
163
- let x_vtx = lenv.get_var("x").unwrap();
164
- assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "String");
57
+ if let Some(module_node) = node.as_module_node() {
58
+ return process_module_node(genv, lenv, changes, source, &module_node);
165
59
  }
166
60
 
167
- #[test]
168
- fn test_install_multiple_vars() {
169
- let source = r#"
170
- x = "hello"
171
- y = 42
172
- "#;
173
- let parse_result = parse_ruby_source(source, "test.rb".to_string()).unwrap();
174
-
175
- let mut genv = GlobalEnv::new();
176
- let mut lenv = LocalEnv::new();
177
- let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
178
-
179
- let root = parse_result.node();
180
- if let Some(program_node) = root.as_program_node() {
181
- let statements = program_node.statements();
182
- for stmt in &statements.body() {
183
- installer.install_node(&stmt);
184
- }
185
- }
61
+ if let Some(def_node) = node.as_def_node() {
62
+ return process_def_node(genv, lenv, changes, source, &def_node);
63
+ }
186
64
 
187
- installer.finish();
65
+ if let Some(block_node) = node.as_block_node() {
66
+ return process_block_node(genv, lenv, changes, source, &block_node);
67
+ }
188
68
 
189
- let x_vtx = lenv.get_var("x").unwrap();
190
- let y_vtx = lenv.get_var("y").unwrap();
69
+ if let Some(if_node) = node.as_if_node() {
70
+ return process_if_node(genv, lenv, changes, source, &if_node);
71
+ }
72
+ if let Some(unless_node) = node.as_unless_node() {
73
+ return process_unless_node(genv, lenv, changes, source, &unless_node);
74
+ }
75
+ if let Some(case_node) = node.as_case_node() {
76
+ return process_case_node(genv, lenv, changes, source, &case_node);
77
+ }
191
78
 
192
- assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "String");
193
- assert_eq!(genv.get_vertex(y_vtx).unwrap().show(), "Integer");
79
+ match dispatch_simple(genv, lenv, node) {
80
+ DispatchResult::Vertex(vtx) => return Some(vtx),
81
+ DispatchResult::NotHandled => {}
194
82
  }
195
83
 
196
- #[test]
197
- fn test_install_method_call() {
198
- let source = r#"
199
- x = "hello"
200
- y = x.upcase
201
- "#;
202
- let parse_result = parse_ruby_source(source, "test.rb".to_string()).unwrap();
203
-
204
- let mut genv = GlobalEnv::new();
205
- genv.register_builtin_method(Type::string(), "upcase", Type::string());
206
-
207
- let mut lenv = LocalEnv::new();
208
- let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
209
-
210
- let root = parse_result.node();
211
- if let Some(program_node) = root.as_program_node() {
212
- let statements = program_node.statements();
213
- for stmt in &statements.body() {
214
- installer.install_node(&stmt);
215
- }
216
- }
84
+ if let Some(vtx) = install_literal_node(genv, lenv, changes, source, node) {
85
+ return Some(vtx);
86
+ }
217
87
 
218
- installer.finish();
88
+ if let Some(kind) = dispatch_needs_child(node, source) {
89
+ return process_needs_child(genv, lenv, changes, source, kind);
90
+ }
219
91
 
220
- let x_vtx = lenv.get_var("x").unwrap();
221
- let y_vtx = lenv.get_var("y").unwrap();
92
+ None
93
+ }
222
94
 
223
- assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "String");
224
- assert_eq!(genv.get_vertex(y_vtx).unwrap().show(), "String");
95
+ /// Process multiple statements (returns last expression's VertexId)
96
+ pub(crate) fn install_statements(
97
+ genv: &mut GlobalEnv,
98
+ lenv: &mut LocalEnv,
99
+ changes: &mut ChangeSet,
100
+ source: &str,
101
+ statements: &ruby_prism::StatementsNode,
102
+ ) -> Option<VertexId> {
103
+ let mut last_vtx = None;
104
+ for stmt in &statements.body() {
105
+ last_vtx = install_node(genv, lenv, changes, source, &stmt);
225
106
  }
107
+ last_vtx
226
108
  }
@@ -1,63 +1,217 @@
1
1
  //! Literal Handlers - Processing Ruby literal values
2
2
  //!
3
3
  //! This module is responsible for:
4
- //! - String, Integer, Array, Hash literals
4
+ //! - String, Integer, Float, Regexp literals
5
5
  //! - nil, true, false, Symbol literals
6
+ //! - Array, Hash, Range literals with element type inference
6
7
  //! - Creating Source vertices with fixed types
7
8
 
8
- use crate::env::GlobalEnv;
9
- use crate::graph::VertexId;
9
+ use crate::env::{GlobalEnv, LocalEnv};
10
+ use crate::graph::{ChangeSet, VertexId};
10
11
  use crate::types::Type;
11
12
  use ruby_prism::Node;
13
+ use std::collections::HashSet;
12
14
 
13
- /// Install literal nodes and return their VertexId
14
- pub fn install_literal(genv: &mut GlobalEnv, node: &Node) -> Option<VertexId> {
15
- // "hello"
15
+ /// Install literal node (including complex types: Array, Hash, Range)
16
+ pub(crate) fn install_literal_node(
17
+ genv: &mut GlobalEnv,
18
+ lenv: &mut LocalEnv,
19
+ changes: &mut ChangeSet,
20
+ source: &str,
21
+ node: &Node,
22
+ ) -> Option<VertexId> {
23
+ if node.as_array_node().is_some() {
24
+ let elements: Vec<Node> = node.as_array_node().unwrap().elements().iter().collect();
25
+ return install_array_literal_elements(genv, lenv, changes, source, elements);
26
+ }
27
+
28
+ if node.as_hash_node().is_some() {
29
+ let elements: Vec<Node> = node.as_hash_node().unwrap().elements().iter().collect();
30
+ return install_hash_literal_elements(genv, lenv, changes, source, elements);
31
+ }
32
+
33
+ if let Some(range_node) = node.as_range_node() {
34
+ return install_range_literal(genv, lenv, changes, source, &range_node);
35
+ }
36
+
37
+ install_simple_literal(genv, node)
38
+ }
39
+
40
+ /// Install simple literal nodes (String, Integer, Float, nil, true, false, Symbol, Regexp)
41
+ fn install_simple_literal(genv: &mut GlobalEnv, node: &Node) -> Option<VertexId> {
16
42
  if node.as_string_node().is_some() {
17
43
  return Some(genv.new_source(Type::string()));
18
44
  }
19
-
20
- // 42
21
45
  if node.as_integer_node().is_some() {
22
46
  return Some(genv.new_source(Type::integer()));
23
47
  }
48
+ if node.as_float_node().is_some() {
49
+ return Some(genv.new_source(Type::float()));
50
+ }
51
+ if node.as_nil_node().is_some() {
52
+ return Some(genv.new_source(Type::Nil));
53
+ }
54
+ if node.as_true_node().is_some() {
55
+ return Some(genv.new_source(Type::instance("TrueClass")));
56
+ }
57
+ if node.as_false_node().is_some() {
58
+ return Some(genv.new_source(Type::instance("FalseClass")));
59
+ }
60
+ if node.as_symbol_node().is_some() {
61
+ return Some(genv.new_source(Type::symbol()));
62
+ }
63
+ if node.as_regular_expression_node().is_some() {
64
+ return Some(genv.new_source(Type::regexp()));
65
+ }
66
+ None
67
+ }
24
68
 
25
- // [1, 2, 3]
26
- if node.as_array_node().is_some() {
69
+ /// Install array literal with element type inference
70
+ fn install_array_literal_elements(
71
+ genv: &mut GlobalEnv,
72
+ lenv: &mut LocalEnv,
73
+ changes: &mut ChangeSet,
74
+ source: &str,
75
+ elements: Vec<Node>,
76
+ ) -> Option<VertexId> {
77
+ if elements.is_empty() {
27
78
  return Some(genv.new_source(Type::array()));
28
79
  }
29
80
 
30
- // {a: 1}
31
- if node.as_hash_node().is_some() {
32
- return Some(genv.new_source(Type::hash()));
33
- }
81
+ let mut element_types: HashSet<Type> = HashSet::new();
34
82
 
35
- // nil
36
- if node.as_nil_node().is_some() {
37
- return Some(genv.new_source(Type::Nil));
83
+ for element in &elements {
84
+ if let Some(vtx) = super::install::install_node(genv, lenv, changes, source, element) {
85
+ if let Some(src) = genv.get_source(vtx) {
86
+ element_types.insert(src.ty.clone());
87
+ } else if let Some(vertex) = genv.get_vertex(vtx) {
88
+ for ty in vertex.types.keys() {
89
+ element_types.insert(ty.clone());
90
+ }
91
+ }
92
+ }
38
93
  }
39
94
 
40
- // true
41
- if node.as_true_node().is_some() {
42
- return Some(genv.new_source(Type::Instance {
43
- class_name: "TrueClass".to_string(),
44
- }));
45
- }
95
+ let array_type = if element_types.is_empty() {
96
+ Type::array()
97
+ } else if element_types.len() == 1 {
98
+ let elem_type = element_types.into_iter().next().unwrap();
99
+ Type::array_of(elem_type)
100
+ } else {
101
+ let types_vec: Vec<Type> = element_types.into_iter().collect();
102
+ let union_type = Type::Union(types_vec);
103
+ Type::array_of(union_type)
104
+ };
46
105
 
47
- // false
48
- if node.as_false_node().is_some() {
49
- return Some(genv.new_source(Type::Instance {
50
- class_name: "FalseClass".to_string(),
51
- }));
106
+ Some(genv.new_source(array_type))
107
+ }
108
+
109
+ /// Install hash literal with key/value type inference
110
+ fn install_hash_literal_elements(
111
+ genv: &mut GlobalEnv,
112
+ lenv: &mut LocalEnv,
113
+ changes: &mut ChangeSet,
114
+ source: &str,
115
+ elements: Vec<Node>,
116
+ ) -> Option<VertexId> {
117
+ if elements.is_empty() {
118
+ return Some(genv.new_source(Type::hash()));
52
119
  }
53
120
 
54
- // :symbol
55
- if node.as_symbol_node().is_some() {
56
- return Some(genv.new_source(Type::Instance {
57
- class_name: "Symbol".to_string(),
58
- }));
121
+ let mut key_types: HashSet<Type> = HashSet::new();
122
+ let mut value_types: HashSet<Type> = HashSet::new();
123
+
124
+ for element in &elements {
125
+ if let Some(assoc_node) = element.as_assoc_node() {
126
+ if let Some(key_vtx) =
127
+ super::install::install_node(genv, lenv, changes, source, &assoc_node.key())
128
+ {
129
+ if let Some(src) = genv.get_source(key_vtx) {
130
+ key_types.insert(src.ty.clone());
131
+ } else if let Some(vertex) = genv.get_vertex(key_vtx) {
132
+ for ty in vertex.types.keys() {
133
+ key_types.insert(ty.clone());
134
+ }
135
+ }
136
+ }
137
+
138
+ if let Some(value_vtx) =
139
+ super::install::install_node(genv, lenv, changes, source, &assoc_node.value())
140
+ {
141
+ if let Some(src) = genv.get_source(value_vtx) {
142
+ value_types.insert(src.ty.clone());
143
+ } else if let Some(vertex) = genv.get_vertex(value_vtx) {
144
+ for ty in vertex.types.keys() {
145
+ value_types.insert(ty.clone());
146
+ }
147
+ }
148
+ }
149
+ }
59
150
  }
60
151
 
152
+ let hash_type = if key_types.is_empty() || value_types.is_empty() {
153
+ Type::hash()
154
+ } else {
155
+ let key_type = if key_types.len() == 1 {
156
+ key_types.into_iter().next().unwrap()
157
+ } else {
158
+ let types_vec: Vec<Type> = key_types.into_iter().collect();
159
+ Type::Union(types_vec)
160
+ };
161
+ let value_type = if value_types.len() == 1 {
162
+ value_types.into_iter().next().unwrap()
163
+ } else {
164
+ let types_vec: Vec<Type> = value_types.into_iter().collect();
165
+ Type::Union(types_vec)
166
+ };
167
+ Type::hash_of(key_type, value_type)
168
+ };
169
+
170
+ Some(genv.new_source(hash_type))
171
+ }
172
+
173
+ /// Install range literal with element type inference
174
+ fn install_range_literal(
175
+ genv: &mut GlobalEnv,
176
+ lenv: &mut LocalEnv,
177
+ changes: &mut ChangeSet,
178
+ source: &str,
179
+ range_node: &ruby_prism::RangeNode,
180
+ ) -> Option<VertexId> {
181
+ let element_type = if let Some(left) = range_node.left() {
182
+ infer_range_element_type(genv, lenv, changes, source, &left)
183
+ } else if let Some(right) = range_node.right() {
184
+ infer_range_element_type(genv, lenv, changes, source, &right)
185
+ } else {
186
+ None
187
+ };
188
+
189
+ let range_type = match element_type {
190
+ Some(ty) => Type::range_of(ty),
191
+ None => Type::range(),
192
+ };
193
+
194
+ Some(genv.new_source(range_type))
195
+ }
196
+
197
+ /// Infer element type from a range endpoint node
198
+ fn infer_range_element_type(
199
+ genv: &mut GlobalEnv,
200
+ lenv: &mut LocalEnv,
201
+ changes: &mut ChangeSet,
202
+ source: &str,
203
+ node: &Node,
204
+ ) -> Option<Type> {
205
+ if let Some(vtx) = super::install::install_node(genv, lenv, changes, source, node) {
206
+ if let Some(src) = genv.get_source(vtx) {
207
+ return Some(src.ty.clone());
208
+ }
209
+ if let Some(vertex) = genv.get_vertex(vtx) {
210
+ if let Some(ty) = vertex.types.keys().next() {
211
+ return Some(ty.clone());
212
+ }
213
+ }
214
+ }
61
215
  None
62
216
  }
63
217
 
@@ -68,9 +222,6 @@ mod tests {
68
222
  #[test]
69
223
  fn test_install_string_literal() {
70
224
  let mut genv = GlobalEnv::new();
71
-
72
- // Create a mock string node - we test via integration instead
73
- // Unit test just verifies the type creation
74
225
  let vtx = genv.new_source(Type::string());
75
226
  assert_eq!(genv.get_source(vtx).unwrap().ty.show(), "String");
76
227
  }
@@ -78,8 +229,21 @@ mod tests {
78
229
  #[test]
79
230
  fn test_install_integer_literal() {
80
231
  let mut genv = GlobalEnv::new();
81
-
82
232
  let vtx = genv.new_source(Type::integer());
83
233
  assert_eq!(genv.get_source(vtx).unwrap().ty.show(), "Integer");
84
234
  }
235
+
236
+ #[test]
237
+ fn test_install_float_literal() {
238
+ let mut genv = GlobalEnv::new();
239
+ let vtx = genv.new_source(Type::float());
240
+ assert_eq!(genv.get_source(vtx).unwrap().ty.show(), "Float");
241
+ }
242
+
243
+ #[test]
244
+ fn test_install_regexp_literal() {
245
+ let mut genv = GlobalEnv::new();
246
+ let vtx = genv.new_source(Type::regexp());
247
+ assert_eq!(genv.get_source(vtx).unwrap().ty.show(), "Regexp");
248
+ }
85
249
  }
@@ -1,11 +1,12 @@
1
+ mod attributes;
2
+ mod blocks;
1
3
  mod calls;
4
+ mod conditionals;
2
5
  mod definitions;
3
6
  mod dispatch;
4
7
  mod install;
5
8
  mod literals;
9
+ mod parameters;
6
10
  mod variables;
7
11
 
8
- #[cfg(test)]
9
- mod tests;
10
-
11
12
  pub use install::AstInstaller;