ibex-runtime 0.2.0 → 0.4.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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +100 -272
  3. data/lib/ibex/runtime/cst/green/node.rb +1 -1
  4. data/lib/ibex/runtime/cst/green/token.rb +1 -1
  5. data/lib/ibex/runtime/cst/green/trivia.rb +1 -1
  6. data/lib/ibex/runtime/cst/incremental/blender.rb +1 -1
  7. data/lib/ibex/runtime/cst/incremental/lexed_syntax.rb +2 -2
  8. data/lib/ibex/runtime/cst/incremental/parse_memo.rb +2 -2
  9. data/lib/ibex/runtime/cst/incremental/relexer.rb +3 -0
  10. data/lib/ibex/runtime/cst/incremental/session.rb +26 -2
  11. data/lib/ibex/runtime/cst/parse_result.rb +7 -7
  12. data/lib/ibex/runtime/cst/serialize.rb +6 -3
  13. data/lib/ibex/runtime/cst/syntax_node.rb +4 -4
  14. data/lib/ibex/runtime/cst/syntax_token.rb +5 -5
  15. data/lib/ibex/runtime/cst/typed_node.rb +1 -1
  16. data/lib/ibex/runtime/cst/validator.rb +40 -30
  17. data/lib/ibex/runtime/embedded_source.rb +98 -0
  18. data/lib/ibex/runtime/event.rb +7 -4
  19. data/lib/ibex/runtime/event_jsonl_tracer.rb +1 -1
  20. data/lib/ibex/runtime/event_sanitizer.rb +32 -26
  21. data/lib/ibex/runtime/generated_lexer.rb +23 -16
  22. data/lib/ibex/runtime/lexer_input.rb +1 -1
  23. data/lib/ibex/runtime/location_span.rb +15 -15
  24. data/lib/ibex/runtime/observation.rb +1 -1
  25. data/lib/ibex/runtime/parser.rb +309 -191
  26. data/lib/ibex/runtime/parser_sync_recovery.rb +19 -15
  27. data/lib/ibex/runtime/repair.rb +36 -5
  28. data/lib/ibex/runtime/repair_priority_queue.rb +10 -6
  29. data/lib/ibex/runtime/repair_search.rb +92 -29
  30. data/lib/ibex/runtime/syntax_repair.rb +490 -0
  31. data/lib/ibex/runtime/syntax_session.rb +416 -0
  32. data/lib/ibex/runtime/table_format.rb +1 -1
  33. data/lib/ibex/runtime/version.rb +1 -1
  34. data/lib/ibex/runtime.rb +2 -1
  35. data/lib/ibex/tables/compact.rb +16 -12
  36. data/lib/ibex/tables/compact_actions.rb +45 -33
  37. data/lib/ibex/tables/compact_productions.rb +18 -15
  38. data/sig/ibex/runtime/cst/green/node.rbs +2 -2
  39. data/sig/ibex/runtime/cst/green/token.rbs +2 -2
  40. data/sig/ibex/runtime/cst/green/trivia.rbs +2 -2
  41. data/sig/ibex/runtime/cst/incremental/blender.rbs +2 -2
  42. data/sig/ibex/runtime/cst/incremental/lexed_syntax.rbs +3 -3
  43. data/sig/ibex/runtime/cst/incremental/parse_memo.rbs +4 -4
  44. data/sig/ibex/runtime/cst/incremental/session.rbs +8 -2
  45. data/sig/ibex/runtime/cst/parse_result.rbs +9 -9
  46. data/sig/ibex/runtime/cst/serialize.rbs +8 -6
  47. data/sig/ibex/runtime/cst/syntax_node.rbs +6 -6
  48. data/sig/ibex/runtime/cst/syntax_token.rbs +10 -10
  49. data/sig/ibex/runtime/cst/typed_node.rbs +2 -2
  50. data/sig/ibex/runtime/cst/validator.rbs +46 -42
  51. data/sig/ibex/runtime/embedded_source.rbs +21 -0
  52. data/sig/ibex/runtime/event.rbs +7 -5
  53. data/sig/ibex/runtime/event_jsonl_tracer.rbs +1 -1
  54. data/sig/ibex/runtime/event_sanitizer.rbs +50 -44
  55. data/sig/ibex/runtime/generated_lexer.rbs +31 -24
  56. data/sig/ibex/runtime/lexer_input.rbs +2 -2
  57. data/sig/ibex/runtime/location_span.rbs +28 -28
  58. data/sig/ibex/runtime/observation.rbs +2 -2
  59. data/sig/ibex/runtime/parser.rbs +266 -252
  60. data/sig/ibex/runtime/parser_sync_recovery.rbs +15 -15
  61. data/sig/ibex/runtime/repair.rbs +28 -8
  62. data/sig/ibex/runtime/repair_priority_queue.rbs +9 -7
  63. data/sig/ibex/runtime/repair_search.rbs +38 -19
  64. data/sig/ibex/runtime/syntax_repair.rbs +177 -0
  65. data/sig/ibex/runtime/syntax_session.rbs +186 -0
  66. data/sig/ibex/tables/compact.rbs +12 -12
  67. data/sig/ibex/tables/compact_actions.rbs +10 -17
  68. data/sig/ibex/tables/compact_productions.rbs +19 -19
  69. metadata +12 -6
  70. data/lib/ibex/runtime/jsonl_tracer.rb +0 -75
  71. data/sig/ibex/runtime/jsonl_tracer.rbs +0 -42
