diff --git a/Steepfile b/Steepfile index 2aad7a87..0c34556c 100644 --- a/Steepfile +++ b/Steepfile @@ -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" diff --git a/lib/lrama/counterexamples.rb b/lib/lrama/counterexamples.rb index aaf2bb20..ee2b5d59 100644 --- a/lib/lrama/counterexamples.rb +++ b/lib/lrama/counterexamples.rb @@ -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 @@ -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 @@ -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 @@ -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 @@ -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] @@ -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) @@ -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 @@ -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 @@ -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 @@ -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])) diff --git a/lib/lrama/counterexamples/derivation.rb b/lib/lrama/counterexamples/derivation.rb index b5c2fcba..368d7f10 100644 --- a/lib/lrama/counterexamples/derivation.rb +++ b/lib/lrama/counterexamples/derivation.rb @@ -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 @@ -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 diff --git a/lib/lrama/counterexamples/example.rb b/lib/lrama/counterexamples/example.rb index 8dda0d17..bb08428f 100644 --- a/lib/lrama/counterexamples/example.rb +++ b/lib/lrama/counterexamples/example.rb @@ -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 @@ -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 @@ -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) diff --git a/lib/lrama/counterexamples/path.rb b/lib/lrama/counterexamples/path.rb index 243b6b42..0a5823dd 100644 --- a/lib/lrama/counterexamples/path.rb +++ b/lib/lrama/counterexamples/path.rb @@ -20,6 +20,10 @@ def to_s "#" end alias :inspect :to_s + + def type + raise NotImplementedError + end end end end diff --git a/rbs_collection.lock.yaml b/rbs_collection.lock.yaml index 2e642e97..cc74a7fd 100644 --- a/rbs_collection.lock.yaml +++ b/rbs_collection.lock.yaml @@ -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 @@ -26,7 +26,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: stackprof @@ -34,7 +34,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: strscan diff --git a/sig/lrama/counterexamples.rbs b/sig/lrama/counterexamples.rbs new file mode 100644 index 00000000..c32c2488 --- /dev/null +++ b/sig/lrama/counterexamples.rbs @@ -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: () -> "#" + 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 diff --git a/sig/lrama/counterexamples/derivation.rbs b/sig/lrama/counterexamples/derivation.rbs new file mode 100644 index 00000000..2b2db25c --- /dev/null +++ b/sig/lrama/counterexamples/derivation.rbs @@ -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 diff --git a/sig/lrama/counterexamples/example.rbs b/sig/lrama/counterexamples/example.rbs new file mode 100644 index 00000000..fae1a98b --- /dev/null +++ b/sig/lrama/counterexamples/example.rbs @@ -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 diff --git a/sig/lrama/counterexamples/path.rbs b/sig/lrama/counterexamples/path.rbs new file mode 100644 index 00000000..8a9d8b83 --- /dev/null +++ b/sig/lrama/counterexamples/path.rbs @@ -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 diff --git a/sig/lrama/counterexamples/production_path.rbs b/sig/lrama/counterexamples/production_path.rbs new file mode 100644 index 00000000..777d0804 --- /dev/null +++ b/sig/lrama/counterexamples/production_path.rbs @@ -0,0 +1,11 @@ +module Lrama + class Counterexamples + class ProductionPath < Path + def type: () -> :production + + def transition?: () -> false + + def production?: () -> true + end + end +end diff --git a/sig/lrama/counterexamples/start_path.rbs b/sig/lrama/counterexamples/start_path.rbs new file mode 100644 index 00000000..5f83a9cc --- /dev/null +++ b/sig/lrama/counterexamples/start_path.rbs @@ -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 diff --git a/sig/lrama/counterexamples/state_item.rbs b/sig/lrama/counterexamples/state_item.rbs new file mode 100644 index 00000000..32eab848 --- /dev/null +++ b/sig/lrama/counterexamples/state_item.rbs @@ -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 diff --git a/sig/lrama/counterexamples/transition_path.rbs b/sig/lrama/counterexamples/transition_path.rbs new file mode 100644 index 00000000..7dd9397d --- /dev/null +++ b/sig/lrama/counterexamples/transition_path.rbs @@ -0,0 +1,11 @@ +module Lrama + class Counterexamples + class TransitionPath < Path + def type: () -> :transition + + def transition?: () -> true + + def production?: () -> false + end + end +end diff --git a/sig/lrama/counterexamples/triple.rbs b/sig/lrama/counterexamples/triple.rbs new file mode 100644 index 00000000..1b3cae35 --- /dev/null +++ b/sig/lrama/counterexamples/triple.rbs @@ -0,0 +1,20 @@ +module Lrama + class Counterexamples + class Triple + attr_accessor s: State + attr_accessor itm: States::Item + attr_accessor l: Set[Grammar::Symbol] + + alias state s + alias item itm + alias precise_lookahead_set l + + def initialize: (State s, States::Item itm, Set[Grammar::Symbol] l) -> void + + def state_item: () -> StateItem + def inspect: () -> ::String + + alias to_s inspect + end + end +end diff --git a/sig/lrama/grammar.rbs b/sig/lrama/grammar.rbs index 886cc82c..9042525c 100644 --- a/sig/lrama/grammar.rbs +++ b/sig/lrama/grammar.rbs @@ -91,5 +91,18 @@ module Lrama def fill_sym_to_rules: () -> Array[Rule] def validate_rule_lhs_is_nterm!: () -> void def set_locations: () -> void + + interface _DelegatedMethods + def rules: () -> Array[Rule] + def accept_symbol: () -> Grammar::Symbol + def eof_symbol: () -> Grammar::Symbol + def undef_symbol: () -> Grammar::Symbol + + # delegate to @symbols_resolver + def symbols: () -> Array[Grammar::Symbol] + def terms: () -> Array[Grammar::Symbol] + def nterms: () -> Array[Grammar::Symbol] + def find_symbol_by_s_value!: (Grammar::Symbol s_value) -> Grammar::Symbol + end end end diff --git a/sig/lrama/grammar/symbol.rbs b/sig/lrama/grammar/symbol.rbs index 7a84f246..32ba678a 100644 --- a/sig/lrama/grammar/symbol.rbs +++ b/sig/lrama/grammar/symbol.rbs @@ -13,7 +13,7 @@ module Lrama attr_accessor destructor: Destructor? attr_accessor error_token: ErrorToken - attr_accessor first_set: Set[Array[Symbol]] | Set[Symbol] + attr_accessor first_set: Set[Grammar::Symbol] attr_accessor first_set_bitmap: Integer attr_writer eof_symbol: bool attr_writer error_symbol: bool diff --git a/sig/lrama/state.rbs b/sig/lrama/state.rbs new file mode 100644 index 00000000..13b5de66 --- /dev/null +++ b/sig/lrama/state.rbs @@ -0,0 +1,79 @@ +module Lrama + class State + @id: untyped + + @accessing_symbol: untyped + + @kernels: untyped + + @items: Array[States::Item] + + @items_to_state: untyped + + @conflicts: Array[State::ShiftReduceConflict|State::ReduceReduceConflict] + + @resolved_conflicts: untyped + + @default_reduction_rule: untyped + + @closure: untyped + + @nterm_transitions: untyped + + @term_transitions: untyped + + @transitions: Array[[Shift, State]] + + attr_reader id: untyped + + attr_reader accessing_symbol: untyped + + attr_reader kernels: untyped + + attr_reader conflicts: Array[State::ShiftReduceConflict|State::ReduceReduceConflict] + + attr_reader resolved_conflicts: untyped + + attr_reader default_reduction_rule: untyped + + attr_reader closure: untyped + + attr_reader items: Array[States::Item] + + attr_accessor shifts: Array[Shift] + + attr_accessor reduces: untyped + + def initialize: (untyped id, untyped accessing_symbol, Array[States::Item] kernels) -> void + + def closure=: (untyped closure) -> untyped + + def non_default_reduces: () -> untyped + + def compute_shifts_reduces: () -> untyped + + def set_items_to_state: (untyped items, untyped next_state) -> untyped + + def set_look_ahead: (untyped rule, untyped look_ahead) -> untyped + + def nterm_transitions: () -> untyped + + def term_transitions: () -> untyped + + def transitions: () -> Array[[Shift, State]] + + def selected_term_transitions: () -> untyped + + def transition: (untyped sym) -> untyped + + def find_reduce_by_item!: (untyped item) -> untyped + + def default_reduction_rule=: (untyped default_reduction_rule) -> untyped + + def has_conflicts?: () -> untyped + + def sr_conflicts: () -> untyped + + def rr_conflicts: () -> untyped + end +end diff --git a/sig/lrama/state/reduce_reduce_conflict.rbs b/sig/lrama/state/reduce_reduce_conflict.rbs index 23ced680..24a8de03 100644 --- a/sig/lrama/state/reduce_reduce_conflict.rbs +++ b/sig/lrama/state/reduce_reduce_conflict.rbs @@ -1,11 +1,11 @@ module Lrama class State class ReduceReduceConflict - attr_accessor symbols: Array[Grammar::Symbol?] + attr_accessor symbols: Array[Grammar::Symbol] attr_accessor reduce1: State::Reduce attr_accessor reduce2: State::Reduce - def initialize: (?symbols: Array[Grammar::Symbol?], ?reduce1: State::Reduce, ?reduce2: State::Reduce) -> void + def initialize: (?symbols: Array[Grammar::Symbol], ?reduce1: State::Reduce, ?reduce2: State::Reduce) -> void def type: () -> :reduce_reduce end diff --git a/sig/lrama/states.rbs b/sig/lrama/states.rbs new file mode 100644 index 00000000..fda9c2d2 --- /dev/null +++ b/sig/lrama/states.rbs @@ -0,0 +1,101 @@ +module Lrama + class States + include Grammar::_DelegatedMethods + + @grammar: untyped + + @warning: untyped + + @trace_state: untyped + + @states: Array[State] + + @direct_read_sets: untyped + + @reads_relation: untyped + + @read_sets: untyped + + @includes_relation: untyped + + @lookback_relation: untyped + + @follow_sets: untyped + + @la: untyped + + extend Forwardable + + include Lrama::Report::Duration + + attr_reader states: Array[State] + + attr_reader reads_relation: untyped + + attr_reader includes_relation: untyped + + attr_reader lookback_relation: untyped + + def initialize: (untyped grammar, untyped warning, ?trace_state: bool) -> void + + def compute: () -> untyped + + def reporter: () -> untyped + + def states_count: () -> untyped + + def direct_read_sets: () -> untyped + + def read_sets: () -> untyped + + def follow_sets: () -> untyped + + def la: () -> untyped + + private + + def sr_conflicts: () -> untyped + + def rr_conflicts: () -> untyped + + def trace_state: () { (untyped) -> untyped } -> (untyped | nil) + + def create_state: (untyped accessing_symbol, untyped kernels, untyped states_created) -> (::Array[untyped | false] | ::Array[untyped | true]) + + def setup_state: (untyped state) -> untyped + + def enqueue_state: (untyped states, untyped state) -> untyped + + def compute_lr0_states: () -> untyped + + def nterm_transitions: () -> untyped + + def compute_direct_read_sets: () -> untyped + + def compute_reads_relation: () -> untyped + + def compute_read_sets: () -> untyped + + def transition: (untyped state, untyped symbols) -> untyped + + def compute_includes_relation: () -> untyped + + def compute_lookback_relation: () -> untyped + + def compute_follow_sets: () -> untyped + + def compute_look_ahead_sets: () -> untyped + + def bitmap_to_terms: (untyped bit) -> untyped + + def compute_conflicts: () -> untyped + + def compute_shift_reduce_conflicts: () -> untyped + + def compute_reduce_reduce_conflicts: () -> untyped + + def compute_default_reduction: () -> untyped + + def check_conflicts: () -> untyped + end +end