lrama 0.4.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/test.yaml +19 -3
- data/Gemfile +2 -0
- data/README.md +29 -2
- data/Steepfile +7 -0
- data/lib/lrama/command.rb +7 -3
- data/lib/lrama/grammar.rb +5 -5
- data/lib/lrama/lexer.rb +23 -3
- data/lib/lrama/output.rb +1 -3
- data/lib/lrama/parser/token_scanner.rb +55 -0
- data/lib/lrama/parser.rb +1 -52
- data/lib/lrama/state/reduce.rb +35 -0
- data/lib/lrama/state/shift.rb +13 -0
- data/lib/lrama/state.rb +184 -0
- data/lib/lrama/states.rb +6 -238
- data/lib/lrama/states_reporter.rb +4 -4
- data/lib/lrama/version.rb +1 -1
- data/lib/lrama.rb +1 -0
- data/sample/calc.output +263 -0
- data/sample/calc.y +98 -0
- data/sig/lrama/bitmap.rbs +7 -0
- data/template/bison/yacc.c +1 -1
- data/template/bison/yacc.h +1 -1
- metadata +12 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20a0927bedeb14281abb8a5c59daf643fdfe556b5899cbe4872878b022804da2
|
4
|
+
data.tar.gz: 731fdd0201266db9724a2ebcc1162427a2cfaa9d0d94543e1298b3f2683eb14f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a6e7c0ef4e7266dae41dba15e3a5d7609cb7171b82e0dbd5b75595e8e3cf8a4a402c5b710cc0f714c54357ef9fc20fd0935e55b8c529c99cd3c09ea173efb87
|
7
|
+
data.tar.gz: fa867c9be43384c7f282bf22ecd2b0939cd0dca263a51914871dba42f409ddc01fb00c34d8696288c3668177fdc450ff1bc9fb5e90fdf053e2d56a5bf31bbe04
|
data/.github/workflows/test.yaml
CHANGED
@@ -22,6 +22,20 @@ jobs:
|
|
22
22
|
bundler-cache: true
|
23
23
|
- run: bundle install
|
24
24
|
- run: bundle exec rspec
|
25
|
+
steep-check:
|
26
|
+
runs-on: ubuntu-20.04
|
27
|
+
strategy:
|
28
|
+
fail-fast: false
|
29
|
+
matrix:
|
30
|
+
ruby: ['head']
|
31
|
+
steps:
|
32
|
+
- uses: actions/checkout@v3
|
33
|
+
- uses: ruby/setup-ruby@v1
|
34
|
+
with:
|
35
|
+
ruby-version: ${{ matrix.ruby }}
|
36
|
+
bundler-cache: true
|
37
|
+
- run: bundle install
|
38
|
+
- run: bundle exec steep check
|
25
39
|
test-ruby:
|
26
40
|
runs-on: ubuntu-20.04
|
27
41
|
strategy:
|
@@ -40,10 +54,12 @@ jobs:
|
|
40
54
|
bundler-cache: true
|
41
55
|
- run: git clone --depth=1 https://github.com/ruby/ruby.git -b ${{ matrix.ruby_branch }} ../ruby
|
42
56
|
working-directory:
|
43
|
-
- run: mkdir tool/lrama
|
57
|
+
- run: mkdir -p tool/lrama
|
44
58
|
working-directory: ../ruby
|
45
59
|
- name: Copy Lrama to ruby/tool
|
46
|
-
run: cp -r exe lib
|
60
|
+
run: cp -r exe lib ../ruby/tool/lrama
|
61
|
+
# TODO: Consider how to manage changes on ruby/ruby master and ruby/lrama
|
62
|
+
# run: cp -r exe lib template ../ruby/tool/lrama
|
47
63
|
working-directory:
|
48
64
|
- run: tree tool/lrama
|
49
65
|
working-directory: ../ruby
|
@@ -68,5 +84,5 @@ jobs:
|
|
68
84
|
- run: sudo apt-get --purge remove bison
|
69
85
|
- run: ../autogen.sh
|
70
86
|
- run: ../configure -C --disable-install-doc
|
71
|
-
- run: make
|
87
|
+
- run: make
|
72
88
|
- run: make test-all
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -21,9 +21,11 @@ $ gem install lrama
|
|
21
21
|
From source codes,
|
22
22
|
|
23
23
|
```shell
|
24
|
+
$ cd "$(lrama root)"
|
25
|
+
$ bundle install
|
24
26
|
$ bundle exec rake install
|
25
|
-
$ lrama --version
|
26
|
-
0.
|
27
|
+
$ bundle exec lrama --version
|
28
|
+
0.5.0
|
27
29
|
```
|
28
30
|
|
29
31
|
## Usage
|
@@ -33,11 +35,36 @@ $ lrama --version
|
|
33
35
|
$ lrama -d sample/parse.y
|
34
36
|
```
|
35
37
|
|
38
|
+
```shell
|
39
|
+
# "calc", "calc.c", and "calc.h" are generated
|
40
|
+
$ lrama -d sample/calc.y -o calc.c && gcc -Wall calc.c -o calc && ./calc
|
41
|
+
Enter the formula:
|
42
|
+
1
|
43
|
+
=> 1
|
44
|
+
1+2*3
|
45
|
+
=> 7
|
46
|
+
(1+2)*3
|
47
|
+
=> 9
|
48
|
+
```
|
49
|
+
|
50
|
+
## Versions and Branches
|
51
|
+
|
52
|
+
### v0_4 (`lrama_0_4` branch)
|
53
|
+
|
54
|
+
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
|
+
|
36
56
|
## Build Ruby
|
37
57
|
|
38
58
|
1. Install Lrama
|
39
59
|
2. Run `make YACC=lrama`
|
40
60
|
|
61
|
+
## Release flow
|
62
|
+
|
63
|
+
1. Update `Lrama::VERSION`
|
64
|
+
2. Release as a gem by `rake release`
|
65
|
+
3. Update Lrama in ruby/ruby by `cp -r exe lib ruby/tool/lrama`
|
66
|
+
4. Create new release on [GitHub](https://github.com/ruby/lrama/releases)
|
67
|
+
|
41
68
|
## License
|
42
69
|
|
43
70
|
See LEGAL.md file.
|
data/Steepfile
ADDED
data/lib/lrama/command.rb
CHANGED
@@ -57,14 +57,18 @@ module Lrama
|
|
57
57
|
end
|
58
58
|
|
59
59
|
if !grammar_file
|
60
|
-
|
61
|
-
exit 1
|
60
|
+
abort "File should be specified\n"
|
62
61
|
end
|
63
62
|
|
64
63
|
Report::Duration.enable if trace_opts[:time]
|
65
64
|
|
66
65
|
warning = Lrama::Warning.new
|
67
|
-
|
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
|
68
72
|
grammar = Lrama::Parser.new(y).parse
|
69
73
|
states = Lrama::States.new(grammar, warning, trace_state: (trace_opts[:automaton] || trace_opts[:closure]))
|
70
74
|
states.compute
|
data/lib/lrama/grammar.rb
CHANGED
@@ -166,7 +166,7 @@ module Lrama
|
|
166
166
|
when ref.type == :at # @n
|
167
167
|
raise "@#{ref.number} can not be used in %printer."
|
168
168
|
else
|
169
|
-
raise "Unexpected. #{
|
169
|
+
raise "Unexpected. #{self}, #{ref}"
|
170
170
|
end
|
171
171
|
|
172
172
|
t_code[first_column..last_column] = str
|
@@ -205,7 +205,7 @@ module Lrama
|
|
205
205
|
i = -ref.position_in_rhs + ref.number
|
206
206
|
str = "(yylsp[#{i}])"
|
207
207
|
else
|
208
|
-
raise "Unexpected. #{
|
208
|
+
raise "Unexpected. #{self}, #{ref}"
|
209
209
|
end
|
210
210
|
|
211
211
|
t_code[first_column..last_column] = str
|
@@ -235,7 +235,7 @@ module Lrama
|
|
235
235
|
when ref.type == :at # @n
|
236
236
|
raise "@#{ref.number} can not be used in initial_action."
|
237
237
|
else
|
238
|
-
raise "Unexpected. #{
|
238
|
+
raise "Unexpected. #{self}, #{ref}"
|
239
239
|
end
|
240
240
|
|
241
241
|
t_code[first_column..last_column] = str
|
@@ -716,7 +716,7 @@ module Lrama
|
|
716
716
|
# If id is Token::Char, it uses ASCII code
|
717
717
|
if sym.term? && sym.token_id.nil?
|
718
718
|
if sym.id.type == Token::Char
|
719
|
-
#
|
719
|
+
# Ignore ' on the both sides
|
720
720
|
case sym.id.s_value[1..-2]
|
721
721
|
when "\\b"
|
722
722
|
sym.token_id = 8
|
@@ -844,7 +844,7 @@ module Lrama
|
|
844
844
|
|
845
845
|
return if invalid.empty?
|
846
846
|
|
847
|
-
raise "Symbol number is
|
847
|
+
raise "Symbol number is duplicated. #{invalid}"
|
848
848
|
end
|
849
849
|
end
|
850
850
|
end
|
data/lib/lrama/lexer.rb
CHANGED
@@ -206,6 +206,8 @@ module Lrama
|
|
206
206
|
when ss.scan(/\/\*/)
|
207
207
|
# TODO: Need to keep comment?
|
208
208
|
line = lex_comment(ss, line, lines, "")
|
209
|
+
when ss.scan(/\/\//)
|
210
|
+
line = lex_line_comment(ss, line, "")
|
209
211
|
when ss.scan(/'(.)'/)
|
210
212
|
tokens << create_token(Token::Char, ss[0], line, ss.pos - column)
|
211
213
|
when ss.scan(/'\\(.)'/) # '\\', '\t'
|
@@ -218,7 +220,7 @@ module Lrama
|
|
218
220
|
l = line - lines.first[1]
|
219
221
|
split = ss.string.split("\n")
|
220
222
|
col = ss.pos - split[0...l].join("\n").length
|
221
|
-
raise "Parse error (
|
223
|
+
raise "Parse error (unknown token): #{split[l]} \"#{ss.string[ss.pos]}\" (#{line}: #{col})"
|
222
224
|
end
|
223
225
|
end
|
224
226
|
end
|
@@ -276,6 +278,9 @@ module Lrama
|
|
276
278
|
when ss.scan(/\/\*/)
|
277
279
|
str << ss[0]
|
278
280
|
line = lex_comment(ss, line, lines, str)
|
281
|
+
when ss.scan(/\/\//)
|
282
|
+
str << ss[0]
|
283
|
+
line = lex_line_comment(ss, line, str)
|
279
284
|
else
|
280
285
|
# noop, just consume char
|
281
286
|
str << ss.getch
|
@@ -314,8 +319,6 @@ module Lrama
|
|
314
319
|
raise "Parse error (quote mismatch): #{ss.string.split("\n")[l]} \"#{ss.string[ss.pos]}\" (#{line}: #{ss.pos})"
|
315
320
|
end
|
316
321
|
|
317
|
-
# TODO: Need to handle // style comment
|
318
|
-
#
|
319
322
|
# /* */ style comment
|
320
323
|
def lex_comment(ss, line, lines, str)
|
321
324
|
while !ss.eos? do
|
@@ -337,6 +340,23 @@ module Lrama
|
|
337
340
|
raise "Parse error (comment mismatch): #{ss.string.split("\n")[l]} \"#{ss.string[ss.pos]}\" (#{line}: #{ss.pos})"
|
338
341
|
end
|
339
342
|
|
343
|
+
# // style comment
|
344
|
+
def lex_line_comment(ss, line, str)
|
345
|
+
while !ss.eos? do
|
346
|
+
case
|
347
|
+
when ss.scan(/\n/)
|
348
|
+
return line + 1
|
349
|
+
else
|
350
|
+
str << ss.getch
|
351
|
+
next
|
352
|
+
end
|
353
|
+
|
354
|
+
str << ss[0]
|
355
|
+
end
|
356
|
+
|
357
|
+
line # Reach to end of input
|
358
|
+
end
|
359
|
+
|
340
360
|
def lex_grammar_rules_tokens
|
341
361
|
lex_common(@grammar_rules, @grammar_rules_tokens)
|
342
362
|
end
|
data/lib/lrama/output.rb
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Parser
|
3
|
+
class TokenScanner
|
4
|
+
def initialize(tokens)
|
5
|
+
@tokens = tokens
|
6
|
+
@index = 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def current_token
|
10
|
+
@tokens[@index]
|
11
|
+
end
|
12
|
+
|
13
|
+
def current_type
|
14
|
+
current_token && current_token.type
|
15
|
+
end
|
16
|
+
|
17
|
+
def next
|
18
|
+
token = current_token
|
19
|
+
@index += 1
|
20
|
+
return token
|
21
|
+
end
|
22
|
+
|
23
|
+
def consume(*token_types)
|
24
|
+
if token_types.include?(current_type)
|
25
|
+
token = current_token
|
26
|
+
self.next
|
27
|
+
return token
|
28
|
+
end
|
29
|
+
|
30
|
+
return nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def consume!(*token_types)
|
34
|
+
consume(*token_types) || (raise "#{token_types} is expected but #{current_type}. #{current_token}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def consume_multi(*token_types)
|
38
|
+
a = []
|
39
|
+
|
40
|
+
while token_types.include?(current_type)
|
41
|
+
a << current_token
|
42
|
+
self.next
|
43
|
+
end
|
44
|
+
|
45
|
+
raise "No token is consumed. #{token_types}" if a.empty?
|
46
|
+
|
47
|
+
return a
|
48
|
+
end
|
49
|
+
|
50
|
+
def eots?
|
51
|
+
current_token.nil?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/lrama/parser.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "lrama/report"
|
2
|
+
require "lrama/parser/token_scanner"
|
2
3
|
|
3
4
|
module Lrama
|
4
5
|
# Parser for parse.y, generates a grammar
|
@@ -7,58 +8,6 @@ module Lrama
|
|
7
8
|
|
8
9
|
T = Lrama::Lexer::Token
|
9
10
|
|
10
|
-
class TokenScanner
|
11
|
-
def initialize(tokens)
|
12
|
-
@tokens = tokens
|
13
|
-
@index = 0
|
14
|
-
end
|
15
|
-
|
16
|
-
def current_token
|
17
|
-
@tokens[@index]
|
18
|
-
end
|
19
|
-
|
20
|
-
def current_type
|
21
|
-
current_token && current_token.type
|
22
|
-
end
|
23
|
-
|
24
|
-
def next
|
25
|
-
token = current_token
|
26
|
-
@index += 1
|
27
|
-
return token
|
28
|
-
end
|
29
|
-
|
30
|
-
def consume(*token_types)
|
31
|
-
if token_types.include?(current_type)
|
32
|
-
token = current_token
|
33
|
-
self.next
|
34
|
-
return token
|
35
|
-
end
|
36
|
-
|
37
|
-
return nil
|
38
|
-
end
|
39
|
-
|
40
|
-
def consume!(*token_types)
|
41
|
-
consume(*token_types) || (raise "#{token_types} is expected but #{current_type}. #{current_token}")
|
42
|
-
end
|
43
|
-
|
44
|
-
def consume_multi(*token_types)
|
45
|
-
a = []
|
46
|
-
|
47
|
-
while token_types.include?(current_type)
|
48
|
-
a << current_token
|
49
|
-
self.next
|
50
|
-
end
|
51
|
-
|
52
|
-
raise "No token is consumed. #{token_types}" if a.empty?
|
53
|
-
|
54
|
-
return a
|
55
|
-
end
|
56
|
-
|
57
|
-
def eots?
|
58
|
-
current_token.nil?
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
11
|
def initialize(text)
|
63
12
|
@text = text
|
64
13
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Lrama
|
2
|
+
class State
|
3
|
+
class Reduce
|
4
|
+
# https://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html
|
5
|
+
attr_reader :item, :look_ahead, :not_selected_symbols
|
6
|
+
attr_accessor :default_reduction
|
7
|
+
|
8
|
+
def initialize(item)
|
9
|
+
@item = item
|
10
|
+
@look_ahead = nil
|
11
|
+
@not_selected_symbols = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def rule
|
15
|
+
@item.rule
|
16
|
+
end
|
17
|
+
|
18
|
+
def look_ahead=(look_ahead)
|
19
|
+
@look_ahead = look_ahead.freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_not_selected_symbol(sym)
|
23
|
+
@not_selected_symbols << sym
|
24
|
+
end
|
25
|
+
|
26
|
+
def selected_look_ahead
|
27
|
+
if @look_ahead
|
28
|
+
@look_ahead - @not_selected_symbols
|
29
|
+
else
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/lrama/state.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
require "lrama/state/reduce"
|
2
|
+
require "lrama/state/shift"
|
3
|
+
|
4
|
+
module Lrama
|
5
|
+
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
|
+
attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts,
|
35
|
+
:default_reduction_rule, :closure, :items
|
36
|
+
attr_accessor :shifts, :reduces
|
37
|
+
|
38
|
+
def initialize(id, accessing_symbol, kernels)
|
39
|
+
@id = id
|
40
|
+
@accessing_symbol = accessing_symbol
|
41
|
+
@kernels = kernels.freeze
|
42
|
+
@items = @kernels
|
43
|
+
# Manage relationships between items to state
|
44
|
+
# to resolve next state
|
45
|
+
@items_to_state = {}
|
46
|
+
@conflicts = []
|
47
|
+
@resolved_conflicts = []
|
48
|
+
@default_reduction_rule = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def closure=(closure)
|
52
|
+
@closure = closure
|
53
|
+
@items = @kernels + @closure
|
54
|
+
end
|
55
|
+
|
56
|
+
def non_default_reduces
|
57
|
+
reduces.select do |reduce|
|
58
|
+
reduce.rule != @default_reduction_rule
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def compute_shifts_reduces
|
63
|
+
_shifts = {}
|
64
|
+
reduces = []
|
65
|
+
items.each do |item|
|
66
|
+
# TODO: Consider what should be pushed
|
67
|
+
if item.end_of_rule?
|
68
|
+
reduces << Reduce.new(item)
|
69
|
+
else
|
70
|
+
key = item.next_sym
|
71
|
+
_shifts[key] ||= []
|
72
|
+
_shifts[key] << item.new_by_next_position
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# It seems Bison 3.8.2 iterates transitions order by symbol number
|
77
|
+
shifts = _shifts.sort_by do |next_sym, new_items|
|
78
|
+
next_sym.number
|
79
|
+
end.map do |next_sym, new_items|
|
80
|
+
Shift.new(next_sym, new_items.flatten)
|
81
|
+
end
|
82
|
+
self.shifts = shifts.freeze
|
83
|
+
self.reduces = reduces.freeze
|
84
|
+
end
|
85
|
+
|
86
|
+
def set_items_to_state(items, next_state)
|
87
|
+
@items_to_state[items] = next_state
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
def set_look_ahead(rule, look_ahead)
|
92
|
+
reduce = reduces.find do |r|
|
93
|
+
r.rule == rule
|
94
|
+
end
|
95
|
+
|
96
|
+
reduce.look_ahead = look_ahead
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns array of [nterm, next_state]
|
100
|
+
def nterm_transitions
|
101
|
+
return @nterm_transitions if @nterm_transitions
|
102
|
+
|
103
|
+
@nterm_transitions = []
|
104
|
+
|
105
|
+
shifts.each do |shift|
|
106
|
+
next if shift.next_sym.term?
|
107
|
+
|
108
|
+
@nterm_transitions << [shift, @items_to_state[shift.next_items]]
|
109
|
+
end
|
110
|
+
|
111
|
+
@nterm_transitions
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns array of [term, next_state]
|
115
|
+
def term_transitions
|
116
|
+
return @term_transitions if @term_transitions
|
117
|
+
|
118
|
+
@term_transitions = []
|
119
|
+
|
120
|
+
shifts.each do |shift|
|
121
|
+
next if shift.next_sym.nterm?
|
122
|
+
|
123
|
+
@term_transitions << [shift, @items_to_state[shift.next_items]]
|
124
|
+
end
|
125
|
+
|
126
|
+
@term_transitions
|
127
|
+
end
|
128
|
+
|
129
|
+
def selected_term_transitions
|
130
|
+
term_transitions.select do |shift, next_state|
|
131
|
+
!shift.not_selected
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Move to next state by sym
|
136
|
+
def transition(sym)
|
137
|
+
result = nil
|
138
|
+
|
139
|
+
if sym.term?
|
140
|
+
term_transitions.each do |shift, next_state|
|
141
|
+
term = shift.next_sym
|
142
|
+
result = next_state if term == sym
|
143
|
+
end
|
144
|
+
else
|
145
|
+
nterm_transitions.each do |shift, next_state|
|
146
|
+
nterm = shift.next_sym
|
147
|
+
result = next_state if nterm == sym
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
raise "Can not transit by #{sym} #{self}" if result.nil?
|
152
|
+
|
153
|
+
result
|
154
|
+
end
|
155
|
+
|
156
|
+
def find_reduce_by_item!(item)
|
157
|
+
reduces.find do |r|
|
158
|
+
r.item == item
|
159
|
+
end || (raise "reduce is not found. #{item}")
|
160
|
+
end
|
161
|
+
|
162
|
+
def default_reduction_rule=(default_reduction_rule)
|
163
|
+
@default_reduction_rule = default_reduction_rule
|
164
|
+
|
165
|
+
reduces.each do |r|
|
166
|
+
if r.rule == default_reduction_rule
|
167
|
+
r.default_reduction = true
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def sr_conflicts
|
173
|
+
@conflicts.select do |conflict|
|
174
|
+
conflict.type == :shift_reduce
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def rr_conflicts
|
179
|
+
@conflicts.select do |conflict|
|
180
|
+
conflict.type == :reduce_reduce
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|