@@ -10,15 +10,15 @@ module Ibex
10
10
  private_constant :ERROR_ACTION
11
11
 
12
12
  # @rbs!
13
- # private def parser_tables: () -> Hash[Symbol, untyped]
14
- # private def table_lookup: (untyped, Integer, Integer) -> untyped
15
- # private def default_action: (Integer) -> untyped
16
- # private def token_to_str: (untyped) -> String
13
+ # private def parser_tables: () -> Hash[Symbol, Object?]
14
+ # private def table_lookup: (Object?, Integer, Integer) -> IR::runtime_action?
15
+ # private def default_action: (Integer) -> IR::runtime_action?
16
+ # private def token_to_str: (Object?) -> String
17
17
  # private def trace: (String) -> void
18
- # private def continue_recovery: () -> untyped
18
+ # private def continue_recovery: () -> ([ :continue ] | [ :done, nil ])
19
19
  # private def reject_recovery_eof: () -> [:done, nil]
20
- # private def finish_recovery: (untyped, untyped, untyped, untyped, Integer, Array[untyped],
21
- # Hash[String, untyped]?, String, Array[Proc]?) -> [:continue]
20
+ # private def finish_recovery: (Object?, Object?, Object?, Object?, Integer, Array[Object?],
21
+ # Hash[String, Object?]?, String, Array[Proc]?) -> [:continue]
22
22
 
23
23
  private
24
24
 
@@ -33,8 +33,8 @@ module Ibex
33
33
  !@sync_recovery_context.nil?
34
34
  end
35
35
 
36
- # @rbs (Hash[Symbol, untyped] context, Hash[String, untyped]? token_data,
37
- # Array[Proc]? observers) -> untyped
36
+ # @rbs (Hash[Symbol, Object?] context, Hash[String, Object?]? token_data,
37
+ # Array[Proc]? observers) -> ([ :continue ] | [ :done, nil ])
38
38
  def begin_sync_recovery(context, token_data, observers)
39
39
  @sync_recovery_context = context
40
40
  @sync_recovery_token_data = token_data
@@ -42,7 +42,7 @@ module Ibex
42
42
  continue_sync_recovery
43
43
  end
44
44
 
45
- # @rbs () -> untyped
45
+ # @rbs () -> ([ :continue ] | [ :done, nil ])
46
46
  def continue_sync_recovery
47
47
  return reject_sync_recovery_eof if @lookahead == Parser::EOF_TOKEN
48
48
  return finish_sync_recovery if sync_token?(@lookahead) && synchronize_for_current_token
@@ -50,7 +50,7 @@ module Ibex
50
50
  continue_recovery
51
51
  end
52
52
 
53
- # @rbs (untyped token_id) -> bool
53
+ # @rbs (Integer token_id) -> bool
54
54
  def sync_token?(token_id)
55
55
  tokens = parser_tables[:recovery_sync_tokens]
56
56
  tokens.is_a?(Array) && tokens.include?(token_id)
@@ -70,9 +70,10 @@ module Ibex
70
70
  end
71
71
  end
72
72
 
73
- # @rbs (Integer state, Integer token_id) -> untyped
73
+ # @rbs (Integer state, Integer token_id) -> IR::runtime_action
74
74
  def sync_action(state, token_id)
75
- return ERROR_ACTION unless parser_tables.fetch(:token_names).key?(token_id)
75
+ token_names = parser_tables.fetch(:token_names)
76
+ return ERROR_ACTION unless token_names.is_a?(Hash) && token_names.key?(token_id)
76
77
 
