method-ray 0.1.10 → 0.2.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/core/Cargo.toml +1 -1
- data/core/src/analyzer/blocks.rs +1 -1
- data/core/src/analyzer/compound_assignments.rs +194 -0
- data/core/src/analyzer/conditionals.rs +268 -2
- data/core/src/analyzer/dispatch.rs +234 -22
- data/core/src/analyzer/install.rs +36 -1
- data/core/src/analyzer/lambdas.rs +153 -0
- data/core/src/analyzer/literals.rs +11 -0
- data/core/src/analyzer/mod.rs +3 -0
- data/core/src/analyzer/variables.rs +224 -0
- data/core/src/analyzer/yields.rs +24 -0
- data/core/src/env/global_env.rs +50 -0
- data/core/src/env/scope.rs +91 -7
- data/core/src/graph/box.rs +11 -0
- data/core/src/types.rs +34 -0
- data/ext/Cargo.toml +1 -1
- data/lib/methodray/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 23dc6a87888386dca19ac6c02c051df5712943cffd755ed29437117273347f51
|
|
4
|
+
data.tar.gz: ba211cf132132316f6efa5fbfa5061df93ba9b81b0766192e85a25273720efd8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f485df1d4c80d86bdaa60e4ffb968fc919669e7df4142a91093a2b4004ce758112aeb701936f1e96b2a2a933724c7a38eb9cae982ae55994b664ac604f2c19e8
|
|
7
|
+
data.tar.gz: ad0ab16ba07f4966ff06acde966527bfcac0a96e1a6b65563f5a82dc1454e541ed3c74a35fc8c950a15f662abe2ac00fb66809296c2b4761a9cc523ba3db71de
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.0] - 2026-04-05
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Add pattern matching type inference ([#103](https://github.com/dak2/method-ray/pull/103))
|
|
13
|
+
- Add Lambda/Proc type inference support ([#104](https://github.com/dak2/method-ray/pull/104))
|
|
14
|
+
- Add compound assignment type inference ([#105](https://github.com/dak2/method-ray/pull/105))
|
|
15
|
+
- Add `defined?` type inference support ([#106](https://github.com/dak2/method-ray/pull/106))
|
|
16
|
+
- Add `yield` type inference support ([#107](https://github.com/dak2/method-ray/pull/107))
|
|
17
|
+
- Add control flow keyword support (`break`, `next`, `retry`, `redo`) ([#108](https://github.com/dak2/method-ray/pull/108))
|
|
18
|
+
- Add backtick string type inference ([#109](https://github.com/dak2/method-ray/pull/109))
|
|
19
|
+
- Add predicate type propagation to pattern variables in `case...in` ([#110](https://github.com/dak2/method-ray/pull/110))
|
|
20
|
+
- Add constant type inference support ([#102](https://github.com/dak2/method-ray/pull/102))
|
|
21
|
+
- Add global variable (`$var`) type tracking ([#100](https://github.com/dak2/method-ray/pull/100))
|
|
22
|
+
- Add class variable (`@@var`) type tracking ([#99](https://github.com/dak2/method-ray/pull/99))
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Fix uninitialized `@@var` reads silently skipping downstream type checking ([#101](https://github.com/dak2/method-ray/pull/101))
|
|
27
|
+
- Fix release workflow not triggered after tag push ([#88](https://github.com/dak2/method-ray/pull/88))
|
|
28
|
+
|
|
8
29
|
## [0.1.10] - 2026-03-24
|
|
9
30
|
|
|
10
31
|
### Added
|
|
@@ -159,6 +180,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
159
180
|
- Initial release
|
|
160
181
|
- `methodray check` - Static type checking for Ruby files
|
|
161
182
|
|
|
183
|
+
[0.2.0]: https://github.com/dak2/method-ray/releases/tag/v0.2.0
|
|
162
184
|
[0.1.10]: https://github.com/dak2/method-ray/releases/tag/v0.1.10
|
|
163
185
|
[0.1.9]: https://github.com/dak2/method-ray/releases/tag/v0.1.9
|
|
164
186
|
[0.1.8]: https://github.com/dak2/method-ray/releases/tag/v0.1.8
|
data/core/Cargo.toml
CHANGED
data/core/src/analyzer/blocks.rs
CHANGED
|
@@ -56,7 +56,7 @@ pub(crate) fn process_block_node_with_params(
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/// Install block parameters and return their vertex IDs
|
|
59
|
-
fn install_block_parameters_with_vtxs(
|
|
59
|
+
pub(crate) fn install_block_parameters_with_vtxs(
|
|
60
60
|
genv: &mut GlobalEnv,
|
|
61
61
|
lenv: &mut LocalEnv,
|
|
62
62
|
changes: &mut ChangeSet,
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
//! Compound assignment handlers (`x += 1`, `x ||= val`, `x &&= val`).
|
|
2
|
+
//!
|
|
3
|
+
//! `||=` and `&&=` use the same Union(existing, val) approximation — a sound
|
|
4
|
+
//! over-approximation without condition narrowing.
|
|
5
|
+
|
|
6
|
+
use crate::env::{GlobalEnv, LocalEnv};
|
|
7
|
+
use crate::graph::{ChangeSet, VertexId};
|
|
8
|
+
use crate::types::Type;
|
|
9
|
+
|
|
10
|
+
use super::calls::install_method_call;
|
|
11
|
+
use super::variables::{
|
|
12
|
+
install_class_var_read, install_class_var_write, install_constant_read, install_constant_write,
|
|
13
|
+
install_global_var_read, install_global_var_write, install_ivar_read, install_ivar_write,
|
|
14
|
+
install_local_var_read, install_local_var_write,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
pub(crate) enum CompoundVarKind {
|
|
18
|
+
Local(String),
|
|
19
|
+
Ivar(String),
|
|
20
|
+
ClassVar(String),
|
|
21
|
+
GlobalVar(String),
|
|
22
|
+
Constant(String),
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
impl CompoundVarKind {
|
|
26
|
+
fn read(&self, genv: &GlobalEnv, lenv: &LocalEnv) -> Option<VertexId> {
|
|
27
|
+
match self {
|
|
28
|
+
Self::Local(name) => install_local_var_read(lenv, name),
|
|
29
|
+
Self::Ivar(name) => install_ivar_read(genv, name),
|
|
30
|
+
Self::ClassVar(name) => install_class_var_read(genv, name),
|
|
31
|
+
Self::GlobalVar(name) => install_global_var_read(genv, name),
|
|
32
|
+
Self::Constant(name) => install_constant_read(genv, name),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fn write(
|
|
37
|
+
self,
|
|
38
|
+
genv: &mut GlobalEnv,
|
|
39
|
+
lenv: &mut LocalEnv,
|
|
40
|
+
changes: &mut ChangeSet,
|
|
41
|
+
value_vtx: VertexId,
|
|
42
|
+
) -> VertexId {
|
|
43
|
+
match self {
|
|
44
|
+
Self::Local(name) => install_local_var_write(genv, lenv, changes, name, value_vtx),
|
|
45
|
+
Self::Ivar(name) => install_ivar_write(genv, name, value_vtx),
|
|
46
|
+
Self::ClassVar(name) => install_class_var_write(genv, name, value_vtx),
|
|
47
|
+
Self::GlobalVar(name) => install_global_var_write(genv, name, value_vtx),
|
|
48
|
+
Self::Constant(name) => install_constant_write(genv, name, value_vtx),
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
pub(crate) enum CompoundOp {
|
|
54
|
+
Operator(String),
|
|
55
|
+
Logical,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
pub(crate) fn process_compound_write(
|
|
59
|
+
genv: &mut GlobalEnv,
|
|
60
|
+
lenv: &mut LocalEnv,
|
|
61
|
+
changes: &mut ChangeSet,
|
|
62
|
+
var_kind: CompoundVarKind,
|
|
63
|
+
op: CompoundOp,
|
|
64
|
+
value_vtx: VertexId,
|
|
65
|
+
) -> VertexId {
|
|
66
|
+
match op {
|
|
67
|
+
CompoundOp::Operator(operator) => {
|
|
68
|
+
let current_vtx = var_kind.read(genv, lenv)
|
|
69
|
+
.unwrap_or_else(|| genv.new_source(Type::Nil));
|
|
70
|
+
let result_vtx = install_method_call(
|
|
71
|
+
genv, current_vtx, operator, vec![value_vtx], None, None, false,
|
|
72
|
+
);
|
|
73
|
+
var_kind.write(genv, lenv, changes, result_vtx)
|
|
74
|
+
}
|
|
75
|
+
CompoundOp::Logical => {
|
|
76
|
+
let merge_vtx = genv.new_vertex();
|
|
77
|
+
if let Some(current_vtx) = var_kind.read(genv, lenv) {
|
|
78
|
+
genv.add_edge(current_vtx, merge_vtx);
|
|
79
|
+
}
|
|
80
|
+
genv.add_edge(value_vtx, merge_vtx);
|
|
81
|
+
var_kind.write(genv, lenv, changes, merge_vtx)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#[cfg(test)]
|
|
87
|
+
mod tests {
|
|
88
|
+
use super::*;
|
|
89
|
+
|
|
90
|
+
#[test]
|
|
91
|
+
fn test_operator_write() {
|
|
92
|
+
let mut genv = GlobalEnv::new();
|
|
93
|
+
let mut lenv = LocalEnv::new();
|
|
94
|
+
let mut changes = ChangeSet::new();
|
|
95
|
+
|
|
96
|
+
let int_vtx = genv.new_source(Type::integer());
|
|
97
|
+
install_local_var_write(&mut genv, &mut lenv, &mut changes, "x".to_string(), int_vtx);
|
|
98
|
+
|
|
99
|
+
let rhs_vtx = genv.new_source(Type::integer());
|
|
100
|
+
let result = process_compound_write(
|
|
101
|
+
&mut genv, &mut lenv, &mut changes,
|
|
102
|
+
CompoundVarKind::Local("x".to_string()),
|
|
103
|
+
CompoundOp::Operator("+".to_string()),
|
|
104
|
+
rhs_vtx,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
assert_ne!(result, int_vtx);
|
|
108
|
+
assert_eq!(install_local_var_read(&lenv, "x"), Some(result));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
#[test]
|
|
112
|
+
fn test_operator_write_uninitialized() {
|
|
113
|
+
let mut genv = GlobalEnv::new();
|
|
114
|
+
let mut lenv = LocalEnv::new();
|
|
115
|
+
let mut changes = ChangeSet::new();
|
|
116
|
+
|
|
117
|
+
let rhs_vtx = genv.new_source(Type::integer());
|
|
118
|
+
let result = process_compound_write(
|
|
119
|
+
&mut genv, &mut lenv, &mut changes,
|
|
120
|
+
CompoundVarKind::Local("x".to_string()),
|
|
121
|
+
CompoundOp::Operator("+".to_string()),
|
|
122
|
+
rhs_vtx,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
assert_eq!(install_local_var_read(&lenv, "x"), Some(result));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
#[test]
|
|
129
|
+
fn test_logical_write_produces_union() {
|
|
130
|
+
let mut genv = GlobalEnv::new();
|
|
131
|
+
let mut lenv = LocalEnv::new();
|
|
132
|
+
let mut changes = ChangeSet::new();
|
|
133
|
+
|
|
134
|
+
let str_vtx = genv.new_source(Type::string());
|
|
135
|
+
install_local_var_write(&mut genv, &mut lenv, &mut changes, "x".to_string(), str_vtx);
|
|
136
|
+
|
|
137
|
+
let int_vtx = genv.new_source(Type::integer());
|
|
138
|
+
let result = process_compound_write(
|
|
139
|
+
&mut genv, &mut lenv, &mut changes,
|
|
140
|
+
CompoundVarKind::Local("x".to_string()),
|
|
141
|
+
CompoundOp::Logical,
|
|
142
|
+
int_vtx,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
assert_eq!(install_local_var_read(&lenv, "x"), Some(result));
|
|
146
|
+
genv.apply_changes(changes);
|
|
147
|
+
genv.run_all();
|
|
148
|
+
let types = genv.get_receiver_types(result).unwrap();
|
|
149
|
+
assert!(types.contains(&Type::string()));
|
|
150
|
+
assert!(types.contains(&Type::integer()));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#[test]
|
|
154
|
+
fn test_logical_write_uninitialized() {
|
|
155
|
+
let mut genv = GlobalEnv::new();
|
|
156
|
+
let mut lenv = LocalEnv::new();
|
|
157
|
+
let mut changes = ChangeSet::new();
|
|
158
|
+
|
|
159
|
+
let str_vtx = genv.new_source(Type::string());
|
|
160
|
+
let result = process_compound_write(
|
|
161
|
+
&mut genv, &mut lenv, &mut changes,
|
|
162
|
+
CompoundVarKind::Local("x".to_string()),
|
|
163
|
+
CompoundOp::Logical,
|
|
164
|
+
str_vtx,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
genv.apply_changes(changes);
|
|
168
|
+
genv.run_all();
|
|
169
|
+
let types = genv.get_receiver_types(result).unwrap();
|
|
170
|
+
assert!(types.contains(&Type::string()));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#[test]
|
|
174
|
+
fn test_ivar_compound_write() {
|
|
175
|
+
let mut genv = GlobalEnv::new();
|
|
176
|
+
let mut lenv = LocalEnv::new();
|
|
177
|
+
let mut changes = ChangeSet::new();
|
|
178
|
+
genv.enter_class("TestClass".to_string(), None);
|
|
179
|
+
genv.enter_method("test_method".to_string());
|
|
180
|
+
|
|
181
|
+
let int_vtx = genv.new_source(Type::integer());
|
|
182
|
+
install_ivar_write(&mut genv, "@count".to_string(), int_vtx);
|
|
183
|
+
|
|
184
|
+
let rhs_vtx = genv.new_source(Type::integer());
|
|
185
|
+
let result = process_compound_write(
|
|
186
|
+
&mut genv, &mut lenv, &mut changes,
|
|
187
|
+
CompoundVarKind::Ivar("@count".to_string()),
|
|
188
|
+
CompoundOp::Operator("+".to_string()),
|
|
189
|
+
rhs_vtx,
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
assert_eq!(result, int_vtx);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Conditionals - if/unless/case type inference
|
|
1
|
+
//! Conditionals - if/unless/case type inference
|
|
2
2
|
//!
|
|
3
3
|
//! Collects types from each branch and merges them into a Union
|
|
4
4
|
//! via edges into a single result Vertex.
|
|
@@ -6,9 +6,14 @@
|
|
|
6
6
|
use crate::env::{GlobalEnv, LocalEnv};
|
|
7
7
|
use crate::graph::{ChangeSet, VertexId};
|
|
8
8
|
use crate::types::Type;
|
|
9
|
-
use ruby_prism::{
|
|
9
|
+
use ruby_prism::{
|
|
10
|
+
ArrayPatternNode, CapturePatternNode, CaseMatchNode, CaseNode, ElseNode, FindPatternNode,
|
|
11
|
+
HashPatternNode, IfNode, InNode, Node, UnlessNode, WhenNode,
|
|
12
|
+
};
|
|
10
13
|
|
|
14
|
+
use super::bytes_to_name;
|
|
11
15
|
use super::install::{install_node, install_statements};
|
|
16
|
+
use super::variables::install_local_var_write;
|
|
12
17
|
|
|
13
18
|
/// Process IfNode: if/elsif/else chain
|
|
14
19
|
pub(crate) fn process_if_node(
|
|
@@ -137,6 +142,47 @@ pub(crate) fn process_case_node(
|
|
|
137
142
|
Some(result_vtx)
|
|
138
143
|
}
|
|
139
144
|
|
|
145
|
+
/// Process CaseMatchNode: case/in pattern matching
|
|
146
|
+
pub(crate) fn process_case_match_node(
|
|
147
|
+
genv: &mut GlobalEnv,
|
|
148
|
+
lenv: &mut LocalEnv,
|
|
149
|
+
changes: &mut ChangeSet,
|
|
150
|
+
source: &str,
|
|
151
|
+
node: &CaseMatchNode,
|
|
152
|
+
) -> Option<VertexId> {
|
|
153
|
+
let predicate_vtx = node
|
|
154
|
+
.predicate()
|
|
155
|
+
.and_then(|pred| install_node(genv, lenv, changes, source, &pred));
|
|
156
|
+
|
|
157
|
+
let result_vtx = genv.new_vertex();
|
|
158
|
+
|
|
159
|
+
for condition in &node.conditions() {
|
|
160
|
+
if let Some(in_node) = condition.as_in_node() {
|
|
161
|
+
let vtx = process_in_clause(genv, lenv, changes, source, &in_node, predicate_vtx);
|
|
162
|
+
if let Some(vtx) = vtx {
|
|
163
|
+
genv.add_edge(vtx, result_vtx);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let has_else = if let Some(else_node) = node.else_clause() {
|
|
169
|
+
let vtx = process_else_clause(genv, lenv, changes, source, &else_node);
|
|
170
|
+
if let Some(vtx) = vtx {
|
|
171
|
+
genv.add_edge(vtx, result_vtx);
|
|
172
|
+
}
|
|
173
|
+
true
|
|
174
|
+
} else {
|
|
175
|
+
false
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
if !has_else {
|
|
179
|
+
let nil_vtx = genv.new_source(Type::Nil);
|
|
180
|
+
genv.add_edge(nil_vtx, result_vtx);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
Some(result_vtx)
|
|
184
|
+
}
|
|
185
|
+
|
|
140
186
|
/// Process subsequent node (elsif chain or else)
|
|
141
187
|
fn process_subsequent(
|
|
142
188
|
genv: &mut GlobalEnv,
|
|
@@ -188,3 +234,223 @@ fn process_when_clause(
|
|
|
188
234
|
.statements()
|
|
189
235
|
.and_then(|stmts| install_statements(genv, lenv, changes, source, &stmts))
|
|
190
236
|
}
|
|
237
|
+
|
|
238
|
+
/// Process InNode clause body
|
|
239
|
+
fn process_in_clause(
|
|
240
|
+
genv: &mut GlobalEnv,
|
|
241
|
+
lenv: &mut LocalEnv,
|
|
242
|
+
changes: &mut ChangeSet,
|
|
243
|
+
source: &str,
|
|
244
|
+
in_node: &InNode,
|
|
245
|
+
predicate_vtx: Option<VertexId>,
|
|
246
|
+
) -> Option<VertexId> {
|
|
247
|
+
process_pattern(genv, lenv, changes, source, &in_node.pattern(), predicate_vtx);
|
|
248
|
+
in_node
|
|
249
|
+
.statements()
|
|
250
|
+
.and_then(|s| install_statements(genv, lenv, changes, source, &s))
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/// Dispatch pattern processing based on pattern type
|
|
254
|
+
fn process_pattern(
|
|
255
|
+
genv: &mut GlobalEnv,
|
|
256
|
+
lenv: &mut LocalEnv,
|
|
257
|
+
changes: &mut ChangeSet,
|
|
258
|
+
source: &str,
|
|
259
|
+
pattern: &Node,
|
|
260
|
+
predicate_vtx: Option<VertexId>,
|
|
261
|
+
) {
|
|
262
|
+
// Guard pattern (in x if condition)
|
|
263
|
+
if let Some(if_node) = pattern.as_if_node() {
|
|
264
|
+
if let Some(stmts) = if_node.statements() {
|
|
265
|
+
for stmt in &stmts.body() {
|
|
266
|
+
process_pattern(genv, lenv, changes, source, &stmt, predicate_vtx);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
install_node(genv, lenv, changes, source, &if_node.predicate());
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if let Some(cap) = pattern.as_capture_pattern_node() {
|
|
274
|
+
process_capture_pattern(genv, lenv, changes, source, &cap);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ImplicitNode: hash shorthand pattern { name: } wraps LocalVariableTargetNode
|
|
279
|
+
if let Some(implicit) = pattern.as_implicit_node() {
|
|
280
|
+
process_pattern(genv, lenv, changes, source, &implicit.value(), predicate_vtx);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// LocalVariableTargetNode: single variable binding (in x)
|
|
285
|
+
if let Some(target) = pattern.as_local_variable_target_node() {
|
|
286
|
+
let var_name = bytes_to_name(target.name().as_slice());
|
|
287
|
+
let type_vtx = predicate_vtx.unwrap_or_else(|| genv.new_source(Type::Bot));
|
|
288
|
+
install_local_var_write(genv, lenv, changes, var_name, type_vtx);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if let Some(arr) = pattern.as_array_pattern_node() {
|
|
293
|
+
process_array_pattern(genv, lenv, changes, source, &arr, predicate_vtx);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if let Some(find) = pattern.as_find_pattern_node() {
|
|
298
|
+
process_find_pattern(genv, lenv, changes, source, &find, predicate_vtx);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if let Some(hash) = pattern.as_hash_pattern_node() {
|
|
303
|
+
process_hash_pattern(genv, lenv, changes, source, &hash, predicate_vtx);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// AlternationPatternNode: 1 | 2 | 3
|
|
308
|
+
if let Some(alt) = pattern.as_alternation_pattern_node() {
|
|
309
|
+
process_pattern(genv, lenv, changes, source, &alt.left(), predicate_vtx);
|
|
310
|
+
process_pattern(genv, lenv, changes, source, &alt.right(), predicate_vtx);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// PinnedVariableNode: ^x
|
|
315
|
+
if let Some(pinned) = pattern.as_pinned_variable_node() {
|
|
316
|
+
install_node(genv, lenv, changes, source, &pinned.variable());
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// PinnedExpressionNode: ^(expr)
|
|
321
|
+
if let Some(pinned) = pattern.as_pinned_expression_node() {
|
|
322
|
+
install_node(genv, lenv, changes, source, &pinned.expression());
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Literal patterns (Integer, String, etc.) - side effects only
|
|
327
|
+
install_node(genv, lenv, changes, source, pattern);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/// Process capture pattern: Integer => x, Api::User => u
|
|
331
|
+
fn process_capture_pattern(
|
|
332
|
+
genv: &mut GlobalEnv,
|
|
333
|
+
lenv: &mut LocalEnv,
|
|
334
|
+
changes: &mut ChangeSet,
|
|
335
|
+
source: &str,
|
|
336
|
+
cap: &CapturePatternNode,
|
|
337
|
+
) {
|
|
338
|
+
let value_node = cap.value();
|
|
339
|
+
let target = cap.target();
|
|
340
|
+
let var_name = bytes_to_name(target.name().as_slice());
|
|
341
|
+
|
|
342
|
+
let type_vtx = if let Some(name) = super::definitions::extract_constant_path(&value_node) {
|
|
343
|
+
genv.new_source(Type::instance(&name))
|
|
344
|
+
} else {
|
|
345
|
+
install_node(genv, lenv, changes, source, &value_node)
|
|
346
|
+
.unwrap_or_else(|| genv.new_vertex())
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
install_local_var_write(genv, lenv, changes, var_name, type_vtx);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// TODO: Remove clone
|
|
353
|
+
fn type_arg_source(genv: &mut GlobalEnv, vtx: VertexId, index: usize) -> Option<VertexId> {
|
|
354
|
+
let source = genv.get_source(vtx)?;
|
|
355
|
+
let ty = match &source.ty {
|
|
356
|
+
Type::Generic { type_args, .. } => type_args.get(index)?.clone(),
|
|
357
|
+
_ => return None,
|
|
358
|
+
};
|
|
359
|
+
Some(genv.new_source(ty))
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/// Process array pattern: [x, y] or [x, *rest]
|
|
363
|
+
fn process_array_pattern(
|
|
364
|
+
genv: &mut GlobalEnv,
|
|
365
|
+
lenv: &mut LocalEnv,
|
|
366
|
+
changes: &mut ChangeSet,
|
|
367
|
+
source: &str,
|
|
368
|
+
arr: &ArrayPatternNode,
|
|
369
|
+
predicate_vtx: Option<VertexId>,
|
|
370
|
+
) {
|
|
371
|
+
let element_vtx = predicate_vtx.and_then(|vtx| type_arg_source(genv, vtx, 0));
|
|
372
|
+
|
|
373
|
+
for elem in &arr.requireds() {
|
|
374
|
+
process_pattern(genv, lenv, changes, source, &elem, element_vtx);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if let Some(target) = arr
|
|
378
|
+
.rest()
|
|
379
|
+
.and_then(|r| r.as_splat_node())
|
|
380
|
+
.and_then(|s| s.expression())
|
|
381
|
+
.and_then(|e| e.as_local_variable_target_node())
|
|
382
|
+
{
|
|
383
|
+
let var_name = bytes_to_name(target.name().as_slice());
|
|
384
|
+
let rest_vtx = predicate_vtx.unwrap_or_else(|| genv.new_source(Type::array_of(Type::Bot)));
|
|
385
|
+
install_local_var_write(genv, lenv, changes, var_name, rest_vtx);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
for elem in &arr.posts() {
|
|
389
|
+
process_pattern(genv, lenv, changes, source, &elem, element_vtx);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/// Process hash pattern: { name:, age: }
|
|
394
|
+
fn process_hash_pattern(
|
|
395
|
+
genv: &mut GlobalEnv,
|
|
396
|
+
lenv: &mut LocalEnv,
|
|
397
|
+
changes: &mut ChangeSet,
|
|
398
|
+
source: &str,
|
|
399
|
+
hash: &HashPatternNode,
|
|
400
|
+
predicate_vtx: Option<VertexId>,
|
|
401
|
+
) {
|
|
402
|
+
let value_vtx = predicate_vtx.and_then(|vtx| type_arg_source(genv, vtx, 1));
|
|
403
|
+
|
|
404
|
+
for elem in &hash.elements() {
|
|
405
|
+
if let Some(assoc) = elem.as_assoc_node() {
|
|
406
|
+
process_pattern(genv, lenv, changes, source, &assoc.value(), value_vtx);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if let Some(target) = hash
|
|
411
|
+
.rest()
|
|
412
|
+
.and_then(|r| r.as_assoc_splat_node())
|
|
413
|
+
.and_then(|s| s.value())
|
|
414
|
+
.and_then(|v| v.as_local_variable_target_node())
|
|
415
|
+
{
|
|
416
|
+
let var_name = bytes_to_name(target.name().as_slice());
|
|
417
|
+
let hash_vtx = genv.new_source(Type::hash());
|
|
418
|
+
install_local_var_write(genv, lenv, changes, var_name, hash_vtx);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/// Process find pattern: [*, x, *]
|
|
423
|
+
fn process_find_pattern(
|
|
424
|
+
genv: &mut GlobalEnv,
|
|
425
|
+
lenv: &mut LocalEnv,
|
|
426
|
+
changes: &mut ChangeSet,
|
|
427
|
+
source: &str,
|
|
428
|
+
find: &FindPatternNode,
|
|
429
|
+
predicate_vtx: Option<VertexId>,
|
|
430
|
+
) {
|
|
431
|
+
let element_vtx = predicate_vtx.and_then(|vtx| type_arg_source(genv, vtx, 0));
|
|
432
|
+
let rest_vtx = predicate_vtx.unwrap_or_else(|| genv.new_source(Type::array_of(Type::Bot)));
|
|
433
|
+
|
|
434
|
+
if let Some(target) = find
|
|
435
|
+
.left()
|
|
436
|
+
.expression()
|
|
437
|
+
.and_then(|e| e.as_local_variable_target_node())
|
|
438
|
+
{
|
|
439
|
+
let var_name = bytes_to_name(target.name().as_slice());
|
|
440
|
+
install_local_var_write(genv, lenv, changes, var_name, rest_vtx);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
for elem in &find.requireds() {
|
|
444
|
+
process_pattern(genv, lenv, changes, source, &elem, element_vtx);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if let Some(target) = find
|
|
448
|
+
.right()
|
|
449
|
+
.as_splat_node()
|
|
450
|
+
.and_then(|s| s.expression())
|
|
451
|
+
.and_then(|e| e.as_local_variable_target_node())
|
|
452
|
+
{
|
|
453
|
+
let var_name = bytes_to_name(target.name().as_slice());
|
|
454
|
+
install_local_var_write(genv, lenv, changes, var_name, rest_vtx);
|
|
455
|
+
}
|
|
456
|
+
}
|