ibex 0.3.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.
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ module Impact
6
+ # Converts command-line symbols and Diff rule ids into analysis seeds.
7
+ class Seeds
8
+ # @rbs @grammar: IR::Grammar
9
+ # @rbs @sets: Analysis::Sets
10
+ # @rbs @records: Array[Hash[Symbol, Object?]]
11
+ # @rbs @ids: Array[Integer]
12
+ attr_reader :ids #: Array[Integer]
13
+ attr_reader :records #: Array[Hash[Symbol, Object?]]
14
+
15
+ # @rbs (IR::Grammar grammar, Array[String] names, ?origin: String) -> void
16
+ def initialize(grammar, names, origin: "symbol")
17
+ @grammar = grammar
18
+ @sets = Analysis::Sets.new(grammar)
19
+ @records = names.flat_map { |name| resolve(name, origin) }.sort_by { |record| seed_symbol(record) }
20
+ @ids = @records.map { |record| seed_id(record) }.uniq.freeze
21
+ @records = @records.freeze
22
+ freeze
23
+ end
24
+
25
+ # @rbs (IR::Grammar grammar, Hash[Symbol, Object?] diff, ?origin: String) -> Seeds
26
+ def self.from_diff(grammar, diff, origin: "diff")
27
+ rules = diff.fetch(:rules) #: Hash[Symbol, Object?]
28
+ names = %i[added removed changed].flat_map do |section|
29
+ records = rules.fetch(section) #: Array[Hash[Symbol, Object?]]
30
+ records.map { |record| record.fetch(:id).to_s }
31
+ end.uniq.sort
32
+ new(grammar, names, origin: origin)
33
+ end
34
+
35
+ private
36
+
37
+ # @rbs (Hash[Symbol, Object?] record) -> String
38
+ def seed_symbol(record)
39
+ record.fetch(:symbol) #: String
40
+ end
41
+
42
+ # @rbs (Hash[Symbol, Object?] record) -> Integer
43
+ def seed_id(record)
44
+ record.fetch(:id) #: Integer
45
+ end
46
+
47
+ # @rbs (String name, String origin) -> Array[Hash[Symbol, Object?]]
48
+ def resolve(name, origin)
49
+ definition = @grammar.symbol(name)
50
+ raise Ibex::Error, "(impact):1:1: unknown symbol #{name}" unless definition
51
+ raise Ibex::Error, "(impact):1:1: impact seed #{name} is not a nonterminal" unless definition.nonterminal?
52
+
53
+ [{ symbol: definition.name, id: definition.id, origin: origin,
54
+ nullable_boundary: nullable_boundary?(definition.id) }]
55
+ end
56
+
57
+ # @rbs (Integer) -> bool
58
+ def nullable_boundary?(id)
59
+ return true if @sets.nullable?(id)
60
+
61
+ @grammar.productions.any? do |production|
62
+ production.rhs.include?(id) && production.rhs.any? do |symbol_id|
63
+ @grammar.symbol_by_id(symbol_id)&.nonterminal? && @sets.nullable?(symbol_id)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require "json"
5
+ require "optparse"
6
+
7
+ module Ibex
8
+ module Impact
9
+ # Stores only conflict identities so a baseline is stable across state ids.
10
+ class Baseline
11
+ # @rbs (String path) -> void
12
+ def initialize(path)
13
+ @path = path
14
+ end
15
+
16
+ # @rbs () -> Array[String]
17
+ def conflicts
18
+ return [] unless File.file?(@path)
19
+
20
+ value = JSON.parse(File.binread(@path))
21
+ validate_document(value)
22
+ conflicts = value.fetch("conflicts") #: Array[untyped]
23
+ unless conflicts.all? { |identity| identity.is_a?(String) && !identity.empty? }
24
+ raise Ibex::Error, "#{@path}:1:1: invalid impact baseline: conflicts must contain non-empty strings"
25
+ end
26
+
27
+ conflicts.sort.uniq
28
+ rescue JSON::ParserError => e
29
+ raise Ibex::Error, "#{@path}:1:1: invalid impact baseline: #{e.message}"
30
+ end
31
+
32
+ # @rbs (Object?) -> void
33
+ def validate_document(value)
34
+ unless value.is_a?(Hash) && value.keys.sort == %w[conflicts schema_version]
35
+ raise Ibex::Error, "#{@path}:1:1: invalid impact baseline: expected schema_version and conflicts"
36
+ end
37
+ unless value.fetch("conflicts").is_a?(Array)
38
+ raise Ibex::Error, "#{@path}:1:1: invalid impact baseline: conflicts must be an array"
39
+ end
40
+ return if value.fetch("schema_version") == 1
41
+
42
+ raise Ibex::Error, "#{@path}:1:1: unsupported impact baseline schema"
43
+ end
44
+
45
+ # @rbs (Array[String] identities) -> void
46
+ def write(identities)
47
+ File.write(@path, "#{JSON.pretty_generate({ 'schema_version' => 1, 'conflicts' => identities.sort.uniq })}\n")
48
+ end
49
+ end
50
+
51
+ # Severity ranking and CI gate evaluation for impact findings.
52
+ module Severity
53
+ LEVELS = %w[info low medium high critical].freeze #: Array[String]
54
+ FAIL_ON = %w[
55
+ new_conflict nullable_change first_change follow_change action_arity unreachable
56
+ ].freeze #: Array[String]
57
+ RANK = LEVELS.each_with_index.to_h.freeze #: Hash[String, Integer]
58
+ GATE_TO_KIND = {
59
+ "nullable_change" => "nullable", "first_change" => "first", "follow_change" => "follow"
60
+ }.freeze #: Hash[String, String]
61
+
62
+ module_function
63
+
64
+ # @rbs (String left, String right) -> String
65
+ def max(left, right)
66
+ RANK.fetch(left) >= RANK.fetch(right) ? left : right
67
+ end
68
+
69
+ # @rbs (Hash[String, Array[String]], Array[Hash[Symbol, String]]) -> Hash[String, String]
70
+ def symbols(changes, actions)
71
+ levels = Hash.new("info") #: Hash[String, String]
72
+ changes.each do |name, kinds|
73
+ kinds.each do |kind|
74
+ level = level_for_kind(kind)
75
+ levels[name] = max(levels[name], level)
76
+ end
77
+ end
78
+ actions.each do |finding|
79
+ name = finding.fetch(:production).split(" -> ").first
80
+ levels[name] = max(levels[name], finding.fetch(:severity))
81
+ end
82
+ levels
83
+ end
84
+
85
+ # @rbs (String) -> String
86
+ def level_for_kind(kind)
87
+ return "high" if %w[first follow nullable].include?(kind)
88
+ return "medium" if %w[reference precedence].include?(kind)
89
+
90
+ "low"
91
+ end
92
+
93
+ # @rbs (Hash[Symbol, Object?] report, Array[String] gates) -> bool
94
+ def fails?(report, gates)
95
+ return false if gates.empty?
96
+
97
+ return true if conflict_gate?(report, gates)
98
+ return true if unreachable_gate?(report, gates)
99
+ return true if action_gate?(report, gates)
100
+
101
+ symbol_records = report.fetch(:symbols, []) #: Array[Hash[Symbol, Object?]]
102
+ gates.any? do |gate|
103
+ kind = GATE_TO_KIND[gate]
104
+ kind && symbol_records.any? { |item| item[:kinds].is_a?(Array) && item[:kinds].include?(kind) }
105
+ end
106
+ end
107
+
108
+ # @rbs (Hash[Symbol, Object?], Array[String]) -> bool
109
+ def conflict_gate?(report, gates)
110
+ gates.include?("new_conflict") && report.dig(:automaton, :conflicts, :added)&.any?
111
+ end
112
+
113
+ # @rbs (Hash[Symbol, Object?], Array[String]) -> bool
114
+ def unreachable_gate?(report, gates)
115
+ return false unless gates.include?("unreachable")
116
+
117
+ states = report.dig(:automaton, :unreachable) || []
118
+ nonterminals = report.dig(:automaton, :unreachable_nonterminals) || []
119
+ states.any? || nonterminals.any?
120
+ end
121
+
122
+ # @rbs (Hash[Symbol, Object?], Array[String]) -> bool
123
+ def action_gate?(report, gates)
124
+ return false unless gates.include?("action_arity")
125
+
126
+ action_records = report.fetch(:actions, []) #: Array[Hash[Symbol, Object?]]
127
+ action_records.any? { |item| item[:severity] == "high" }
128
+ end
129
+
130
+ # @rbs (Array[String] gates) -> Array[String]
131
+ def validate_gates(gates)
132
+ unknown = gates - FAIL_ON
133
+ raise OptionParser::InvalidArgument, "unknown --fail-on value #{unknown.first.inspect}" unless unknown.empty?
134
+
135
+ gates.uniq
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require_relative "impact/graph"
5
+ require_relative "impact/propagation"
6
+ require_relative "impact/seeds"
7
+ require_relative "impact/automaton_impact"
8
+ require_relative "impact/action_impact"
9
+ require_relative "impact/coverage_impact"
10
+ require_relative "impact/severity"
11
+ require_relative "impact/report"
12
+
13
+ module Ibex
14
+ module Impact
15
+ end
16
+ end
data/lib/ibex/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Ibex generates and runs Pure Ruby LR parsers.
4
4
  module Ibex
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end
data/lib/ibex.rb CHANGED
@@ -27,6 +27,7 @@ require_relative "ibex/verify"
27
27
  require_relative "ibex/equiv"
