lrama 0.5.2 → 0.5.4

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yaml +10 -1
  3. data/.gitignore +1 -0
  4. data/Gemfile +1 -0
  5. data/LEGAL.md +1 -16
  6. data/README.md +11 -1
  7. data/Steepfile +2 -1
  8. data/doc/TODO.md +8 -3
  9. data/exe/lrama +1 -1
  10. data/lib/lrama/command.rb +91 -72
  11. data/lib/lrama/context.rb +11 -1
  12. data/lib/lrama/counterexamples/derivation.rb +63 -0
  13. data/lib/lrama/counterexamples/example.rb +124 -0
  14. data/lib/lrama/counterexamples/path.rb +69 -0
  15. data/lib/lrama/counterexamples/state_item.rb +6 -0
  16. data/lib/lrama/counterexamples/triple.rb +21 -0
  17. data/lib/lrama/counterexamples.rb +285 -0
  18. data/lib/lrama/digraph.rb +2 -3
  19. data/lib/lrama/grammar/auxiliary.rb +7 -0
  20. data/lib/lrama/grammar/code.rb +123 -0
  21. data/lib/lrama/grammar/error_token.rb +9 -0
  22. data/lib/lrama/grammar/precedence.rb +11 -0
  23. data/lib/lrama/grammar/printer.rb +9 -0
  24. data/lib/lrama/grammar/reference.rb +22 -0
  25. data/lib/lrama/grammar/rule.rb +39 -0
  26. data/lib/lrama/grammar/symbol.rb +87 -0
  27. data/lib/lrama/grammar/union.rb +10 -0
  28. data/lib/lrama/grammar.rb +89 -282
  29. data/lib/lrama/lexer/token/type.rb +8 -0
  30. data/lib/lrama/lexer/token.rb +77 -0
  31. data/lib/lrama/lexer.rb +4 -74
  32. data/lib/lrama/output.rb +32 -4
  33. data/lib/lrama/parser/token_scanner.rb +3 -6
  34. data/lib/lrama/parser.rb +9 -1
  35. data/lib/lrama/report/duration.rb +25 -0
  36. data/lib/lrama/report/profile.rb +25 -0
  37. data/lib/lrama/report.rb +2 -47
  38. data/lib/lrama/state/reduce_reduce_conflict.rb +9 -0
  39. data/lib/lrama/state/resolved_conflict.rb +29 -0
  40. data/lib/lrama/state/shift_reduce_conflict.rb +9 -0
  41. data/lib/lrama/state.rb +13 -30
  42. data/lib/lrama/states/item.rb +79 -0
  43. data/lib/lrama/states.rb +24 -73
  44. data/lib/lrama/states_reporter.rb +28 -3
  45. data/lib/lrama/type.rb +4 -0
  46. data/lib/lrama/version.rb +1 -1
  47. data/lib/lrama.rb +2 -0
  48. data/lrama.gemspec +1 -1
  49. data/sig/lrama/{report.rbs → report/duration.rbs} +0 -4
  50. data/sig/lrama/report/profile.rbs +7 -0
  51. data/template/bison/yacc.c +371 -0
  52. metadata +30 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26bcef726eebf61de01200b02250b2aef91ca957df43db5d72e99ae30e320f4e
4
- data.tar.gz: 751b62d184806c0cff319bffc4990ad0de9dd143accafdf6bdf1652eb02512b7
3
+ metadata.gz: f2e675399217ba6b1c8cc57aaa36bbf863ed8b22dc3e22777c88d0d0aaf1cb26
4
+ data.tar.gz: 940b0da60c6b25edb1c10ed80539b17617e033d7ccc3a5c9c6036959c356ae37
5
5
  SHA512:
6
- metadata.gz: 02001b5b137d023c4f5ce48c1d9284ce90c5aad2ec269d7ef7a17e6ffa65be9e553e213400fc63d8256c0209908fc4b314de261993442af4a25772449453fbeb
7
- data.tar.gz: 76a9f6bac19c0dcbdc6f9e2834d3c437f9fc5eb999d1b45d20362d52a94c9dc74bca3411b681993e28f388374df7f9b8ea7d0f534e53399efb8cc24112f75ea4
6
+ metadata.gz: 26eb48911cf5ba12b3087390bb7f7053165ff092a8919f7969e31b3e784a0085e876140d419cf4b463426cbfc4ae5f3625504a6c1433f9f9842ee9c32490082f
7
+ data.tar.gz: 8bce42121ffb5d45076dc3a1664e61209fec0f1a06a08d50d3a4279eb5158911b35165e21f8de0ac87c19e7cab1d2b7b0cac16bcaba0606ec61ea3b1c6195e0b
@@ -22,6 +22,15 @@ jobs:
22
22
  bundler-cache: true
23
23
  - run: bundle install
24
24
  - run: bundle exec rspec
25
+ check-misc:
26
+ runs-on: ubuntu-20.04
27
+ steps:
28
+ - uses: actions/checkout@v3
29
+ # Copy from https://github.com/ruby/ruby/blob/089227e94823542acfdafa68541d330eee42ffea/.github/workflows/check_misc.yml#L27
30
+ - name: Check for trailing spaces
31
+ run: |
32
+ git grep -I -n '[ ]$' -- '*.rb' '*.[chy]' '*.rs' && exit 1 || :
33
+ git grep -n '^[ ][ ]*$' -- '*.md' && exit 1 || :
25
34
  steep-check:
26
35
  runs-on: ubuntu-20.04
27
36
  strategy:
@@ -58,7 +67,7 @@ jobs:
58
67
  - run: mkdir -p tool/lrama
59
68
  working-directory: ../ruby
60
69
  - name: Copy Lrama to ruby/tool
61
- run: cp -r exe lib template ../ruby/tool/lrama
70
+ run: cp -r LEGAL.md MIT exe lib template ../ruby/tool/lrama
62
71
  working-directory:
63
72
  - run: tree tool/lrama
64
73
  working-directory: ../ruby
data/.gitignore CHANGED
@@ -3,3 +3,4 @@
3
3
  .irbrc
4
4
  /Gemfile.lock
5
5
  /pkg/
6
+ coverage/
data/Gemfile CHANGED
@@ -8,3 +8,4 @@ gem "stackprof"
8
8
  gem "rake"
9
9
  gem "rbs", require: false
10
10
  gem "steep", require: false
11
+ gem "simplecov", require: false
data/LEGAL.md CHANGED
@@ -5,22 +5,7 @@ mentioned below.
5
5
 
6
6
  ## GNU General Public License version 3
7
7
 
8
- These files are licensed under the GNU General Public License version 3. See these files for more information.
8
+ These files are licensed under the GNU General Public License version 3 or later. See these files for more information.
9
9
 
10
10
  * template/bison/yacc.c
11
11
  * template/bison/yacc.h
12
-
13
- ## Same with Ruby
14
-
15
- These files are licensed same with Ruby. See https://github.com/ruby/ruby/blob/master/COPYING for more information.
16
-
17
- * spec/fixtures/integration/ruby_3_0_5/parse.tmp.y
18
- * spec/fixtures/integration/ruby_3_0_5/y.tab.c
19
- * spec/fixtures/integration/ruby_3_0_5/y.tab.h
20
- * spec/fixtures/integration/ruby_3_1_0/parse.tmp.y
21
- * spec/fixtures/integration/ruby_3_1_0/y.tab.c
22
- * spec/fixtures/integration/ruby_3_1_0/y.tab.h
23
- * spec/fixtures/integration/ruby_3_2_0/parse.tmp.y
24
- * spec/fixtures/integration/ruby_3_2_0/y.tab.c
25
- * spec/fixtures/integration/ruby_3_2_0/y.tab.h
26
-
data/README.md CHANGED
@@ -49,10 +49,20 @@ Enter the formula:
49
49
 
50
50
  ## Versions and Branches