77
78
  table_lookup(parser_tables.fetch(:actions), state, token_id) || default_action(state) || ERROR_ACTION
78
79
  end
@@ -85,9 +86,12 @@ module Ibex
85
86
  clear_sync_recovery
86
87
  @recovery_shifts = Parser::RECOVERY_SHIFTS
87
88
  trace("recover: synchronized before #{token_to_str(@lookahead)} in state #{@state_stack.last}") if @yydebug
89
+ value_stack = context.fetch(:value_stack) #: Array[Object?]
90
+ state = context.fetch(:state) #: Integer
91
+ reason = context.fetch(:reason) #: String
88
92
  finish_recovery(
89
- context[:token_id], context[:token_display], context[:value], context[:location], context[:state],
90
- context.fetch(:value_stack), token_data, context.fetch(:reason), observers
93
+ context[:token_id], context[:token_display], context[:value], context[:location], state,
94
+ value_stack, token_data, reason, observers
91
95
  )
92
96
  end
93
97
 
@@ -40,6 +40,8 @@ module Ibex
40
40
 
41
41
  # One immutable insertion, deletion, or replacement.
42
42
  class RepairEdit
43
+ # @rbs! type document_value = Symbol | Integer | String
44
+
43
45
  KINDS = %i[insert delete replace].freeze #: Array[Symbol]
44
46
 
45
47
  attr_reader :kind #: Symbol
@@ -62,7 +64,7 @@ module Ibex
62
64
  freeze
63
65
  end
64
66
 
65
- # @rbs () -> Hash[Symbol, untyped]
67
+ # @rbs () -> Hash[Symbol, document_value]
66
68
  def to_h
67
69
  { kind: @kind, position: @position, token_id: @token_id, token_name: @token_name, cost: @cost }.freeze
68
70
  end
@@ -84,20 +86,49 @@ module Ibex
84
86
  freeze
85
87
  end
86
88
 
87
- # @rbs () -> Hash[Symbol, untyped]
89
+ # @rbs () -> Hash[Symbol, Integer | Array[Hash[Symbol, RepairEdit::document_value]]]
88
90
  def to_h
89
91
  { cost: @cost, configurations: @configurations, edits: @edits.map(&:to_h).freeze }.freeze
90
92
  end
91
93
  end
92
94
 
95
+ # Closed internal outcome for callers that must distinguish bounded search
96
+ # exhaustion from a complete search with no repair.
97
+ class RepairSearchResult
98
+ STATUSES = %i[selected need_input exhausted not_found].freeze #: Array[Symbol]
99
+
100
+ attr_reader :status #: Symbol
101
+ attr_reader :plan #: RepairPlan?
102
+ attr_reader :configurations #: Integer
103
+
104
+ # @rbs (status: Symbol, plan: RepairPlan?, configurations: Integer) -> void
105
+ def initialize(status:, plan:, configurations:)
106
+ raise ArgumentError, "unknown repair search status #{status.inspect}" unless STATUSES.include?(status)
107
+ unless configurations.is_a?(Integer) && configurations >= 0
108
+ raise ArgumentError, "repair search configurations must be nonnegative"
109
+ end
110
+ unless (status == :selected) == plan.is_a?(RepairPlan)
111
+ raise ArgumentError, "selected repair search status and plan must agree"
112
+ end
113
+
114
+ @status = status
115
+ @plan = plan
116
+ @configurations = configurations
117
+ freeze
118
+ end
119
+
120
+ # @rbs () -> bool
121
+ def selected? = @status == :selected
122
+ end
123
+
93
124
  # Internal normalized input record retained while repair looks ahead.
94
125
  class RepairInput
95
126
  attr_reader :token_id #: Integer
96
127
  attr_reader :token_name #: String
97
- attr_reader :value #: untyped
98
- attr_reader :location #: untyped
128
+ attr_reader :value #: Object?
129
+ attr_reader :location #: Object?
99
130
 
100
- # @rbs (token_id: Integer, token_name: String, value: untyped, location: untyped) -> void
131
+ # @rbs (token_id: Integer, token_name: String, value: Object?, location: Object?) -> void
101
132
  def initialize(token_id:, token_name:, value:, location:)
102
133
  @token_id = token_id
103
134
  @token_name = token_name.dup.freeze
@@ -5,7 +5,9 @@ module Ibex
5
5
  module Runtime