28
28
  require_relative "ibex/diff"
29
29
  require_relative "ibex/metrics"
30
+ require_relative "ibex/impact"
30
31
  require_relative "ibex/fix"
31
32
  require_relative "ibex/bison_import"
32
33
  require_relative "ibex/grammar_tests"
@@ -0,0 +1,208 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://raw.githubusercontent.com/ydah/ibex/main/schema/impact-v1.schema.json",
4
+ "title": "Ibex grammar impact report v1",
5
+ "type": "object",
6
+ "required": ["ibex_report", "schema_version", "mode", "algorithm", "grammar_digest", "seeds", "symbols", "automaton", "actions", "coverage", "warnings", "totals"],
7
+ "properties": {
8
+ "ibex_report": { "const": "impact" },
9
+ "schema_version": { "const": 1 },
10
+ "mode": { "enum": ["potential", "diff"] },
11
+ "algorithm": { "enum": ["slr", "lalr1", "ielr1", "lr1"] },
12
+ "grammar_digest": {
13
+ "type": "object",
14
+ "required": ["before", "after"],
15
+ "properties": {
16
+ "before": { "type": ["string", "null"], "pattern": "^sha256:[0-9a-f]{64}$" },
17
+ "after": { "type": ["string", "null"], "pattern": "^sha256:[0-9a-f]{64}$" }
18
+ },
19
+ "additionalProperties": false
20
+ },
21
+ "seeds": {
22
+ "type": "array",
23
+ "items": {
24
+ "type": "object",
25
+ "required": ["symbol", "origin", "nullable_boundary"],
26
+ "properties": {
27
+ "symbol": { "type": "string" },
28
+ "origin": { "enum": ["symbol", "diff"] },
29
+ "nullable_boundary": { "type": "boolean" }
30
+ },
31
+ "additionalProperties": false
32
+ },
33
+ "maxItems": 100000
34
+ },
35
+ "symbols": {
36
+ "type": "array",
37
+ "items": { "$ref": "#/$defs/symbol" },
38
+ "maxItems": 100000
39
+ },
40
+ "automaton": {
41
+ "type": "object",
42
+ "required": ["states", "affected_states", "conflicts", "unreachable", "unreachable_nonterminals"],
43
+ "properties": {
44
+ "states": {
45
+ "type": "object",
46
+ "required": ["before", "after", "delta"],
47
+ "properties": {
48
+ "before": { "type": ["integer", "null"], "minimum": 0 },
49
+ "after": { "type": "integer", "minimum": 0 },
50
+ "delta": { "type": ["integer", "null"] }
51
+ },
52
+ "additionalProperties": false
53
+ },
54
+ "affected_states": { "type": "array", "items": { "type": "integer", "minimum": 0 }, "uniqueItems": true, "maxItems": 100000 },
55
+ "conflicts": { "$ref": "#/$defs/classification" },
56
+ "unreachable": { "type": "array", "items": { "type": "integer", "minimum": 0 }, "uniqueItems": true, "maxItems": 100000 },
57
+ "unreachable_nonterminals": {
58
+ "type": "array",
59
+ "items": { "type": "string" },
60
+ "uniqueItems": true,
61
+ "maxItems": 100000
62
+ }
63
+ },
64
+ "additionalProperties": false
65
+ },
66
+ "actions": {
67
+ "type": "array",
68
+ "items": { "$ref": "#/$defs/action" },
69
+ "maxItems": 100000
70
+ },
71
+ "coverage": {
72
+ "type": "object",
73
+ "required": ["status", "reports"],
74
+ "properties": {
75
+ "status": { "enum": ["not_requested", "matched", "unmatched"] },
76
+ "reports": {
77
+ "type": "array",
78
+ "items": {
79
+ "type": "object",
80
+ "required": ["path", "productions"],
81
+ "properties": {
82
+ "path": { "type": "string" },
83
+ "productions": { "type": "array", "items": { "type": "integer", "minimum": 0 }, "uniqueItems": true, "maxItems": 100000 }
84
+ },
85
+ "additionalProperties": false
86
+ },
87
+ "maxItems": 100000
88
+ }
89
+ },
90
+ "additionalProperties": false
91
+ },
92
+ "warnings": { "type": "array", "items": { "type": "string" }, "uniqueItems": true, "maxItems": 100000 },
93
+ "totals": {
94
+ "type": "object",
95
+ "required": ["info", "low", "medium", "high", "critical"],
96
+ "properties": {
97
+ "info": { "type": "integer", "minimum": 0 },
98
+ "low": { "type": "integer", "minimum": 0 },
99
+ "medium": { "type": "integer", "minimum": 0 },
100
+ "high": { "type": "integer", "minimum": 0 },
101
+ "critical": { "type": "integer", "minimum": 0 }
102
+ },
103
+ "additionalProperties": false
104
+ }
105
+ },
106
+ "additionalProperties": false,
107
+ "$defs": {
108
+ "classification": {
109
+ "type": "object",
110
+ "required": ["added", "removed", "changed"],
111
+ "properties": {
112
+ "added": { "type": "array", "items": { "$ref": "#/$defs/classification_record" }, "maxItems": 100000 },
113
+ "removed": { "type": "array", "items": { "$ref": "#/$defs/classification_record" }, "maxItems": 100000 },
114
+ "changed": { "type": "array", "items": { "$ref": "#/$defs/classification_record" }, "maxItems": 100000 }
115
+ },
116
+ "additionalProperties": false
117
+ },
118
+ "classification_record": {
119
+ "oneOf": [
120
+ {
121
+ "type": "object",
122
+ "required": ["id", "value"],
123
+ "properties": { "id": { "type": "string" }, "value": {} },
124
+ "additionalProperties": false
125
+ },
126
+ {
127
+ "type": "object",
128
+ "required": ["id", "before", "after"],
129
+ "properties": { "id": { "type": "string" }, "before": {}, "after": {} },
130
+ "additionalProperties": false
131
+ }
132
+ ]
133
+ },
134
+ "change": {
135
+ "type": "object",
136
+ "required": ["added", "removed"],
137
+ "properties": {
138
+ "added": { "type": "array", "items": { "type": "string" }, "maxItems": 100000 },
139
+ "removed": { "type": "array", "items": { "type": "string" }, "maxItems": 100000 }
140
+ },
141
+ "additionalProperties": false
142
+ },
143
+ "symbol": {
144
+ "type": "object",
145
+ "required": ["symbol", "severity", "kinds", "distance", "component", "sets", "witness"],
146
+ "properties": {
147
+ "symbol": { "type": "string" },
148
+ "severity": { "enum": ["info", "low", "medium", "high", "critical"] },
149
+ "kinds": { "type": "array", "items": { "type": "string" }, "uniqueItems": true, "maxItems": 100000 },
150
+ "distance": { "type": "integer", "minimum": 0 },
151
+ "component": { "type": "array", "items": { "type": "string" }, "uniqueItems": true, "maxItems": 100000 },
152
+ "sets": {
153
+ "type": "object",
154
+ "required": ["first", "follow", "nullable"],
155
+ "properties": {
156
+ "first": { "$ref": "#/$defs/change" },
157
+ "follow": { "$ref": "#/$defs/change" },
158
+ "nullable": {
159
+ "type": ["object", "null"],
160
+ "required": ["before", "after"],
161
+ "properties": { "before": { "type": "boolean" }, "after": { "type": "boolean" } },
162
+ "additionalProperties": false
163
+ }
164
+ },
165
+ "additionalProperties": false
166
+ },
167
+ "witness": { "type": "array", "items": { "$ref": "#/$defs/witness" }, "maxItems": 100000 }
168
+ },
169
+ "additionalProperties": false
170
+ },
171
+ "witness": {
172
+ "type": "object",
173
+ "required": ["from", "to", "production", "loc"],
174
+ "properties": {
175
+ "from": { "type": "string" },
176
+ "to": { "type": "string" },
177
+ "production": { "type": ["string", "null"] },
178
+ "loc": {
179
+ "type": ["object", "null"],
180
+ "properties": {
181
+ "file": { "type": "string" },
182
+ "line": { "type": "integer", "minimum": 1 },
183
+ "column": { "type": "integer", "minimum": 1 }
184
+ },
185
+ "additionalProperties": false
186
+ }
187
+ },
188
+ "additionalProperties": false
189
+ },
190
+ "action": {
191
+ "type": "object",
192
+ "required": ["production", "severity", "reason", "context_length", "loc"],
193
+ "properties": {
194
+ "production": { "type": "string" },
195
+ "severity": { "enum": ["medium", "high"] },
196
+ "reason": { "type": "string" },
197
+ "context_length": {
198
+ "type": "object",
199
+ "required": ["before", "after"],
200
+ "properties": { "before": { "type": "integer", "minimum": 0 }, "after": { "type": "integer", "minimum": 0 } },
201
+ "additionalProperties": false
202
+ },
203
+ "loc": { "$ref": "#/$defs/witness/properties/loc" }
204
+ },
205
+ "additionalProperties": false
206
+ }
207
+ }
208
+ }
@@ -10,6 +10,13 @@ module Ibex
10
10
 
