lrama 0.7.0 → 0.7.1

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 (260) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.github/workflows/codespell.yaml +1 -1
  4. data/.github/workflows/gh-pages.yml +5 -6
  5. data/.github/workflows/test.yaml +25 -14
  6. data/Gemfile +4 -3
  7. data/NEWS.md +370 -35
  8. data/README.md +7 -88
  9. data/Rakefile +3 -2
  10. data/Steepfile +11 -5
  11. data/doc/Index.md +1 -1
  12. data/doc/development/compressed_state_table/parser.rb +2 -0
  13. data/doc/development/profiling.md +44 -0
  14. data/exe/lrama +1 -1
  15. data/lib/lrama/bitmap.rb +18 -5
  16. data/lib/lrama/command.rb +95 -43
  17. data/lib/lrama/context.rb +22 -24
  18. data/lib/lrama/counterexamples/derivation.rb +14 -4
  19. data/lib/lrama/counterexamples/example.rb +47 -22
  20. data/lib/lrama/counterexamples/node.rb +30 -0
  21. data/lib/lrama/counterexamples/path.rb +12 -14
  22. data/lib/lrama/counterexamples/state_item.rb +24 -1
  23. data/lib/lrama/counterexamples/triple.rb +27 -9
  24. data/lib/lrama/counterexamples.rb +216 -88
  25. data/lib/lrama/diagram.rb +77 -0
  26. data/lib/lrama/digraph.rb +28 -7
  27. data/lib/lrama/erb.rb +29 -0
  28. data/lib/lrama/grammar/auxiliary.rb +6 -1
  29. data/lib/lrama/grammar/binding.rb +37 -25
  30. data/lib/lrama/grammar/code/destructor_code.rb +11 -0
  31. data/lib/lrama/grammar/code/initial_action_code.rb +3 -0
  32. data/lib/lrama/grammar/code/no_reference_code.rb +3 -0
  33. data/lib/lrama/grammar/code/printer_code.rb +11 -0
  34. data/lib/lrama/grammar/code/rule_action.rb +17 -0
  35. data/lib/lrama/grammar/code.rb +16 -1
  36. data/lib/lrama/grammar/counter.rb +10 -0
  37. data/lib/lrama/grammar/destructor.rb +14 -1
  38. data/lib/lrama/grammar/error_token.rb +14 -1
  39. data/lib/lrama/grammar/inline/resolver.rb +80 -0
  40. data/lib/lrama/grammar/inline.rb +3 -0
  41. data/lib/lrama/grammar/{parameterizing_rule → parameterized}/resolver.rb +19 -8
  42. data/lib/lrama/grammar/{parameterizing_rule → parameterized}/rhs.rb +7 -2
  43. data/lib/lrama/grammar/parameterized/rule.rb +36 -0
  44. data/lib/lrama/grammar/parameterized.rb +5 -0
  45. data/lib/lrama/grammar/percent_code.rb +12 -1
  46. data/lib/lrama/grammar/precedence.rb +43 -1
  47. data/lib/lrama/grammar/printer.rb +9 -0
  48. data/lib/lrama/grammar/reference.rb +13 -0
  49. data/lib/lrama/grammar/rule.rb +61 -1
  50. data/lib/lrama/grammar/rule_builder.rb +84 -69
  51. data/lib/lrama/grammar/stdlib.y +68 -48
  52. data/lib/lrama/grammar/symbol.rb +63 -19
  53. data/lib/lrama/grammar/symbols/resolver.rb +64 -3
  54. data/lib/lrama/grammar/type.rb +13 -1
  55. data/lib/lrama/grammar/union.rb +12 -1
  56. data/lib/lrama/grammar.rb +231 -35
  57. data/lib/lrama/lexer/location.rb +25 -8
  58. data/lib/lrama/lexer/token/base.rb +73 -0
  59. data/lib/lrama/lexer/token/char.rb +15 -2
  60. data/lib/lrama/lexer/token/empty.rb +14 -0
  61. data/lib/lrama/lexer/token/ident.rb +2 -2
  62. data/lib/lrama/lexer/token/instantiate_rule.rb +4 -4
  63. data/lib/lrama/lexer/token/int.rb +14 -0
  64. data/lib/lrama/lexer/token/str.rb +11 -0
  65. data/lib/lrama/lexer/token/tag.rb +2 -2
  66. data/lib/lrama/lexer/token/token.rb +11 -0
  67. data/lib/lrama/lexer/token/user_code.rb +63 -37
  68. data/lib/lrama/lexer/token.rb +6 -56
  69. data/lib/lrama/lexer.rb +51 -23
  70. data/lib/lrama/logger.rb +12 -2
  71. data/lib/lrama/option_parser.rb +63 -9
  72. data/lib/lrama/options.rb +25 -7
  73. data/lib/lrama/output.rb +4 -11
  74. data/lib/lrama/parser.rb +854 -723
  75. data/lib/lrama/reporter/conflicts.rb +44 -0
  76. data/lib/lrama/reporter/grammar.rb +39 -0
  77. data/lib/lrama/reporter/precedences.rb +54 -0
  78. data/lib/lrama/reporter/profile/call_stack.rb +45 -0
  79. data/lib/lrama/reporter/profile/memory.rb +44 -0
  80. data/lib/lrama/reporter/profile.rb +4 -0
  81. data/lib/lrama/reporter/rules.rb +43 -0
  82. data/lib/lrama/reporter/states.rb +387 -0
  83. data/lib/lrama/reporter/terms.rb +44 -0
  84. data/lib/lrama/reporter.rb +39 -0
  85. data/lib/lrama/state/action/goto.rb +33 -0
  86. data/lib/lrama/state/action/reduce.rb +71 -0
  87. data/lib/lrama/state/action/shift.rb +39 -0
  88. data/lib/lrama/state/action.rb +5 -0
  89. data/lib/lrama/state/inadequacy_annotation.rb +140 -0
  90. data/lib/lrama/{states → state}/item.rb +33 -4
  91. data/lib/lrama/state/reduce_reduce_conflict.rb +14 -1
  92. data/lib/lrama/state/resolved_conflict.rb +38 -4
  93. data/lib/lrama/state/shift_reduce_conflict.rb +14 -1
  94. data/lib/lrama/state.rb +301 -200
  95. data/lib/lrama/states.rb +447 -175
  96. data/lib/lrama/tracer/actions.rb +22 -0
  97. data/lib/lrama/tracer/closure.rb +30 -0
  98. data/lib/lrama/tracer/duration.rb +38 -0
  99. data/lib/lrama/tracer/only_explicit_rules.rb +24 -0
  100. data/lib/lrama/tracer/rules.rb +23 -0
  101. data/lib/lrama/tracer/state.rb +33 -0
  102. data/lib/lrama/tracer.rb +51 -0
  103. data/lib/lrama/version.rb +2 -1
  104. data/lib/lrama/warnings/conflicts.rb +27 -0
  105. data/lib/lrama/warnings/implicit_empty.rb +29 -0
  106. data/lib/lrama/warnings/name_conflicts.rb +63 -0
  107. data/lib/lrama/warnings/redefined_rules.rb +23 -0
  108. data/lib/lrama/warnings/required.rb +23 -0
  109. data/lib/lrama/warnings/useless_precedence.rb +25 -0
  110. data/lib/lrama/warnings.rb +33 -0
  111. data/lib/lrama.rb +5 -5
  112. data/parser.y +495 -404
  113. data/rbs_collection.lock.yaml +27 -3
  114. data/rbs_collection.yaml +2 -0
  115. data/sig/generated/lrama/bitmap.rbs +12 -4
  116. data/sig/generated/lrama/counterexamples/derivation.rbs +36 -0
  117. data/sig/generated/lrama/counterexamples/example.rbs +58 -0
  118. data/sig/generated/lrama/counterexamples/node.rbs +18 -0
  119. data/sig/generated/lrama/counterexamples/path.rbs +23 -0
  120. data/sig/generated/lrama/counterexamples/state_item.rbs +19 -0
  121. data/sig/generated/lrama/counterexamples/triple.rbs +32 -0
  122. data/sig/generated/lrama/counterexamples.rbs +98 -0
  123. data/sig/generated/lrama/diagram.rbs +34 -0
  124. data/sig/generated/lrama/digraph.rbs +26 -6
  125. data/sig/generated/lrama/erb.rbs +14 -0
  126. data/sig/generated/lrama/grammar/auxiliary.rbs +16 -0
  127. data/sig/generated/lrama/grammar/binding.rbs +18 -12
  128. data/sig/generated/lrama/grammar/code/destructor_code.rbs +26 -0
  129. data/sig/{lrama → generated/lrama}/grammar/code/initial_action_code.rbs +6 -0
  130. data/sig/{lrama → generated/lrama}/grammar/code/no_reference_code.rbs +6 -0
  131. data/sig/generated/lrama/grammar/code/printer_code.rbs +26 -0
  132. data/sig/generated/lrama/grammar/code/rule_action.rbs +63 -0
  133. data/sig/generated/lrama/grammar/code.rbs +38 -0
  134. data/sig/{lrama → generated/lrama}/grammar/counter.rbs +4 -0
  135. data/sig/generated/lrama/grammar/destructor.rbs +19 -0
  136. data/sig/generated/lrama/grammar/error_token.rbs +19 -0
  137. data/sig/generated/lrama/grammar/inline/resolver.rbs +26 -0
  138. data/sig/generated/lrama/grammar/parameterized/resolver.rbs +42 -0
  139. data/sig/generated/lrama/grammar/parameterized/rhs.rbs +21 -0
  140. data/sig/generated/lrama/grammar/parameterized/rule.rbs +28 -0
  141. data/sig/{lrama → generated/lrama}/grammar/percent_code.rbs +8 -0
  142. data/sig/generated/lrama/grammar/precedence.rbs +45 -0
  143. data/sig/{lrama/grammar/error_token.rbs → generated/lrama/grammar/printer.rbs} +8 -3
  144. data/sig/generated/lrama/grammar/reference.rbs +31 -0
  145. data/sig/generated/lrama/grammar/rule.rbs +83 -0
  146. data/sig/generated/lrama/grammar/rule_builder.rbs +91 -0
  147. data/sig/generated/lrama/grammar/symbol.rbs +89 -0
  148. data/sig/generated/lrama/grammar/symbols/resolver.rbs +131 -0
  149. data/sig/generated/lrama/grammar/type.rbs +21 -0
  150. data/sig/generated/lrama/grammar/union.rbs +17 -0
  151. data/sig/generated/lrama/grammar.rbs +289 -0
  152. data/sig/generated/lrama/lexer/location.rbs +12 -3
  153. data/sig/generated/lrama/lexer/token/base.rbs +53 -0
  154. data/sig/generated/lrama/lexer/token/char.rbs +9 -2
  155. data/sig/generated/lrama/lexer/token/empty.rbs +11 -0
  156. data/sig/generated/lrama/lexer/token/ident.rbs +2 -2
  157. data/sig/generated/lrama/lexer/token/instantiate_rule.rbs +5 -5
  158. data/sig/generated/lrama/lexer/token/int.rbs +13 -0
  159. data/sig/generated/lrama/lexer/token/str.rbs +10 -0
  160. data/sig/generated/lrama/lexer/token/tag.rbs +2 -2
  161. data/sig/generated/lrama/lexer/token/token.rbs +10 -0
  162. data/sig/generated/lrama/lexer/token/user_code.rbs +2 -2
  163. data/sig/generated/lrama/lexer/token.rbs +1 -39
  164. data/sig/generated/lrama/lexer.rbs +54 -0
  165. data/sig/generated/lrama/logger.rbs +6 -0
  166. data/sig/generated/lrama/option_parser.rbs +52 -0
  167. data/sig/{lrama → generated/lrama}/options.rbs +27 -3
  168. data/sig/generated/lrama/reporter/conflicts.rbs +18 -0
  169. data/sig/generated/lrama/reporter/grammar.rbs +13 -0
  170. data/sig/generated/lrama/reporter/precedences.rbs +15 -0
  171. data/sig/generated/lrama/reporter/profile/call_stack.rbs +19 -0
  172. data/sig/generated/lrama/reporter/profile/memory.rbs +19 -0
  173. data/sig/generated/lrama/reporter/rules.rbs +13 -0
  174. data/sig/generated/lrama/reporter/states.rbs +69 -0
  175. data/sig/generated/lrama/reporter/terms.rbs +13 -0
  176. data/sig/generated/lrama/reporter.rbs +13 -0
  177. data/sig/generated/lrama/state/action/goto.rbs +28 -0
  178. data/sig/generated/lrama/state/action/reduce.rbs +49 -0
  179. data/sig/generated/lrama/state/action/shift.rbs +33 -0
  180. data/sig/generated/lrama/state/inadequacy_annotation.rbs +45 -0
  181. data/sig/generated/lrama/state/item.rbs +75 -0
  182. data/sig/generated/lrama/state/reduce_reduce_conflict.rbs +19 -0
  183. data/sig/generated/lrama/state/resolved_conflict.rbs +38 -0
  184. data/sig/generated/lrama/state/shift_reduce_conflict.rbs +19 -0
  185. data/sig/generated/lrama/state.rbs +231 -0
  186. data/sig/generated/lrama/states.rbs +215 -0
  187. data/sig/generated/lrama/tracer/actions.rbs +13 -0
  188. data/sig/generated/lrama/tracer/closure.rbs +13 -0
  189. data/sig/generated/lrama/tracer/duration.rbs +18 -0
  190. data/sig/generated/lrama/tracer/only_explicit_rules.rbs +13 -0
  191. data/sig/generated/lrama/tracer/rules.rbs +13 -0
  192. data/sig/generated/lrama/tracer/state.rbs +16 -0
  193. data/sig/generated/lrama/tracer.rbs +23 -0
  194. data/sig/generated/lrama/version.rbs +5 -0
  195. data/sig/generated/lrama/warnings/conflicts.rbs +13 -0
  196. data/sig/generated/lrama/warnings/implicit_empty.rbs +17 -0
  197. data/sig/generated/lrama/warnings/name_conflicts.rbs +31 -0
  198. data/sig/generated/lrama/warnings/redefined_rules.rbs +13 -0
  199. data/sig/generated/lrama/warnings/required.rbs +13 -0
  200. data/sig/generated/lrama/warnings/useless_precedence.rbs +13 -0
  201. data/sig/generated/lrama/warnings.rbs +11 -0
  202. data/sig/railroad_diagrams/railroad_diagrams.rbs +16 -0
  203. data/template/bison/_yacc.h +8 -0
  204. data/template/diagram/diagram.html +102 -0
  205. metadata +126 -66
  206. data/lib/lrama/counterexamples/production_path.rb +0 -19
  207. data/lib/lrama/counterexamples/start_path.rb +0 -23
  208. data/lib/lrama/counterexamples/transition_path.rb +0 -19
  209. data/lib/lrama/diagnostics.rb +0 -36
  210. data/lib/lrama/grammar/parameterizing_rule/rule.rb +0 -24
  211. data/lib/lrama/grammar/parameterizing_rule.rb +0 -5
  212. data/lib/lrama/grammar_validator.rb +0 -37
  213. data/lib/lrama/report/duration.rb +0 -27
  214. data/lib/lrama/report/profile.rb +0 -16
  215. data/lib/lrama/report.rb +0 -4
  216. data/lib/lrama/state/reduce.rb +0 -37
  217. data/lib/lrama/state/shift.rb +0 -15
  218. data/lib/lrama/states_reporter.rb +0 -362
  219. data/lib/lrama/trace_reporter.rb +0 -45
  220. data/sig/generated/lrama/trace_reporter.rbs +0 -25
  221. data/sig/lrama/counterexamples/derivation.rbs +0 -33
  222. data/sig/lrama/counterexamples/example.rbs +0 -45
  223. data/sig/lrama/counterexamples/path.rbs +0 -21
  224. data/sig/lrama/counterexamples/production_path.rbs +0 -11
  225. data/sig/lrama/counterexamples/start_path.rbs +0 -13
  226. data/sig/lrama/counterexamples/state_item.rbs +0 -10
  227. data/sig/lrama/counterexamples/transition_path.rbs +0 -11
  228. data/sig/lrama/counterexamples/triple.rbs +0 -20
  229. data/sig/lrama/counterexamples.rbs +0 -29
  230. data/sig/lrama/grammar/auxiliary.rbs +0 -10
  231. data/sig/lrama/grammar/code/destructor_code.rbs +0 -14
  232. data/sig/lrama/grammar/code/printer_code.rbs +0 -14
  233. data/sig/lrama/grammar/code/rule_action.rbs +0 -19
  234. data/sig/lrama/grammar/code.rbs +0 -24
  235. data/sig/lrama/grammar/destructor.rbs +0 -13
  236. data/sig/lrama/grammar/parameterizing_rule/resolver.rbs +0 -24
  237. data/sig/lrama/grammar/parameterizing_rule/rhs.rbs +0 -14
  238. data/sig/lrama/grammar/parameterizing_rule/rule.rbs +0 -16
  239. data/sig/lrama/grammar/parameterizing_rule.rbs +0 -6
  240. data/sig/lrama/grammar/precedence.rbs +0 -13
  241. data/sig/lrama/grammar/printer.rbs +0 -13
  242. data/sig/lrama/grammar/reference.rbs +0 -22
  243. data/sig/lrama/grammar/rule.rbs +0 -45
  244. data/sig/lrama/grammar/rule_builder.rbs +0 -47
  245. data/sig/lrama/grammar/symbol.rbs +0 -38
  246. data/sig/lrama/grammar/symbols/resolver.rbs +0 -60
  247. data/sig/lrama/grammar/type.rbs +0 -11
  248. data/sig/lrama/grammar/union.rbs +0 -12
  249. data/sig/lrama/grammar.rbs +0 -108
  250. data/sig/lrama/report/duration.rbs +0 -11
  251. data/sig/lrama/report/profile.rbs +0 -7
  252. data/sig/lrama/state/reduce.rbs +0 -20
  253. data/sig/lrama/state/reduce_reduce_conflict.rbs +0 -13
  254. data/sig/lrama/state/resolved_conflict.rbs +0 -14
  255. data/sig/lrama/state/shift.rbs +0 -14
  256. data/sig/lrama/state/shift_reduce_conflict.rbs +0 -13
  257. data/sig/lrama/state.rbs +0 -79
  258. data/sig/lrama/states/item.rbs +0 -30
  259. data/sig/lrama/states.rbs +0 -101
  260. data/sig/lrama/warning.rbs +0 -16
