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