11
11
  attr_reader follow_bits: Array[Integer]
12
12
 
13
+ # The index is a symbol id; each value lists the symbols that must be
14
+ # recomputed when the indexed symbol's set changes. These edges already
15
+ # point in the impact-propagation direction.
16
+ attr_reader first_dependencies: Array[Array[Integer]]
17
+
18
+ attr_reader follow_dependencies: Array[Array[Integer]]
19
+
13
20
  @grammar: IR::Grammar
14
21
 
15
22
  # @rbs (IR::Grammar grammar) -> void
@@ -44,6 +51,9 @@ module Ibex
44
51
  # @rbs () -> void
45
52
  def compute_follow: () -> void
46
53
 
54
+ # @rbs (Array[Array[Integer]] dependencies) -> Array[Array[Integer]]
55
+ def freeze_dependencies: (Array[Array[Integer]] dependencies) -> Array[Array[Integer]]
56
+
47
57
  # @rbs (IR::Production production, Array[Array[Integer]] dependencies) -> void
48
58
  def initialize_follow: (IR::Production production, Array[Array[Integer]] dependencies) -> void
49
59
 
@@ -0,0 +1,95 @@
1
+ # Generated from lib/ibex/cli/impact.rb with RBS::Inline
2
+
3
+ module Ibex
4
+ # CLI adapter for potential and confirmed grammar impact reports.
5
+ # rubocop:disable Metrics/ModuleLength, Metrics/MethodLength, Metrics/AbcSize
6
+ module CLIImpact
7
+ include CLIAnalysis
8
+
9
+ private
10
+
11
+ # @rbs (Array[String] arguments) -> Integer
12
+ def run_impact_command: (Array[String] arguments) -> Integer
13
+
14
+ # @rbs (Array[String]) -> Hash[Symbol, untyped]
15
+ def impact_option_parser: (Array[String]) -> Hash[Symbol, untyped]
16
+
17
+ # @rbs (String value) -> Array[Symbol]
18
+ def parse_kinds: (String value) -> Array[Symbol]
19
+
20
+ # @rbs (Array[String], Hash[Symbol, untyped]) -> [Hash[Symbol, Object?], Array[String], Hash[Symbol, Object?]]
21
+ def potential_impact: (Array[String], Hash[Symbol, untyped]) -> [ Hash[Symbol, Object?], Array[String], Hash[Symbol, Object?] ]
22
+
23
+ # @rbs (Array[String], Hash[Symbol, untyped]) -> [Hash[Symbol, Object?], Array[String], Hash[Symbol, Object?]]
24
+ def confirmed_impact: (Array[String], Hash[Symbol, untyped]) -> [ Hash[Symbol, Object?], Array[String], Hash[Symbol, Object?] ]
25
+
26
+ # @rbs (Impact::Graph, Array[Integer], Hash[Symbol, untyped]) ->
27
+ # [Hash[Integer, Impact::Node], Hash[Integer, Array[String]]]
28
+ def propagate: (Impact::Graph, Array[Integer], Hash[Symbol, untyped]) -> [ Hash[Integer, Impact::Node], Hash[Integer, Array[String]] ]
29
+
30
+ # @rbs (IR::Automaton, Array[Integer], Hash[Symbol, untyped]) -> Impact::CoverageImpact
31
+ def load_coverage: (IR::Automaton, Array[Integer], Hash[Symbol, untyped]) -> Impact::CoverageImpact
32
+
33
+ # @rbs (Impact::Seeds, Impact::CoverageImpact) -> Array[String]
34
+ def nullable_warnings: (Impact::Seeds, Impact::CoverageImpact) -> Array[String]
35
+
36
+ # @rbs (IR::Automaton, Impact::AutomatonImpact) -> Hash[Symbol, Object?]
37
+ def potential_automaton_document: (IR::Automaton, Impact::AutomatonImpact) -> Hash[Symbol, Object?]
38
+
39
+ # @rbs (IR::Automaton, IR::Automaton, Hash[Symbol, Object?], Impact::AutomatonImpact) -> Hash[Symbol, Object?]
40
+ def confirmed_automaton_document: (IR::Automaton, IR::Automaton, Hash[Symbol, Object?], Impact::AutomatonImpact) -> Hash[Symbol, Object?]
41
+
42
+ # @rbs (IR::Grammar) -> Array[String]
43
+ def unreachable_nonterminal_names: (IR::Grammar) -> Array[String]
44
+
45
+ # @rbs (IR::Grammar, IR::Grammar) -> Array[String]
46
+ def newly_unreachable_nonterminal_names: (IR::Grammar, IR::Grammar) -> Array[String]
47
+
48
+ # @rbs (IR::Automaton) -> Array[Integer]
49
+ def unreachable_state_ids: (IR::Automaton) -> Array[Integer]
50
+
51
+ # @rbs (IR::Automaton, IR::Automaton) -> Array[Integer]
52
+ def newly_unreachable_state_ids: (IR::Automaton, IR::Automaton) -> Array[Integer]
53
+
54
+ # @rbs (IR::Automaton) -> Array[String]
55
+ def unreachable_state_keys: (IR::Automaton) -> Array[String]
56
+
57
+ # @rbs (IR::Automaton, Integer) -> String
58
+ def unreachable_state_key: (IR::Automaton, Integer) -> String
59
+
60
+ # @rbs (IR::Automaton, IR::Automaton) ->
61
+ # [Hash[String, Hash[Symbol, Object?]], Hash[String, Array[String]]]
62
+ def compare_sets: (IR::Automaton, IR::Automaton) -> [ Hash[String, Hash[Symbol, Object?]], Hash[String, Array[String]] ]
63
+
64
+ # @rbs (Analysis::Sets, Analysis::Sets, IR::GrammarSymbol?, IR::GrammarSymbol?, Symbol) ->
65
+ # Hash[Symbol, Array[String]]
66
+ def set_change: (Analysis::Sets, Analysis::Sets, IR::GrammarSymbol?, IR::GrammarSymbol?, Symbol) -> Hash[Symbol, Array[String]]
67
+
68
+ # @rbs (Hash[Symbol, Array[String]], Hash[Symbol, Array[String]], Hash[Symbol, Object?]?) -> bool
69
+ def set_change_empty?: (Hash[Symbol, Array[String]], Hash[Symbol, Array[String]], Hash[Symbol, Object?]?) -> bool
70
+
71
+ # @rbs (Analysis::Sets, Analysis::Sets, IR::GrammarSymbol?, IR::GrammarSymbol?) -> Hash[Symbol, Object?]?
72
+ def nullable_change: (Analysis::Sets, Analysis::Sets, IR::GrammarSymbol?, IR::GrammarSymbol?) -> Hash[Symbol, Object?]?
73
+
74
+ # @rbs (Hash[Integer, Array[String]], Hash[String, Array[String]], IR::Grammar) -> Hash[Integer, Array[String]]
75
+ def merge_symbol_kinds: (Hash[Integer, Array[String]], Hash[String, Array[String]], IR::Grammar) -> Hash[Integer, Array[String]]
76
+
77
+ # @rbs (Hash[Symbol, Array[Hash[Symbol, Object?]]], IR::Grammar) -> Hash[String, Array[String]]
78
+ def precedence_symbol_kinds: (Hash[Symbol, Array[Hash[Symbol, Object?]]], IR::Grammar) -> Hash[String, Array[String]]
79
+
80
+ # @rbs (IR::Automaton) -> Array[String]
81
+ def conflict_identities: (IR::Automaton) -> Array[String]
82
+
83
+ # @rbs (IR::Grammar, Hash[Symbol, Object?]) -> String
84
+ def conflict_identity: (IR::Grammar, Hash[Symbol, Object?]) -> String
85
+
86
+ # @rbs (IR::Grammar, Integer) -> String
87
+ def production_shape: (IR::Grammar, Integer) -> String
88
+
89
+ # @rbs (Hash[Symbol, Object?], Array[String], Hash[Symbol, untyped]) -> void
90
+ def apply_baseline: (Hash[Symbol, Object?], Array[String], Hash[Symbol, untyped]) -> void
91
+
92
+ # @rbs (Hash[Symbol, Object?], String) -> void
93
+ def write_impact_report: (Hash[Symbol, Object?], String) -> void
94
+ end
95
+ end
@@ -0,0 +1,58 @@
1
+ # Generated from lib/ibex/impact/action_impact.rb with RBS::Inline
2
+
3
+ module Ibex
4
+ module Impact
5
+ # Checks only structured action metadata; action source remains opaque.
6
+ class ActionImpact
7
+ # @rbs @before: IR::Grammar
8
+ # @rbs @after: IR::Grammar
9
+ # @rbs @affected_names: Array[String]?
10
+ attr_reader findings: Array[Hash[Symbol, Object?]]
11
+
12
+ # @rbs (IR::Grammar before, IR::Grammar after, ?affected_names: Array[String]?) -> void
13
+ def initialize: (IR::Grammar before, IR::Grammar after, ?affected_names: Array[String]?) -> void
14
+
15
+ # @rbs () -> Array[Hash[Symbol, Object?]]
16
+ def to_a: () -> Array[Hash[Symbol, Object?]]
17
+
18
+ private
19
+
20
+ # @rbs () -> Array[Hash[Symbol, Object?]]
21
+ def compare: () -> Array[Hash[Symbol, Object?]]
22
+
23
+ # @rbs (String name) -> Array[Hash[Symbol, Object?]]
24
+ def compare_rule: (String name) -> Array[Hash[Symbol, Object?]]
25
+
26
+ # @rbs (Array[IR::Production] before, Array[IR::Production] after) -> Array[[IR::Production, IR::Production]]
27
+ def pair_productions: (Array[IR::Production] before, Array[IR::Production] after) -> Array[[ IR::Production, IR::Production ]]
28
+
29
+ # @rbs (Array[IR::Production] unmatched, Array[IR::Production] after) -> Array[[IR::Production, IR::Production]]
30
+ def pair_exact_productions: (Array[IR::Production] unmatched, Array[IR::Production] after) -> Array[[ IR::Production, IR::Production ]]
31
+
32
+ # @rbs (Array[IR::Production] unmatched, Array[IR::Production] after,
33
+ # Array[[IR::Production, IR::Production]]) -> Array[[IR::Production, IR::Production]]
34
+ def pair_by_location: (Array[IR::Production] unmatched, Array[IR::Production] after, Array[[ IR::Production, IR::Production ]]) -> Array[[ IR::Production, IR::Production ]]
35
+
36
+ # @rbs (IR::Grammar grammar, String name) -> Array[IR::Production]
37
+ def productions_for: (IR::Grammar grammar, String name) -> Array[IR::Production]
38
+
39
+ # @rbs (IR::Grammar grammar, IR::Production production) -> [Array[String], String?]
40
+ def production_signature: (IR::Grammar grammar, IR::Production production) -> [ Array[String], String? ]
41
+
42
+ # @rbs (IR::Production production) -> [Integer, Integer]?
43
+ def action_location_identity: (IR::Production production) -> [ Integer, Integer ]?
44
+
45
+ # @rbs (IR::Production before, IR::Production after) -> Hash[Symbol, Object?]
46
+ def finding_for: (IR::Production before, IR::Production after) -> Hash[Symbol, Object?]
47
+
48
+ # @rbs (IR::Action?, Integer, Integer) -> [String, String]
49
+ def finding_reason: (IR::Action?, Integer, Integer) -> [ String, String ]
50
+
51
+ # @rbs (IR::Action?, Integer) -> bool
52
+ def out_of_range?: (IR::Action?, Integer) -> bool
53
+
54
+ # @rbs (IR::Grammar grammar, IR::Production production) -> String
55
+ def production_name: (IR::Grammar grammar, IR::Production production) -> String
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,37 @@
1
+ # Generated from lib/ibex/impact/automaton_impact.rb with RBS::Inline
2
+
3
+ module Ibex
4
+ module Impact
5
+ # Maps propagated grammar symbols to parser states and conflict changes.
6
+ class AutomatonImpact
7
+ # @rbs @automaton: IR::Automaton
8
+ # @rbs @symbol_ids: Array[Integer]
9
+ # @rbs @production_ids: Array[Integer]
10
+ attr_reader affected_states: Array[Integer]
11
+
12
+ attr_reader production_ids: Array[Integer]
13
+
14
+ attr_reader conflict_states: Array[Integer]
15
+
16
+ # @rbs (IR::Automaton automaton, Array[Integer]) -> void
17
+ def initialize: (IR::Automaton automaton, Array[Integer]) -> void
18
+
19
+ # @rbs (Hash[Symbol, Object?] diff) -> Hash[Symbol, Object?]
20
+ def conflict_changes: (Hash[Symbol, Object?] diff) -> Hash[Symbol, Object?]
21
+
22
+ # @rbs () -> Hash[Symbol, Object?]
23
+ def to_h: () -> Hash[Symbol, Object?]
24
+
25
+ private
26
+
27
+ # @rbs () -> Array[Integer]
28
+ def affected_productions: () -> Array[Integer]
29
+
30
+ # @rbs () -> Array[Integer]
31
+ def affected_state_ids: () -> Array[Integer]
32
+
33
+ # @rbs () -> Array[Integer]
34
+ def conflict_state_ids: () -> Array[Integer]
35
+ end
36
+ end
37
+ end