katakata_irb 0.1.3 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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