@@ -1,29 +1,27 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Lrama
4
5
  class Counterexamples
5
6
  class Path
6
- def initialize(from_state_item, to_state_item)
7
- @from_state_item = from_state_item
8
- @to_state_item = to_state_item
9
- end
7
+ # @rbs!
8
+ # @state_item: StateItem
9
+ # @parent: Path?
10
10
 
11
- def from
12
- @from_state_item
13
- end
11
+ attr_reader :state_item #: StateItem
12
+ attr_reader :parent #: Path?
14
13
 
15
- def to
16
- @to_state_item
14
+ # @rbs (StateItem state_item, Path? parent) -> void
15
+ def initialize(state_item, parent)
16
+ @state_item = state_item
17
+ @parent = parent
17
18
  end
18
19
 
20
+ # @rbs () -> ::String
19
21
  def to_s
20
- "#<Path(#{type})>"
22
+ "#<Path>"
21
23
  end
22
24
  alias :inspect :to_s
23
-
24
- def type
25
- raise NotImplementedError
26
- end
27
25
  end
28
26
  end
29
27
  end
@@ -1,8 +1,31 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Lrama
4
5
  class Counterexamples
5
- class StateItem < Struct.new(:state, :item)
6
+ class StateItem
7
+ attr_reader :id #: Integer
8
+ attr_reader :state #: State
9
+ attr_reader :item #: State::Item
10
+
11
+ # @rbs (Integer id, State state, State::Item item) -> void
12
+ def initialize(id, state, item)
13
+ @id = id
14
+ @state = state
15
+ @item = item
16
+ end
17
+
18
+ # @rbs () -> (:start | :transition | :production)
19
+ def type
20
+ case
21
+ when item.start_item?
22
+ :start
23
+ when item.beginning_of_rule?
24
+ :production
25
+ else
26
+ :transition
27
+ end
28
+ end
6
29
  end
