katakata_irb 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07b0eb3288cc4eec15c83de9bd27948379bca2c1dfa233104cba59dff6826c7c
4
- data.tar.gz: 20d4feb90c4df7e10554e045addfb242af50205dd0b187635a81ac3bc5b073b2
3
+ metadata.gz: 5d3d6ff924cd8fa8f05b097253525517a988c377d59e63ed1e5c6e5867cfde88
4
+ data.tar.gz: af6afd4a833e2b9bfd08a05877db80e45faadcac845be5148b53e3266d5539d8
5
5
  SHA512:
6
- metadata.gz: 7a376d7deb729f3a2e2f3b18041e89606d668e331ece73c32d5b7ae9ff6b8d87bffa89f4ab3d1dc66c8354b3af2ad733bee211868e01bd8814055f3cc72fe83b
7
- data.tar.gz: 50eaa7061da8609623f1fccc5d0fbe12685f888d7a565eede06e19993b435aa766ce6458668fa787369bef62ce5601b6dd9c4118fa534457c89341881367722d
6
+ metadata.gz: 35b76a766b99913226c5888b00efcc1c88c35ecb1bbb38338cf69a47772153caecb204bf5cad0a4f7affb475a179b0bd084ecd673c793260165731eef88a258b
7
+ data.tar.gz: e2dc520f4d5fe10a95795d5cbcdb784289e674c415a8eddea2c06883262f02847a5fd5b4fb7dc83d219e8a2add95e5fb5ed7c3d7000b4c83ea539825d50c5f20
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- katakata_irb (0.1.0)
4
+ katakata_irb (0.1.2)
5
5
  rbs
6
6
 
7
7
  GEM
data/bin/console CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  require_relative '../lib/katakata_irb'
3
3
  require_relative '../lib/katakata_irb/reline_patch'
4
- KatakataIrb::RelinePatch.require_patched_reline
5
4
  KatakataIrb.log_output = STDERR
5
+ KatakataIrb::RelinePatch.require_patched_reline
6
6
  require 'bundler/setup'
7
7
  require 'katakata_irb'
8
8
  require 'katakata_irb/ruby_lex_patch'
data/exe/kirb CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ require_relative '../lib/katakata_irb'
3
+ KatakataIrb.log_output = STDERR if ARGV.delete '--debug-output'
2
4
  unless ARGV.delete '--without-patch'
3
5
  require_relative '../lib/katakata_irb/reline_patch'
4
6
  KatakataIrb::RelinePatch.require_patched_reline
@@ -6,5 +8,4 @@ unless ARGV.delete '--without-patch'
6
8
  KatakataIrb::RubyLexPatch.patch_to_ruby_lex
7
9
  end
8
10
  require 'katakata_irb'
9
- KatakataIrb.log_output = STDERR if ARGV.delete '--debug-output'
10
11
  KatakataIrb.repl
@@ -51,6 +51,11 @@ module KatakataIrb::Completor
51
51
  end
52
52
  IRB::InputCompletor::CompletionProc.define_singleton_method :call do |*args|
53
53
  completion_proc.call(*args)
54
+ rescue => e
55
+ $error = e
56
+ KatakataIrb.log_puts
57
+ KatakataIrb.log_puts "#{e.inspect} stored to $error"
58
+ KatakataIrb.log_puts
54
59
  end
55
60
  end
56
61
 
@@ -61,25 +66,26 @@ module KatakataIrb::Completor
61
66
  code = lvars_code + code
62
67
  tokens = RubyLex.ripper_lex_without_warning code
63
68
  tokens = KatakataIrb::TRex.interpolate_ripper_ignored_tokens code, tokens
64
- last_opens, unclosed_heredocs = KatakataIrb::TRex.parse(tokens)
65
- closings = (last_opens + unclosed_heredocs).map do |t,|
69
+ last_opens = KatakataIrb::TRex.parse(tokens)
70
+ closings = last_opens.map do |t|
66
71
  case t.tok
67
72
  when /\A%.[<>]\z/
68
- '>'
73
+ $/ + '>'
69
74
  when '{', '#{', /\A%.?[{}]\z/
70
- '}'
75
+ $/ + '}'
71
76
  when '(', /\A%.?[()]\z/
77
+ # do not insert \n before closing paren. workaround to avoid syntax error of "a in ^(b\n)"
72
78
  ')'
73
79
  when '[', /\A%.?[\[\]]\z/
74
- ']'
80
+ $/ + ']'
75
81
  when /\A%.?(.)\z/
76
82
  $1
77
83
  when '"', "'", '/', '`'
78
84
  t.tok
79
- when /\A<<(?:"(?<s>.+)"|'(?<s>.+)'|(?<s>.+))/
80
- $3
85
+ when /\A<<[~-]?(?:"(?<s>.+)"|'(?<s>.+)'|(?<s>.+))/
86
+ $/ + ($1 || $2 || $3) + $/
81
87
  else
82
- 'end'
88
+ $/ + 'end'
83
89
  end
84
90
  end
85
91
 
@@ -107,8 +113,7 @@ module KatakataIrb::Completor
107
113
  return
