katakata_irb 0.1.4 → 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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/katakata_irb/completor.rb +86 -11
- data/lib/katakata_irb/nesting_parser.rb +34 -3
- data/lib/katakata_irb/scope.rb +248 -0
- data/lib/katakata_irb/type_simulator.rb +135 -329
- data/lib/katakata_irb/types.rb +93 -13
- data/lib/katakata_irb/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 882c0834b6d52916911b26b82083e1100491dd24cafdb1c948c4c1795fe03d7e
|
|
4
|
+
data.tar.gz: 2250eaec5d0b1cabc236f238249e132206b62e70ada1c16429567b680ace2093
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 60f3c4de13636f2948e740b3a57b6091c1f193ae7fa259d665c69a404833ef413b90aca3da7360f97be2d7a669e9a0d5fc101ff172890c4f0714c9eb63db8dd4
|
|
7
|
+
data.tar.gz: eb496c54819de4f1e035e261a6afffcc13d2809b316428602567614dbc5671df5ead97fb4a0696afbb34a7ade9ce8a2b3e9579ebad4ddd006d9a32b120b3e05d
|
data/Gemfile.lock
CHANGED
|
@@ -27,18 +27,18 @@ module KatakataIrb::Completor
|
|
|
27
27
|
((self_call ? type.all_methods: type.methods).map(&:to_s) - HIDDEN_METHODS) | type.constants
|
|
28
28
|
in [:const, type, name]
|
|
29
29
|
type.constants
|
|
30
|
-
in [:ivar, name, _scope]
|
|
30
|
+
in [:ivar, name, *_scope]
|
|
31
31
|
# TODO: scope
|
|
32
32
|
ivars = binding.eval('self').instance_variables rescue []
|
|
33
33
|
cvars = (binding.eval('self').class_variables rescue nil) if name == '@'
|
|
34
34
|
ivars | (cvars || [])
|
|
35
|
-
in [:cvar, name, _scope]
|
|
35
|
+
in [:cvar, name, *_scope]
|
|
36
36
|
# TODO: scope
|
|
37
37
|
binding.eval('self').class_variables rescue []
|
|
38
38
|
in [:gvar, name]
|
|
39
39
|
global_variables
|
|
40
40
|
in [:symbol, name]
|
|
41
|
-
Symbol.all_symbols
|
|
41
|
+
Symbol.all_symbols.map { _1.inspect[1..] }
|
|
42
42
|
in [:call, type, name, self_call]
|
|
43
43
|
(self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS
|
|
44
44
|
in [:lvar_or_method, name, scope]
|
|
@@ -67,15 +67,15 @@ module KatakataIrb::Completor
|
|
|
67
67
|
method_doc = -> type do
|
|
68
68
|
type = type.types.find { _1.all_methods.include? name.to_sym }
|
|
69
69
|
if type in KatakataIrb::Types::SingletonType
|
|
70
|
-
"#{type.module_or_class
|
|
70
|
+
"#{KatakataIrb::Types.class_name_of(type.module_or_class)}.#{name}"
|
|
71
71
|
elsif type in KatakataIrb::Types::InstanceType
|
|
72
|
-
"#{type.klass
|
|
72
|
+
"#{KatakataIrb::Types.class_name_of(type.klass)}##{name}"
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
75
|
call_or_const_doc = -> type do
|
|
76
76
|
if name =~ /\A[A-Z]/
|
|
77
77
|
type = type.types.grep(KatakataIrb::Types::SingletonType).find { _1.module_or_class.const_defined?(name) }
|
|
78
|
-
type.module_or_class == Object ? name : "#{type.module_or_class
|
|
78
|
+
type.module_or_class == Object ? name : "#{KatakataIrb::Types.class_name_of(type.module_or_class)}::#{name}" if type
|
|
79
79
|
else
|
|
80
80
|
method_doc.call(type)
|
|
81
81
|
end
|
|
@@ -97,9 +97,50 @@ module KatakataIrb::Completor
|
|
|
97
97
|
end
|
|
98
98
|
end
|
|
99
99
|
}
|
|
100
|
+
setup_type_dialog
|
|
100
101
|
end
|
|
101
102
|
|
|
102
|
-
def self.
|
|
103
|
+
def self.setup_type_dialog
|
|
104
|
+
type_dialog_proc = -> {
|
|
105
|
+
return if just_cursor_moving && completion_journey_data
|
|
106
|
+
cursor_pos_to_render, _result, pointer, autocomplete_dialog = context.last(4)
|
|
107
|
+
return unless cursor_pos_to_render && autocomplete_dialog&.width && pointer.nil?
|
|
108
|
+
receiver_type = (
|
|
109
|
+
case KatakataIrb::Completor.prev_analyze_result
|
|
110
|
+
in [:call_or_const, type, name, _self_call] if name.empty?
|
|
111
|
+
type
|
|
112
|
+
in [:call, type, name, _self_call] if name.empty?
|
|
113
|
+
type
|
|
114
|
+
else
|
|
115
|
+
return
|
|
116
|
+
end
|
|
117
|
+
)
|
|
118
|
+
return unless receiver_type
|
|
119
|
+
types = type.types
|
|
120
|
+
contents = types.filter_map do |type|
|
|
121
|
+
case type
|
|
122
|
+
when KatakataIrb::Types::InstanceType
|
|
123
|
+
KatakataIrb::Types.class_name_of type.klass
|
|
124
|
+
when KatakataIrb::Types::SingletonType
|
|
125
|
+
module_name = KatakataIrb::Types.class_name_of type.module_or_class
|
|
126
|
+
"#{module_name}.itself" if module_name
|
|
127
|
+
end
|
|
128
|
+
end.uniq
|
|
129
|
+
return if contents.empty?
|
|
130
|
+
|
|
131
|
+
width = contents.map { Reline::Unicode.calculate_width _1 }.max
|
|
132
|
+
x = cursor_pos_to_render.x + autocomplete_dialog.width
|
|
133
|
+
y = cursor_pos_to_render.y
|
|
134
|
+
Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: 44, fg_color: 37)
|
|
135
|
+
}
|
|
136
|
+
Reline.add_dialog_proc(:show_type, type_dialog_proc, Reline::DEFAULT_DIALOG_CONTEXT)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def self.empty_binding()
|
|
140
|
+
Kernel.binding
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def self.analyze(code, binding = empty_binding)
|
|
103
144
|
lvars_code = binding.local_variables.map do |name|
|
|
104
145
|
"#{name}="
|
|
105
146
|
end.join + "nil;\n"
|
|
@@ -124,10 +165,20 @@ module KatakataIrb::Completor
|
|
|
124
165
|
t.tok
|
|
125
166
|
when /\A<<[~-]?(?:"(?<s>.+)"|'(?<s>.+)'|(?<s>.+))/
|
|
126
167
|
$/ + ($1 || $2 || $3) + $/
|
|
168
|
+
when ':"', ":'", ':'
|
|
169
|
+
t.tok[1]
|
|
170
|
+
when '?'
|
|
171
|
+
# ternary operator
|
|
172
|
+
' : value'
|
|
173
|
+
when '|'
|
|
174
|
+
# block args
|
|
175
|
+
'|'
|
|
127
176
|
else
|
|
128
177
|
$/ + 'end'
|
|
129
178
|
end
|
|
130
179
|
end
|
|
180
|
+
# remove error tokens
|
|
181
|
+
tokens.pop while tokens&.last&.tok&.empty?
|
|
131
182
|
|
|
132
183
|
case tokens.last
|
|
133
184
|
in { event: :on_ignored_by_ripper, tok: '.' }
|
|
@@ -136,6 +187,9 @@ module KatakataIrb::Completor
|
|
|
136
187
|
in { dot: true }
|
|
137
188
|
suffix = 'method'
|
|
138
189
|
name = ''
|
|
190
|
+
in { event: :on_symbeg }
|
|
191
|
+
suffix = 'symbol'
|
|
192
|
+
name = ''
|
|
139
193
|
in { event: :on_ident | :on_kw, tok: }
|
|
140
194
|
return unless code.delete_suffix! tok
|
|
141
195
|
suffix = 'method'
|
|
@@ -148,10 +202,21 @@ module KatakataIrb::Completor
|
|
|
148
202
|
return unless code.delete_suffix! tok
|
|
149
203
|
suffix = 'string'
|
|
150
204
|
name = tok.rstrip
|
|
205
|
+
in { event: :on_gvar, tok: }
|
|
206
|
+
return unless code.delete_suffix! tok
|
|
207
|
+
suffix = '$gvar'
|
|
208
|
+
name = tok
|
|
209
|
+
in { event: :on_ivar, tok: }
|
|
210
|
+
return unless code.delete_suffix! tok
|
|
211
|
+
suffix = '@ivar'
|
|
212
|
+
name = tok
|
|
213
|
+
in { event: :on_cvar, tok: }
|
|
214
|
+
return unless code.delete_suffix! tok
|
|
215
|
+
suffix = '@@cvar'
|
|
216
|
+
name = tok
|
|
151
217
|
else
|
|
152
218
|
return
|
|
153
219
|
end
|
|
154
|
-
|
|
155
220
|
sexp = Ripper.sexp code + suffix + closings.reverse.join
|
|
156
221
|
lines = code.lines
|
|
157
222
|
line_no = lines.size
|
|
@@ -175,16 +240,26 @@ module KatakataIrb::Completor
|
|
|
175
240
|
return [:cvar, name] if icvar_available
|
|
176
241
|
end
|
|
177
242
|
return unless expression
|
|
243
|
+
calculate_scope = -> { KatakataIrb::TypeSimulator.calculate_binding_scope binding, parents, expression }
|
|
244
|
+
calculate_receiver = -> receiver { KatakataIrb::TypeSimulator.calculate_receiver binding, parents, receiver }
|
|
245
|
+
|
|
178
246
|
if (target in [:@tstring_content,]) && (parents[-4] in [:command, [:@ident, 'require' | 'require_relative' => require_method,],])
|
|
247
|
+
# `require 'target'`
|
|
179
248
|
return [require_method.to_sym, name.rstrip]
|
|
180
249
|
end
|
|
181
|
-
|
|
182
|
-
|
|
250
|
+
if (target in [:@ident,]) && (expression in [:symbol,]) && (parents[-2] in [:args_add_block, Array => args, [:symbol_literal, ^expression]])
|
|
251
|
+
# `method(&:target)`
|
|
252
|
+
receiver_ref = [:var_ref, [:@ident, '_1', [0, 0]]]
|
|
253
|
+
block_statements = [receiver_ref]
|
|
254
|
+
parents[-1] = parents[-2][-1] = [:brace_block, nil, block_statements]
|
|
255
|
+
parents << block_statements
|
|
256
|
+
return [:call, calculate_receiver.call(receiver_ref), name, false]
|
|
257
|
+
end
|
|
183
258
|
case expression
|
|
184
259
|
in [:vcall | :var_ref, [:@ident,]]
|
|
185
260
|
[:lvar_or_method, name, calculate_scope.call]
|
|
186
261
|
in [:symbol, [:@ident | :@const | :@op | :@kw,]]
|
|
187
|
-
[:symbol, name]
|
|
262
|
+
[:symbol, name] unless name.empty?
|
|
188
263
|
in [:var_ref | :const_ref, [:@const,]]
|
|
189
264
|
# TODO
|
|
190
265
|
[:const, KatakataIrb::Types::SingletonType.new(Object), name]
|
|
@@ -26,6 +26,8 @@ module KatakataIrb::NestingParser
|
|
|
26
26
|
interpolated
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
IGNOREABLE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
|
|
30
|
+
|
|
29
31
|
def self.parse(tokens)
|
|
30
32
|
opens = []
|
|
31
33
|
pending_heredocs = []
|
|
@@ -42,7 +44,7 @@ module KatakataIrb::NestingParser
|
|
|
42
44
|
when :in_lambda_head
|
|
43
45
|
opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do')
|
|
44
46
|
when :in_method_head
|
|
45
|
-
unless
|
|
47
|
+
unless IGNOREABLE_TOKENS.include?(t.event)
|
|
46
48
|
next_args = []
|
|
47
49
|
body = nil
|
|
48
50
|
if args.include?(:receiver)
|
|
@@ -127,13 +129,27 @@ module KatakataIrb::NestingParser
|
|
|
127
129
|
skip = true if t.event == :on_kw && t.tok == 'do'
|
|
128
130
|
opens[-1] = [last_tok, nil]
|
|
129
131
|
end
|
|
132
|
+
when :in_block_head
|
|
133
|
+
if t.event == :on_op && t.tok == '|'
|
|
134
|
+
opens[-1] = [last_tok, nil]
|
|
135
|
+
opens << [t, :in_block_args]
|
|
136
|
+
elsif !IGNOREABLE_TOKENS.include?(t.event)
|
|
137
|
+
opens[-1] = [last_tok, nil]
|
|
138
|
+
end
|
|
139
|
+
when :in_block_args
|
|
140
|
+
if t.event == :on_op && t.tok == '|' && t.state.allbits?(Ripper::EXPR_BEG)
|
|
141
|
+
opens.pop
|
|
142
|
+
skip = true
|
|
143
|
+
end
|
|
130
144
|
end
|
|
131
145
|
|
|
132
146
|
unless skip
|
|
133
147
|
case t.event
|
|
134
148
|
when :on_kw
|
|
135
149
|
case t.tok
|
|
136
|
-
when '
|
|
150
|
+
when 'do'
|
|
151
|
+
opens << [t, :in_block_head]
|
|
152
|
+
when 'begin', 'class', 'module', 'case'
|
|
137
153
|
opens << [t, nil]
|
|
138
154
|
when 'end'
|
|
139
155
|
opens.pop
|
|
@@ -163,9 +179,15 @@ module KatakataIrb::NestingParser
|
|
|
163
179
|
opens << [t, nil]
|
|
164
180
|
end
|
|
165
181
|
end
|
|
182
|
+
when :on_lbrace
|
|
183
|
+
if t.state.allbits?(Ripper::EXPR_LABEL)
|
|
184
|
+
opens << [t, nil]
|
|
185
|
+
else
|
|
186
|
+
opens << [t, :in_block_head]
|
|
187
|
+
end
|
|
166
188
|
when :on_tlambda
|
|
167
189
|
opens << [t, :in_lambda_head]
|
|
168
|
-
when :on_lparen, :on_lbracket, :
|
|
190
|
+
when :on_lparen, :on_lbracket, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg
|
|
169
191
|
opens << [t, nil]
|
|
170
192
|
when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end
|
|
171
193
|
opens.pop
|
|
@@ -179,6 +201,15 @@ module KatakataIrb::NestingParser
|
|
|
179
201
|
opens << [t, nil]
|
|
180
202
|
when :on_tstring_end, :on_regexp_end, :on_label_end
|
|
181
203
|
opens.pop
|
|
204
|
+
when :on_op
|
|
205
|
+
case t.tok
|
|
206
|
+
when '?'
|
|
207
|
+
# opening of `cond ? value : value``
|
|
208
|
+
opens << [t, nil]
|
|
209
|
+
when ':'
|
|
210
|
+
# closing of `cond ? value : value``
|
|
211
|
+
opens.pop
|
|
212
|
+
end
|
|
182
213
|
when :on_symbeg
|
|
183
214
|
if t.tok == ':'
|
|
184
215
|
opens << [t, :in_unquoted_symbol]
|
|
@@ -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
|