7
30
  end
8
31
  end
@@ -1,21 +1,39 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Lrama
4
5
  class Counterexamples
5
- # s: state
6
- # itm: item within s
7
- # l: precise lookahead set
8
- class Triple < Struct.new(:s, :itm, :l)
9
- alias :state :s
10
- alias :item :itm
11
- alias :precise_lookahead_set :l
6
+ class Triple
7
+ attr_reader :precise_lookahead_set #: Bitmap::bitmap
12
8
 
9
+ alias :l :precise_lookahead_set
10
+
11
+ # @rbs (StateItem state_item, Bitmap::bitmap precise_lookahead_set) -> void
12
+ def initialize(state_item, precise_lookahead_set)
13
+ @state_item = state_item
14
+ @precise_lookahead_set = precise_lookahead_set
15
+ end
16
+
17
+ # @rbs () -> State
18
+ def state
19
+ @state_item.state
20
+ end
21
+ alias :s :state
22
+
23
+ # @rbs () -> State::Item
24
+ def item
25
+ @state_item.item
26
+ end
27
+ alias :itm :item
28
+
29
+ # @rbs () -> StateItem
13
30
  def state_item
14
- StateItem.new(state, item)
31
+ @state_item
15
32
  end
16
33
 
34
+ # @rbs () -> ::String
17
35
  def inspect