108
114
  end
109
115
 
110
- closings = $/ + closings.reverse.join($/)
111
- sexp = Ripper.sexp code + suffix + closings
116
+ sexp = Ripper.sexp code + suffix + closings.reverse.join
112
117
  lines = code.lines
113
118
  line_no = lines.size
114
119
  col = lines.last.bytesize
@@ -117,14 +122,14 @@ module KatakataIrb::Completor
117
122
  col = 0
118
123
  end
119
124
 
120
- if sexp in [:program, [lvars_exp, *rest_statements]]
125
+ if sexp in [:program, [_lvars_exp, *rest_statements]]
121
126
  sexp = [:program, rest_statements]
122
127
  end
123
128
 
124
129
  *parents, expression, target = find_target sexp, line_no, col
125
130
  in_class_module = parents&.any? { _1 in [:class | :module,] }
126
131
  icvar_available = !in_class_module
127
- return unless target in [type, String, [Integer, Integer]]
132
+ return unless target in [_type, String, [Integer, Integer]]
128
133
  if target in [:@ivar,]
129
134
  return [:ivar, name] if icvar_available
130
135
  elsif target in [:@cvar,]
@@ -16,14 +16,17 @@ module KatakataIrb::RelinePatch
16
16
  diff.split(/^@@.+\n/).drop(1).map(&:lines).each do |lines|
17
17
  target = lines.reject { _1[0] == '+' }.map { _1[1..] }.join
18
18
  replace = lines.reject { _1[0] == '-' }.map { _1[1..] }.join
19
- raise unless code.include? target
20
- code.sub! target, replace
19
+ if code.include? target
20
+ code = code.sub target, replace
21
+ elsif !code.include?(replace)
22
+ raise
23
+ end
21
24
  end
22
25
  current_patched[path] = code
23
26
  end
24
27
  patched.update current_patched
25
28
  rescue
26
- puts "Failed to apply katakata_irb/reline_patches/#{patch_name}.patch to reline"
29
+ KatakataIrb.log_puts "Failed to apply katakata_irb/reline_patches/#{patch_name}.patch to reline"
27
30
  end
28
31
 
29
32
  RelinePatchIseqLoader.define_method :load_iseq do |fname|
@@ -3,7 +3,7 @@ require_relative 'trex'
3
3
 
4
4
  module KatakataIrb::RubyLexPatch
5
5
  def self.patch_to_ruby_lex
6
- (RubyLex.instance_methods(false) - [:initialize_input, :set_prompt, :process_continue, :check_code_block]).each { RubyLex.remove_method _1 }
6
+ (RubyLex.instance_methods(false) - [:set_prompt, :process_continue, :check_code_block]).each { RubyLex.remove_method _1 }
7
7
  RubyLex.prepend self
8
8
  end
9
9
 
@@ -12,20 +12,20 @@ module KatakataIrb::RubyLexPatch
12
12
  KatakataIrb::TRex.interpolate_ripper_ignored_tokens(code, incomplete_tokens)
13
13
  end
14
14
 
15
- def calc_nesting_depth(tokens)
15
+ def calc_nesting_depth(opens)
16
16
  indent_level = 0
17
17
  nesting_level = 0
18
- tokens.each_with_index do |t, index|
18
+ opens.each_with_index do |t, index|
19
19
  case t.event
20
20
  when :on_heredoc_beg
21
- if tokens[index + 1]&.event != :on_heredoc_beg
21
+ if opens[index + 1]&.event != :on_heredoc_beg
22
22
  if t.tok.start_with?('<<~')
23
23
  indent_level += 1
24
24
  else
25
25
  indent_level = 0
26
26
  end
27
27
  end
28
- when :on_tstring_beg, :on_regexp_beg
28
+ when :on_tstring_beg, :on_regexp_beg, :on_symbeg
29
29
  indent_level += 1 if t.tok[0] == '%'
30
30
  when :on_embdoc_beg
31
31
  indent_level = 0
@@ -38,18 +38,18 @@ module KatakataIrb::RubyLexPatch
38
38
  end
39
39
 
40
40
  def process_indent_level(tokens)
41
- opens, heredocs = KatakataIrb::TRex.parse(tokens)
42
- indent, _nesting = calc_nesting_depth(opens.map(&:first) + heredocs)
41
+ opens = KatakataIrb::TRex.parse(tokens)
42
+ indent, _nesting = calc_nesting_depth(opens)
43
43
  indent * 2
44
44
  end
45
45
 
46
46
  def check_corresponding_token_depth(tokens, line_index)
47
- lines, = KatakataIrb::TRex.parse_line(tokens)
48
- result = lines[line_index]
47
+ line_results = KatakataIrb::TRex.parse_line(tokens)
48
+ result = line_results[line_index]
49
49
  return unless result
50
50
  _tokens, prev, opens, min_depth = result
51
- depth, = calc_nesting_depth(opens.take(min_depth).map(&:first))
52
- prev_depth, = calc_nesting_depth(prev.map(&:first))
51
+ depth, = calc_nesting_depth(opens.take(min_depth))
52
+ prev_depth, = calc_nesting_depth(prev)
53
53
  depth * 2 if depth < prev_depth
