Skip to content

Commit

Permalink
Merge pull request #483 from Little-Rubyist/add_types_to_counterexamples
Browse files Browse the repository at this point in the history
Add types to counterexamples
  • Loading branch information
yui-knk authored Dec 22, 2024
2 parents d6946ec + 613c68e commit 84eb007
Show file tree
Hide file tree
Showing 20 changed files with 430 additions and 23 deletions.
3 changes: 3 additions & 0 deletions Steepfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ target :lib do
signature "sig"

check "lib/lrama/grammar"

check "lib/lrama/counterexamples"
check "lib/lrama/lexer"
check "lib/lrama/report"
check "lib/lrama/state"
check "lib/lrama/states"
check "lib/lrama/bitmap.rb"
check "lib/lrama/counterexamples.rb"
check "lib/lrama/digraph.rb"
check "lib/lrama/grammar.rb"
check "lib/lrama/options.rb"
Expand Down
28 changes: 19 additions & 9 deletions lib/lrama/counterexamples.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ def compute(conflict_state)
conflict_state.conflicts.flat_map do |conflict|
case conflict.type
when :shift_reduce
# @type var conflict: State::ShiftReduceConflict
shift_reduce_example(conflict_state, conflict)
when :reduce_reduce
# @type var conflict: State::ReduceReduceConflict
reduce_reduce_examples(conflict_state, conflict)
end
end.compact
Expand All @@ -48,7 +50,7 @@ def setup_transitions
@reverse_transitions = {}

@states.states.each do |src_state|
trans = {}
trans = {} #: Hash[Grammar::Symbol, State]

src_state.transitions.each do |shift, next_state|
trans[shift.next_sym] = next_state
Expand All @@ -66,6 +68,7 @@ def setup_transitions

@transitions[[src_state_item, sym]] = dest_state_item

# @type var key: [StateItem, Grammar::Symbol]
key = [dest_state_item, sym]
@reverse_transitions[key] ||= Set.new
@reverse_transitions[key] << src_state_item
Expand All @@ -82,7 +85,7 @@ def setup_productions

@states.states.each do |state|
# LHS => Set(Item)
h = {}
h = {} #: Hash[Grammar::Symbol, Set[States::Item]]

state.closure.each do |item|
sym = item.lhs
Expand All @@ -97,6 +100,7 @@ def setup_productions

sym = item.next_sym
state_item = StateItem.new(state, item)
# @type var key: [State, Grammar::Symbol]
key = [state, sym]

@productions[state_item] = h[sym]
Expand All @@ -109,6 +113,7 @@ def setup_productions

def shift_reduce_example(conflict_state, conflict)
conflict_symbol = conflict.symbols.first
# @type var shift_conflict_item: ::Lrama::States::Item
shift_conflict_item = conflict_state.items.find { |item| item.next_sym == conflict_symbol }
path2 = shortest_path(conflict_state, conflict.reduce.item, conflict_symbol)
path1 = find_shift_conflict_shortest_path(path2, conflict_state, shift_conflict_item)
Expand Down Expand Up @@ -153,12 +158,14 @@ def find_shift_conflict_shortest_state_items(reduce_path, conflict_state, confli
prev_state_item = prev_path&.to

if target_state_item == state_item || target_state_item.item.start_item?
result.concat(reversed_reduce_path[_j..-1].map(&:to))
result.concat(
reversed_reduce_path[_j..-1] #: Array[StartPath|TransitionPath|ProductionPath]
.map(&:to))
break
end

if target_state_item.item.beginning_of_rule?
queue = []
queue = [] #: Array[Array[StateItem]]
queue << [target_state_item]

# Find reverse production
Expand All @@ -174,15 +181,17 @@ def find_shift_conflict_shortest_state_items(reduce_path, conflict_state, confli
end

if si.item.beginning_of_rule?
# @type var key: [State, Grammar::Symbol]
key = [si.state, si.item.lhs]
@reverse_productions[key].each do |item|
state_item = StateItem.new(si.state, item)
queue << (sis + [state_item])
end
else
# @type var key: [StateItem, Grammar::Symbol]
key = [si, si.item.previous_sym]
@reverse_transitions[key].each do |prev_target_state_item|
next if prev_target_state_item.state != prev_state_item.state
next if prev_target_state_item.state != prev_state_item&.state
sis.shift
result.concat(sis)
result << prev_target_state_item
Expand All @@ -195,9 +204,10 @@ def find_shift_conflict_shortest_state_items(reduce_path, conflict_state, confli
end
else
# Find reverse transition
# @type var key: [StateItem, Grammar::Symbol]
key = [target_state_item, target_state_item.item.previous_sym]
@reverse_transitions[key].each do |prev_target_state_item|
next if prev_target_state_item.state != prev_state_item.state
next if prev_target_state_item.state != prev_state_item&.state
result << prev_target_state_item
target_state_item = prev_target_state_item
i = j
Expand All @@ -224,9 +234,9 @@ def build_paths_from_state_items(state_items)

def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
# queue: is an array of [Triple, [Path]]
queue = []
visited = {}
start_state = @states.states.first
queue = [] #: Array[[Triple, Array[StartPath|TransitionPath|ProductionPath]]]
visited = {} #: Hash[Triple, true]
start_state = @states.states.first #: Lrama::State
raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1

start = Triple.new(start_state, start_state.kernels.first, Set.new([@states.eof_symbol]))
Expand Down
9 changes: 5 additions & 4 deletions lib/lrama/counterexamples/derivation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def to_s
alias :inspect :to_s

def render_strings_for_report
result = []
result = [] #: Array[String]
_render_for_report(self, 0, result, 0)
result.map(&:rstrip)
end
Expand Down Expand Up @@ -51,11 +51,12 @@ def _render_for_report(derivation, offset, strings, index)
end

if derivation.right&.left
length = _render_for_report(derivation.right.left, str.length, strings, index + 1)
str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} "
left = derivation.right&.left #: Derivation
length = _render_for_report(left, str.length, strings, index + 1)
str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} " # steep:ignore
str << " " * (length - str.length) if length > str.length
elsif item.next_next_sym
str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} "
str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} " # steep:ignore
end