18
- "#{state.inspect}. #{item.display_name}. #{l.map(&:id).map(&:s_value)}"
36
+ "#{state.inspect}. #{item.display_name}. #{l.to_s(2)}"
19
37
  end
20
38
  alias :to_s :inspect
21
39
  end
@@ -1,35 +1,64 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require "set"
5
+ require "timeout"
4
6
 
5
7
  require_relative "counterexamples/derivation"
6
8
  require_relative "counterexamples/example"
9
+ require_relative "counterexamples/node"
7
10
  require_relative "counterexamples/path"
8
- require_relative "counterexamples/production_path"
9
- require_relative "counterexamples/start_path"
10
11
  require_relative "counterexamples/state_item"
11
- require_relative "counterexamples/transition_path"
12
12
  require_relative "counterexamples/triple"
13
13
 
14
14
  module Lrama
15
15
  # See: https://www.cs.cornell.edu/andru/papers/cupex/cupex.pdf
16
16
  # 4. Constructing Nonunifying Counterexamples
17
17
  class Counterexamples
18
- attr_reader :transitions, :productions
19
-
18
+ PathSearchTimeLimit = 10 # 10 sec
19
+ CumulativeTimeLimit = 120 # 120 sec
20
+
21
+ # @rbs!
22
+ # @states: States
23
+ # @iterate_count: Integer
24
+ # @total_duration: Float
25
+ # @exceed_cumulative_time_limit: bool
26
+ # @state_items: Hash[[State, State::Item], StateItem]
27
+ # @triples: Hash[Integer, Triple]
28
+ # @transitions: Hash[[StateItem, Grammar::Symbol], StateItem]
29
+ # @reverse_transitions: Hash[[StateItem, Grammar::Symbol], Set[StateItem]]
30
+ # @productions: Hash[StateItem, Set[StateItem]]
31
+ # @reverse_productions: Hash[[State, Grammar::Symbol], Set[StateItem]] # Grammar::Symbol is nterm
32
+ # @state_item_shift: Integer
33
+
34
+ attr_reader :transitions #: Hash[[StateItem, Grammar::Symbol], StateItem]
35
+ attr_reader :productions #: Hash[StateItem, Set[StateItem]]
36
+
37
+ # @rbs (States states) -> void
20
38
  def initialize(states)