54
54
  end
55
55
 
@@ -62,9 +62,9 @@ module KatakataIrb::RubyLexPatch
62
62
  ?'
63
63
  when ?", /^<</, /^%.$/, /^%Q.$/
64
64
  ?"
65
- when ":'", ':"', ':', /^%s$/
65
+ when ":'", ':"', ':', /^%s.$/
66
66
  ':'
67
- when /^%[iwIW]$/
67
+ when /^%[iwIW].$/
68
68
  ']'
69
69
  when '/', /^%r.$/
70
70
  '/'
@@ -72,14 +72,28 @@ module KatakataIrb::RubyLexPatch
72
72
  end
73
73
 
74
74
  def check_termination_in_prev_line(code, context: nil)
75
- *lines, last_line = code.lines
76
- last_line if lines.any? && check_termination(lines.join, context: context)
75
+ tokens = self.class.ripper_lex_without_warning(code, context: context)
76
+ last_newline_index = tokens.rindex { |t| t.tok.include?("\n") }
77
+ index = (0...last_newline_index).reverse_each.find { |i| tokens[i].tok.include?("\n") }
78
+ return false unless index
79
+
80
+ last_line_tokens = tokens[(index + 1)..(tokens.size - 1)]
81
+ first_token = last_line_tokens.find do |t|
82
+ ![:on_sp, :on_ignored_sp, :on_comment].include?(t.event)
83
+ end
84
+
85
+ if first_token && first_token.state != Ripper::EXPR_DOT
86
+ tokens_without_last_line = tokens[0..index]
87
+ if check_termination(tokens_without_last_line)
88
+ return last_line_tokens.map(&:tok).join
89
+ end
90
+ end
91
+ false
77
92
  end
78
93
 
79
- def check_termination(code, context: nil)
80
- tokens = KatakataIrb::RubyLexPatch.complete_tokens(code, context: context)
81
- opens, heredocs = KatakataIrb::TRex.parse(tokens)
82
- opens.empty? && heredocs.empty? && !process_continue(tokens)
94
+ def check_termination(tokens)
95
+ opens = KatakataIrb::TRex.parse(tokens)
96
+ opens.empty? && !process_continue(tokens)
83
97
  end
84
98
 
85
99
  def set_input(io, p = nil, context: nil, &block)
@@ -99,7 +113,8 @@ module KatakataIrb::RubyLexPatch
99
113
  false
100
114
  end
101
115
  else
102
- check_termination(code, context: context)
116
+ tokens = KatakataIrb::RubyLexPatch.complete_tokens(code, context: context)
117
+ check_termination(tokens)
103
118
  end
104
119
  end
105
120
  end
@@ -108,10 +123,14 @@ module KatakataIrb::RubyLexPatch
108
123
  lines << '' if lines.empty?
109
124
  code = lines.map{ |l| l + "\n" }.join
110
125
  tokens = KatakataIrb::RubyLexPatch.complete_tokens code, context: context
111
- lines, _unclosed_heredocs = KatakataIrb::TRex.parse_line(tokens)
126
+ line_results = KatakataIrb::TRex.parse_line(tokens)
112
127
  continue = false
113
- lines.map.with_index do |(line, _prev_opens, next_opens), line_num_offset|
114
- unless (c = process_continue(line.map(&:first))).nil?
128
+ tokens_until_line = []
129
+ line_results.map.with_index do |(line_tokens, _prev_opens, next_opens), line_num_offset|
130
+ line_tokens.each do |token, _s|
131
+ tokens_until_line << token if token != tokens_until_line.last
132
+ end
133
+ unless (c = process_continue(tokens_until_line)).nil?
115
134
  continue = c
116
135
  end
117
136
  prompt next_opens, continue, line_num_offset
@@ -146,8 +165,8 @@ module KatakataIrb::RubyLexPatch
146
165
  end
147
166
 
148
167
  def prompt(opens, continue, line_num_offset)
149
- ltype = ltype_from_open_tokens opens.map(&:first)
150
- _indent, nesting_level = calc_nesting_depth opens.map(&:first)
168
+ ltype = ltype_from_open_tokens(opens)
169
+ _indent, nesting_level = calc_nesting_depth(opens)
151
170
  @prompt.call(ltype, nesting_level, opens.any? || continue, @line_no + line_num_offset)
152
171
  end
153
172
 
@@ -166,10 +185,13 @@ module KatakataIrb::RubyLexPatch
166
185
  store_prompt_to_irb [], false, 0
167
186
  loop do
168
187
  l = @input.call
169
- next if l.nil?
188
+ unless l
189
+ return if line.empty?
190
+ next
191
+ end
170
192
  line << l
171
193
  tokens = KatakataIrb::RubyLexPatch.complete_tokens(line, context: context)
172
- _line, _prev_opens, next_opens = KatakataIrb::TRex.parse_line(tokens).first.last
194
+ _line_tokens, _prev_opens, next_opens = KatakataIrb::TRex.parse_line(tokens).last
173
195
  return line if next_opens.empty?
174
196
  line_offset += 1