51
51
 
52
+ ### v0_5 (`master` branch)
53
+
54
+ This branch is for Ruby 3.3. `lrama_0_5` branch is created from this branch, once Ruby 3.3 is released.
55
+
52
56
  ### v0_4 (`lrama_0_4` branch)
53
57
 
54
58
  This branch generates "parse.c" compatible with Bison 3.8.2 for ruby 3.0, 3.1, 3.2. The first version migrated to ruby is ["0.4.0"](https://github.com/ruby/ruby/pull/7798) therefore keep this branch for Bison compatible branch.
55
59
 
60
+ ## Supported Ruby version
61
+
62
+ Lrama is executed with BASERUBY when building ruby from source code. Therefore Lrama needs to support BASERUBY, currently 2.5, or later version.
63
+
64
+ This also requires Lrama to be able to run with only default gems and bundled gems.
65
+
56
66
  ## Build Ruby
57
67
 
58
68
  1. Install Lrama
@@ -62,7 +72,7 @@ This branch generates "parse.c" compatible with Bison 3.8.2 for ruby 3.0, 3.1, 3
62
72
 
63
73
  1. Update `Lrama::VERSION`
64
74
  2. Release as a gem by `rake release`
65
- 3. Update Lrama in ruby/ruby by `cp -r LEGAL.md MIT exe lib ruby/tool/lrama`
75
+ 3. Update Lrama in ruby/ruby by `cp -r LEGAL.md MIT exe lib template ruby/tool/lrama`
66
76
  4. Create new release on [GitHub](https://github.com/ruby/lrama/releases)
67
77
 
68
78
  ## License
data/Steepfile CHANGED
@@ -4,6 +4,7 @@ target :lib do
4
4
  signature "sig"
5
5
 
6
6
  check "lib/lrama/bitmap.rb"
7
- check "lib/lrama/report.rb"
7
+ check "lib/lrama/report/duration.rb"
8
+ check "lib/lrama/report/profile.rb"
8
9
  check "lib/lrama/warning.rb"
9
10
  end
data/doc/TODO.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # TODO
2
2
 
3
3
  * command
4
- * [ ] Add "--bison" option
5
4
  * lexer
6
5
  * [x] Basic functionalities
7
6
  * parser
@@ -27,7 +26,7 @@
27
26
  * [x] Table compaction
28
27
  * [x] -d option
29
28
  * yacc.c
30
- * [ ] %lex-param
29
+ * [x] %lex-param
31
30
  * [x] %parse-param
32
31
  * [x] %printer
33
32
  * [x] Replace $, @ in user codes
@@ -45,8 +44,14 @@
45
44
  * Reporting
46
45
  * [ ] Bison style
47
46
  * [ ] Wrap not selected reduce with "[]". See basic.output file generated by Bison.
47
+ * Counterexamples
48
+ * [x] Nonunifying Counterexamples
49
+ * [ ] Unifying Counterexamples
50
+ * [ ] Performance improvement using reverse_transitions and reverse_productions
48
51
  * Error Tolerance
49
- * [x] Subset of Corchuelo et al.
52
+ * [x] Corchuelo et al. algorithm with N = 1 (this means the next token when error is raised)
53
+ * [x] Add new decl for error token semantic value initialization (%error-token)
54
+ * [x] Use YYMALLOC & YYFREE
50
55
  * Lex state
51
56
  * CI
52
57
  * [x] Setup CI
data/exe/lrama CHANGED
@@ -4,4 +4,4 @@
4
4
  $LOAD_PATH << File.join(__dir__, "../lib")
5
5
  require "lrama"
6
6
 
7
- Lrama::Command.new.run(ARGV.dup)
7
+ Lrama::Command.new(ARGV.dup).run
data/lib/lrama/command.rb CHANGED
@@ -2,94 +2,57 @@ require 'optparse'
2
2
 
3
3
  module Lrama
4
4
  class Command
5
- def run(argv)
6
- opt = OptionParser.new
7
-
8
- # opt.on('-h') {|v| p v }
9
- opt.on('-V', '--version') {|v| puts Lrama::VERSION ; exit 0 }
10
-
11
- # Tuning the Parser
12
- skeleton = "bison/yacc.c"
13
-
14
- opt.on('-S', '--skeleton=FILE') {|v| skeleton = v }
15
- opt.on('-t') { } # Do nothing
16
-
17
- # Output Files:
18
- header = false
19
- header_file = nil
20
- report = []
21
- report_file = nil
22
- outfile = "y.tab.c"
23
-
24
- opt.on('-h', '--header=[FILE]') {|v| header = true; header_file = v }
25
- opt.on('-d') { header = true }
26
- opt.on('-r', '--report=THINGS') {|v| report = v.split(',') }
27
- opt.on('--report-file=FILE') {|v| report_file = v }
28
- opt.on('-v') { } # Do nothing
29
- opt.on('-o', '--output=FILE') {|v| outfile = v }
30
-
31
- # Hidden
32
- trace = []
33
- opt.on('--trace=THINGS') {|v| trace = v.split(',') }
34
-
35
- # Error Recovery
36
- error_recovery = false
37
- opt.on('-e') {|v| error_recovery = true }
38
-
39
- opt.parse!(argv)
40
-
41
- trace_opts = validate_trace(trace)
42
- report_opts = validate_report(report)
43
-
44
- grammar_file = argv.shift
45
-
46
- if !report.empty? && report_file.nil? && grammar_file
47
- report_file = File.dirname(grammar_file) + "/" + File.basename(grammar_file, ".*") + ".output"
48
- end
5
+ def initialize(argv)
6
+ @argv = argv
7
+
8
+ @version = nil
9
+ @skeleton = "bison/yacc.c"
10
+ @header = false
11
+ @header_file = nil
12
+ @report = []
13
+ @report_file = nil
14
+ @outfile = "y.tab.c"
15
+ @trace = []
16
+ @error_recovery = false
17
+ @grammar_file = nil
18
+ @report_file = nil
19
+ @trace_opts = nil
20
+ @report_opts = nil
21
+ end
49
22
 
50
- if !header_file && header
51
- case
52
- when outfile
53
- header_file = File.dirname(outfile) + "/" + File.basename(outfile, ".*") + ".h"
54
- when grammar_file
55
- header_file = File.dirname(grammar_file) + "/" + File.basename(grammar_file, ".*") + ".h"
56
- end
57
- end
23
+ def run
24
+ parse_option
58
25
 
59
- if !grammar_file
60
- abort "File should be specified\n"
26
+ if @version
27
+ puts Lrama::VERSION
28
+ exit 0
61
29
  end
62
30
 
63
- Report::Duration.enable if trace_opts[:time]
31
+ Report::Duration.enable if @trace_opts[:time]
64
32
 
65
33
  warning = Lrama::Warning.new
66
- if grammar_file == '-'
67
- grammar_file = argv.shift or abort "File name for STDIN should be specified\n"
68
- y = STDIN.read
69
- else
70
- y = File.read(grammar_file)
71
- end
72
- grammar = Lrama::Parser.new(y).parse
73
- states = Lrama::States.new(grammar, warning, trace_state: (trace_opts[:automaton] || trace_opts[:closure]))
34
+ grammar = Lrama::Parser.new(@y.read).parse
35
+ states = Lrama::States.new(grammar, warning, trace_state: (@trace_opts[:automaton] || @trace_opts[:closure]))
74
36
  states.compute
75
37
  context = Lrama::Context.new(states)
76
38
 
77
- if report_file
39
+ if @report_file
78
40
  reporter = Lrama::StatesReporter.new(states)
79
- File.open(report_file, "w+") do |f|
80
- reporter.report(f, **report_opts)
41
+ File.open(@report_file, "w+") do |f|
42
+ reporter.report(f, **@report_opts)
81
43
  end
82
44
  end
83
45
 
84
- File.open(outfile, "w+") do |f|
46
+ File.open(@outfile, "w+") do |f|
85
47
  Lrama::Output.new(
86
48
  out: f,
87
- output_file_path: outfile,
88
- template_name: skeleton,
89
- grammar_file_path: grammar_file,
90
- header_file_path: header_file,
49
+ output_file_path: @outfile,
50
+ template_name: @skeleton,
51
+ grammar_file_path: @grammar_file,
52
+ header_file_path: @header_file,
91
53
  context: context,
92
54
  grammar: grammar,
55
+ error_recovery: @error_recovery,
93
56
  ).render
94
57
  end
95
58
 
@@ -104,7 +67,7 @@ module Lrama
104
67
  bison_list = %w[states itemsets lookaheads solved counterexamples cex all none]
105
68
  others = %w[verbose]
106
69
  list = bison_list + others
107
- not_supported = %w[counterexamples cex none]
70
+ not_supported = %w[cex none]
108
71
  h = { grammar: true }
109
72
 
110
73
  report.each do |r|
@@ -144,5 +107,61 @@ module Lrama
144
107
 
145
108
  return h
146
109
  end
110
+
111
+ def parse_option
112
+ opt = OptionParser.new
113
+
114
+ # opt.on('-h') {|v| p v }
115
+ opt.on('-V', '--version') {|v| @version = true }
116
+
117
+ # Tuning the Parser
118
+ opt.on('-S', '--skeleton=FILE') {|v| @skeleton = v }
119
+ opt.on('-t') { } # Do nothing
120
+
121
+ # Output Files:
122
+ opt.on('-h', '--header=[FILE]') {|v| @header = true; @header_file = v }
123
+ opt.on('-d') { @header = true }
124
+ opt.on('-r', '--report=THINGS', Array) {|v| @report = v }
125
+ opt.on('--report-file=FILE') {|v| @report_file = v }
126
+ opt.on('-v') { } # Do nothing
127
+ opt.on('-o', '--output=FILE') {|v| @outfile = v }
128
+
129
+ # Hidden
130
+ opt.on('--trace=THINGS', Array) {|v| @trace = v }
131
+
132
+ # Error Recovery
133
+ opt.on('-e') {|v| @error_recovery = true }
134
+
135
+ opt.parse!(@argv)
136
+
137
+ @trace_opts = validate_trace(@trace)
138
+ @report_opts = validate_report(@report)
139
+
140
+ @grammar_file = @argv.shift
141
+
142
+ if !@grammar_file
143
+ abort "File should be specified\n"
144
+ end
145
+
146
+ if @grammar_file == '-'
147
+ @grammar_file = @argv.shift or abort "File name for STDIN should be specified\n"
148
+ @y = STDIN
149
+ else
150
+ @y = File.open(@grammar_file, 'r')
151
+ end
152
+
153
+ if !@report.empty? && @report_file.nil? && @grammar_file
154
+ @report_file = File.dirname(@grammar_file) + "/" + File.basename(@grammar_file, ".*") + ".output"
155
+ end
156
+
157
+ if !@header_file && @header
158
+ case
159
+ when @outfile
160
+ @header_file = File.dirname(@outfile) + "/" + File.basename(@outfile, ".*") + ".h"
161
+ when @grammar_file
162
+ @header_file = File.dirname(@grammar_file) + "/" + File.basename(@grammar_file, ".*") + ".h"
163
+ end
164
+ end
165
+ end
147
166
  end
148
167
  end
data/lib/lrama/context.rb CHANGED
@@ -1,4 +1,4 @@
1
- require "lrama/report"
1
+ require "lrama/report/duration"
2
2
 
3
3
  module Lrama
4
4
  # This is passed to a template
@@ -89,6 +89,16 @@ module Lrama
89
89
  return a
90
90
  end
91
91
 
92
+ def yytranslate_inverted
93
+ a = Array.new(@states.symbols.count, @states.undef_symbol.token_id)
94
+
95
+ @states.terms.each do |term|
96
+ a[term.number] = term.token_id
97
+ end
98
+
99
+ return a
100
+ end
101
+
92
102
  # Mapping from rule number to line number of the rule is defined.
93
103
  # Dummy rule is appended as the first element whose value is 0
94
104
  # because 0 means error in yydefact.
@@ -0,0 +1,63 @@
1
+ module Lrama
2
+ class Counterexamples
3
+ class Derivation
4
+ attr_reader :item, :left, :right
5
+ attr_writer :right
6
+
7
+ def initialize(item, left, right = nil)
8
+ @item = item
9
+ @left = left
10
+ @right = right
11
+ end
12
+
13
+ def to_s
14
+ "#<Derivation(#{item.display_name})>"
15
+ end
16
+ alias :inspect :to_s
17
+
18
+ def render_strings_for_report
19
+ result = []
20
+ _render_for_report(self, 0, result, 0)
21
+ result.map(&:rstrip)
22
+ end
23
+
24
+ def render_for_report
25
+ render_strings_for_report.join("\n")
26
+ end
27
+
28
+ private
29
+
30
+ def _render_for_report(derivation, offset, strings, index)
31
+ item = derivation.item
32
+ if strings[index]
33
+ strings[index] << " " * (offset - strings[index].length)
34
+ else
35
+ strings[index] = " " * offset
36
+ end
37
+ str = strings[index]
38
+ str << "#{item.rule_id}: #{item.symbols_before_dot.map(&:display_name).join(" ")} "
39
+
40
+ if derivation.left
41
+ len = str.length
42
+ str << "#{item.next_sym.display_name}"
43
+ length = _render_for_report(derivation.left, len, strings, index + 1)
44
+ # I want String#ljust!
45
+ str << " " * (length - str.length)
46
+ else
47
+ str << " • #{item.symbols_after_dot.map(&:display_name).join(" ")} "
48
+ return str.length
49
+ end
50
+
51
+ if derivation.right&.left
52
+ length = _render_for_report(derivation.right.left, str.length, strings, index + 1)
53
+ str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} "
54
+ str << " " * (length - str.length) if length > str.length
55
+ elsif item.next_next_sym
56
+ str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} "
57
+ end
58
+
59
+ return str.length
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,124 @@
1
+ module Lrama
2
+ class Counterexamples
3
+ class Example
4
+ attr_reader :path1, :path2, :conflict, :conflict_symbol
5
+
6
+ # path1 is shift conflict when S/R conflict
7
+ # path2 is always reduce conflict
8
+ def initialize(path1, path2, conflict, conflict_symbol, counterexamples)
9
+ @path1 = path1
10
+ @path2 = path2
11
+ @conflict = conflict
12
+ @conflict_symbol = conflict_symbol
13
+ @counterexamples = counterexamples
14
+ end
15
+
16
+ def type
17
+ @conflict.type
18
+ end
19
+
20
+ def path1_item
21
+ @path1.last.to.item
22
+ end
23
+
24
+ def path2_item
25
+ @path2.last.to.item
26
+ end
27
+
28
+ def derivations1
29
+ @derivations1 ||= _derivations(path1)
30
+ end
31
+
32
+ def derivations2
33
+ @derivations2 ||= _derivations(path2)
34
+ end
35
+
36
+ private
37
+
38
+ def _derivations(paths)
39
+ derivation = nil
40
+ current = :production
41
+ lookahead_sym = paths.last.to.item.end_of_rule? ? @conflict_symbol : nil
42
+
43
+ paths.reverse.each do |path|
44
+ item = path.to.item
45
+
46
+ case current
47
+ when :production
48
+ case path
49
+ when StartPath
50
+ derivation = Derivation.new(item, derivation)
51
+ current = :start
52
+ when TransitionPath
53
+ derivation = Derivation.new(item, derivation)
54
+ current = :transition
55
+ when ProductionPath
56
+ derivation = Derivation.new(item, derivation)
57
+ current = :production
58
+ end
59
+
60
+ if lookahead_sym && item.next_next_sym && item.next_next_sym.first_set.include?(lookahead_sym)
61
+ state_item = @counterexamples.transitions[[path.to, item.next_sym]]
62
+ derivation2 = find_derivation_for_symbol(state_item, lookahead_sym)
63
+ derivation.right = derivation2
64
+ lookahead_sym = nil
65
+ end
66
+
67
+ when :transition
68
+ case path
69
+ when StartPath
70
+ derivation = Derivation.new(item, derivation)
71
+ current = :start
72
+ when TransitionPath
73
+ # ignore
74
+ current = :transition
75
+ when ProductionPath
76
+ # ignore
77
+ current = :production
78
+ end
79
+ else
80
+ raise "BUG: Unknown #{current}"
81
+ end
82
+
83
+ break if current == :start
84
+ end
85
+
86
+ derivation
87
+ end
88
+
89
+ def find_derivation_for_symbol(state_item, sym)
90
+ queue = []
91
+ queue << [state_item]
92
+
93
+ while (sis = queue.shift)
94
+ si = sis.last
95
+ next_sym = si.item.next_sym
96
+
97
+ if next_sym == sym
98
+ derivation = nil
99
+
100
+ sis.reverse.each do |si|
101
+ derivation = Derivation.new(si.item, derivation)
102
+ end
103
+
104
+ return derivation
105
+ end
106
+
107
+ if next_sym.nterm? && next_sym.first_set.include?(sym)
108
+ @counterexamples.productions[si].each do |next_item|
109
+ next if next_item.empty_rule?
110
+ next_si = StateItem.new(si.state, next_item)
111
+ next if sis.include?(next_si)
112
+ queue << (sis + [next_si])
113
+ end
114
+
115
+ if next_sym.nullable
116
+ next_si = @counterexamples.transitions[[si, next_sym]]
117
+ queue << (sis + [next_si])
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,69 @@
1
+ module Lrama
2
+ class Counterexamples
3
+ class Path
4
+ def initialize(from_state_item, to_state_item)
5
+ @from_state_item = from_state_item
6
+ @to_state_item = to_state_item
7
+ end
8
+
9
+ def from
10
+ @from_state_item
11
+ end
12
+
13
+ def to
14
+ @to_state_item
15
+ end
16
+
17
+ def to_s
18
+ "#<Path(#{type})>"
19
+ end
20
+ alias :inspect :to_s
21
+ end
22
+
23
+ class StartPath < Path
24
+ def initialize(to_state_item)
25
+ super nil, to_state_item
26
+ end
27
+
28
+ def type
29
+ :start
30
+ end
31
+
32
+ def transition?
33
+ false
34
+ end
35
+
36
+ def production?
37
+ false
38
+ end
39
+ end
40
+
41
+ class TransitionPath < Path
42
+ def type
43
+ :transition
44
+ end
45
+
46
+ def transition?
47
+ true
48
+ end
49
+
50
+ def production?
51
+ false
52
+ end
53
+ end
54
+
55
+ class ProductionPath < Path
56
+ def type
57
+ :production
58
+ end
59
+
60
+ def transition?
61
+ false
62
+ end
63
+
64
+ def production?
65
+ true
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,6 @@
1
+ module Lrama
2
+ class Counterexamples
3
+ class StateItem < Struct.new(:state, :item)
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ module Lrama
2
+ class Counterexamples
3
+ # s: state
4
+ # itm: item within s
5
+ # l: precise lookahead set
6
+ class Triple < Struct.new(:s, :itm, :l)
7
+ alias :state :s
8
+ alias :item :itm
9
+ alias :precise_lookahead_set :l
10
+
11
+ def state_item
12
+ StateItem.new(state, item)
13
+ end
14
+
15
+ def inspect
16
+ "#{state.inspect}. #{item.display_name}. #{l.map(&:id).map(&:s_value)}"
17
+ end
18
+ alias :to_s :inspect
19
+ end
20
+ end
21
+ end