21
39
  @states = states
40
+ @iterate_count = 0
41
+ @total_duration = 0
42
+ @exceed_cumulative_time_limit = false
43
+ @triples = {}
44
+ setup_state_items
22
45
  setup_transitions
23
46
  setup_productions
24
47
  end
25
48
 
49
+ # @rbs () -> "#<Counterexamples>"
26
50
  def to_s
27
51
  "#<Counterexamples>"
28
52
  end
29
53
  alias :inspect :to_s
30
54
 
55
+ # @rbs (State conflict_state) -> Array[Example]
31
56
  def compute(conflict_state)
32
57
  conflict_state.conflicts.flat_map do |conflict|
58
+ # Check cumulative time limit for not each path search method call but each conflict
59
+ # to avoid one of example's path to be nil.
60
+ next if @exceed_cumulative_time_limit
61
+
33
62
  case conflict.type
34
63
  when :shift_reduce
35
64
  # @type var conflict: State::ShiftReduceConflict
@@ -38,22 +67,50 @@ module Lrama
38
67
  # @type var conflict: State::ReduceReduceConflict
39
68
  reduce_reduce_examples(conflict_state, conflict)
40
69
  end
70
+ rescue Timeout::Error => e
71
+ STDERR.puts "Counterexamples calculation for state #{conflict_state.id} #{e.message} with #{@iterate_count} iteration"
72
+ increment_total_duration(PathSearchTimeLimit)
73
+ nil
41
74
  end.compact
42
75
  end
43
76
 
44
77
  private
45
78
 
79
+ # @rbs (State state, State::Item item) -> StateItem
80
+ def get_state_item(state, item)
81
+ @state_items[[state, item]]
82
+ end
83
+
84
+ # For optimization, create all StateItem in advance
85
+ # and use them by fetching an instance from `@state_items`.
86
+ # Do not create new StateItem instance in the shortest path search process
87
+ # to avoid miss hash lookup.
88
+ #
89
+ # @rbs () -> void
90
+ def setup_state_items
91
+ @state_items = {}
92
+ count = 0
93
+
94
+ @states.states.each do |state|
95
+ state.items.each do |item|
96
+ @state_items[[state, item]] = StateItem.new(count, state, item)
97
+ count += 1
98
+ end
99
+ end
100
+
101
+ @state_item_shift = Math.log(count, 2).ceil
102
+ end
103
+
104
+ # @rbs () -> void
46
105
  def setup_transitions
47
- # Hash [StateItem, Symbol] => StateItem
48
106
  @transitions = {}
49
- # Hash [StateItem, Symbol] => Set(StateItem)
50
107
  @reverse_transitions = {}
51
108
 
52
109
  @states.states.each do |src_state|
53
110
  trans = {} #: Hash[Grammar::Symbol, State]
54
111
 
