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
@@ -11,7 +11,7 @@ module Lrama
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def current_type
|
14
|
-
current_token
|
14
|
+
current_token&.type
|
15
15
|
end
|
16
16
|
|
17
17
|
def previous_token
|
@@ -26,9 +26,7 @@ module Lrama
|
|
26
26
|
|
27
27
|
def consume(*token_types)
|
28
28
|
if token_types.include?(current_type)
|
29
|
-
|
30
|
-
self.next
|
31
|
-
return token
|
29
|
+
return self.next
|
32
30
|
end
|
33
31
|
|
34
32
|
return nil
|
@@ -42,8 +40,7 @@ module Lrama
|
|
42
40
|
a = []
|
43
41
|
|
44
42
|
while token_types.include?(current_type)
|
45
|
-
a <<
|
46
|
-
self.next
|
43
|
+
a << self.next
|
47
44
|
end
|
48
45
|
|
49
46
|
raise "No token is consumed. #{token_types}" if a.empty?
|
data/lib/lrama/parser.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require "lrama/report"
|
1
|
+
require "lrama/report/duration"
|
2
2
|
require "lrama/parser/token_scanner"
|
3
3
|
|
4
4
|
module Lrama
|
@@ -22,6 +22,7 @@ module Lrama
|
|
22
22
|
process_epilogue(grammar, lexer)
|
23
23
|
grammar.prepare
|
24
24
|
grammar.compute_nullable
|
25
|
+
grammar.compute_first_set
|
25
26
|
grammar.validate!
|
26
27
|
|
27
28
|
grammar
|
@@ -59,6 +60,13 @@ module Lrama
|
|
59
60
|
code = grammar.build_code(:printer, code)
|
60
61
|
ident_or_tags = ts.consume_multi(T::Ident, T::Tag)
|
61
62
|
grammar.add_printer(ident_or_tags: ident_or_tags, code: code, lineno: lineno)
|
63
|
+
when T::P_error_token
|
64
|
+
lineno = ts.current_token.line
|
65
|
+
ts.next
|
66
|
+
code = ts.consume!(T::User_code)
|
67
|
+
code = grammar.build_code(:printer, code)
|
68
|
+
ident_or_tags = ts.consume_multi(T::Ident, T::Tag)
|
69
|
+
grammar.add_error_token(ident_or_tags: ident_or_tags, code: code, lineno: lineno)
|
62
70
|
when T::P_lex_param
|
63
71
|
ts.next
|
64
72
|
code = ts.consume!(T::User_code)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Report
|
3
|
+
module Duration
|
4
|
+
def self.enable
|
5
|
+
@_report_duration_enabled = true
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.enabled?
|
9
|
+
!!@_report_duration_enabled
|
10
|
+
end
|
11
|
+
|
12
|
+
def report_duration(method_name)
|
13
|
+
time1 = Time.now.to_f
|
14
|
+
result = yield
|
15
|
+
time2 = Time.now.to_f
|
16
|
+
|
17
|
+
if Duration.enabled?
|
18
|
+
puts sprintf("%s %10.5f s", method_name, time2 - time1)
|
19
|
+
end
|
20
|
+
|
21
|
+
return result
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Report
|
3
|
+
module Profile
|
4
|
+
# 1. Wrap target method with Profile.report_profile like below:
|
5
|
+
#
|
6
|
+
# Lrama::Report::Profile.report_profile { method }
|
7
|
+
#
|
8
|
+
# 2. Run lrama command, for example
|
9
|
+
#
|
10
|
+
# $ ./exe/lrama --trace=time spec/fixtures/integration/ruby_3_2_0/parse.tmp.y
|
11
|
+
#
|
12
|
+
# 3. Generate html file
|
13
|
+
#
|
14
|
+
# $ stackprof --d3-flamegraph tmp/stackprof-cpu-myapp.dump > tmp/flamegraph.html
|
15
|
+
#
|
16
|
+
def self.report_profile
|
17
|
+
require "stackprof"
|
18
|
+
|
19
|
+
StackProf.run(mode: :cpu, raw: true, out: 'tmp/stackprof-cpu-myapp.dump') do
|
20
|
+
yield
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/lrama/report.rb
CHANGED
@@ -1,47 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Profile
|
4
|
-
# 1. Wrap target method with Profile.report_profile like below:
|
5
|
-
#
|
6
|
-
# Lrama::Report::Profile.report_profile { method }
|
7
|
-
#
|
8
|
-
# 2. Run lrama command, for example
|
9
|
-
#
|
10
|
-
# $ ./exe/lrama --trace=time spec/fixtures/integration/ruby_3_2_0/parse.tmp.y
|
11
|
-
#
|
12
|
-
# 3. Generate html file
|
13
|
-
#
|
14
|
-
# $ stackprof --d3-flamegraph tmp/stackprof-cpu-myapp.dump > tmp/flamegraph.html
|
15
|
-
#
|
16
|
-
def self.report_profile
|
17
|
-
require "stackprof"
|
18
|
-
|
19
|
-
StackProf.run(mode: :cpu, raw: true, out: 'tmp/stackprof-cpu-myapp.dump') do
|
20
|
-
yield
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
module Duration
|
26
|
-
def self.enable
|
27
|
-
@_report_duration_enabled = true
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.enabled?
|
31
|
-
!!@_report_duration_enabled
|
32
|
-
end
|
33
|
-
|
34
|
-
def report_duration(method_name)
|
35
|
-
time1 = Time.now.to_f
|
36
|
-
result = yield
|
37
|
-
time2 = Time.now.to_f
|
38
|
-
|
39
|
-
if Duration.enabled?
|
40
|
-
puts sprintf("%s %10.5f s", method_name, time2 - time1)
|
41
|
-
end
|
42
|
-
|
43
|
-
return result
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
1
|
+
require 'lrama/report/duration'
|
2
|
+
require 'lrama/report/profile'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Lrama
|
2
|
+
class State
|
3
|
+
# * symbol: A symbol under discussion
|
4
|
+
# * reduce: A reduce under discussion
|
5
|
+
# * which: For which a conflict is resolved. :shift, :reduce or :error (for nonassociative)
|
6
|
+
class ResolvedConflict < Struct.new(:symbol, :reduce, :which, :same_prec, keyword_init: true)
|
7
|
+
def report_message
|
8
|
+
s = symbol.display_name
|
9
|
+
r = reduce.rule.precedence_sym.display_name
|
10
|
+
case
|
11
|
+
when which == :shift && same_prec
|
12
|
+
msg = "resolved as #{which} (%right #{s})"
|
13
|
+
when which == :shift
|
14
|
+
msg = "resolved as #{which} (#{r} < #{s})"
|
15
|
+
when which == :reduce && same_prec
|
16
|
+
msg = "resolved as #{which} (%left #{s})"
|
17
|
+
when which == :reduce
|
18
|
+
msg = "resolved as #{which} (#{s} < #{r})"
|
19
|
+
when which == :error
|
20
|
+
msg = "resolved as an #{which} (%nonassoc #{s})"
|
21
|
+
else
|
22
|
+
raise "Unknown direction. #{self}"
|
23
|
+
end
|
24
|
+
|
25
|
+
"Conflict between rule #{reduce.rule.id} and token #{s} #{msg}."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/lrama/state.rb
CHANGED
@@ -1,36 +1,11 @@
|
|
1
1
|
require "lrama/state/reduce"
|
2
|
+
require "lrama/state/reduce_reduce_conflict"
|
3
|
+
require "lrama/state/resolved_conflict"
|
2
4
|
require "lrama/state/shift"
|
5
|
+
require "lrama/state/shift_reduce_conflict"
|
3
6
|
|
4
7
|
module Lrama
|
5
8
|
class State
|
6
|
-
# * symbol: A symbol under discussion
|
7
|
-
# * reduce: A reduce under discussion
|
8
|
-
# * which: For which a conflict is resolved. :shift, :reduce or :error (for nonassociative)
|
9
|
-
ResolvedConflict = Struct.new(:symbol, :reduce, :which, :same_prec, keyword_init: true) do
|
10
|
-
def report_message
|
11
|
-
s = symbol.display_name
|
12
|
-
r = reduce.rule.precedence_sym.display_name
|
13
|
-
case
|
14
|
-
when which == :shift && same_prec
|
15
|
-
msg = "resolved as #{which} (%right #{s})"
|
16
|
-
when which == :shift
|
17
|
-
msg = "resolved as #{which} (#{r} < #{s})"
|
18
|
-
when which == :reduce && same_prec
|
19
|
-
msg = "resolved as #{which} (%left #{s})"
|
20
|
-
when which == :reduce
|
21
|
-
msg = "resolved as #{which} (#{s} < #{r})"
|
22
|
-
when which == :error
|
23
|
-
msg = "resolved as an #{which} (%nonassoc #{s})"
|
24
|
-
else
|
25
|
-
raise "Unknown direction. #{self}"
|
26
|
-
end
|
27
|
-
|
28
|
-
"Conflict between rule #{reduce.rule.id} and token #{s} #{msg}."
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
Conflict = Struct.new(:symbols, :reduce, :type, keyword_init: true)
|
33
|
-
|
34
9
|
attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts,
|
35
10
|
:default_reduction_rule, :closure, :items
|
36
11
|
attr_accessor :shifts, :reduces
|
@@ -96,7 +71,7 @@ module Lrama
|
|
96
71
|
reduce.look_ahead = look_ahead
|
97
72
|
end
|
98
73
|
|
99
|
-
# Returns array of [
|
74
|
+
# Returns array of [Shift, next_state]
|
100
75
|
def nterm_transitions
|
101
76
|
return @nterm_transitions if @nterm_transitions
|
102
77
|
|
@@ -111,7 +86,7 @@ module Lrama
|
|
111
86
|
@nterm_transitions
|
112
87
|
end
|
113
88
|
|
114
|
-
# Returns array of [
|
89
|
+
# Returns array of [Shift, next_state]
|
115
90
|
def term_transitions
|
116
91
|
return @term_transitions if @term_transitions
|
117
92
|
|
@@ -126,6 +101,10 @@ module Lrama
|
|
126
101
|
@term_transitions
|
127
102
|
end
|
128
103
|
|
104
|
+
def transitions
|
105
|
+
term_transitions + nterm_transitions
|
106
|
+
end
|
107
|
+
|
129
108
|
def selected_term_transitions
|
130
109
|
term_transitions.select do |shift, next_state|
|
131
110
|
!shift.not_selected
|
@@ -169,6 +148,10 @@ module Lrama
|
|
169
148
|
end
|
170
149
|
end
|
171
150
|
|
151
|
+
def has_conflicts?
|
152
|
+
!@conflicts.empty?
|
153
|
+
end
|
154
|
+
|
172
155
|
def sr_conflicts
|
173
156
|
@conflicts.select do |conflict|
|
174
157
|
conflict.type == :shift_reduce
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# TODO: Validate position is not over rule rhs
|
2
|
+
|
3
|
+
module Lrama
|
4
|
+
class States
|
5
|
+
class Item < Struct.new(:rule, :position, keyword_init: true)
|
6
|
+
# Optimization for States#setup_state
|
7
|
+
def hash
|
8
|
+
[rule.id, position].hash
|
9
|
+
end
|
10
|
+
|
11
|
+
def rule_id
|
12
|
+
rule.id
|
13
|
+
end
|
14
|
+
|
15
|
+
def empty_rule?
|
16
|
+
rule.empty_rule?
|
17
|
+
end
|
18
|
+
|
19
|
+
def number_of_rest_symbols
|
20
|
+
rule.rhs.count - position
|
21
|
+
end
|
22
|
+
|
23
|
+
def lhs
|
24
|
+
rule.lhs
|
25
|
+
end
|
26
|
+
|
27
|
+
def next_sym
|
28
|
+
rule.rhs[position]
|
29
|
+
end
|
30
|
+
|
31
|
+
def next_next_sym
|
32
|
+
rule.rhs[position + 1]
|
33
|
+
end
|
34
|
+
|
35
|
+
def previous_sym
|
36
|
+
rule.rhs[position - 1]
|
37
|
+
end
|
38
|
+
|
39
|
+
def end_of_rule?
|
40
|
+
rule.rhs.count == position
|
41
|
+
end
|
42
|
+
|
43
|
+
def beginning_of_rule?
|
44
|
+
position == 0
|
45
|
+
end
|
46
|
+
|
47
|
+
def start_item?
|
48
|
+
rule.id == 0 && position == 0
|
49
|
+
end
|
50
|
+
|
51
|
+
def new_by_next_position
|
52
|
+
Item.new(rule: rule, position: position + 1)
|
53
|
+
end
|
54
|
+
|
55
|
+
def symbols_before_dot
|
56
|
+
rule.rhs[0...position]
|
57
|
+
end
|
58
|
+
|
59
|
+
def symbols_after_dot
|
60
|
+
rule.rhs[position..-1]
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
"#{lhs.id.s_value}: #{display_name}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def display_name
|
68
|
+
r = rule.rhs.map(&:display_name).insert(position, "•").join(" ")
|
69
|
+
"#{r} (rule #{rule.id})"
|
70
|
+
end
|
71
|
+
|
72
|
+
# Right after position
|
73
|
+
def display_rest
|
74
|
+
r = rule.rhs[position..-1].map(&:display_name).join(" ")
|
75
|
+
". #{r} (rule #{rule.id})"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/lrama/states.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "forwardable"
|
2
|
-
require "lrama/report"
|
2
|
+
require "lrama/report/duration"
|
3
|
+
require "lrama/states/item"
|
3
4
|
|
4
5
|
module Lrama
|
5
6
|
# States is passed to a template file
|
@@ -11,46 +12,7 @@ module Lrama
|
|
11
12
|
include Lrama::Report::Duration
|
12
13
|
|
13
14
|
def_delegators "@grammar", :symbols, :terms, :nterms, :rules,
|
14
|
-
:accept_symbol, :eof_symbol, :find_symbol_by_s_value!
|
15
|
-
|
16
|
-
# TODO: Validate position is not over rule rhs
|
17
|
-
Item = Struct.new(:rule, :position, keyword_init: true) do
|
18
|
-
# Optimization for States#setup_state
|
19
|
-
def hash
|
20
|
-
[rule.id, position].hash
|
21
|
-
end
|
22
|
-
|
23
|
-
def rule_id
|
24
|
-
rule.id
|
25
|
-
end
|
26
|
-
|
27
|
-
def next_sym
|
28
|
-
rule.rhs[position]
|
29
|
-
end
|
30
|
-
|
31
|
-
def end_of_rule?
|
32
|
-
rule.rhs.count == position
|
33
|
-
end
|
34
|
-
|
35
|
-
def new_by_next_position
|
36
|
-
Item.new(rule: rule, position: position + 1)
|
37
|
-
end
|
38
|
-
|
39
|
-
def previous_sym
|
40
|
-
rule.rhs[position - 1]
|
41
|
-
end
|
42
|
-
|
43
|
-
def display_name
|
44
|
-
r = rule.rhs.map(&:display_name).insert(position, "•").join(" ")
|
45
|
-
"#{r} (rule #{rule.id})"
|
46
|
-
end
|
47
|
-
|
48
|
-
# Right after position
|
49
|
-
def display_rest
|
50
|
-
r = rule.rhs[position..-1].map(&:display_name).join(" ")
|
51
|
-
". #{r} (rule #{rule.id})"
|
52
|
-
end
|
53
|
-
end
|
15
|
+
:accept_symbol, :eof_symbol, :undef_symbol, :find_symbol_by_s_value!
|
54
16
|
|
55
17
|
attr_reader :states, :reads_relation, :includes_relation, :lookback_relation
|
56
18
|
|
@@ -140,43 +102,27 @@ module Lrama
|
|
140
102
|
end
|
141
103
|
|
142
104
|
def direct_read_sets
|
143
|
-
|
144
|
-
|
145
|
-
@direct_read_sets.each do |k, v|
|
146
|
-
h[k] = bitmap_to_terms(v)
|
105
|
+
@direct_read_sets.transform_values do |v|
|
106
|
+
bitmap_to_terms(v)
|
147
107
|
end
|
148
|
-
|
149
|
-
return h
|
150
108
|
end
|
151
109
|
|
152
110
|
def read_sets
|
153
|
-
|
154
|
-
|
155
|
-
@read_sets.each do |k, v|
|
156
|
-
h[k] = bitmap_to_terms(v)
|
111
|
+
@read_sets.transform_values do |v|
|
112
|
+
bitmap_to_terms(v)
|
157
113
|
end
|
158
|
-
|
159
|
-
return h
|
160
114
|
end
|
161
115
|
|
162
116
|
def follow_sets
|
163
|
-
|
164
|
-
|
165
|
-
@follow_sets.each do |k, v|
|
166
|
-
h[k] = bitmap_to_terms(v)
|
117
|
+
@follow_sets.transform_values do |v|
|
118
|
+
bitmap_to_terms(v)
|
167
119
|
end
|
168
|
-
|
169
|
-
return h
|
170
120
|
end
|
171
121
|
|
172
122
|
def la
|
173
|
-
|
174
|
-
|
175
|
-
@la.each do |k, v|
|
176
|
-
h[k] = bitmap_to_terms(v)
|
123
|
+
@la.transform_values do |v|
|
124
|
+
bitmap_to_terms(v)
|
177
125
|
end
|
178
|
-
|
179
|
-
return h
|
180
126
|
end
|
181
127
|
|
182
128
|
private
|
@@ -490,7 +436,7 @@ module Lrama
|
|
490
436
|
|
491
437
|
# Can resolve only when both have prec
|
492
438
|
unless shift_prec && reduce_prec
|
493
|
-
state.conflicts << State::
|
439
|
+
state.conflicts << State::ShiftReduceConflict.new(symbols: [sym], shift: shift, reduce: reduce)
|
494
440
|
next
|
495
441
|
end
|
496
442
|
|
@@ -539,16 +485,21 @@ module Lrama
|
|
539
485
|
|
540
486
|
def compute_reduce_reduce_conflicts
|
541
487
|
states.each do |state|
|
542
|
-
|
488
|
+
count = state.reduces.count
|
489
|
+
|
490
|
+
for i in 0...count do
|
491
|
+
reduce1 = state.reduces[i]
|
492
|
+
next if reduce1.look_ahead.nil?
|
543
493
|
|
544
|
-
|
545
|
-
|
494
|
+
for j in (i+1)...count do
|
495
|
+
reduce2 = state.reduces[j]
|
496
|
+
next if reduce2.look_ahead.nil?
|
546
497
|
|
547
|
-
|
548
|
-
a += reduce.look_ahead
|
498
|
+
intersection = reduce1.look_ahead & reduce2.look_ahead
|
549
499
|
|
550
|
-
|
551
|
-
|
500
|
+
if !intersection.empty?
|
501
|
+
state.conflicts << State::ReduceReduceConflict.new(symbols: intersection, reduce1: reduce1, reduce2: reduce2)
|
502
|
+
end
|
552
503
|
end
|
553
504
|
end
|
554
505
|
end
|
@@ -14,13 +14,13 @@ module Lrama
|
|
14
14
|
|
15
15
|
private
|
16
16
|
|
17
|
-
def _report(io, grammar: false, states: false, itemsets: false, lookaheads: false, solved: false, verbose: false)
|
17
|
+
def _report(io, grammar: false, states: false, itemsets: false, lookaheads: false, solved: false, counterexamples: false, verbose: false)
|
18
18
|
# TODO: Unused terms
|
19
19
|
# TODO: Unused rules
|
20
20
|
|
21
21
|
report_conflicts(io)
|
22
22
|
report_grammar(io) if grammar
|
23
|
-
report_states(io, itemsets, lookaheads, solved, verbose)
|
23
|
+
report_states(io, itemsets, lookaheads, solved, counterexamples, verbose)
|
24
24
|
end
|
25
25
|
|
26
26
|
def report_conflicts(io)
|
@@ -71,7 +71,11 @@ module Lrama
|
|
71
71
|
io << "\n\n"
|
72
72
|
end
|
73
73
|
|
74
|
-
def report_states(io, itemsets, lookaheads, solved, verbose)
|
74
|
+
def report_states(io, itemsets, lookaheads, solved, counterexamples, verbose)
|
75
|
+
if counterexamples
|
76
|
+
cex = Counterexamples.new(@states)
|
77
|
+
end
|
78
|
+
|
75
79
|
@states.states.each do |state|
|
76
80
|
# Report State
|
77
81
|
io << "State #{state.id}\n\n"
|
@@ -194,6 +198,27 @@ module Lrama
|
|
194
198
|
io << "\n" if !state.resolved_conflicts.empty?
|
195
199
|
end
|
196
200
|
|
201
|
+
if counterexamples && state.has_conflicts?
|
202
|
+
# Report counterexamples
|
203
|
+
examples = cex.compute(state)
|
204
|
+
examples.each do |example|
|
205
|
+
label0 = example.type == :shift_reduce ? "shift/reduce" : "reduce/reduce"
|
206
|
+
label1 = example.type == :shift_reduce ? "Shift derivation" : "First Reduce derivation"
|
207
|
+
label2 = example.type == :shift_reduce ? "Reduce derivation" : "Second Reduce derivation"
|
208
|
+
|
209
|
+
io << " #{label0} conflict on token #{example.conflict_symbol.id.s_value}:\n"
|
210
|
+
io << " #{example.path1_item.to_s}\n"
|
211
|
+
io << " #{example.path2_item.to_s}\n"
|
212
|
+
io << " #{label1}\n"
|
213
|
+
example.derivations1.render_strings_for_report.each do |str|
|
214
|
+
io << " #{str}\n"
|
215
|
+
end
|
216
|
+
io << " #{label2}\n"
|
217
|
+
example.derivations2.render_strings_for_report.each do |str|
|
218
|
+
io << " #{str}\n"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
197
222
|
|
198
223
|
if verbose
|
199
224
|
# Report direct_read_sets
|
data/lib/lrama/type.rb
ADDED
data/lib/lrama/version.rb
CHANGED
data/lib/lrama.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "lrama/bitmap"
|
2
2
|
require "lrama/command"
|
3
3
|
require "lrama/context"
|
4
|
+
require "lrama/counterexamples"
|
4
5
|
require "lrama/digraph"
|
5
6
|
require "lrama/grammar"
|
6
7
|
require "lrama/lexer"
|
@@ -10,5 +11,6 @@ require "lrama/report"
|
|
10
11
|
require "lrama/state"
|
11
12
|
require "lrama/states"
|
12
13
|
require "lrama/states_reporter"
|
14
|
+
require "lrama/type"
|
13
15
|
require "lrama/version"
|
14
16
|
require "lrama/warning"
|
data/lrama.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.description = "LALR (1) parser generator written by Ruby"
|
11
11
|
spec.homepage = "https://github.com/ruby/lrama"
|
12
12
|
# See LEGAL.md file for detail
|
13
|
-
spec.license = "
|
13
|
+
spec.license = "GPL-3.0-or-later"
|
14
14
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
|
15
15
|
|
16
16
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|