katakata_irb 0.1.3 → 0.1.5

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.
@@ -0,0 +1,248 @@
1
+ require 'set'
2
+ require_relative 'types'
3
+
4
+ module KatakataIrb
5
+ class BaseScope
6
+ SELF = '%self'
7
+ BREAK_RESULT = '%break'
8
+ NEXT_RESULT = '%next'
9
+ RETURN_RESULT = '%return'
10
+ PATTERNMATCH_BREAK = '%match'
11
+ RAISE_BREAK = '%raise'
12
+
13
+ def initialize(binding, self_object)
14
+ @binding, @self_object = binding, self_object
15
+ @cache = { SELF => KatakataIrb::Types.type_from_object(self_object) }
16
+ @local_variables = binding.local_variables.map(&:to_s).to_set
17
+ end
18
+
19
+ def level() = 0
20
+
21
+ def level_of(name)
22
+ has?(name) ? 0 : nil
23
+ end
24
+
25
+ def mutable?() = false
26
+
27
+ def [](name)
28
+ @cache[name] ||= (
29
+ fallback = KatakataIrb::Types::NIL
30
+ case BaseScope.type_by_name name
31
+ when :cvar
32
+ BaseScope.type_of(fallback: fallback) { @self_object.class_variable_get name }
33
+ when :ivar
34
+ BaseScope.type_of(fallback: fallback) { @self_object.instance_variable_get name }
35
+ when :lvar
36
+ BaseScope.type_of(fallback: fallback) { @binding.local_variable_get(name) }
37
+ when :const
38
+ BaseScope.type_of(fallback: fallback) { @binding.eval name }
39
+ end
40
+ )
41
+ end
42
+
43
+ def self_type
44
+ self[SELF]
45
+ end
46
+
47
+ def local_variables
48
+ @local_variables.to_a
49
+ end
50
+
51
+ def self.type_of(fallback: KatakataIrb::Types::OBJECT)
52
+ begin
53
+ KatakataIrb::Types.type_from_object yield
54
+ rescue
55
+ fallback
56
+ end
57
+ end
58
+
59
+ def self.type_by_name(name)
60
+ if name.start_with? '@@'
61
+ :cvar
62
+ elsif name.start_with? '@'
63
+ :ivar
64
+ elsif name.start_with? '$'
65
+ :gvar
66
+ elsif name.start_with? '%'
67
+ :internal
68
+ elsif name[0].downcase != name[0]
69
+ :const
70
+ else
71
+ :lvar
72
+ end
73
+ end
74
+
75
+ def has?(name)
76
+ case BaseScope.type_by_name name
77
+ when :lvar
78
+ @local_variables.include? name
79
+ when :const
80
+ @binding.eval("#{name};true") rescue false
81
+ when :gvar, :cvar, :ivar
82
+ true
83
+ when :internal
84
+ true
85
+ end
86
+ end
87
+ end
88
+
89
+ class Scope < BaseScope
90
+ attr_reader :parent, :jump_branches, :mergeable_changes, :level
91
+
92
+ def self.from_binding(binding) = new(BaseScope.new(binding, binding.eval('self')))
93
+
94
+ def initialize(parent, table = {}, trace_cvar: true, trace_ivar: true, trace_lvar: true, passthrough: false)
95
+ @table = table
96
+ @parent = parent
97
+ @level = parent.level + (passthrough ? 0 : 1)
98
+ @trace_cvar = trace_cvar
99
+ @trace_ivar = trace_ivar
100
+ @trace_lvar = trace_lvar
101
+ @passthrough = passthrough
102
+ @terminated = false
103
+ @jump_branches = []
104
+ @mergeable_changes = @changes = table.transform_values { [level, _1] }
105
+ end
106
+
107
+ def mutable? = true
108
+
109
+ def terminated?
110
+ @terminated
111
+ end
112
+
113
+ def terminate_with(type, value)
114
+ return if terminated?
115
+ store_jump type, value, @mergeable_changes
116
+ terminate
117
+ end
118
+
119
+ def store_jump(type, value, changes)
120
+ return if terminated?
121
+ if has_own?(type)
122
+ changes[type] = [level, value]
123
+ @jump_branches << changes
124
+ elsif @parent.mutable?
125
+ @parent.store_jump(type, value, changes)
126
+ end
127
+ end
128
+
129
+ def terminate
130
+ return if terminated?
131
+ @terminated = true
132
+ @changes = @mergeable_changes.dup
133
+ end
134
+
135
+ def branch_table_clone() = @tables.last.dup
136
+
137
+ def trace?(name)
138
+ return false unless @parent
139
+ type = BaseScope.type_by_name(name)
140
+ type == :cvar ? @trace_cvar : type == :ivar ? @trace_ivar : type == :lvar ? @trace_lvar : true
141
+ end
142
+
143
+ def level_of(name)
144
+ variable_level, = @changes[name]
145
+ variable_level || parent.level_of(name)
146
+ end
147
+
148
+ def [](name)
149
+ level, value = @changes[name]
150
+ if level
151
+ value
152
+ elsif trace? name
153
+ @parent[name] if trace? name
154
+ end
155
+ end
156
+
157
+ def []=(name, value)
158
+ variable_level = level_of(name) || level
159
+ @changes[name] = [variable_level, value]
160
+ end
161
+
162
+ def self_type
163
+ self[SELF]
164
+ end
165
+
166
+ def local_variables
167
+ lvar_keys = @changes.keys.select do |name|
168
+ BaseScope.type_by_name(name) == :lvar
169
+ end
170
+ lvar_keys |= @parent.local_variables if @trace_lvar
171
+ lvar_keys
172
+ end
173
+
174
+ def merge_jumps
175
+ if terminated?
176
+ @terminated = false
177
+ @changes = @mergeable_changes
178
+ merge @jump_branches
179
+ @terminated = true
180
+ else
181
+ merge [*@jump_branches, {}]
182
+ end
183
+ end
184
+
185
+ def conditional(&block)
186
+ run_branches(block, ->(_s) {}).first || KatakataIrb::Types::NIL
187
+ end
188
+
189
+ def never(&block)
190
+ block.call Scope.new(self, { BREAK_RESULT => nil, NEXT_RESULT => nil, PATTERNMATCH_BREAK => nil, RETURN_RESULT => nil, RAISE_BREAK => nil }, passthrough: true)
191
+ end
192
+
193
+ def run(*args, **option)
194
+ scope = Scope.new(self, *args, **option)
195
+ yield scope
196
+ merge_jumps
197
+ update scope
198
+ end
199
+
200
+ def run_branches(*blocks)
201
+ results = []
202
+ branches = []
203
+ blocks.each do |block|
204
+ scope = Scope.new self, passthrough: true
205
+ result = block.call scope
206
+ next if scope.terminated?
207
+ results << result
208
+ branches << scope.mergeable_changes
209
+ end
210
+ terminate if branches.empty?
211
+ merge branches
212
+ results
213
+ end
214
+
215
+ def has?(name)
216
+ has_own?(name) || (trace?(name) && @parent.has?(name))
217
+ end
218
+
219
+ def has_own?(name)
220
+ @changes.key? name
221
+ end
222
+
223
+ def update(child_scope)
224
+ current_level = level
225
+ child_scope.mergeable_changes.each do |name, (level, value)|
226
+ self[name] = value if level <= current_level
227
+ end
228
+ end
229
+
230
+ protected
231
+
232
+ def merge(branches)
233
+ current_level = level
234
+ merge = {}
235
+ branches.each do |changes|
236
+ changes.each do |name, (level, value)|
237
+ next if current_level < level
238
+ (merge[name] ||= []) << value
239
+ end
240
+ end
241
+ merge.each do |name, values|
242
+ values << self[name] unless values.size == branches.size
243
+ values.compact!
244
+ self[name] = KatakataIrb::Types::UnionType[*values.compact] unless values.empty?
245
+ end
246
+ end
247
+ end
248
+ end