ibex-runtime 0.2.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 (102) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +337 -0
  4. data/lib/ibex/runtime/ast_data.rb +23 -0
  5. data/lib/ibex/runtime/cst/annotation.rb +16 -0
  6. data/lib/ibex/runtime/cst/cursor.rb +68 -0
  7. data/lib/ibex/runtime/cst/diff.rb +80 -0
  8. data/lib/ibex/runtime/cst/editing.rb +56 -0
  9. data/lib/ibex/runtime/cst/editor.rb +94 -0
  10. data/lib/ibex/runtime/cst/green/builder.rb +211 -0
  11. data/lib/ibex/runtime/cst/green/cache.rb +150 -0
  12. data/lib/ibex/runtime/cst/green/node.rb +78 -0
  13. data/lib/ibex/runtime/cst/green/token.rb +91 -0
  14. data/lib/ibex/runtime/cst/green/trivia.rb +36 -0
  15. data/lib/ibex/runtime/cst/incremental/blender.rb +233 -0
  16. data/lib/ibex/runtime/cst/incremental/lexed_syntax.rb +28 -0
  17. data/lib/ibex/runtime/cst/incremental/parse_memo.rb +95 -0
  18. data/lib/ibex/runtime/cst/incremental/relexer.rb +134 -0
  19. data/lib/ibex/runtime/cst/incremental/session.rb +229 -0
  20. data/lib/ibex/runtime/cst/incremental/token_memo.rb +66 -0
  21. data/lib/ibex/runtime/cst/kind.rb +133 -0
  22. data/lib/ibex/runtime/cst/parse_result.rb +41 -0
  23. data/lib/ibex/runtime/cst/rewriter.rb +75 -0
  24. data/lib/ibex/runtime/cst/serialize.rb +127 -0
  25. data/lib/ibex/runtime/cst/serialized_tree.rb +44 -0
  26. data/lib/ibex/runtime/cst/source_text.rb +112 -0
  27. data/lib/ibex/runtime/cst/syntax_node.rb +432 -0
  28. data/lib/ibex/runtime/cst/syntax_token.rb +160 -0
  29. data/lib/ibex/runtime/cst/text_edit.rb +77 -0
  30. data/lib/ibex/runtime/cst/typed_node.rb +87 -0
  31. data/lib/ibex/runtime/cst/validator.rb +428 -0
  32. data/lib/ibex/runtime/cst.rb +38 -0
  33. data/lib/ibex/runtime/event.rb +44 -0
  34. data/lib/ibex/runtime/event_jsonl_tracer.rb +37 -0
  35. data/lib/ibex/runtime/event_sanitizer.rb +273 -0
  36. data/lib/ibex/runtime/generated_lexer.rb +514 -0
  37. data/lib/ibex/runtime/jsonl_tracer.rb +75 -0
  38. data/lib/ibex/runtime/lexer_input.rb +107 -0
  39. data/lib/ibex/runtime/location_span.rb +114 -0
  40. data/lib/ibex/runtime/observation.rb +82 -0
  41. data/lib/ibex/runtime/parser.rb +3027 -0
  42. data/lib/ibex/runtime/parser_sync_recovery.rb +108 -0
  43. data/lib/ibex/runtime/repair.rb +126 -0
  44. data/lib/ibex/runtime/repair_priority_queue.rb +60 -0
  45. data/lib/ibex/runtime/repair_search.rb +318 -0
  46. data/lib/ibex/runtime/resource_limits.rb +64 -0
  47. data/lib/ibex/runtime/table_format.rb +11 -0
  48. data/lib/ibex/runtime/version.rb +7 -0
  49. data/lib/ibex/runtime.rb +27 -0
  50. data/lib/ibex/tables/compact.rb +186 -0
  51. data/lib/ibex/tables/compact_actions.rb +135 -0
  52. data/lib/ibex/tables/compact_productions.rb +167 -0
  53. data/sig/ibex/runtime/ast_data.rbs +12 -0
  54. data/sig/ibex/runtime/cst/annotation.rbs +13 -0
  55. data/sig/ibex/runtime/cst/cursor.rbs +34 -0
  56. data/sig/ibex/runtime/cst/diff.rbs +29 -0
  57. data/sig/ibex/runtime/cst/editing.rbs +23 -0
  58. data/sig/ibex/runtime/cst/editor.rbs +43 -0
  59. data/sig/ibex/runtime/cst/green/builder.rbs +84 -0
  60. data/sig/ibex/runtime/cst/green/cache.rbs +60 -0
  61. data/sig/ibex/runtime/cst/green/node.rbs +49 -0
  62. data/sig/ibex/runtime/cst/green/token.rbs +59 -0
  63. data/sig/ibex/runtime/cst/green/trivia.rbs +29 -0
  64. data/sig/ibex/runtime/cst/incremental/blender.rbs +114 -0
  65. data/sig/ibex/runtime/cst/incremental/lexed_syntax.rbs +20 -0
  66. data/sig/ibex/runtime/cst/incremental/parse_memo.rbs +59 -0
  67. data/sig/ibex/runtime/cst/incremental/relexer.rbs +54 -0
  68. data/sig/ibex/runtime/cst/incremental/session.rbs +89 -0
  69. data/sig/ibex/runtime/cst/incremental/token_memo.rbs +31 -0
  70. data/sig/ibex/runtime/cst/kind.rbs +93 -0
  71. data/sig/ibex/runtime/cst/parse_result.rbs +35 -0
  72. data/sig/ibex/runtime/cst/rewriter.rbs +31 -0
  73. data/sig/ibex/runtime/cst/serialize.rbs +39 -0
  74. data/sig/ibex/runtime/cst/serialized_tree.rbs +34 -0
  75. data/sig/ibex/runtime/cst/source_text.rbs +51 -0
  76. data/sig/ibex/runtime/cst/syntax_node.rbs +195 -0
  77. data/sig/ibex/runtime/cst/syntax_token.rbs +112 -0
  78. data/sig/ibex/runtime/cst/text_edit.rbs +33 -0
  79. data/sig/ibex/runtime/cst/typed_node.rbs +50 -0
  80. data/sig/ibex/runtime/cst/validator.rbs +108 -0
  81. data/sig/ibex/runtime/cst.rbs +9 -0
  82. data/sig/ibex/runtime/event.rbs +26 -0
  83. data/sig/ibex/runtime/event_jsonl_tracer.rbs +25 -0
  84. data/sig/ibex/runtime/event_sanitizer.rbs +92 -0
  85. data/sig/ibex/runtime/generated_lexer.rbs +169 -0
  86. data/sig/ibex/runtime/jsonl_tracer.rbs +42 -0
  87. data/sig/ibex/runtime/lexer_input.rbs +44 -0
  88. data/sig/ibex/runtime/location_span.rbs +64 -0
  89. data/sig/ibex/runtime/observation.rbs +35 -0
  90. data/sig/ibex/runtime/parser.rbs +926 -0
  91. data/sig/ibex/runtime/parser_sync_recovery.rbs +60 -0
  92. data/sig/ibex/runtime/repair.rbs +92 -0
  93. data/sig/ibex/runtime/repair_priority_queue.rbs +27 -0
  94. data/sig/ibex/runtime/repair_search.rbs +114 -0
  95. data/sig/ibex/runtime/resource_limits.rbs +39 -0
  96. data/sig/ibex/runtime/table_format.rbs +11 -0
  97. data/sig/ibex/runtime/version.rbs +7 -0
  98. data/sig/ibex/runtime.rbs +6 -0
  99. data/sig/ibex/tables/compact.rbs +70 -0
  100. data/sig/ibex/tables/compact_actions.rbs +57 -0
  101. data/sig/ibex/tables/compact_productions.rbs +68 -0
  102. metadata +144 -0
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "runtime/version"
4
+ require_relative "runtime/table_format"
5
+ require_relative "tables/compact"
6
+ require_relative "tables/compact_actions"
7
+ require_relative "tables/compact_productions"
8
+ require_relative "runtime/event_sanitizer"
9
+ require_relative "runtime/event"
10
+ require_relative "runtime/observation"
11
+ require_relative "runtime/cst"
12
+ require_relative "runtime/ast_data"
13
+ require_relative "runtime/resource_limits"
14
+ require_relative "runtime/repair"
15
+ require_relative "runtime/repair_priority_queue"
16
+ require_relative "runtime/repair_search"
17
+ require_relative "runtime/parser_sync_recovery"
18
+ require_relative "runtime/parser"
19
+ require_relative "runtime/lexer_input"
20
+ require_relative "runtime/generated_lexer"
21
+ require_relative "runtime/jsonl_tracer"
22
+ require_relative "runtime/event_jsonl_tracer"
23
+
24
+ module Ibex
25
+ module Runtime
26
+ end
27
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ module Tables
6
+ # Encodes nonnegative integer arrays into compact generated-source
7
+ # literals. Zero represents nil; positive values are offset by one.
8
+ module PackedIntegers
9
+ module_function
10
+
11
+ # @rbs (Array[Integer?] values) -> String
12
+ def encode(values)
13
+ encoded = values.map do |value|
14
+ raise ArgumentError, "packed integer must be nonnegative or nil" if value&.negative?
15
+
16
+ value ? value + 1 : 0
17
+ end.pack("w*")
18
+ [encoded].pack("m0")
19
+ end
20
+
21
+ # @rbs (String source) -> Array[Integer?]
22
+ def decode(source)
23
+ binary = source.unpack1("m0")
24
+ raise ArgumentError, "packed integer source is invalid" unless binary.is_a?(String)
25
+
26
+ values = binary.unpack("w*") #: Array[Integer]
27
+ values.map { |value| value.zero? ? nil : value - 1 }
28
+ rescue ArgumentError
29
+ raise ArgumentError, "packed integer source is invalid"
30
+ end
31
+
32
+ # @rbs (String source) -> Array[Integer]
33
+ def decode_required(source)
34
+ decode(source).map do |value|
35
+ raise ArgumentError, "packed integer source contains nil" unless value
36
+
37
+ value
38
+ end
39
+ end
40
+
41
+ # Zigzag-encode signed values after reserving zero for nil.
42
+ # @rbs (Array[Integer?] values) -> String
43
+ def encode_signed(values)
44
+ encode(
45
+ values.map do |value|
46
+ next unless value
47
+
48
+ value.negative? ? (-value * 2) - 1 : value * 2
49
+ end
50
+ )
51
+ end
52
+
53
+ # @rbs (String source) -> Array[Integer?]
54
+ def decode_signed(source)
55
+ decode(source).map do |value|
56
+ next unless value
57
+
58
+ value.even? ? value / 2 : -((value + 1) / 2)
59
+ end
60
+ end
61
+ end
62
+
63
+ # Sparse table represented by per-row offsets and ownership checks.
64
+ class Compact
65
+ DENSE_CELL_LIMIT = 1_000_000 #: Integer
66
+
67
+ attr_reader :offsets #: Array[Integer]
68
+ attr_reader :values #: Array[untyped]
69
+ attr_reader :checks #: Array[Integer?]
70
+ attr_reader :row_count #: Integer
71
+ attr_reader :dense_values #: Array[untyped]?
72
+ attr_reader :dense_width #: Integer?
73
+
74
+ class << self
75
+ # @rbs (Array[Hash[Integer, untyped]] rows, ?dense: bool) -> Compact
76
+ def build(rows, dense: true)
77
+ offsets = Array.new(rows.length, 0)
78
+ values = [] #: Array[untyped]
79
+ checks = [] #: Array[Integer?]
80
+ next_offsets = {} #: Hash[Array[Integer], Integer]
81
+ rows.each_index.sort_by { |row| [-rows[row].length, row] }.each do |row|
82
+ offset = find_offset(rows[row].keys, checks, next_offsets)
83
+ offsets[row] = offset
84
+ rows[row].each do |column, value|
85
+ index = offset + column
86
+ values[index] = value
87
+ checks[index] = row
88
+ end
89
+ end
90
+ dense_width = rows.flat_map(&:keys).max.to_i + 1 if dense
91
+ dense_width = nil if dense_width && (rows.length * dense_width) > DENSE_CELL_LIMIT
92
+ new(
93
+ offsets: offsets, values: values, checks: checks, row_count: rows.length,
94
+ dense_width: dense_width
95
+ )
96
+ end
97
+
98
+ # @rbs (String offsets, String values, String checks, row_count: Integer,
99
+ # ?dense_width: Integer?) -> Compact
100
+ def packed(offsets, values, checks, row_count:, dense_width: nil)
101
+ new(
102
+ offsets: PackedIntegers.decode_required(offsets),
103
+ values: PackedIntegers.decode(values),
104
+ checks: PackedIntegers.decode(checks),
105
+ row_count: row_count,
106
+ dense_width: dense_width
107
+ )
108
+ end
109
+
110
+ private
111
+
112
+ # @rbs (Array[Integer] columns, Array[Integer?] checks, Hash[Array[Integer], Integer] next_offsets) -> Integer
113
+ def find_offset(columns, checks, next_offsets)
114
+ return 0 if columns.empty?
115
+
116
+ signature = columns.sort
117
+ offset = next_offsets.fetch(signature, 0)
118
+ offset += 1 while columns.any? { |column| checks[offset + column] }
119
+ next_offsets[signature] = offset + 1
120
+ offset
121
+ end
122
+ end
123
+
124
+ # @rbs (offsets: Array[Integer], values: Array[untyped], checks: Array[Integer?],
125
+ # row_count: Integer, ?dense_width: Integer?) -> void
126
+ def initialize(offsets:, values:, checks:, row_count:, dense_width: nil)
127
+ @offsets = offsets.freeze
128
+ @values = values.freeze
129
+ @checks = checks.freeze
130
+ @row_count = row_count
131
+ @dense_width = dense_width
132
+ @dense_values = dense_layout(dense_width)
133
+ freeze
134
+ end
135
+
136
+ # Keep predicate dispatch out of this lookup because every parser action
137
+ # and goto crosses it.
138
+ # rubocop:disable Style/NumericPredicate
139
+ # @rbs (Integer row, Integer column) -> untyped
140
+ def lookup(row, column)
141
+ return nil if row < 0 || row >= @row_count || column < 0
142
+
143
+ index = @offsets[row] + column
144
+ return nil if index < 0 || index >= @checks.length
145
+
146
+ @checks[index] == row ? @values[index] : nil
147
+ end
148
+ # rubocop:enable Style/NumericPredicate
149
+
150
+ # @rbs (Integer row) -> Hash[Integer, untyped]
151
+ def row(row)
152
+ return {} unless row.between?(0, @row_count - 1)
153
+
154
+ result = {} #: Hash[Integer, untyped]
155
+ @checks.each_index do |index|
156
+ next unless @checks[index] == row
157
+
158
+ result[index - @offsets[row]] = @values[index]
159
+ end
160
+ result
161
+ end
162
+
163
+ private
164
+
165
+ # @rbs (Integer? dense_width) -> Array[untyped]?
166
+ def dense_layout(dense_width)
167
+ return nil unless dense_width
168
+ raise ArgumentError, "compact dense width must be positive" unless dense_width.positive?
169
+ return nil if (@row_count * dense_width) > DENSE_CELL_LIMIT
170
+
171
+ dense = Array.new(@row_count * dense_width) #: Array[untyped]
172
+ @checks.each_index do |index|
173
+ row = @checks[index]
174
+ next unless row
175
+
176
+ column = index - @offsets.fetch(row)
177
+ raise ArgumentError, "compact column exceeds the dense row width" unless
178
+ column.between?(0, dense_width - 1)
179
+
180
+ dense[(row * dense_width) + column] = @values[index]
181
+ end
182
+ dense.freeze
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require_relative "compact" unless defined?(Ibex::Tables::Compact)
5
+
6
+ module Ibex
7
+ module Tables
8
+ # Compact action table with an integer-coded hot representation and a
9
+ # compatible decoded lookup surface for the generic runtime.
10
+ class CompactActions < Compact
11
+ ACCEPT_CODE = 0 #: Integer
12
+ ERROR_CODE = -1 #: Integer
13
+ SHIFT_BASE = 1 #: Integer
14
+ REDUCE_BASE = -2 #: Integer
15
+ LEGACY_ERROR_CODE = 1 #: Integer
16
+ LEGACY_SHIFT_BASE = 2 #: Integer
17
+ LEGACY_REDUCE_BASE = 3 #: Integer
18
+ private_constant :LEGACY_ERROR_CODE, :LEGACY_SHIFT_BASE, :LEGACY_REDUCE_BASE
19
+
20
+ attr_reader :codes #: Array[Integer?]
21
+ attr_reader :dense_codes #: Array[Integer?]?
22
+ attr_reader :column_count #: Integer?
23
+
24
+ class << self
25
+ # @rbs (Array[Hash[Integer, untyped]] rows) -> CompactActions
26
+ def build(rows)
27
+ packed_rows = rows.map do |row|
28
+ row.transform_values { |action| pack(action) }
29
+ end
30
+ layout = Compact.build(packed_rows, dense: false)
31
+ column_count = rows.flat_map(&:keys).max.to_i + 1
32
+ column_count = nil if (rows.length * column_count) > Compact::DENSE_CELL_LIMIT
33
+ new(
34
+ offsets: layout.offsets,
35
+ codes: layout.values,
36
+ checks: layout.checks,
37
+ row_count: layout.row_count,
38
+ encoding: :signed,
39
+ column_count: column_count
40
+ )
41
+ end
42
+
43
+ # @rbs (String offsets, String codes, String checks, row_count: Integer,
44
+ # ?encoding: Symbol, ?column_count: Integer?) -> CompactActions
45
+ def packed(offsets, codes, checks, row_count:, encoding: :legacy, column_count: nil)
46
+ new(
47
+ offsets: PackedIntegers.decode_required(offsets),
48
+ codes: encoding == :signed ? PackedIntegers.decode_signed(codes) : PackedIntegers.decode(codes),
49
+ checks: PackedIntegers.decode(checks),
50
+ row_count: row_count,
51
+ encoding: encoding,
52
+ column_count: column_count
53
+ )
54
+ end
55
+
56
+ # @rbs (untyped action) -> Integer?
57
+ def pack(action)
58
+ return unless action
59
+
60
+ case action.fetch(0)
61
+ when :accept then ACCEPT_CODE
62
+ when :error then ERROR_CODE
63
+ when :shift then SHIFT_BASE + action.fetch(1)
64
+ when :reduce then REDUCE_BASE - action.fetch(1)
65
+ else raise ArgumentError, "unknown compact parser action #{action.inspect}"
66
+ end
67
+ end
68
+
69
+ # @rbs (Integer? code) -> untyped
70
+ def unpack(code)
71
+ return unless code
72
+ return [:accept].freeze if code == ACCEPT_CODE
73
+ return [:error].freeze if code == ERROR_CODE
74
+ return [:shift, code - SHIFT_BASE].freeze if code.positive?
75
+
76
+ [:reduce, REDUCE_BASE - code].freeze
77
+ end
78
+
79
+ # Convert codes emitted before signed compact actions without changing
80
+ # their generated table literals.
81
+ # @rbs (Integer? code) -> Integer?
82
+ def legacy_to_signed(code)
83
+ return unless code
84
+ return ACCEPT_CODE if code == ACCEPT_CODE
85
+ return ERROR_CODE if code == LEGACY_ERROR_CODE
86
+ return SHIFT_BASE + ((code - LEGACY_SHIFT_BASE) / 2) if code.even?
87
+
88
+ REDUCE_BASE - ((code - LEGACY_REDUCE_BASE) / 2)
89
+ end
90
+ end
91
+
92
+ # @rbs (offsets: Array[Integer], codes: Array[Integer?], checks: Array[Integer?],
93
+ # row_count: Integer, ?encoding: Symbol, ?column_count: Integer?) -> void
94
+ def initialize(offsets:, codes:, checks:, row_count:, encoding: :legacy, column_count: nil)
95
+ codes = codes.map { |code| self.class.legacy_to_signed(code) } if encoding == :legacy
96
+ unless %i[legacy signed].include?(encoding)
97
+ raise ArgumentError, "unknown compact action encoding #{encoding.inspect}"
98
+ end
99
+
100
+ @codes = codes.freeze
101
+ @column_count = column_count
102
+ @dense_codes = dense_action_layout(offsets, codes, checks, row_count, column_count)
103
+ decoded_cache = {} #: Hash[Integer, untyped]
104
+ decoded = codes.map do |code|
105
+ code.nil? ? nil : decoded_cache[code] ||= self.class.unpack(code)
106
+ end
107
+ super(offsets: offsets, values: decoded, checks: checks, row_count: row_count)
108
+ end
109
+
110
+ private
111
+
112
+ # @rbs (Array[Integer] offsets, Array[Integer?] codes, Array[Integer?] checks,
113
+ # Integer row_count, Integer? column_count) -> Array[Integer?]?
114
+ def dense_action_layout(offsets, codes, checks, row_count, column_count)
115
+ return nil unless column_count
116
+ raise ArgumentError, "compact action column count must be positive" unless column_count.positive?
117
+
118
+ return nil if (row_count * column_count) > Compact::DENSE_CELL_LIMIT
119
+
120
+ dense = Array.new(row_count * column_count) #: Array[Integer?]
121
+ checks.each_index do |index|
122
+ row = checks[index]
123
+ next unless row
124
+
125
+ column = index - offsets.fetch(row)
126
+ raise ArgumentError, "compact action column exceeds the dense row width" unless
127
+ column.between?(0, column_count - 1)
128
+
129
+ dense[(row * column_count) + column] = codes[index]
130
+ end
131
+ dense.freeze
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ module Tables
6
+ # Production metadata with parallel primitive arrays for the direct parser
7
+ # and a compatible Array-of-Hash surface for generic runtime consumers.
8
+ class CompactProductions < Array #[Hash[Symbol, untyped]]
9
+ VALUES_ACTION = 1 #: Integer
10
+ BORROWED_VALUES_ACTION = 2 #: Integer
11
+ LOCATION_ACTION = 4 #: Integer
12
+ COMPOSITION_ACTION = 8 #: Integer
13
+ POSITIONAL_ACTION = 16 #: Integer
14
+ VALID_FLAGS = VALUES_ACTION | BORROWED_VALUES_ACTION | LOCATION_ACTION |
15
+ COMPOSITION_ACTION | POSITIONAL_ACTION #: Integer
16
+ CORE_FIELDS = %i[
17
+ lhs length action values_action borrowed_values_action location_action composition_action
18
+ ].freeze #: Array[Symbol]
19
+ private_constant :VALID_FLAGS, :CORE_FIELDS
20
+
21
+ attr_reader :lhs_ids #: Array[Integer]
22
+ attr_reader :lengths #: Array[Integer]
23
+ attr_reader :actions #: Array[Symbol?]
24
+ attr_reader :flags #: Array[Integer]
25
+
26
+ class << self
27
+ # @rbs (Array[Hash[Symbol, untyped]] productions) -> CompactProductions
28
+ def build(productions)
29
+ lhs_ids = [] #: Array[Integer]
30
+ lengths = [] #: Array[Integer]
31
+ actions = [] #: Array[Symbol?]
32
+ flags = [] #: Array[Integer]
33
+ metadata = {} #: Hash[Integer, Hash[Symbol, untyped]]
34
+ productions.each_with_index do |production, index|
35
+ lhs_ids << production.fetch(:lhs)
36
+ lengths << production.fetch(:length)
37
+ actions << production[:action]
38
+ flags << flags_for(production)
39
+ extra = production.except(
40
+ :lhs, :length, :action, :values_action, :borrowed_values_action, :location_action, :composition_action
41
+ )
42
+ metadata[index] = extra unless extra.empty?
43
+ end
44
+ new(lhs_ids: lhs_ids, lengths: lengths, actions: actions, flags: flags, metadata: metadata)
45
+ end
46
+
47
+ # @rbs (String lhs_ids, String lengths, String flags,
48
+ # ?metadata: Hash[Integer, Hash[Symbol, untyped]]) -> CompactProductions
49
+ def packed(lhs_ids, lengths, flags, metadata: {})
50
+ decoded_flags = PackedIntegers.decode_required(flags)
51
+ actions = decoded_flags.each_index.map do |index|
52
+ :"_ibex_action_#{index}" unless decoded_flags[index].zero?
53
+ end
54
+ new(
55
+ lhs_ids: PackedIntegers.decode_required(lhs_ids),
56
+ lengths: PackedIntegers.decode_required(lengths),
57
+ actions: actions,
58
+ flags: decoded_flags,
59
+ metadata: metadata
60
+ )
61
+ end
62
+
63
+ private
64
+
65
+ # @rbs (Hash[Symbol, untyped] production) -> Integer
66
+ def flags_for(production)
67
+ value = 0
68
+ value |= VALUES_ACTION if production[:values_action] == true
69
+ value |= BORROWED_VALUES_ACTION if production[:borrowed_values_action] == true
70
+ value |= LOCATION_ACTION if production[:location_action] == true
71
+ value |= COMPOSITION_ACTION if production[:composition_action] == true
72
+ value
73
+ end
74
+ end
75
+
76
+ # @rbs (lhs_ids: Array[Integer], lengths: Array[Integer], actions: Array[Symbol?],
77
+ # flags: Array[Integer], ?metadata: Hash[Integer, Hash[Symbol, untyped]]) -> void
78
+ def initialize(lhs_ids:, lengths:, actions:, flags:, metadata: {})
79
+ size = lhs_ids.length
80
+ unless lengths.length == size && actions.length == size && flags.length == size
81
+ raise ArgumentError, "compact production arrays must have the same length"
82
+ end
83
+
84
+ @lhs_ids = lhs_ids.map { |value| nonnegative_integer!(value, "lhs") }.freeze
85
+ @lengths = lengths.map { |value| nonnegative_integer!(value, "length") }.freeze
86
+ @actions = actions.map { |value| action!(value) }.freeze
87
+ @flags = flags.map { |value| flags!(value) }.freeze
88
+ validate_markers!
89
+ validate_metadata!(metadata, size)
90
+ super(Array.new(size) { |index| decode(index, metadata[index]) })
91
+ freeze
92
+ end
93
+
94
+ # @rbs () -> bool
95
+ def direct_values?
96
+ @direct_values
97
+ end
98
+
99
+ private
100
+
101
+ # @rbs (untyped value, String name) -> Integer
102
+ def nonnegative_integer!(value, name)
103
+ return value if value.is_a?(Integer) && !value.negative?
104
+
105
+ raise ArgumentError, "compact production #{name} must be a nonnegative Integer"
106
+ end
107
+
108
+ # @rbs (untyped value) -> Symbol?
109
+ def action!(value)
110
+ return value if value.nil? || value.is_a?(Symbol)
111
+
112
+ raise ArgumentError, "compact production action must be a Symbol or nil"
113
+ end
114
+
115
+ # @rbs (untyped value) -> Integer
116
+ def flags!(value)
117
+ return value if value.is_a?(Integer) && !value.negative? && value.nobits?(~VALID_FLAGS)
118
+
119
+ raise ArgumentError, "compact production flags are invalid"
120
+ end
121
+
122
+ # @rbs () -> void
123
+ def validate_markers!
124
+ @flags.each_with_index do |value, index|
125
+ borrowed = value.anybits?(BORROWED_VALUES_ACTION)
126
+ values = value.anybits?(VALUES_ACTION)
127
+ positional = value.anybits?(POSITIONAL_ACTION)
128
+ raise ArgumentError, "borrowed production #{index} must be a values action" if borrowed && !values
129
+ if positional && value.anybits?(VALUES_ACTION | LOCATION_ACTION | COMPOSITION_ACTION)
130
+ raise ArgumentError, "positional production #{index} cannot combine action ABI flags"
131
+ end
132
+ end
133
+ @direct_values = @actions.each_index.all? do |index|
134
+ @actions[index].nil? || @flags[index].anybits?(VALUES_ACTION | POSITIONAL_ACTION)
135
+ end
136
+ end
137
+
138
+ # @rbs (Hash[Integer, Hash[Symbol, untyped]] metadata, Integer size) -> void
139
+ def validate_metadata!(metadata, size)
140
+ metadata.each do |index, entry|
141
+ unless index.is_a?(Integer) && index.between?(0, size - 1) && entry.is_a?(Hash)
142
+ raise ArgumentError, "compact production metadata is invalid"
143
+ end
144
+ if entry.keys.any? { |key| CORE_FIELDS.include?(key) }
145
+ raise ArgumentError, "compact production metadata cannot replace core fields"
146
+ end
147
+ end
148
+ end
149
+
150
+ # @rbs (Integer index, Hash[Symbol, untyped]? metadata) -> Hash[Symbol, untyped]
151
+ def decode(index, metadata)
152
+ entry = {} #: Hash[Symbol, untyped]
153
+ entry[:lhs] = @lhs_ids[index]
154
+ entry[:length] = @lengths[index]
155
+ entry[:action] = @actions[index]
156
+ value = @flags[index]
157
+ entry[:values_action] = true if value.anybits?(VALUES_ACTION)
158
+ entry[:borrowed_values_action] = true if value.anybits?(BORROWED_VALUES_ACTION)
159
+ entry[:location_action] = true if value.anybits?(LOCATION_ACTION)
160
+ entry[:composition_action] = true if value.anybits?(COMPOSITION_ACTION)
161
+ entry[:positional_action] = true if value.anybits?(POSITIONAL_ACTION)
162
+ entry.merge!(metadata) if metadata
163
+ entry.freeze
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,12 @@
1
+ # Generated from lib/ibex/runtime/ast_data.rb with RBS::Inline
2
+
3
+ module Ibex
4
+ module Runtime
5
+ # Builds Data classes on Ruby 3.2+ and an immutable Struct-compatible
6
+ # fallback on older supported Rubies.
7
+ module ASTData
8
+ # @rbs (*Symbol members) -> singleton(Object)
9
+ def self?.define: (*Symbol members) -> singleton(Object)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ # Generated from lib/ibex/runtime/cst/annotation.rb with RBS::Inline
2
+
3
+ module Ibex
4
+ module Runtime
5
+ module CST
6
+ # Opaque identity used to track a syntax occurrence across path-copying edits.
7
+ class SyntaxAnnotation
8
+ # @rbs () -> void
9
+ def initialize: () -> void
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ # Generated from lib/ibex/runtime/cst/cursor.rb with RBS::Inline
2
+
3
+ module Ibex
4
+ module Runtime
5
+ module CST
6
+ # Allocation-light cursor over Green elements and absolute offsets.
7
+ # rubocop:disable Naming/PredicateMethod -- cursor movement names follow the public tree-cursor convention.
8
+ class Cursor
9
+ type green = GreenNode | GreenToken
10
+
11
+ type frame = [ GreenNode, Integer, Integer ]
12
+
13
+ attr_reader green: green
14
+
15
+ attr_reader offset: Integer
16
+
17
+ # @rbs (SyntaxNode node) -> void
18
+ def initialize: (SyntaxNode node) -> void
19
+
20
+ # @rbs () -> bool
21
+ def goto_first_child: () -> bool
22
+
23
+ # @rbs () -> bool
24
+ def goto_next_sibling: () -> bool
25
+
26
+ # @rbs () -> bool
27
+ def goto_parent: () -> bool
28
+
29
+ # @rbs () -> Integer
30
+ def kind: () -> Integer
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ # Generated from lib/ibex/runtime/cst/diff.rb with RBS::Inline
2
+
3
+ module Ibex
4
+ module Runtime
5
+ module CST
6
+ # Computes byte edits while skipping physically shared Green subtrees.
7
+ module Diff
8
+ # @rbs (SyntaxNode old_root, SyntaxNode new_root) -> Array[TextEdit]
9
+ def self?.text_edits: (SyntaxNode old_root, SyntaxNode new_root) -> Array[TextEdit]
10
+
11
+ # @rbs (GreenNode | GreenToken old_green, GreenNode | GreenToken new_green,
12
+ # Integer offset, Array[TextEdit] edits) -> void
13
+ def self?.compare: (GreenNode | GreenToken old_green, GreenNode | GreenToken new_green, Integer offset, Array[TextEdit] edits) -> void
14
+
15
+ # @rbs (GreenNode | GreenToken left, GreenNode | GreenToken right) -> bool
16
+ def self?.compatible_nodes?: (GreenNode | GreenToken left, GreenNode | GreenToken right) -> bool
17
+
18
+ # @rbs (String old_text, String new_text, Integer offset, Array[TextEdit] edits) -> void
19
+ def self?.append_trimmed_edit: (String old_text, String new_text, Integer offset, Array[TextEdit] edits) -> void
20
+
21
+ # @rbs (String left, String right) -> Integer
22
+ def self?.common_prefix: (String left, String right) -> Integer
23
+
24
+ # @rbs (String left, String right, Integer prefix) -> Integer
25
+ def self?.common_suffix: (String left, String right, Integer prefix) -> Integer
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ # Generated from lib/ibex/runtime/cst/editing.rb with RBS::Inline
2
+
3
+ module Ibex
4
+ module Runtime
5
+ module CST
6
+ # Shared path-copying operations for Red syntax elements.
7
+ module Editing
8
+ type element = SyntaxNode | SyntaxToken
9
+
10
+ type green = GreenNode | GreenToken
11
+
12
+ # @rbs (element target, green | element replacement) -> SyntaxNode
13
+ def self?.replace: (element target, green | element replacement) -> SyntaxNode
14
+
15
+ # @rbs (SyntaxNode previous, GreenNode green) -> SyntaxNode
16
+ def self?.red_root: (SyntaxNode previous, GreenNode green) -> SyntaxNode
17
+
18
+ # @rbs (green | element value) -> green
19
+ def self?.green_element: (green | element value) -> green
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,43 @@
1
+ # Generated from lib/ibex/runtime/cst/editor.rb with RBS::Inline
2
+
3
+ module Ibex
4
+ module Runtime
5
+ module CST
6
+ class EditConflictError < StandardError
7
+ end
8
+
9
+ # Applies multiple occurrence-addressed replacements in one path-copying pass.
10
+ class SyntaxEditor
11
+ type element = SyntaxNode | SyntaxToken
12
+
13
+ type green = GreenNode | GreenToken
14
+
15
+ type edit = [ element, green ]
16
+
17
+ @root: SyntaxNode
18
+
19
+ @edits: Array[edit]
20
+
21
+ # @rbs (SyntaxNode root) -> void
22
+ def initialize: (SyntaxNode root) -> void
23
+
24
+ # @rbs (element target, green | element replacement) -> self
25
+ def replace: (element target, green | element replacement) -> self
26
+
27
+ # @rbs () -> SyntaxNode
28
+ def apply: () -> SyntaxNode
29
+
30
+ private
31
+
32
+ # @rbs (element element) -> green
33
+ def apply_element: (element element) -> green
34
+
35
+ # @rbs (element possible_ancestor, element element) -> bool
36
+ def ancestor?: (element possible_ancestor, element element) -> bool
37
+
38
+ # @rbs (element left, element right) -> bool
39
+ def same_occurrence?: (element left, element right) -> bool
40
+ end
41
+ end
42
+ end
43
+ end