175
197
  store_prompt_to_irb next_opens, true, line_offset
@@ -178,7 +200,6 @@ module KatakataIrb::RubyLexPatch
178
200
  end
179
201
 
180
202
  def each_top_level_statement(context = nil)
181
- initialize_input
182
203
  loop do
183
204
  begin
184
205
  line = readmultiline(context)
@@ -188,9 +209,7 @@ module KatakataIrb::RubyLexPatch
188
209
  yield line, @line_no
189
210
  end
190
211
  @line_no += line.count("\n")
191
- raise RubyLex::TerminateLineInput if @io.eof?
192
212
  rescue RubyLex::TerminateLineInput
193
- initialize_input
194
213
  end
195
214
  end
196
215
  end
@@ -176,7 +176,7 @@ module KatakataIrb::TRex
176
176
  end
177
177
  yield t, index, opens if block_given?
178
178
  end
179
- [opens, pending_heredocs.reverse]
179
+ opens.map(&:first) + pending_heredocs.reverse
180
180
  end
181
181
 
182
182
  def self.parse_line(tokens)
@@ -184,14 +184,14 @@ module KatakataIrb::TRex
184
184
  prev_opens = []
185
185
  min_depth = 0
186
186
  output = []
187
- last_opens, unclosed_heredocs = KatakataIrb::TRex.parse(tokens) do |t, _index, opens|
187
+ last_opens = KatakataIrb::TRex.parse(tokens) do |t, _index, opens|
188
188
  depth = t == opens.last&.first ? opens.size - 1 : opens.size
189
189
  min_depth = depth if depth < min_depth
190
190
  if t.tok.include? "\n"
191
191
  t.tok.each_line do |line|
192
192
  line_tokens << [t, line]
193
193
  next if line[-1] != "\n"
194
- next_opens = opens.dup
194
+ next_opens = opens.map(&:first)
195
195
  output << [line_tokens, prev_opens, next_opens, min_depth]
196
196
  prev_opens = next_opens
197
197
  min_depth = prev_opens.size
@@ -202,6 +202,6 @@ module KatakataIrb::TRex
202
202
  end
203
203
  end
204
204
  output << [line_tokens, prev_opens, last_opens, min_depth] if line_tokens.any?
205
- [output, unclosed_heredocs]
205
+ output
206
206
  end
207
207
  end
@@ -104,12 +104,18 @@ class KatakataIrb::TypeSimulator
104
104
  @terminated
105
105
  end
106
106
 
107
- def terminate_with(type)
107
+ def terminate_with(type, value)
108
+ return if terminated?
109
+ self[type] = value
110
+ store_jump type
111
+ terminate
112
+ end
113
+
114
+ def store_jump(type)
108
115
  scopes = ancestors.select(&:mutable?)
109
116
  scope = scopes.find { _1.has_own? type } || scopes.last
110
117
  index = scopes.index scope
111
118
  scope.jump_branches << scopes.drop(index).map(&:branch_table_clone)
112
- terminate
113
119
  end
114
120
 
115
121
  def terminate
@@ -132,8 +138,7 @@ class KatakataIrb::TypeSimulator
132
138
  end
133
139
 
134
140
  def []=(name, type)
135
- # return if terminated?
136
- if @passthrough && !BaseScope.type_by_name(name) == :internal
141
+ if @passthrough && BaseScope.type_by_name(name) != :internal
137
142
  @parent[name] = type
138
143
  elsif trace?(name) && @parent.mutable? && !@tables.any? { _1.key? name } && @parent.has?(name)
139
144
  @parent[name] = type
@@ -189,6 +194,10 @@ class KatakataIrb::TypeSimulator
189
194
  run_branches(block, -> {}).first || KatakataIrb::Types::NIL
190
195
  end
191
196
 
197
+ def never(&block)
198
+ branch(&block)
199
+ end
200
+
192
201
  def run_branches(*blocks)
193
202
  results = blocks.map { branch(&_1) }.reject(&:last)
194
203
  merge results.map { _2 }
@@ -263,6 +272,8 @@ class KatakataIrb::TypeSimulator
263
272
  BREAK_RESULT = '%break'
264
273
  NEXT_RESULT = '%next'
265
274
  RETURN_RESULT = '%return'
275
+ PATTERNMATCH_BREAK = '%match'
276
+ RAISE_BREAK = '%raise'
266
277
 
267
278
  def initialize(dig_targets)
268
279
  @dig_targets = dig_targets
@@ -279,8 +290,8 @@ class KatakataIrb::TypeSimulator
279
290
  in [:program, statements]
280
291
  statements.map { simulate_evaluate _1, scope }.last
281
292
  in [:def | :defs,]
282
- sexp in [:def, method_name_exp, params, body_stmt]
283
- sexp in [:defs, receiver_exp, dot_exp, method_name_exp, params, body_stmt]
293
+ sexp in [:def, _method_name_exp, params, body_stmt]
294
+ sexp in [:defs, receiver_exp, _dot_exp, _method_name_exp, params, body_stmt]
284
295
  if receiver_exp
285
296
  receiver_exp in [:paren, receiver_exp]