55
- src_state.transitions.each do |shift, next_state|
56
- trans[shift.next_sym] = next_state
112
+ src_state.transitions.each do |transition|
113
+ trans[transition.next_sym] = transition.to_state
57
114
  end
58
115
 
59
116
  src_state.items.each do |src_item|
@@ -63,8 +120,8 @@ module Lrama
63
120
 
64
121
  dest_state.kernels.each do |dest_item|
65
122
  next unless (src_item.rule == dest_item.rule) && (src_item.position + 1 == dest_item.position)
66
- src_state_item = StateItem.new(src_state, src_item)
67
- dest_state_item = StateItem.new(dest_state, dest_item)
123
+ src_state_item = get_state_item(src_state, src_item)
124
+ dest_state_item = get_state_item(dest_state, dest_item)
68
125
 
69
126
  @transitions[[src_state_item, sym]] = dest_state_item
70
127
 
@@ -77,21 +134,20 @@ module Lrama
77
134
  end
78
135
  end
79
136
 
137
+ # @rbs () -> void
80
138
  def setup_productions
81
- # Hash [StateItem] => Set(Item)
82
139
  @productions = {}
83
- # Hash [State, Symbol] => Set(Item). Symbol is nterm
84
140
  @reverse_productions = {}
85
141
 
86
142
  @states.states.each do |state|
87
- # LHS => Set(Item)
88
- h = {} #: Hash[Grammar::Symbol, Set[States::Item]]
143
+ # Grammar::Symbol is LHS
144
+ h = {} #: Hash[Grammar::Symbol, Set[StateItem]]
89
145
 
90
146
  state.closure.each do |item|
91
147
  sym = item.lhs
92
148
 
93
149
  h[sym] ||= Set.new
94
- h[sym] << item
150
+ h[sym] << get_state_item(state, item)
95
151
  end
96
152
 
97
153
  state.items.each do |item|
@@ -99,101 +155,118 @@ module Lrama
99
155
  next if item.next_sym.term?
100
156
 
101
157
  sym = item.next_sym
102
- state_item = StateItem.new(state, item)
103
- # @type var key: [State, Grammar::Symbol]
104
- key = [state, sym]
105
-
158
+ state_item = get_state_item(state, item)
106
159
  @productions[state_item] = h[sym]
107
160
 
161
+ # @type var key: [State, Grammar::Symbol]
162
+ key = [state, sym]
108
163
  @reverse_productions[key] ||= Set.new
109
- @reverse_productions[key] << item
164
+ @reverse_productions[key] << state_item
110
165
  end
111
166
  end
112
167
  end
113
168
 
169
+ # For optimization, use same Triple if it's already created.
170
+ # Do not create new Triple instance anywhere else
171
+ # to avoid miss hash lookup.
172
+ #
173
+ # @rbs (StateItem state_item, Bitmap::bitmap precise_lookahead_set) -> Triple
174
+ def get_triple(state_item, precise_lookahead_set)
175
+ key = (precise_lookahead_set << @state_item_shift) | state_item.id
176
+ @triples[key] ||= Triple.new(state_item, precise_lookahead_set)
177
+ end
178
+
179
+ # @rbs (State conflict_state, State::ShiftReduceConflict conflict) -> Example
114
180
  def shift_reduce_example(conflict_state, conflict)
115
181
  conflict_symbol = conflict.symbols.first
116
- # @type var shift_conflict_item: ::Lrama::States::Item
182
+ # @type var shift_conflict_item: ::Lrama::State::Item
117
183
  shift_conflict_item = conflict_state.items.find { |item| item.next_sym == conflict_symbol }
118
- path2 = shortest_path(conflict_state, conflict.reduce.item, conflict_symbol)
119
- path1 = find_shift_conflict_shortest_path(path2, conflict_state, shift_conflict_item)
184
+ path2 = with_timeout("#shortest_path:") do
185
+ shortest_path(conflict_state, conflict.reduce.item, conflict_symbol)
186
+ end
187
+ path1 = with_timeout("#find_shift_conflict_shortest_path:") do
188
+ find_shift_conflict_shortest_path(path2, conflict_state, shift_conflict_item)
189
+ end
120
190
 
121
191
  Example.new(path1, path2, conflict, conflict_symbol, self)
122
192
  end
123
193
 
194
+ # @rbs (State conflict_state, State::ReduceReduceConflict conflict) -> Example
124
195
  def reduce_reduce_examples(conflict_state, conflict)
125
196
  conflict_symbol = conflict.symbols.first
126
- path1 = shortest_path(conflict_state, conflict.reduce1.item, conflict_symbol)
127
- path2 = shortest_path(conflict_state, conflict.reduce2.item, conflict_symbol)
197
+ path1 = with_timeout("#shortest_path:") do
198
+ shortest_path(conflict_state, conflict.reduce1.item, conflict_symbol)
199
+ end
200
+ path2 = with_timeout("#shortest_path:") do
201
+ shortest_path(conflict_state, conflict.reduce2.item, conflict_symbol)
202
+ end
128
203
 
129
204
  Example.new(path1, path2, conflict, conflict_symbol, self)
130
205
  end
131
206
 
132
- def find_shift_conflict_shortest_path(reduce_path, conflict_state, conflict_item)
133
- state_items = find_shift_conflict_shortest_state_items(reduce_path, conflict_state, conflict_item)
134
- build_paths_from_state_items(state_items)
135
- end
207
+ # @rbs (Array[StateItem]? reduce_state_items, State conflict_state, State::Item conflict_item) -> Array[StateItem]
208
+ def find_shift_conflict_shortest_path(reduce_state_items, conflict_state, conflict_item)
209
+ time1 = Time.now.to_f
210
+ @iterate_count = 0
136
211
 