6
6
  # Minimal binary heap ordered by an immutable Array priority.
7
7
  class RepairPriorityQueue
8
- # @rbs @entries: Array[[Array[untyped], untyped]]
8
+ # @rbs!
9
+ # type priority = Array[untyped]
10
+ # @rbs @entries: Array[[priority, Object?]]
9
11
 
10
12
  # @rbs () -> void
11
13
  def initialize
@@ -15,9 +17,9 @@ module Ibex
15
17
  # @rbs () -> bool
16
18
  def empty? = @entries.empty?
17
19
 
18
- # @rbs (Array[untyped] priority, untyped value) -> void
20
+ # @rbs (priority priority, Object? value) -> void
19
21
  def push(priority, value)
20
- entry = [priority, value] #: [Array[untyped], untyped]
22
+ entry = [priority, value] #: [priority, Object?]
21
23
  @entries << entry
22
24
  index = @entries.length - 1
23
25
  while index.positive?
@@ -30,7 +32,7 @@ module Ibex
30
32
  @entries[index] = entry
31
33
  end
32
34
 
33
- # @rbs () -> [Array[untyped], untyped]?
35
+ # @rbs () -> [priority, Object?]?
34
36
  def pop
35
37
  first = @entries.first
36
38
  tail = @entries.pop
@@ -51,9 +53,11 @@ module Ibex
51
53
 
52
54
  private
53
55
 
54
- # @rbs ([Array[untyped], untyped] left, [Array[untyped], untyped] right) -> Integer
56
+ # @rbs ([priority, Object?] left, [priority, Object?] right) -> Integer
55
57
  def compare(left, right)
56
- (left.fetch(0) <=> right.fetch(0)) || 0
58
+ left_priority = left.fetch(0) #: priority
59
+ right_priority = right.fetch(0) #: priority
60
+ (left_priority <=> right_priority) || 0
57
61
  end
58
62
  end
59
63
  end
@@ -8,20 +8,34 @@ module Ibex
8
8
  # Bounded Dijkstra search over LR state stacks. Semantic actions never run.
9
9
  # rubocop:disable Metrics/ClassLength -- queue policy and LR transitions form one bounded search invariant.
10
10
  class RepairSearch
11
+ # @rbs!
12
+ # type configuration_key = [Array[Integer], Integer, Integer, bool]
13
+ # type priority = Array[untyped]
14
+ # type lookup_table = Tables::action_table | Tables::goto_table
15
+ # type lookup_value = IR::runtime_action | Integer?
16
+
11
17
  NEED_INPUT = Object.new.freeze #: Object
12
18
  LIMIT = Object.new.freeze #: Object
13
- Configuration = Struct.new(:stack, :input_index, :shifts, :cost, :edits, :goal, keyword_init: true)
14
-
15
- # @rbs @tables: Hash[Symbol, untyped]
19
+ Configuration = Struct.new(
20
+ :stack, #: Array[Integer]
21
+ :input_index, #: Integer
22
+ :shifts, #: Integer
23
+ :cost, #: Integer
24
+ :edits, #: Array[RepairEdit]
25
+ :goal, #: bool
26
+ keyword_init: true
27
+ )
28
+
29
+ # @rbs @tables: Hash[Symbol, Object?]
16
30
  # @rbs @policy: RepairPolicy
17
31
  # @rbs @tokens: Array[RepairInput]
18
32
  # @rbs @complete: bool
19
33
  # @rbs @configurations: Integer
20
34
  # @rbs @heap: RepairPriorityQueue
21
- # @rbs @best: Hash[Array[untyped], Array[untyped]]
35
+ # @rbs @best: Hash[configuration_key, priority]
22
36
  # @rbs @candidate_ids: Array[Integer]
23
37
 
24
- # @rbs (Hash[Symbol, untyped] tables, RepairPolicy policy, Array[RepairInput] tokens,
38
+ # @rbs (Hash[Symbol, Object?] tables, RepairPolicy policy, Array[RepairInput] tokens,
25
39
  # complete: bool) -> void
26
40
  def initialize(tables, policy, tokens, complete:)
27
41
  @tables = tables
@@ -32,11 +46,23 @@ module Ibex
32
46
  @heap = RepairPriorityQueue.new
33
47
  @best = {}
34
48
  reserved = [Parser::EOF_TOKEN, Parser::ERROR_TOKEN]