286
297
  self_type = simulate_evaluate receiver_exp, scope
@@ -443,6 +454,10 @@ class KatakataIrb::TypeSimulator
443
454
  return scope[name]
444
455
  end
445
456
  receiver, method, args, kwargs, block, conditional = retrieve_method_call sexp
457
+ if receiver.nil? && method == 'raise'
458
+ scope.terminate_with RAISE_BREAK, KatakataIrb::Types::TRUE
459
+ return KatakataIrb::Types::NIL
460
+ end
446
461
  receiver_type = receiver ? simulate_evaluate(receiver, scope) : scope.self_type
447
462
  evaluate_method = lambda do
448
463
  args_type = args.map do |arg|
@@ -486,6 +501,7 @@ class KatakataIrb::TypeSimulator
486
501
  [KatakataIrb::Types::UnionType[result, *nexts], breaks]
487
502
  end
488
503
  else
504
+ call_block_proc = ->(_block_args) { KatakataIrb::Types::OBJECT }
489
505
  simulate_evaluate block, scope
490
506
  end
491
507
  end
@@ -584,33 +600,37 @@ class KatakataIrb::TypeSimulator
584
600
  inner_scope = Scope.new scope, { BREAK_RESULT => nil }, passthrough: true
585
601
  simulate_evaluate cond, inner_scope
586
602
  scope.conditional { statements.each { simulate_evaluate _1, inner_scope } }
587
- breaks = inner_scope[BREAK_RESULT]
588
603
  inner_scope.merge_jumps
604
+ breaks = inner_scope[BREAK_RESULT]
589
605
  breaks ? KatakataIrb::Types::UnionType[breaks, KatakataIrb::Types::NIL] : KatakataIrb::Types::NIL
590
606
  in [:while_mod | :until_mod, cond, statement]
591
- simulate_evaluate cond, scope
592
- scope.conditional { simulate_evaluate statement, scope }
593
- KatakataIrb::Types::NIL
607
+ inner_scope = Scope.new scope, { BREAK_RESULT => nil }, passthrough: true
608
+ simulate_evaluate cond, inner_scope
609
+ scope.conditional { simulate_evaluate statement, inner_scope }
610
+ inner_scope.merge_jumps
611
+ breaks = inner_scope[BREAK_RESULT]
612
+ breaks ? KatakataIrb::Types::UnionType[breaks, KatakataIrb::Types::NIL] : KatakataIrb::Types::NIL
594
613
  in [:break | :next | :return => jump_type, value]
595
614
  internal_key = jump_type == :break ? BREAK_RESULT : jump_type == :next ? NEXT_RESULT : RETURN_RESULT
596
615
  if value.empty?
597
- scope[internal_key] = KatakataIrb::Types::NIL
616
+ jump_value = KatakataIrb::Types::NIL
598
617
  else
599
618
  values, kw = evaluate_mrhs value, scope
600
619
  values << kw if kw
601
- scope[internal_key] = values.size == 1 ? values.first : KatakataIrb::Types::InstanceType.new(Array, Elem: KatakataIrb::Types::UnionType[*values])
620
+ jump_value = values.size == 1 ? values.first : KatakataIrb::Types::InstanceType.new(Array, Elem: KatakataIrb::Types::UnionType[*values])
602
621
  end
603
- scope.terminate_with internal_key
622
+ scope.terminate_with internal_key, jump_value
604
623
  KatakataIrb::Types::NIL
605
624
  in [:return0]
606
- scope[RETURN_RESULT] = KatakataIrb::Types::NIL
607
- scope.terminate_with RETURN_RESULT
625
+ scope.terminate_with RETURN_RESULT, KatakataIrb::Types::NIL
608
626
  KatakataIrb::Types::NIL
609
627
  in [:yield, args]
610
628
  evaluate_mrhs args, scope
611
629
  KatakataIrb::Types::OBJECT
612
630
  in [:yield0]
613
631
  KatakataIrb::Types::OBJECT
632
+ in [:redo | :retry]
633
+ scope.terminate
614
634
  in [:super, args]
615
635
  args, kwargs, _block = retrieve_method_args args
616
636
  args.each do |arg|
@@ -623,7 +643,9 @@ class KatakataIrb::TypeSimulator
623
643
  simulate_evaluate body_stmt, scope
624
644
  in [:bodystmt, statements, rescue_stmt, _unknown, ensure_stmt]
625
645
  statements = [statements] if statements in [Symbol,] # oneliner-def body
626
- return_type = statements.map { simulate_evaluate _1, scope }.last
646
+ rescue_scope = Scope.new scope, { RAISE_BREAK => nil }, passthrough: true if rescue_stmt
647
+ return_type = statements.map { simulate_evaluate _1, rescue_scope || scope }.last
648
+ rescue_scope&.merge_jumps
627
649
  if rescue_stmt
628
650
  return_type = KatakataIrb::Types::UnionType[return_type, scope.conditional { simulate_evaluate rescue_stmt, scope }]
629
651
  end
@@ -647,7 +669,9 @@ class KatakataIrb::TypeSimulator
647
669
  end