137
- def find_shift_conflict_shortest_state_items(reduce_path, conflict_state, conflict_item)
138
- target_state_item = StateItem.new(conflict_state, conflict_item)
212
+ target_state_item = get_state_item(conflict_state, conflict_item)
139
213
  result = [target_state_item]
140
- reversed_reduce_path = reduce_path.to_a.reverse
214
+ reversed_state_items = reduce_state_items.to_a.reverse
141
215
  # Index for state_item
142
216
  i = 0
143
217
 
144
- while (path = reversed_reduce_path[i])
218
+ while (state_item = reversed_state_items[i])
145
219
  # Index for prev_state_item
146
220
  j = i + 1
147
221
  _j = j
148
222
 
149
- while (prev_path = reversed_reduce_path[j])
150
- if prev_path.production?
223
+ while (prev_state_item = reversed_state_items[j])
224
+ if prev_state_item.type == :production
151
225
  j += 1
152
226
  else
153
227
  break
154
228
  end
155
229
  end
156
230
 
157
- state_item = path.to
158
- prev_state_item = prev_path&.to
159
-
160
231
  if target_state_item == state_item || target_state_item.item.start_item?
161
232
  result.concat(
162
- reversed_reduce_path[_j..-1] #: Array[StartPath|TransitionPath|ProductionPath]
163
- .map(&:to))
233
+ reversed_state_items[_j..-1] #: Array[StateItem]
234
+ )
164
235
  break
165
236
  end
166
237
 
167
- if target_state_item.item.beginning_of_rule?
168
- queue = [] #: Array[Array[StateItem]]
169
- queue << [target_state_item]
238
+ if target_state_item.type == :production
239
+ queue = [] #: Array[Node[StateItem]]
240
+ queue << Node.new(target_state_item, nil)
170
241
 
171
242
  # Find reverse production
172
243
  while (sis = queue.shift)
173
- si = sis.last
244
+ @iterate_count += 1
245
+ si = sis.elem
174
246
 
175
247
  # Reach to start state
176
248
  if si.item.start_item?
177
- sis.shift
178
- result.concat(sis)
249
+ a = Node.to_a(sis).reverse
250
+ a.shift
251
+ result.concat(a)
179
252
  target_state_item = si
180
253
  break
181
254
  end
182
255
 
183
- if si.item.beginning_of_rule?
256
+ if si.type == :production
184
257
  # @type var key: [State, Grammar::Symbol]
185
258
  key = [si.state, si.item.lhs]
186
- @reverse_productions[key].each do |item|
187
- state_item = StateItem.new(si.state, item)
188
- queue << (sis + [state_item])
259
+ @reverse_productions[key].each do |state_item|
260
+ queue << Node.new(state_item, sis)
189
261
  end
190
262
  else
191
263
  # @type var key: [StateItem, Grammar::Symbol]
192
264
  key = [si, si.item.previous_sym]
193
265
  @reverse_transitions[key].each do |prev_target_state_item|
194
266
  next if prev_target_state_item.state != prev_state_item&.state
195
- sis.shift
196
- result.concat(sis)
267
+ a = Node.to_a(sis).reverse
268
+ a.shift
269
+ result.concat(a)
197
270
  result << prev_target_state_item
198
271
  target_state_item = prev_target_state_item
199
272
  i = j
@@ -216,68 +289,106 @@ module Lrama
216
289
  end
217
290
  end
218
291
 
292
+ time2 = Time.now.to_f
293
+ duration = time2 - time1
294
+ increment_total_duration(duration)
295
+
296
+ if Tracer::Duration.enabled?
297
+ STDERR.puts sprintf(" %s %10.5f s", "find_shift_conflict_shortest_path #{@iterate_count} iteration", duration)
298
+ end
299
+
219
300
  result.reverse
220
301
  end
221
302
 
222
- def build_paths_from_state_items(state_items)
223
- state_items.zip([nil] + state_items).map do |si, prev_si|
224
- case
225
- when prev_si.nil?
226
- StartPath.new(si)
227
- when si.item.beginning_of_rule?
228
- ProductionPath.new(prev_si, si)
229
- else
230
- TransitionPath.new(prev_si, si)
303
+ # @rbs (StateItem target) -> Set[StateItem]
304
+ def reachable_state_items(target)
305
+ result = Set.new
306
+ queue = [target]
307
+
308
+ while (state_item = queue.shift)
309
+ next if result.include?(state_item)
310
+ result << state_item
311
+
312
+ @reverse_transitions[[state_item, state_item.item.previous_sym]]&.each do |prev_state_item|
313
+ queue << prev_state_item
314
+ end
315
+
316
+ if state_item.item.beginning_of_rule?
317
+ @reverse_productions[[state_item.state, state_item.item.lhs]]&.each do |si|
318
+ queue << si
319
+ end
231
320
  end
232
321
  end
322
+
323
+ result
233
324
  end
234
325
 
326
+ # @rbs (State conflict_state, State::Item conflict_reduce_item, Grammar::Symbol conflict_term) -> ::Array[StateItem]?
235
327
  def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