return str.length
Expand Down
11 changes: 7 additions & 4 deletions lib/lrama/counterexamples/example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ def derivations2
private

def _derivations(paths)
derivation = nil
derivation = nil #: Derivation
current = :production
lookahead_sym = paths.last.to.item.end_of_rule? ? @conflict_symbol : nil
last_path = paths.last #: Path
lookahead_sym = last_path.to.item.end_of_rule? ? @conflict_symbol : nil

paths.reverse_each do |path|
item = path.to.item
Expand All @@ -57,12 +58,14 @@ def _derivations(paths)
when ProductionPath
derivation = Derivation.new(item, derivation)
current = :production
else
raise "Unexpected. #{path}"
end

if lookahead_sym && item.next_next_sym && item.next_next_sym.first_set.include?(lookahead_sym)
state_item = @counterexamples.transitions[[path.to, item.next_sym]]
derivation2 = find_derivation_for_symbol(state_item, lookahead_sym)
derivation.right = derivation2
derivation.right = derivation2 # steep:ignore
lookahead_sym = nil
end

Expand All @@ -89,7 +92,7 @@ def _derivations(paths)
end

def find_derivation_for_symbol(state_item, sym)
queue = []
queue = [] #: Array[Array[StateItem]]
queue << [state_item]

while (sis = queue.shift)
Expand Down
4 changes: 4 additions & 0 deletions lib/lrama/counterexamples/path.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ def to_s
"#<Path(#{type})>"
end
alias :inspect :to_s

def type
raise NotImplementedError
end
end
end
end
6 changes: 3 additions & 3 deletions rbs_collection.lock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ gems:
source:
type: git
name: ruby/gem_rbs_collection
revision: f2fa96be08e0f7fe5237a45d2b69d9d7a5d0b416
revision: 71fb7ae83789ae150bdee1cd8a7cd3035b5d79e2
remote: https://github.com/ruby/gem_rbs_collection.git
repo_dir: gems
- name: erb
Expand All @@ -26,15 +26,15 @@ gems:
source:
type: git
name: ruby/gem_rbs_collection
revision: f2fa96be08e0f7fe5237a45d2b69d9d7a5d0b416
revision: 71fb7ae83789ae150bdee1cd8a7cd3035b5d79e2
remote: https://github.com/ruby/gem_rbs_collection.git
repo_dir: gems
- name: stackprof
version: '0.2'
source:
type: git
name: ruby/gem_rbs_collection
revision: f2fa96be08e0f7fe5237a45d2b69d9d7a5d0b416
revision: 71fb7ae83789ae150bdee1cd8a7cd3035b5d79e2
remote: https://github.com/ruby/gem_rbs_collection.git
repo_dir: gems
- name: strscan
Expand Down
29 changes: 29 additions & 0 deletions sig/lrama/counterexamples.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module Lrama
class Counterexamples
@states: States
@transitions: Hash[[StateItem, Grammar::Symbol], StateItem]
@reverse_transitions: Hash[[StateItem, Grammar::Symbol], Set[StateItem]]
@productions: Hash[StateItem, Set[States::Item]]
@reverse_productions: Hash[[State, Grammar::Symbol], Set[States::Item]]

attr_reader transitions: Hash[[StateItem, Grammar::Symbol], StateItem]
attr_reader productions: Hash[StateItem, Set[States::Item]]

