lrama 0.5.2 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
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