648
670
  return_type
649
671
  in [:rescue_mod, statement1, statement2]
650
- a = simulate_evaluate statement1, scope
672
+ rescue_scope = Scope.new scope, { RAISE_BREAK => nil }, passthrough: true
673
+ a = simulate_evaluate statement1, rescue_scope
674
+ rescue_scope.merge_jumps
651
675
  b = scope.conditional { simulate_evaluate statement2, scope }
652
676
  KatakataIrb::Types::UnionType[a, b]
653
677
  in [:module, module_stmt, body_stmt]
@@ -669,45 +693,54 @@ class KatakataIrb::TypeSimulator
669
693
  klass_types << KatakataIrb::Types::CLASS if klass_types.empty?
670
694
  simulate_evaluate body_stmt, Scope.new(scope, { SELF => KatakataIrb::Types::UnionType[*klass_types], BREAK_RESULT => nil, NEXT_RESULT => nil, RETURN_RESULT => nil }, trace_cvar: false, trace_ivar: false, trace_lvar: false)
671
695
  in [:for, fields, enum, statements]
672
- fields = [fields] if fields in [:var_field,]
696
+ fields = [fields] if fields in [:var_field | :field | :aref_field,]
673
697
  params = [:params, fields, nil, nil, nil, nil, nil, nil]
674
698
  enum = simulate_evaluate enum, scope
675
699
  extract_param_names(params).each { scope[_1] = KatakataIrb::Types::NIL }
676
700
  response = simulate_call enum, :first, [], nil, nil
677
701
  evaluate_assign_params params, [response], scope
702
+ inner_scope = Scope.new scope, { BREAK_RESULT => nil }, passthrough: true
678
703
  scope.conditional do
679
- statements.each { simulate_evaluate _1, scope }
704
+ statements.each { simulate_evaluate _1, inner_scope }
680
705
  end
681
- enum
682
- in [:in | :when => mode, pattern, if_statements, else_statement]
683
- if mode == :in
684
- if_match = -> { match_pattern case_target, pattern, scope }
685
- else_match = -> { scope.conditional { if_match.call } }
686
- else
687
- eval_pattern = lambda do |pattern, *rest|
688
- simulate_evaluate pattern, scope
689
- scope.conditional { eval_pattern.call(*rest) } if rest.any?
690
- end
691
- if_match = -> { eval_pattern.call(*pattern) }
692
- else_match = -> { pattern.each { simulate_evaluate _1, scope } }
706
+ inner_scope.merge_jumps
707
+ breaks = inner_scope[BREAK_RESULT]
708
+ breaks ? KatakataIrb::Types::UnionType[breaks, enum] : enum
709
+ in [:when, pattern, if_statements, else_statement]
710
+ eval_pattern = lambda do |pattern, *rest|
711
+ simulate_evaluate pattern, scope
712
+ scope.conditional { eval_pattern.call(*rest) } if rest.any?
693
713
  end
694
714
  if_branch = lambda do
695
- if_match.call
715
+ eval_pattern.call(*pattern)
696
716
  if_statements.map { simulate_evaluate _1, scope }.last
697
717
  end
698
718
  else_branch = lambda do
699
- else_match.call
719
+ pattern.each { simulate_evaluate _1, scope }
700
720
  simulate_evaluate(else_statement, scope, case_target:)
701
721
  end
702
722
  if if_statements && else_statement
703
723
  KatakataIrb::Types::UnionType[*scope.run_branches(if_branch, else_branch)]
704
- elsif if_statements
705
- KatakataIrb::Types::UnionType[scope.conditional { if_branch.call }, KatakataIrb::Types::NIL]
706
- elsif else_statement
707
- KatakataIrb::Types::UnionType[scope.conditional { else_branch.call }, KatakataIrb::Types::NIL]
708
724
  else
709
- KatakataIrb::Types::NIL
725
+ KatakataIrb::Types::UnionType[scope.conditional { (if_branch || else_branch).call }, KatakataIrb::Types::NIL]
710
726
  end
727
+ in [:in, [:var_field, [:@ident, name,]], if_statements, else_statement]
728
+ scope.never { simulate_evaluate else_statement, scope } if else_statement
729
+ scope[name] = case_target || KatakataIrb::Types::OBJECT
730
+ if_statements ? if_statements.map { simulate_evaluate _1, scope }.last : KatakataIrb::Types::NIL
731
+ in [:in, pattern, if_statements, else_statement]
732
+ pattern_scope = Scope.new(scope, { PATTERNMATCH_BREAK => nil }, passthrough: true)
733
+ results = scope.run_branches(
734
+ -> {
735
+ match_pattern case_target, pattern, pattern_scope
736
+ if_statements ? if_statements.map { simulate_evaluate _1, scope }.last : KatakataIrb::Types::NIL
737
+ },
738
+ -> {
739
+ pattern_scope.merge_jumps
740
+ else_statement ? simulate_evaluate(else_statement, scope, case_target:) : KatakataIrb::Types::NIL
741
+ }
742
+ )
743
+ KatakataIrb::Types::UnionType[*results]
711
744
  in [:case, target_exp, match_exp]