236
- # queue: is an array of [Triple, [Path]]
237
- queue = [] #: Array[[Triple, Array[StartPath|TransitionPath|ProductionPath]]]
328
+ time1 = Time.now.to_f
329
+ @iterate_count = 0
330
+
331
+ queue = [] #: Array[[Triple, Path]]
238
332
  visited = {} #: Hash[Triple, true]
239
333
  start_state = @states.states.first #: Lrama::State
334
+ conflict_term_bit = Bitmap::from_integer(conflict_term.number)
240
335
  raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1
336
+ reachable = reachable_state_items(get_state_item(conflict_state, conflict_reduce_item))
337
+ start = get_triple(get_state_item(start_state, start_state.kernels.first), Bitmap::from_integer(@states.eof_symbol.number))
241
338
 
242
- start = Triple.new(start_state, start_state.kernels.first, Set.new([@states.eof_symbol]))
339
+ queue << [start, Path.new(start.state_item, nil)]
243
340
 
244
- queue << [start, [StartPath.new(start.state_item)]]
341
+ while (triple, path = queue.shift)
342
+ @iterate_count += 1
245
343
 
246
- while true
247
- triple, paths = queue.shift
344
+ # Found
345
+ if (triple.state == conflict_state) && (triple.item == conflict_reduce_item) && (triple.l & conflict_term_bit != 0)
346
+ state_items = [path.state_item]
248
347
 
249
- next if visited[triple]
250
- visited[triple] = true
348
+ while (path = path.parent)
349
+ state_items << path.state_item
350
+ end
251
351
 
252
- # Found
253
- if triple.state == conflict_state && triple.item == conflict_reduce_item && triple.l.include?(conflict_term)
254
- return paths
352
+ time2 = Time.now.to_f
353
+ duration = time2 - time1
354
+ increment_total_duration(duration)
355
+
356
+ if Tracer::Duration.enabled?
357
+ STDERR.puts sprintf(" %s %10.5f s", "shortest_path #{@iterate_count} iteration", duration)
358
+ end
359
+
360
+ return state_items.reverse
255
361
  end
256
362
 
257
363
  # transition
258
- triple.state.transitions.each do |shift, next_state|
259
- next unless triple.item.next_sym && triple.item.next_sym == shift.next_sym
260
- next_state.kernels.each do |kernel|
261
- next if kernel.rule != triple.item.rule
262
- t = Triple.new(next_state, kernel, triple.l)
263
- queue << [t, paths + [TransitionPath.new(triple.state_item, t.state_item)]]
364
+ next_state_item = @transitions[[triple.state_item, triple.item.next_sym]]
365
+ if next_state_item && reachable.include?(next_state_item)
366
+ # @type var t: Triple
367
+ t = get_triple(next_state_item, triple.l)
368
+ unless visited[t]
369
+ visited[t] = true
370
+ queue << [t, Path.new(t.state_item, path)]
264
371
  end
265
372
  end
266
373
 
267
374
  # production step
268
- triple.state.closure.each do |item|
269
- next unless triple.item.next_sym && triple.item.next_sym == item.lhs
375
+ @productions[triple.state_item]&.each do |si|
376
+ next unless reachable.include?(si)
377
+
270
378
  l = follow_l(triple.item, triple.l)
271
- t = Triple.new(triple.state, item, l)
272
- queue << [t, paths + [ProductionPath.new(triple.state_item, t.state_item)]]
379
+ # @type var t: Triple
380
+ t = get_triple(si, l)
381
+ unless visited[t]
382
+ visited[t] = true
383
+ queue << [t, Path.new(t.state_item, path)]
384
+ end
273
385
  end
274
-
275
- break if queue.empty?
276
386
  end
277
387
 
278
388
  return nil
279
389
  end
280
390
 
391
+ # @rbs (State::Item item, Bitmap::bitmap current_l) -> Bitmap::bitmap
281
392
  def follow_l(item, current_l)
282
393
  # 1. follow_L (A -> X1 ... Xn-1 • Xn) = L
283
394
  # 2. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = {Xk+2} if Xk+2 is a terminal
@@ -287,11 +398,28 @@ module Lrama
287
398
  when item.number_of_rest_symbols == 1
288
399
  current_l
289
400
  when item.next_next_sym.term?
290
- Set.new([item.next_next_sym])
401
+ item.next_next_sym.number_bitmap
291
402
  when !item.next_next_sym.nullable
292
- item.next_next_sym.first_set
403
+ item.next_next_sym.first_set_bitmap
293
404
  else
294
- item.next_next_sym.first_set + follow_l(item.new_by_next_position, current_l)
405
+ item.next_next_sym.first_set_bitmap | follow_l(item.new_by_next_position, current_l)
406
+ end
407
+ end
408
+
409
+ # @rbs [T] (String message) { -> T } -> T
410
+ def with_timeout(message)
411
+ Timeout.timeout(PathSearchTimeLimit, Timeout::Error, message + " timeout of #{PathSearchTimeLimit} sec exceeded") do
412
+ yield
413
+ end
414
+ end
415
+
416
+ # @rbs (Float|Integer duration) -> void
417
+ def increment_total_duration(duration)
418
+ @total_duration += duration
419
+
420
+ if !@exceed_cumulative_time_limit && @total_duration > CumulativeTimeLimit
421
+ @exceed_cumulative_time_limit = true
422
+ STDERR.puts "CumulativeTimeLimit #{CumulativeTimeLimit} sec exceeded then skip following Counterexamples calculation"
295
423
  end
296
424
  end
297
425
  end