35
- @candidate_ids = tables.fetch(:token_names).keys.reject { |id| reserved.include?(id) }.sort.freeze
49
+ token_names = tables.fetch(:token_names) #: Hash[Integer, String]
50
+ @candidate_ids = token_names.keys.reject { |id| reserved.include?(id) }.sort.freeze
36
51
  end
37
52
 
53
+ # Compatibility projection used by the semantic runtime path.
38
54
  # @rbs (Array[Integer] state_stack) -> (RepairPlan | Object | nil)
39
55
  def search(state_stack)
56
+ result = search_result(state_stack)
57
+ return result.plan if result.selected?
58
+ return NEED_INPUT if result.status == :need_input
59
+
60
+ nil
61
+ end
62
+
63
+ # Preserve the bounded outcome for syntax tooling and diagnostics.
64
+ # @rbs (Array[Integer] state_stack) -> RepairSearchResult
65
+ def search_result(state_stack)
40
66
  empty_edits = [] #: Array[RepairEdit]
41
67
  push(
42
68
  Configuration.new(
@@ -51,16 +77,16 @@ module Ibex
51
77
  needs_input = false
52
78
  until @heap.empty?
53
79
  configuration = pop
54
- return nil if configuration.equal?(LIMIT)
80
+ return outcome(:exhausted) if configuration.equal?(LIMIT)
55
81
  next unless configuration.is_a?(Configuration)
56
82
 
57
83
  result = visit(configuration)
58
- return nil if result.equal?(LIMIT)
59
- return result if result.is_a?(RepairPlan)
84
+ return outcome(:exhausted) if result.equal?(LIMIT)
85
+ return outcome(:selected, plan: result) if result.is_a?(RepairPlan)
60
86
 
61
87
  needs_input = true if result.equal?(NEED_INPUT)
62
88
  end
63
- needs_input ? NEED_INPUT : nil
89
+ outcome(needs_input ? :need_input : :not_found)
64
90
  end
65
91
 
66
92
  private
@@ -206,11 +232,18 @@ module Ibex
206
232
  action = action_for(states.last, token_id)
207
233
  case action.first
208
234
  when :shift
209
- states << action.fetch(1)
235
+ target = action.fetch(1)
236
+ return nil unless target.is_a?(Integer)
237
+
238
+ states << target
210
239
  return nil if states.length > @policy.max_stack
211
240
 
212
241
  return RepairAdvance.new(status: :shift, stack: states.freeze)
213
- when :reduce then return nil unless apply_reduction(states, action.fetch(1))
242
+ when :reduce
243
+ production_id = action.fetch(1)
244
+ return nil unless production_id.is_a?(Integer)
245
+
246
+ return nil unless apply_reduction(states, production_id)
214
247
  when :accept then return RepairAdvance.new(status: :accept, stack: states.freeze)
215
248
  else return nil
216
249
  end
@@ -219,31 +252,53 @@ module Ibex
219
252
 
220
253
  # @rbs (Array[Integer] states, Integer production_id) -> Array[Integer]?
221
254
  def apply_reduction(states, production_id)
222
- production = @tables.fetch(:productions).fetch(production_id)
223
- length = production.fetch(:length)
255
+ productions = @tables.fetch(:productions) #: Array[Hash[Symbol, Object?]]
256
+ production = productions.fetch(production_id)
257
+ length = production.fetch(:length) #: Integer
224
258
  return if length >= states.length
225
259
 
226
260
  states.pop(length)
227
- goto = table_lookup(@tables.fetch(:gotos), states.last, production.fetch(:lhs))
228
- return unless goto
261
+ gotos = @tables.fetch(:gotos) #: Tables::goto_table
262
+ lhs = production.fetch(:lhs) #: Integer
263
+ goto = goto_table_lookup(gotos, states.last, lhs)
264
+ return unless goto.is_a?(Integer)
229
265
 
230
266
  states << goto
231
267
  states if states.length <= @policy.max_stack
232
268
  end
233
269
 
234
- # @rbs (Integer state, Integer token_id) -> untyped
270
+ # @rbs (Integer state, Integer token_id) -> IR::runtime_action
235
271
  def action_for(state, token_id)
236
- explicit = table_lookup(@tables.fetch(:actions), state, token_id)
272
+ actions = @tables.fetch(:actions) #: Tables::action_table
273
+ explicit = action_table_lookup(actions, state, token_id)
237
274
  return explicit if explicit
238
275
 
239
- @tables.fetch(:default_actions, Parser::EMPTY_ROW)[state] || [:error]
276
+ defaults = @tables.fetch(:default_actions, Parser::EMPTY_ROW) #: Array[IR::runtime_action?]
277
+ defaults[state] || [:error]
240
278
  end
241
279
 
242
- # @rbs (untyped table, Integer row, Integer column) -> untyped
243
- def table_lookup(table, row, column)
244
- return table.lookup(row, column) if table.respond_to?(:lookup)
280
+ # @rbs (Tables::action_table table, Integer row, Integer column) -> IR::runtime_action?
281
+ def action_table_lookup(table, row, column)
282
+ if table.is_a?(Tables::CompactActions)
283
+ value = table.__send__(:lookup, row, column)
284
+ return value #: IR::runtime_action?
285
+ end
245
286
 
246
- table.fetch(row, Parser::EMPTY_ROW)[column]
287
+ rows = table #: Array[Hash[Integer, IR::runtime_action]]
288
+ empty_row = Parser::EMPTY_ROW #: Hash[Integer, IR::runtime_action]
289
+ rows.fetch(row, empty_row)[column]
290
+ end
291
+
292
+ # @rbs (Tables::goto_table table, Integer row, Integer column) -> Integer?
293
+ def goto_table_lookup(table, row, column)
294
+ if table.is_a?(Tables::Compact)
295
+ value = table.__send__(:lookup, row, column)
296
+ return value #: Integer?
297
+ end
298
+
299
+ rows = table #: Array[Hash[Integer, Integer]]
300
+ empty_row = Parser::EMPTY_ROW #: Hash[Integer, Integer]
301
+ rows.fetch(row, empty_row)[column]
247
302
  end
248
303
 
249
304
  # @rbs (Configuration source, ?stack: Array[Integer], ?input_index: Integer, ?shifts: Integer,
@@ -260,15 +315,22 @@ module Ibex
260
315
  RepairPlan.new(edits: edits, configurations: @configurations)
261
316
  end
262
317
 
318
+ # @rbs (Symbol status, ?plan: RepairPlan?) -> RepairSearchResult
319
+ def outcome(status, plan: nil)
320
+ RepairSearchResult.new(status: status, plan: plan, configurations: @configurations)
321
+ end
322
+
263
323
  # @rbs (Integer token_id) -> String
264
324
  def token_name(token_id)
265
- @tables.fetch(:token_names).fetch(token_id, token_id.to_s)
325
+ token_names = @tables.fetch(:token_names) #: Hash[Integer, String]
326
+ token_names.fetch(token_id, token_id.to_s)
266
327
  end
267
328
 
268
329
  # @rbs (Configuration configuration) -> void
269
330
  def push(configuration)
270
331
  priority = priority_for(configuration)
271
- key = [configuration.stack, configuration.input_index, configuration.shifts, configuration.goal]
332
+ key = [configuration.stack, configuration.input_index, configuration.shifts,
333
+ configuration.goal] #: configuration_key
272
334
  previous = @best[key]
273
335
  comparison = previous <=> priority if previous
274
336
  return if comparison && comparison <= 0
@@ -286,23 +348,24 @@ module Ibex
286
348
  priority, configuration = entry
287
349
  return LIMIT unless configuration.is_a?(Configuration)
288
350
 
289
- key = [configuration.stack, configuration.input_index, configuration.shifts, configuration.goal]
351
+ key = [configuration.stack, configuration.input_index, configuration.shifts,
352
+ configuration.goal] #: configuration_key
290
353
  return configuration if @best[key] == priority
291
354
  end
292
355
  end
293
356
 
294
- # @rbs (Configuration configuration) -> Array[untyped]
357
+ # @rbs (Configuration configuration) -> priority
295
358
  def priority_for(configuration)
296
359
  edit_key = configuration.edits.map do |edit|
297
360
  rank = { delete: 0, insert: 1, replace: 2 }.fetch(edit.kind)
298
- [edit.position, rank, edit.token_id]
361
+ [edit.position, rank, edit.token_id] #: [Integer, Integer, Integer]
299
362
  end
300
363
  goal_rank = configuration.goal ? 0 : 1
301
364
  risk = configuration.edits.sum { |edit| semantic_value_risk(edit) }
302
365
  [
303
366
  configuration.cost, risk, edit_key, goal_rank, -configuration.shifts,
304
367
  configuration.input_index, configuration.stack
305
- ]
368
+ ] #: priority
306
369
  end
307
370
 
308
371
  # Prefer punctuation edits when equal-cost repairs are otherwise