712
745
  target = simulate_evaluate target_exp, scope
713
746
  simulate_evaluate match_exp, scope, case_target: target
@@ -728,6 +761,7 @@ class KatakataIrb::TypeSimulator
728
761
  end
729
762
 
730
763
  def match_pattern(target, pattern, scope)
764
+ breakable = -> { scope.store_jump PATTERNMATCH_BREAK }
731
765
  types = target.types
732
766
  case pattern
733
767
  in [:var_field, [:@ident, name,]]
@@ -736,17 +770,20 @@ class KatakataIrb::TypeSimulator
736
770
  in [:@int | :@float | :@rational | :@imaginary | :@CHAR | :symbol_literal | :string_literal | :regexp_literal,]
737
771
  in [:begin, statement] # in (statement)
738
772
  simulate_evaluate statement, scope
773
+ breakable.call
739
774
  in [:binary, lpattern, :|, rpattern]
740
775
  match_pattern target, lpattern, scope
741
- match_pattern target, rpattern, scope
776
+ scope.conditional { match_pattern target, rpattern, scope }
777
+ breakable.call
742
778
  in [:binary, lpattern, :'=>', [:var_field, [:@ident, name,]] => rpattern]
743
- if lpattern in [:var_ref, [:@const, const_name,]]
779
+ if lpattern in [:var_ref, [:@const, _const_name,]]
744
780
  const_value = simulate_evaluate lpattern, scope
745
781
  if (const_value in KatakataIrb::Types::SingletonType) && const_value.module_or_class.is_a?(Class)
746
782
  scope[name] = KatakataIrb::Types::InstanceType.new const_value.module_or_class
747
783
  else
748
784
  scope[name] = KatakataIrb::Types::OBJECT
749
785
  end
786
+ breakable.call
750
787
  else
751
788
  match_pattern target, lpattern, scope
752
789
  match_pattern target, rpattern, scope
@@ -760,6 +797,7 @@ class KatakataIrb::TypeSimulator
760
797
  end
761
798
  if splat in [:var_field, [:@ident, name,]]
762
799
  scope[name] = KatakataIrb::Types::InstanceType.new Array, Elem: elem
800
+ breakable.call
763
801
  end
764
802
  post_items&.each do |item|
765
803
  match_pattern elem, item, scope
@@ -770,18 +808,21 @@ class KatakataIrb::TypeSimulator
770
808
  key_type = KatakataIrb::Types::UnionType[*hash_types.filter_map { _1.params[:K] }]
771
809
  value_type = KatakataIrb::Types::UnionType[*hash_types.filter_map { _1.params[:V] }]
772
810
  items&.each do |key_pattern, value_pattern|
773
- if key_pattern in [:@label, label,]
811
+ if (key_pattern in [:@label, label,]) && !value_pattern
774
812
  name = label.delete ':'
775
- scope[name] = value_type unless value_pattern
813
+ scope[name] = value_type
814
+ breakable.call
776
815
  end
777
816
  match_pattern value_type, value_pattern, scope if value_pattern
778
817
  end
779
818
  if splat in [:var_field, [:@ident, name,]]
780
819
  scope[name] = KatakataIrb::Types::InstanceType.new Hash, K: key_type, V: value_type
820
+ breakable.call
781
821
  end
782
822
  in [:if_mod, cond, ifpattern]
783
- simulate_evaluate cond, scope
784
823
  match_pattern target, ifpattern, scope
824
+ simulate_evaluate cond, scope
825
+ breakable.call
785
826
  in [:dyna_symbol,]
786
827
  in [:const_path_ref,]
787
828
  else
@@ -852,6 +893,14 @@ class KatakataIrb::TypeSimulator
852
893
  scope[name] = value || KatakataIrb::Types::OBJECT
853
894
  in [:mlhs, *mlhs]
854
895
  evaluate_massign mlhs, value || [], scope
896
+ in [:field, receiver,]
897
+ # (a=x).b, c = value
898
+ simulate_evaluate receiver, scope
899
+ in [:aref_field, *field]
900
+ # (a=x)[i=y, j=z], b = value
901
+ simulate_evaluate [:aref, *field], scope
902
+ in nil
903
+ # a, *, b = value
855
904
  end
856
905
  end
857
906
  end
@@ -930,17 +979,20 @@ class KatakataIrb::TypeSimulator
930
979
  [args, [], nil]
931
980
  in [:args_add_block, [:args_add_star,] => args, block_arg]
932
981
  args, kwargs, = retrieve_method_args args
982
+ block_arg = [:void_stmt] if block_arg.nil? # method(*splat, &)
933
983
  [args, kwargs, block_arg]
934
984
  in [:args_add_block, [*args, [:bare_assoc_hash,] => kw], block_arg]
985
+ block_arg = [:void_stmt] if block_arg.nil? # method(**splat, &)
935
986
  _, kwargs = retrieve_method_args kw
936
987
  [args, kwargs, block_arg]
937
988
  in [:args_add_block, [*args], block_arg]
989
+ block_arg = [:void_stmt] if block_arg.nil? # method(arg, &)
938
990
  [args, [], block_arg]
