lrama 0.5.3 → 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/README.md +11 -1
- data/doc/TODO.md +5 -1
- data/lib/lrama/command.rb +3 -3
- 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/rule.rb +6 -0
- data/lib/lrama/grammar/symbol.rb +4 -11
- data/lib/lrama/grammar.rb +39 -6
- data/lib/lrama/lexer/token/type.rb +8 -0
- data/lib/lrama/lexer/token.rb +3 -2
- data/lib/lrama/output.rb +1 -1
- data/lib/lrama/parser/token_scanner.rb +3 -6
- data/lib/lrama/parser.rb +1 -0
- data/lib/lrama/state/reduce_reduce_conflict.rb +9 -0
- data/lib/lrama/state/shift_reduce_conflict.rb +9 -0
- data/lib/lrama/state.rb +11 -3
- data/lib/lrama/states/item.rb +38 -2
- data/lib/lrama/states.rb +21 -32
- 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/template/bison/yacc.c +103 -95
- metadata +13 -2
data/lib/lrama/grammar/rule.rb
CHANGED
data/lib/lrama/grammar/symbol.rb
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
module Lrama
|
8
8
|
class Grammar
|
9
9
|
class Symbol < Struct.new(:id, :alias_name, :number, :tag, :term, :token_id, :nullable, :precedence, :printer, :error_token, keyword_init: true)
|
10
|
+
attr_accessor :first_set, :first_set_bitmap
|
10
11
|
attr_writer :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol
|
11
12
|
|
12
13
|
def term?
|
@@ -34,11 +35,7 @@ module Lrama
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def display_name
|
37
|
-
|
38
|
-
alias_name
|
39
|
-
else
|
40
|
-
id.s_value
|
41
|
-
end
|
38
|
+
alias_name || id.s_value
|
42
39
|
end
|
43
40
|
|
44
41
|
# name for yysymbol_kind_t
|
@@ -51,11 +48,7 @@ module Lrama
|
|
51
48
|
when eof_symbol?
|
52
49
|
name = "YYEOF"
|
53
50
|
when term? && id.type == Token::Char
|
54
|
-
|
55
|
-
name = number.to_s + alias_name
|
56
|
-
else
|
57
|
-
name = number.to_s + id.s_value
|
58
|
-
end
|
51
|
+
name = number.to_s + display_name
|
59
52
|
when term? && id.type == Token::Ident
|
60
53
|
name = id.s_value
|
61
54
|
when nterm? && (id.s_value.include?("$") || id.s_value.include?("@"))
|
@@ -66,7 +59,7 @@ module Lrama
|
|
66
59
|
raise "Unexpected #{self}"
|
67
60
|
end
|
68
61
|
|
69
|
-
"YYSYMBOL_" + name.gsub(
|
62
|
+
"YYSYMBOL_" + name.gsub(/\W+/, "_")
|
70
63
|
end
|
71
64
|
|
72
65
|
# comment for yysymbol_kind_t
|
data/lib/lrama/grammar.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "lrama/grammar/auxiliary"
|
1
2
|
require "lrama/grammar/code"
|
2
3
|
require "lrama/grammar/error_token"
|
3
4
|
require "lrama/grammar/precedence"
|
@@ -7,16 +8,13 @@ require "lrama/grammar/rule"
|
|
7
8
|
require "lrama/grammar/symbol"
|
8
9
|
require "lrama/grammar/union"
|
9
10
|
require "lrama/lexer"
|
11
|
+
require "lrama/type"
|
10
12
|
|
11
13
|
module Lrama
|
12
|
-
Type = Struct.new(:id, :tag, keyword_init: true)
|
13
14
|
Token = Lrama::Lexer::Token
|
14
15
|
|
15
16
|
# Grammar is the result of parsing an input grammar file
|
16
17
|
class Grammar
|
17
|
-
# Grammar file information not used by States but by Output
|
18
|
-
Aux = Struct.new(:prologue_first_lineno, :prologue, :epilogue_first_lineno, :epilogue, keyword_init: true)
|
19
|
-
|
20
18
|
attr_reader :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux
|
21
19
|
attr_accessor :union, :expect,
|
22
20
|
:printers, :error_tokens,
|
@@ -38,7 +36,7 @@ module Lrama
|
|
38
36
|
@error_symbol = nil
|
39
37
|
@undef_symbol = nil
|
40
38
|
@accept_symbol = nil
|
41
|
-
@aux =
|
39
|
+
@aux = Auxiliary.new
|
42
40
|
|
43
41
|
append_special_symbols
|
44
42
|
end
|
@@ -48,7 +46,7 @@ module Lrama
|
|
48
46
|
end
|
49
47
|
|
50
48
|
def add_error_token(ident_or_tags:, code:, lineno:)
|
51
|
-
@error_tokens << ErrorToken.new(ident_or_tags, code, lineno)
|
49
|
+
@error_tokens << ErrorToken.new(ident_or_tags: ident_or_tags, code: code, lineno: lineno)
|
52
50
|
end
|
53
51
|
|
54
52
|
def add_term(id:, alias_name: nil, tag: nil, token_id: nil, replace: false)
|
@@ -215,6 +213,41 @@ module Lrama
|
|
215
213
|
end
|
216
214
|
end
|
217
215
|
|
216
|
+
def compute_first_set
|
217
|
+
terms.each do |term|
|
218
|
+
term.first_set = Set.new([term]).freeze
|
219
|
+
term.first_set_bitmap = Lrama::Bitmap.from_array([term.number])
|
220
|
+
end
|
221
|
+
|
222
|
+
nterms.each do |nterm|
|
223
|
+
nterm.first_set = Set.new([]).freeze
|
224
|
+
nterm.first_set_bitmap = Lrama::Bitmap.from_array([])
|
225
|
+
end
|
226
|
+
|
227
|
+
while true do
|
228
|
+
changed = false
|
229
|
+
|
230
|
+
@rules.each do |rule|
|
231
|
+
rule.rhs.each do |r|
|
232
|
+
if rule.lhs.first_set_bitmap | r.first_set_bitmap != rule.lhs.first_set_bitmap
|
233
|
+
changed = true
|
234
|
+
rule.lhs.first_set_bitmap = rule.lhs.first_set_bitmap | r.first_set_bitmap
|
235
|
+
end
|
236
|
+
|
237
|
+
break unless r.nullable
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
break unless changed
|
242
|
+
end
|
243
|
+
|
244
|
+
nterms.each do |nterm|
|
245
|
+
nterm.first_set = Lrama::Bitmap.to_array(nterm.first_set_bitmap).map do |number|
|
246
|
+
find_symbol_by_number!(number)
|
247
|
+
end.to_set
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
218
251
|
def find_symbol_by_s_value(s_value)
|
219
252
|
@symbols.find do |sym|
|
220
253
|
sym.id.s_value == s_value
|
data/lib/lrama/lexer/token.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
require 'lrama/lexer/token/type'
|
2
|
+
|
1
3
|
module Lrama
|
2
4
|
class Lexer
|
3
|
-
class Token
|
4
|
-
Type = Struct.new(:id, :name, keyword_init: true)
|
5
|
+
class Token
|
5
6
|
|
6
7
|
attr_accessor :line, :column, :referred
|
7
8
|
# For User_code
|
data/lib/lrama/output.rb
CHANGED
@@ -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
data/lib/lrama/state.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require "lrama/state/reduce"
|
2
|
-
require "lrama/state/
|
2
|
+
require "lrama/state/reduce_reduce_conflict"
|
3
3
|
require "lrama/state/resolved_conflict"
|
4
|
+
require "lrama/state/shift"
|
5
|
+
require "lrama/state/shift_reduce_conflict"
|
4
6
|
|
5
7
|
module Lrama
|
6
8
|
class State
|
7
|
-
Conflict = Struct.new(:symbols, :reduce, :type, keyword_init: true)
|
8
|
-
|
9
9
|
attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts,
|
10
10
|
:default_reduction_rule, :closure, :items
|
11
11
|
attr_accessor :shifts, :reduces
|
@@ -101,6 +101,10 @@ module Lrama
|
|
101
101
|
@term_transitions
|
102
102
|
end
|
103
103
|
|
104
|
+
def transitions
|
105
|
+
term_transitions + nterm_transitions
|
106
|
+
end
|
107
|
+
|
104
108
|
def selected_term_transitions
|
105
109
|
term_transitions.select do |shift, next_state|
|
106
110
|
!shift.not_selected
|
@@ -144,6 +148,10 @@ module Lrama
|
|
144
148
|
end
|
145
149
|
end
|
146
150
|
|
151
|
+
def has_conflicts?
|
152
|
+
!@conflicts.empty?
|
153
|
+
end
|
154
|
+
|
147
155
|
def sr_conflicts
|
148
156
|
@conflicts.select do |conflict|
|
149
157
|
conflict.type == :shift_reduce
|
data/lib/lrama/states/item.rb
CHANGED
@@ -12,20 +12,56 @@ module Lrama
|
|
12
12
|
rule.id
|
13
13
|
end
|
14
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
|
+
|
15
27
|
def next_sym
|
16
28
|
rule.rhs[position]
|
17
29
|
end
|
18
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
|
+
|
19
39
|
def end_of_rule?
|
20
40
|
rule.rhs.count == position
|
21
41
|
end
|
22
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
|
+
|
23
51
|
def new_by_next_position
|
24
52
|
Item.new(rule: rule, position: position + 1)
|
25
53
|
end
|
26
54
|
|
27
|
-
def
|
28
|
-
rule.rhs[position
|
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}"
|
29
65
|
end
|
30
66
|
|
31
67
|
def display_name
|
data/lib/lrama/states.rb
CHANGED
@@ -102,43 +102,27 @@ module Lrama
|
|
102
102
|
end
|
103
103
|
|
104
104
|
def direct_read_sets
|
105
|
-
|
106
|
-
|
107
|
-
@direct_read_sets.each do |k, v|
|
108
|
-
h[k] = bitmap_to_terms(v)
|
105
|
+
@direct_read_sets.transform_values do |v|
|
106
|
+
bitmap_to_terms(v)
|
109
107
|
end
|
110
|
-
|
111
|
-
return h
|
112
108
|
end
|
113
109
|
|
114
110
|
def read_sets
|
115
|
-
|
116
|
-
|
117
|
-
@read_sets.each do |k, v|
|
118
|
-
h[k] = bitmap_to_terms(v)
|
111
|
+
@read_sets.transform_values do |v|
|
112
|
+
bitmap_to_terms(v)
|
119
113
|
end
|
120
|
-
|
121
|
-
return h
|
122
114
|
end
|
123
115
|
|
124
116
|
def follow_sets
|
125
|
-
|
126
|
-
|
127
|
-
@follow_sets.each do |k, v|
|
128
|
-
h[k] = bitmap_to_terms(v)
|
117
|
+
@follow_sets.transform_values do |v|
|
118
|
+
bitmap_to_terms(v)
|
129
119
|
end
|
130
|
-
|
131
|
-
return h
|
132
120
|
end
|
133
121
|
|
134
122
|
def la
|
135
|
-
|
136
|
-
|
137
|
-
@la.each do |k, v|
|
138
|
-
h[k] = bitmap_to_terms(v)
|
123
|
+
@la.transform_values do |v|
|
124
|
+
bitmap_to_terms(v)
|
139
125
|
end
|
140
|
-
|
141
|
-
return h
|
142
126
|
end
|
143
127
|
|
144
128
|
private
|
@@ -452,7 +436,7 @@ module Lrama
|
|
452
436
|
|
453
437
|
# Can resolve only when both have prec
|
454
438
|
unless shift_prec && reduce_prec
|
455
|
-
state.conflicts << State::
|
439
|
+
state.conflicts << State::ShiftReduceConflict.new(symbols: [sym], shift: shift, reduce: reduce)
|
456
440
|
next
|
457
441
|
end
|
458
442
|
|
@@ -501,16 +485,21 @@ module Lrama
|
|
501
485
|
|
502
486
|
def compute_reduce_reduce_conflicts
|
503
487
|
states.each do |state|
|
504
|
-
|
488
|
+
count = state.reduces.count
|
505
489
|
|
506
|
-
|
507
|
-
|
490
|
+
for i in 0...count do
|
491
|
+
reduce1 = state.reduces[i]
|
492
|
+
next if reduce1.look_ahead.nil?
|
508
493
|
|
509
|
-
|
510
|
-
|
494
|
+
for j in (i+1)...count do
|
495
|
+
reduce2 = state.reduces[j]
|
496
|
+
next if reduce2.look_ahead.nil?
|
511
497
|
|
512
|
-
|
513
|
-
|
498
|
+
intersection = reduce1.look_ahead & reduce2.look_ahead
|
499
|
+
|
500
|
+
if !intersection.empty?
|
501
|
+
state.conflicts << State::ReduceReduceConflict.new(symbols: intersection, reduce1: reduce1, reduce2: reduce2)
|
502
|
+
end
|
514
503
|
end
|
515
504
|
end
|
516
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"
|