def initialize: (States states) -> void
def to_s: () -> "#<Counterexamples>"
alias inspect to_s
def compute: (State conflict_state) -> Array[Example]

private

def setup_transitions: () -> void
def setup_productions: () -> void
def shift_reduce_example: (State conflict_state, State::ShiftReduceConflict conflict) -> Example
def reduce_reduce_examples: (State conflict_state, State::ReduceReduceConflict conflict) -> Example
def find_shift_conflict_shortest_path: (::Array[StartPath|TransitionPath|ProductionPath]? reduce_path, State conflict_state, States::Item conflict_item) -> ::Array[StartPath|TransitionPath|ProductionPath]
def find_shift_conflict_shortest_state_items: (::Array[StartPath|TransitionPath|ProductionPath]? reduce_path, State conflict_state, States::Item conflict_item) -> Array[StateItem]
def build_paths_from_state_items: (Array[StateItem] state_items) -> ::Array[StartPath|TransitionPath|ProductionPath]
def shortest_path: (State conflict_state, States::Item conflict_reduce_item, Grammar::Symbol conflict_term) -> ::Array[StartPath|TransitionPath|ProductionPath]?
def follow_l: (States::Item item, Set[Grammar::Symbol] current_l) -> Set[Grammar::Symbol]
end
end
33 changes: 33 additions & 0 deletions sig/lrama/counterexamples/derivation.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module Lrama
class Counterexamples
class Derivation
@item: States::Item

@left: Derivation?

@right: Derivation?

attr_reader item: States::Item

attr_reader left: Derivation?

attr_reader right: Derivation?

attr_writer right: Derivation?

def initialize: (States::Item item, Derivation? left, ?Derivation? right) -> void

def to_s: () -> ::String

alias inspect to_s

def render_strings_for_report: () -> Array[String]

def render_for_report: () -> String

private

def _render_for_report: (Derivation derivation, Integer offset, Array[String] strings, Integer index) -> Integer
end
end
end
45 changes: 45 additions & 0 deletions sig/lrama/counterexamples/example.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module Lrama
class Counterexamples
class Example
@path1: ::Array[StartPath | TransitionPath | ProductionPath]

@path2: ::Array[StartPath | TransitionPath | ProductionPath]

@conflict: (State::ShiftReduceConflict | State::ReduceReduceConflict)

@conflict_symbol: Grammar::Symbol

@counterexamples: Counterexamples

@derivations1: Derivation

@derivations2: Derivation

attr_reader path1: ::Array[StartPath | TransitionPath | ProductionPath]

attr_reader path2: ::Array[StartPath | TransitionPath | ProductionPath]

attr_reader conflict: (State::ShiftReduceConflict | State::ReduceReduceConflict)

attr_reader conflict_symbol: Grammar::Symbol

def initialize: (::Array[StartPath | TransitionPath | ProductionPath]? path1, ::Array[StartPath | TransitionPath | ProductionPath]? path2, (State::ShiftReduceConflict | State::ReduceReduceConflict) conflict, Grammar::Symbol conflict_symbol, Counterexamples counterexamples) -> void

def type: () -> (:shift_reduce | :reduce_reduce)

def path1_item: () -> States::Item

def path2_item: () -> States::Item

def derivations1: () -> Derivation

def derivations2: () -> Derivation

private

def _derivations: (::Array[StartPath | TransitionPath | ProductionPath] paths) -> Derivation

def find_derivation_for_symbol: (StateItem state_item, Grammar::Symbol sym) -> Derivation?
end
end
end
21 changes: 21 additions & 0 deletions sig/lrama/counterexamples/path.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Lrama
class Counterexamples
class Path
@from_state_item: StateItem?

@to_state_item: StateItem

def initialize: (StateItem? from_state_item, StateItem to_state_item) -> void

def from: () -> StateItem?

def to: () -> StateItem

def to_s: () -> ::String

alias inspect to_s

def type: -> bot
end
end
end
11 changes: 11 additions & 0 deletions sig/lrama/counterexamples/production_path.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Lrama
class Counterexamples
class ProductionPath < Path
def type: () -> :production

def transition?: () -> false

def production?: () -> true
end
end
end
13 changes: 13 additions & 0 deletions sig/lrama/counterexamples/start_path.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Lrama
class Counterexamples
class StartPath < Path
def initialize: (StateItem to_state_item) -> void

def type: () -> :start

def transition?: () -> false

def production?: () -> false
end
end
end
10 changes: 10 additions & 0 deletions sig/lrama/counterexamples/state_item.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Lrama
class Counterexamples
class StateItem
attr_accessor state: State
attr_accessor item: States::Item

def initialize: (State state, States::Item item) -> void
end
end
end
Loading

0 comments on commit 84eb007

Please sign in to comment.