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
@@ -0,0 +1,44 @@
1
+ # rbs_inline: enabled
2
+ # frozen_string_literal: true
3
+
4
+ module Lrama
5
+ class Reporter
6
+ class Conflicts
7
+ # @rbs (IO io, Lrama::States states) -> void
8
+ def report(io, states)
9
+ report_conflicts(io, states)
10
+ end
11
+
12
+ private
13
+
14
+ # @rbs (IO io, Lrama::States states) -> void
15
+ def report_conflicts(io, states)
16
+ has_conflict = false
17
+
18
+ states.states.each do |state|
19
+ messages = format_conflict_messages(state.conflicts)
20
+
21
+ unless messages.empty?
22
+ has_conflict = true
23
+ io << "State #{state.id} conflicts: #{messages.join(', ')}\n"
24
+ end
25
+ end
26
+
27
+ io << "\n\n" if has_conflict
28
+ end
29
+
30
+ # @rbs (Array[(Lrama::State::ShiftReduceConflict | Lrama::State::ReduceReduceConflict)] conflicts) -> Array[String]
31
+ def format_conflict_messages(conflicts)
32
+ conflict_types = {
33
+ shift_reduce: "shift/reduce",
34
+ reduce_reduce: "reduce/reduce"
35
+ }
36
+
37
+ conflict_types.keys.map do |type|
38
+ type_conflicts = conflicts.select { |c| c.type == type }
39
+ "#{type_conflicts.count} #{conflict_types[type]}" unless type_conflicts.empty?
40
+ end.compact
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,39 @@
1
+ # rbs_inline: enabled
2
+ # frozen_string_literal: true
3
+
4
+ module Lrama
5
+ class Reporter
6
+ class Grammar
7
+ # @rbs (?grammar: bool, **bool _) -> void
8
+ def initialize(grammar: false, **_)
9
+ @grammar = grammar
10
+ end
11
+
12
+ # @rbs (IO io, Lrama::States states) -> void
13
+ def report(io, states)
14
+ return unless @grammar
15
+
16
+ io << "Grammar\n"
17
+ last_lhs = nil
18
+
19
+ states.rules.each do |rule|
20
+ if rule.empty_rule?
21
+ r = "ε"
22
+ else
23
+ r = rule.rhs.map(&:display_name).join(" ")
24
+ end
25
+
26
+ if rule.lhs == last_lhs
27
+ io << sprintf("%5d %s| %s", rule.id, " " * rule.lhs.display_name.length, r) << "\n"
28
+ else
29
+ io << "\n"
30
+ io << sprintf("%5d %s: %s", rule.id, rule.lhs.display_name, r) << "\n"
31
+ end
32
+
33
+ last_lhs = rule.lhs
34
+ end
35
+ io << "\n\n"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,54 @@
1
+ # rbs_inline: enabled
2
+ # frozen_string_literal: true
3
+
4
+ module Lrama
5
+ class Reporter
6
+ class Precedences
7
+ # @rbs (IO io, Lrama::States states) -> void
8
+ def report(io, states)
9
+ report_precedences(io, states)
10
+ end
11
+
12
+ private
13
+
14
+ # @rbs (IO io, Lrama::States states) -> void
15
+ def report_precedences(io, states)
16
+ used_precedences = states.precedences.select(&:used_by?)
17
+
18
+ return if used_precedences.empty?
19
+
20
+ io << "Precedences\n\n"
21
+
22
+ used_precedences.each do |precedence|
23
+ io << " precedence on #{precedence.symbol.display_name} is used to resolve conflict on\n"
24
+
25
+ if precedence.used_by_lalr?
26
+ io << " LALR\n"
27
+
28
+ precedence.used_by_lalr.uniq.sort_by do |resolved_conflict|
29
+ resolved_conflict.state.id
30
+ end.each do |resolved_conflict|
31
+ io << " state #{resolved_conflict.state.id}. #{resolved_conflict.report_precedences_message}\n"
32
+ end
33
+
34
+ io << "\n"
35
+ end
36
+
37
+ if precedence.used_by_ielr?
38
+ io << " IELR\n"
39
+
40
+ precedence.used_by_ielr.uniq.sort_by do |resolved_conflict|
41
+ resolved_conflict.state.id
42
+ end.each do |resolved_conflict|
43
+ io << " state #{resolved_conflict.state.id}. #{resolved_conflict.report_precedences_message}\n"
44
+ end
45
+
46
+ io << "\n"
47
+ end
48
+ end
49
+
50
+ io << "\n"
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,45 @@
1
+ # rbs_inline: enabled
2
+ # frozen_string_literal: true
3
+
4
+ module Lrama
5
+ class Reporter
6
+ module Profile
7
+ module CallStack
8
+ # See "Call-stack Profiling Lrama" in README.md for how to use.
9
+ #
10
+ # @rbs enabled: bool
11
+ # @rbs &: -> void
12
+ # @rbs return: StackProf::result | void
13
+ def self.report(enabled)
14
+ if enabled && require_stackprof
15
+ ex = nil #: Exception?
16
+ path = 'tmp/stackprof-cpu-myapp.dump'
17
+
18
+ StackProf.run(mode: :cpu, raw: true, out: path) do
19
+ yield
20
+ rescue Exception => e
21
+ ex = e
22
+ end
23
+
24
+ STDERR.puts("Call-stack Profiling result is generated on #{path}")
25
+
26
+ if ex
27
+ raise ex
28
+ end
29
+ else
30
+ yield
31
+ end
32
+ end
33
+
34
+ # @rbs return: bool
35
+ def self.require_stackprof
36
+ require "stackprof"
37
+ true
38
+ rescue LoadError
39
+ warn "stackprof is not installed. Please run `bundle install`."
40
+ false
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,44 @@
1
+ # rbs_inline: enabled
2
+ # frozen_string_literal: true
3
+
4
+ module Lrama
5
+ class Reporter
6
+ module Profile
7
+ module Memory
8
+ # See "Memory Profiling Lrama" in README.md for how to use.
9
+ #
10
+ # @rbs enabled: bool
11
+ # @rbs &: -> void
12
+ # @rbs return: StackProf::result | void
13
+ def self.report(enabled)
14
+ if enabled && require_memory_profiler
15
+ ex = nil #: Exception?
16
+
17
+ report = MemoryProfiler.report do # steep:ignore UnknownConstant
18
+ yield
19
+ rescue Exception => e
20
+ ex = e
21
+ end
22
+
23
+ report.pretty_print(to_file: "tmp/memory_profiler.txt")
24
+
25
+ if ex
26
+ raise ex
27
+ end
28
+ else
29
+ yield
30
+ end
31
+ end
32
+
33
+ # @rbs return: bool
34
+ def self.require_memory_profiler
35
+ require "memory_profiler"
36
+ true
37
+ rescue LoadError
38
+ warn "memory_profiler is not installed. Please run `bundle install`."
39
+ false
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'profile/call_stack'
4
+ require_relative 'profile/memory'
@@ -0,0 +1,43 @@
1
+ # rbs_inline: enabled
2
+ # frozen_string_literal: true
3
+
4
+ module Lrama
5
+ class Reporter
6
+ class Rules
7
+ # @rbs (?rules: bool, **bool _) -> void
8
+ def initialize(rules: false, **_)
9
+ @rules = rules
10
+ end
11
+
12
+ # @rbs (IO io, Lrama::States states) -> void
13
+ def report(io, states)
14
+ return unless @rules
15
+
16
+ used_rules = states.rules.flat_map(&:rhs)
17
+
18
+ unless used_rules.empty?
19
+ io << "Rule Usage Frequency\n\n"
20
+ frequency_counts = used_rules.each_with_object(Hash.new(0)) { |rule, counts| counts[rule] += 1 }
21
+
22
+ frequency_counts
23
+ .select { |rule,| !rule.midrule? }
24
+ .sort_by { |rule, count| [-count, rule.name] }
25
+ .each_with_index { |(rule, count), i| io << sprintf("%5d %s (%d times)", i, rule.name, count) << "\n" }
26
+ io << "\n\n"
27
+ end
28
+
29
+ unused_rules = states.rules.map(&:lhs).select do |rule|
30
+ !used_rules.include?(rule) && rule.token_id != 0
31
+ end
32
+
33
+ unless unused_rules.empty?
34
+ io << "#{unused_rules.count} Unused Rules\n\n"
35
+ unused_rules.each_with_index do |rule, index|
36
+ io << sprintf("%5d %s", index, rule.display_name) << "\n"
37
+ end
38
+ io << "\n\n"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,387 @@
1
+ # rbs_inline: enabled
2
+ # frozen_string_literal: true
3
+
4
+ module Lrama
5
+ class Reporter
6
+ class States
7
+ # @rbs (?itemsets: bool, ?lookaheads: bool, ?solved: bool, ?counterexamples: bool, ?verbose: bool, **bool _) -> void
8
+ def initialize(itemsets: false, lookaheads: false, solved: false, counterexamples: false, verbose: false, **_)
9
+ @itemsets = itemsets
10
+ @lookaheads = lookaheads
11
+ @solved = solved
12
+ @counterexamples = counterexamples
13
+ @verbose = verbose
14
+ end
15
+
16
+ # @rbs (IO io, Lrama::States states, ielr: bool) -> void
17
+ def report(io, states, ielr: false)
18
+ cex = Counterexamples.new(states) if @counterexamples
19
+
20
+ states.compute_la_sources_for_conflicted_states
21
+ report_split_states(io, states.states) if ielr
22
+
23
+ states.states.each do |state|
24
+ report_state_header(io, state)
25
+ report_items(io, state)
26
+ report_conflicts(io, state)
27
+ report_shifts(io, state)
28
+ report_nonassoc_errors(io, state)
29
+ report_reduces(io, state)
30
+ report_nterm_transitions(io, state)
31
+ report_conflict_resolutions(io, state) if @solved
32
+ report_counterexamples(io, state, cex) if @counterexamples && state.has_conflicts? # @type var cex: Lrama::Counterexamples
33
+ report_verbose_info(io, state, states) if @verbose
34
+ # End of Report State
35
+ io << "\n"
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ # @rbs (IO io, Array[Lrama::State] states) -> void
42
+ def report_split_states(io, states)
43
+ ss = states.select(&:split_state?)
44
+
45
+ return if ss.empty?
46
+
47
+ io << "Split States\n\n"
48
+
49
+ ss.each do |state|
50
+ io << " State #{state.id} is split from state #{state.lalr_isocore.id}\n"
51
+ end
52
+
53
+ io << "\n"
54
+ end
55
+
56
+ # @rbs (IO io, Lrama::State state) -> void
57
+ def report_state_header(io, state)
58
+ io << "State #{state.id}\n\n"
59
+ end
60
+
61
+ # @rbs (IO io, Lrama::State state) -> void
62
+ def report_items(io, state)
63
+ last_lhs = nil
64
+ list = @itemsets ? state.items : state.kernels
65
+
66
+ list.sort_by {|i| [i.rule_id, i.position] }.each do |item|
67
+ r = item.empty_rule? ? "ε •" : item.rhs.map(&:display_name).insert(item.position, "•").join(" ")
68
+
69
+ l = if item.lhs == last_lhs
70
+ " " * item.lhs.id.s_value.length + "|"
71
+ else
72
+ item.lhs.id.s_value + ":"
73
+ end
74
+
75
+ la = ""
76
+ if @lookaheads && item.end_of_rule?
77
+ reduce = state.find_reduce_by_item!(item)
78
+ look_ahead = reduce.selected_look_ahead
79
+ unless look_ahead.empty?
80
+ la = " [#{look_ahead.compact.map(&:display_name).join(", ")}]"
81
+ end
82
+ end
83
+
84
+ last_lhs = item.lhs
85
+ io << sprintf("%5i %s %s%s", item.rule_id, l, r, la) << "\n"
86
+ end
87
+
88
+ io << "\n"
89
+ end
90
+
91
+ # @rbs (IO io, Lrama::State state) -> void
92
+ def report_conflicts(io, state)
93
+ return if state.conflicts.empty?
94
+
95
+ state.conflicts.each do |conflict|
96
+ syms = conflict.symbols.map { |sym| sym.display_name }
97
+ io << " Conflict on #{syms.join(", ")}. "
98
+
99
+ case conflict.type
100
+ when :shift_reduce
101
+ # @type var conflict: Lrama::State::ShiftReduceConflict
102
+ io << "shift/reduce(#{conflict.reduce.item.rule.lhs.display_name})\n"
103
+
104
+ conflict.symbols.each do |token|
105
+ conflict.reduce.look_ahead_sources[token].each do |goto| # steep:ignore NoMethod
106
+ io << " #{token.display_name} comes from state #{goto.from_state.id} goto by #{goto.next_sym.display_name}\n"
107
+ end
108
+ end
109
+ when :reduce_reduce
110
+ # @type var conflict: Lrama::State::ReduceReduceConflict
111
+ io << "reduce(#{conflict.reduce1.item.rule.lhs.display_name})/reduce(#{conflict.reduce2.item.rule.lhs.display_name})\n"
112
+
113
+ conflict.symbols.each do |token|
114
+ conflict.reduce1.look_ahead_sources[token].each do |goto| # steep:ignore NoMethod
115
+ io << " #{token.display_name} comes from state #{goto.from_state.id} goto by #{goto.next_sym.display_name}\n"
116
+ end
117
+
118
+ conflict.reduce2.look_ahead_sources[token].each do |goto| # steep:ignore NoMethod
119
+ io << " #{token.display_name} comes from state #{goto.from_state.id} goto by #{goto.next_sym.display_name}\n"
120
+ end
121
+ end
122
+ else
123
+ raise "Unknown conflict type #{conflict.type}"
124
+ end
125
+
126
+ io << "\n"
127
+ end
128
+
129
+ io << "\n"
130
+ end
131
+
132
+ # @rbs (IO io, Lrama::State state) -> void
133
+ def report_shifts(io, state)
134
+ shifts = state.term_transitions.reject(&:not_selected)
135
+
136
+ return if shifts.empty?
137
+
138
+ next_syms = shifts.map(&:next_sym)
139
+ max_len = next_syms.map(&:display_name).map(&:length).max
140
+ shifts.each do |shift|
141
+ io << " #{shift.next_sym.display_name.ljust(max_len)} shift, and go to state #{shift.to_state.id}\n"
142
+ end
143
+
144
+ io << "\n"
145
+ end
146
+
147
+ # @rbs (IO io, Lrama::State state) -> void
148
+ def report_nonassoc_errors(io, state)
149
+ error_symbols = state.resolved_conflicts.select { |resolved| resolved.which == :error }.map { |error| error.symbol.display_name }
150
+
151
+ return if error_symbols.empty?
152
+
153
+ max_len = error_symbols.map(&:length).max
154
+ error_symbols.each do |name|
155
+ io << " #{name.ljust(max_len)} error (nonassociative)\n"
156
+ end
157
+
158
+ io << "\n"
159
+ end
160
+
161
+ # @rbs (IO io, Lrama::State state) -> void
162
+ def report_reduces(io, state)
163
+ reduce_pairs = [] #: Array[[Lrama::Grammar::Symbol, Lrama::State::Action::Reduce]]
164
+
165
+ state.non_default_reduces.each do |reduce|
166
+ reduce.look_ahead&.each do |term|
167
+ reduce_pairs << [term, reduce]
168
+ end
169
+ end
170
+
171
+ return if reduce_pairs.empty? && !state.default_reduction_rule
172
+
173
+ max_len = [
174
+ reduce_pairs.map(&:first).map(&:display_name).map(&:length).max || 0,
175
+ state.default_reduction_rule ? "$default".length : 0
176
+ ].max
177
+
178
+ reduce_pairs.sort_by { |term, _| term.number }.each do |term, reduce|
179
+ rule = reduce.item.rule
180
+ io << " #{term.display_name.ljust(max_len)} reduce using rule #{rule.id} (#{rule.lhs.display_name})\n"
181
+ end
182
+
183
+ if (r = state.default_reduction_rule)
184
+ s = "$default".ljust(max_len)
185
+
186
+ if r.initial_rule?
187
+ io << " #{s} accept\n"
188
+ else
189
+ io << " #{s} reduce using rule #{r.id} (#{r.lhs.display_name})\n"
190
+ end
191
+ end
192
+
193
+ io << "\n"
194
+ end
195
+
196
+ # @rbs (IO io, Lrama::State state) -> void
197
+ def report_nterm_transitions(io, state)
198
+ return if state.nterm_transitions.empty?
199
+
200
+ goto_transitions = state.nterm_transitions.sort_by do |goto|
201
+ goto.next_sym.number
202
+ end
203
+
204
+ max_len = goto_transitions.map(&:next_sym).map do |nterm|
205
+ nterm.id.s_value.length
206
+ end.max
207
+ goto_transitions.each do |goto|
208
+ io << " #{goto.next_sym.id.s_value.ljust(max_len)} go to state #{goto.to_state.id}\n"
209
+ end
210
+
211
+ io << "\n"
212
+ end
213
+
214
+ # @rbs (IO io, Lrama::State state) -> void
215
+ def report_conflict_resolutions(io, state)
216
+ return if state.resolved_conflicts.empty?
217
+
218
+ state.resolved_conflicts.each do |resolved|
219
+ io << " #{resolved.report_message}\n"
220
+ end
221
+
222
+ io << "\n"
223
+ end
224
+
225
+ # @rbs (IO io, Lrama::State state, Lrama::Counterexamples cex) -> void
226
+ def report_counterexamples(io, state, cex)
227
+ examples = cex.compute(state)
228
+
229
+ examples.each do |example|
230
+ is_shift_reduce = example.type == :shift_reduce
231
+ label0 = is_shift_reduce ? "shift/reduce" : "reduce/reduce"
232
+ label1 = is_shift_reduce ? "Shift derivation" : "First Reduce derivation"
233
+ label2 = is_shift_reduce ? "Reduce derivation" : "Second Reduce derivation"
234
+
235
+ io << " #{label0} conflict on token #{example.conflict_symbol.id.s_value}:\n"
236
+ io << " #{example.path1_item}\n"
237
+ io << " #{example.path2_item}\n"
238
+ io << " #{label1}\n"
239
+
240
+ example.derivations1.render_strings_for_report.each do |str|
241
+ io << " #{str}\n"
242
+ end
243
+
244
+ io << " #{label2}\n"
245
+
246
+ example.derivations2.render_strings_for_report.each do |str|
247
+ io << " #{str}\n"
248
+ end
249
+ end
250
+ end
251
+
252
+ # @rbs (IO io, Lrama::State state, Lrama::States states) -> void
253
+ def report_verbose_info(io, state, states)
254
+ report_direct_read_sets(io, state, states)
255
+ report_reads_relation(io, state, states)
256
+ report_read_sets(io, state, states)
257
+ report_includes_relation(io, state, states)
258
+ report_lookback_relation(io, state, states)
259
+ report_follow_sets(io, state, states)
260
+ report_look_ahead_sets(io, state, states)
261
+ end
262
+
263
+ # @rbs (IO io, Lrama::State state, Lrama::States states) -> void
264
+ def report_direct_read_sets(io, state, states)
265
+ io << " [Direct Read sets]\n"
266
+ direct_read_sets = states.direct_read_sets
267
+
268
+ state.nterm_transitions.each do |goto|
269
+ terms = direct_read_sets[goto]
270
+ next unless terms && !terms.empty?
271
+
272
+ str = terms.map { |sym| sym.id.s_value }.join(", ")
273
+ io << " read #{goto.next_sym.id.s_value} shift #{str}\n"
274
+ end
275
+
276
+ io << "\n"
277
+ end
278
+
279
+ # @rbs (IO io, Lrama::State state, Lrama::States states) -> void
280
+ def report_reads_relation(io, state, states)
281
+ io << " [Reads Relation]\n"
282
+
283
+ state.nterm_transitions.each do |goto|
284
+ goto2 = states.reads_relation[goto]
285
+ next unless goto2
286
+
287
+ goto2.each do |goto2|
288
+ io << " (State #{goto2.from_state.id}, #{goto2.next_sym.id.s_value})\n"
289
+ end
290
+ end
291
+
292
+ io << "\n"
293
+ end
294
+
295
+ # @rbs (IO io, Lrama::State state, Lrama::States states) -> void
296
+ def report_read_sets(io, state, states)
297
+ io << " [Read sets]\n"
298
+ read_sets = states.read_sets
299
+
300
+ state.nterm_transitions.each do |goto|
301
+ terms = read_sets[goto]
302
+ next unless terms && !terms.empty?
303
+
304
+ terms.each do |sym|
305
+ io << " #{sym.id.s_value}\n"
306
+ end
307
+ end
308
+
309
+ io << "\n"
310
+ end
311
+
312
+ # @rbs (IO io, Lrama::State state, Lrama::States states) -> void
313
+ def report_includes_relation(io, state, states)
314
+ io << " [Includes Relation]\n"
315
+
316
+ state.nterm_transitions.each do |goto|
317
+ gotos = states.includes_relation[goto]
318
+ next unless gotos
319
+
320
+ gotos.each do |goto2|
321
+ io << " (State #{state.id}, #{goto.next_sym.id.s_value}) -> (State #{goto2.from_state.id}, #{goto2.next_sym.id.s_value})\n"
322
+ end
323
+ end
324
+
325
+ io << "\n"
326
+ end
327
+
328
+ # @rbs (IO io, Lrama::State state, Lrama::States states) -> void
329
+ def report_lookback_relation(io, state, states)
330
+ io << " [Lookback Relation]\n"
331
+
332
+ states.rules.each do |rule|
333
+ gotos = states.lookback_relation.dig(state.id, rule.id)
334
+ next unless gotos
335
+
336
+ gotos.each do |goto2|
337
+ io << " (Rule: #{rule.display_name}) -> (State #{goto2.from_state.id}, #{goto2.next_sym.id.s_value})\n"
338
+ end
339
+ end
340
+
341
+ io << "\n"
342
+ end
343
+
344
+ # @rbs (IO io, Lrama::State state, Lrama::States states) -> void
345
+ def report_follow_sets(io, state, states)
346
+ io << " [Follow sets]\n"
347
+ follow_sets = states.follow_sets
348
+
349
+ state.nterm_transitions.each do |goto|
350
+ terms = follow_sets[goto]
351
+ next unless terms
352
+
353
+ terms.each do |sym|
354
+ io << " #{goto.next_sym.id.s_value} -> #{sym.id.s_value}\n"
355
+ end
356
+ end
357
+
358
+ io << "\n"
359
+ end
360
+
361
+ # @rbs (IO io, Lrama::State state, Lrama::States states) -> void
362
+ def report_look_ahead_sets(io, state, states)
363
+ io << " [Look-Ahead Sets]\n"
364
+ look_ahead_rules = [] #: Array[[Lrama::Grammar::Rule, Array[Lrama::Grammar::Symbol]]]
365
+
366
+ states.rules.each do |rule|
367
+ syms = states.la.dig(state.id, rule.id)
368
+ next unless syms
369
+
370
+ look_ahead_rules << [rule, syms]
371
+ end
372
+
373
+ return if look_ahead_rules.empty?
374
+
375
+ max_len = look_ahead_rules.flat_map { |_, syms| syms.map { |s| s.id.s_value.length } }.max
376
+
377
+ look_ahead_rules.each do |rule, syms|
378
+ syms.each do |sym|
379
+ io << " #{sym.id.s_value.ljust(max_len)} reduce using rule #{rule.id} (#{rule.lhs.id.s_value})\n"
380
+ end
381
+ end
382
+
383
+ io << "\n"
384
+ end
385
+ end
386
+ end
387
+ end