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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yaml +10 -1
- data/.gitignore +1 -0
- data/Gemfile +1 -0
- data/LEGAL.md +1 -16
- data/README.md +11 -1
- data/Steepfile +2 -1
- data/doc/TODO.md +8 -3
- data/exe/lrama +1 -1
- data/lib/lrama/command.rb +91 -72
- data/lib/lrama/context.rb +11 -1
- data/lib/lrama/counterexamples/derivation.rb +63 -0
- data/lib/lrama/counterexamples/example.rb +124 -0
- data/lib/lrama/counterexamples/path.rb +69 -0
- data/lib/lrama/counterexamples/state_item.rb +6 -0
- data/lib/lrama/counterexamples/triple.rb +21 -0
- data/lib/lrama/counterexamples.rb +285 -0
- data/lib/lrama/digraph.rb +2 -3
- data/lib/lrama/grammar/auxiliary.rb +7 -0
- data/lib/lrama/grammar/code.rb +123 -0
- data/lib/lrama/grammar/error_token.rb +9 -0
- data/lib/lrama/grammar/precedence.rb +11 -0
- data/lib/lrama/grammar/printer.rb +9 -0
- data/lib/lrama/grammar/reference.rb +22 -0
- data/lib/lrama/grammar/rule.rb +39 -0
- data/lib/lrama/grammar/symbol.rb +87 -0
- data/lib/lrama/grammar/union.rb +10 -0
- data/lib/lrama/grammar.rb +89 -282
- data/lib/lrama/lexer/token/type.rb +8 -0
- data/lib/lrama/lexer/token.rb +77 -0
- data/lib/lrama/lexer.rb +4 -74
- data/lib/lrama/output.rb +32 -4
- data/lib/lrama/parser/token_scanner.rb +3 -6
- data/lib/lrama/parser.rb +9 -1
- data/lib/lrama/report/duration.rb +25 -0
- data/lib/lrama/report/profile.rb +25 -0
- data/lib/lrama/report.rb +2 -47
- data/lib/lrama/state/reduce_reduce_conflict.rb +9 -0
- data/lib/lrama/state/resolved_conflict.rb +29 -0
- data/lib/lrama/state/shift_reduce_conflict.rb +9 -0
- data/lib/lrama/state.rb +13 -30
- data/lib/lrama/states/item.rb +79 -0
- data/lib/lrama/states.rb +24 -73
- data/lib/lrama/states_reporter.rb +28 -3
- data/lib/lrama/type.rb +4 -0
- data/lib/lrama/version.rb +1 -1
- data/lib/lrama.rb +2 -0
- data/lrama.gemspec +1 -1
- data/sig/lrama/{report.rbs → report/duration.rbs} +0 -4
- data/sig/lrama/report/profile.rbs +7 -0
- data/template/bison/yacc.c +371 -0
- metadata +30 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2e675399217ba6b1c8cc57aaa36bbf863ed8b22dc3e22777c88d0d0aaf1cb26
|
4
|
+
data.tar.gz: 940b0da60c6b25edb1c10ed80539b17617e033d7ccc3a5c9c6036959c356ae37
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26eb48911cf5ba12b3087390bb7f7053165ff092a8919f7969e31b3e784a0085e876140d419cf4b463426cbfc4ae5f3625504a6c1433f9f9842ee9c32490082f
|
7
|
+
data.tar.gz: 8bce42121ffb5d45076dc3a1664e61209fec0f1a06a08d50d3a4279eb5158911b35165e21f8de0ac87c19e7cab1d2b7b0cac16bcaba0606ec61ea3b1c6195e0b
|
data/.github/workflows/test.yaml
CHANGED
@@ -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
data/Gemfile
CHANGED
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
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
|
-
* [
|
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]
|
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
data/lib/lrama/command.rb
CHANGED
@@ -2,94 +2,57 @@ require 'optparse'
|
|
2
2
|
|
3
3
|
module Lrama
|
4
4
|
class Command
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
51
|
-
|
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
|
60
|
-
|
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
|
-
|
67
|
-
|
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,
|
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[
|
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,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
|