939
991
  in [:bare_assoc_hash, kws]
940
992
  kwargs = []
941
993
  kws.each do |kw|
942
994
  if kw in [:assoc_splat, value,]
943
- kwargs << KatakataIrb::Types::Splat.new(value)
995
+ kwargs << KatakataIrb::Types::Splat.new(value) if value
944
996
  elsif kw in [:assoc_new, [:@label, label,] => key, nil]
945
997
  name = label.delete ':'
946
998
  kwargs << [key, [:__var_ref_or_call, [name =~ /\A[A-Z]/ ? :@const : :@ident, name, [0, 0]]]]
@@ -955,7 +1007,7 @@ class KatakataIrb::TypeSimulator
955
1007
  [args, kwargs, nil]
956
1008
  in [:args_add_star, pre_args, star_arg, *post_args]
957
1009
  pre_args, = retrieve_method_args pre_args if pre_args in [:args_add_star,]
958
- args = [*pre_args, KatakataIrb::Types::Splat.new(star_arg), *post_args]
1010
+ args = star_arg ? [*pre_args, KatakataIrb::Types::Splat.new(star_arg), *post_args] : pre_args + post_args
959
1011
  [args, [], nil]
960
1012
  in [:arg_paren, args]
961
1013
  args ? retrieve_method_args(args) : [[], [], nil]
@@ -1001,6 +1053,8 @@ class KatakataIrb::TypeSimulator
1001
1053
  items.each(&extract_mlhs)
1002
1054
  in [:rest_param, item]
1003
1055
  extract_mlhs.call item if item
1056
+ in [:field | :aref_field,]
1057
+ # a.b, c[i] = value
1004
1058
  in [:excessed_comma]
1005
1059
  end
1006
1060
  end
@@ -1025,7 +1079,7 @@ class KatakataIrb::TypeSimulator
1025
1079
 
1026
1080
  def evaluate_assign_params(params, values, scope)
1027
1081
  values = values.dup
1028
- params => [:params, pre_required, optional, rest, post_required, keywords, keyrest, block]
1082
+ params => [:params, pre_required, optional, rest, post_required, _keywords, keyrest, block]
1029
1083
  size = (pre_required&.size || 0) + (optional&.size || 0) + (post_required&.size || 0) + (rest ? 1 : 0)
1030
1084
  if values.size == 1 && size >= 2
1031
1085
  value = values.first
@@ -1042,6 +1096,7 @@ class KatakataIrb::TypeSimulator
1042
1096
  scope[name] = KatakataIrb::Types::InstanceType.new Array, Elem: KatakataIrb::Types::UnionType[*rest_values]
1043
1097
  end
1044
1098
  evaluate_massign post_required, post_values, scope if post_required
1099
+ # TODO: assign keywords
1045
1100
  if keyrest in [:kwrest_param, [:@ident, name,]]
1046
1101
  scope[name] = KatakataIrb::Types::InstanceType.new Hash, K: KatakataIrb::Types::SYMBOL, V: KatakataIrb::Types::OBJECT
1047
1102
  end
@@ -1064,14 +1119,10 @@ class KatakataIrb::TypeSimulator
1064
1119
  name = label.delete ':'
1065
1120
  scope[name] = value ? simulate_evaluate(value, scope) : KatakataIrb::Types::OBJECT
1066
1121
  end
1067
- case keyrest
1068
- in [:args_forward] | nil
1069
- in [:kwrest_param, [:@ident, name,]]
1122
+ if keyrest in [:kwrest_param, [:@ident, name,]]
1070
1123
  scope[name] = KatakataIrb::Types::HASH
1071
1124
  end
1072
- case block
1073
- in :& | nil
1074
- in [:blockarg, [:@ident, name,]]
1125
+ if block in [:blockarg, [:@ident, name,]]
1075
1126
  scope[name] = KatakataIrb::Types::PROC
1076
1127
  end
1077
1128
  end
@@ -17,7 +17,7 @@ module KatakataIrb::Types
17
17
  type_name = RBS::TypeName(ancestor.name).absolute!
18
18
  definition = (singleton ? rbs_builder.build_singleton(type_name) : rbs_builder.build_instance(type_name)) rescue nil
19
19
  method = definition&.methods&.[](method_name)
20
- return method if definition
20
+ return method if method
21
21
  end
22
22
  nil
23
23
  end
@@ -298,7 +298,7 @@ module KatakataIrb::Types
298
298
  type.module_or_class if (type in SingletonType) && type.module_or_class.is_a?(Class)
299
299
  end
300
300
  if classes.empty?
301
- klass = Object.const_get(return_type.name.name)
301
+ klass = return_type.name.to_namespace.path.reduce(Object) { _1.const_get _2 }
302
302
  classes << klass if klass in Class
303
303
  end
304
304
  if return_type.args
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KatakataIrb
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
data/lib/katakata_irb.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'io/console'
1
2
  module KatakataIrb
2
3
  def self.repl
3
4
  require 'katakata_irb/completor'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katakata_irb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - tompng
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-09 00:00:00.000000000 Z
11
+ date: 2022-12-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbs