lrama 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a8a1b04a3e761af04029af3e3b74aaaf4fe9a9e2741205f430264ee3399f66c
4
- data.tar.gz: a97ec2c2b2007f3bad6cbe5d1b37baa87d520cc3025e82403a9d8a70cddccc8a
3
+ metadata.gz: 20a0927bedeb14281abb8a5c59daf643fdfe556b5899cbe4872878b022804da2
4
+ data.tar.gz: 731fdd0201266db9724a2ebcc1162427a2cfaa9d0d94543e1298b3f2683eb14f
5
5
  SHA512:
6
- metadata.gz: 820bce06aefc235c02995cc957f9e0d9b2e8d588ce7748225896f0d63c319135068da39977be860815276c360a66e3dd6c2132802aa30e3292d706bc6ae7ef9d
7
- data.tar.gz: d5c4ec405196bca888c78bf8a16cc9aef47da0f9943d7eee2bf9033c104f9a07fcb07c4cb6aabb3bb49f0e3665bf37932e2206cd9acad925702c30c7124210fb
6
+ metadata.gz: 3a6e7c0ef4e7266dae41dba15e3a5d7609cb7171b82e0dbd5b75595e8e3cf8a4a402c5b710cc0f714c54357ef9fc20fd0935e55b8c529c99cd3c09ea173efb87
7
+ data.tar.gz: fa867c9be43384c7f282bf22ecd2b0939cd0dca263a51914871dba42f409ddc01fb00c34d8696288c3668177fdc450ff1bc9fb5e90fdf053e2d56a5bf31bbe04
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: 'github-actions'
4
+ directory: '/'
5
+ schedule:
6
+ interval: 'weekly'
@@ -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:
@@ -70,5 +84,5 @@ jobs:
70
84
  - run: sudo apt-get --purge remove bison
71
85
  - run: ../autogen.sh
72
86
  - run: ../configure -C --disable-install-doc
73
- - run: make YACC=$(readlink -f $(pwd)/../tool/lrama/exe/lrama)
87
+ - run: make
74
88
  - run: make test-all
data/Gemfile CHANGED
@@ -6,3 +6,5 @@ gem "rspec"
6
6
  gem "pry"
7
7
  gem "stackprof"
8
8
  gem "rake"
9
+ gem "rbs", require: false
10
+ gem "steep", require: false
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.1.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
@@ -0,0 +1,7 @@
1
+ # D = Steep::Diagnostic
2
+ #
3
+ target :lib do
4
+ signature "sig"
5
+
6
+ check "lib/lrama/bitmap.rb"
7
+ end
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. #{code}, #{ref}"
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. #{code}, #{ref}"
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. #{code}, #{ref}"
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
- # Igonre ' on the both sides
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 dupulicated. #{invalid}"
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 (unknow token): #{split[l]} \"#{ss.string[ss.pos]}\" (#{line}: #{col})"
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
@@ -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
@@ -0,0 +1,13 @@
1
+ module Lrama
2
+ class State
3
+ class Shift
4
+ attr_reader :next_sym, :next_items
5
+ attr_accessor :not_selected
6
+
7
+ def initialize(next_sym, next_items)
8
+ @next_sym = next_sym
9
+ @next_items = next_items
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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
data/lib/lrama/states.rb CHANGED
@@ -2,228 +2,6 @@ require "forwardable"
2
2
  require "lrama/report"
3
3
 
4
4
  module Lrama
5
- class State
6
- class Reduce
7
- # https://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html
8
- attr_reader :item, :look_ahead, :not_selected_symbols
9
- attr_accessor :default_reduction
10
-
11
- def initialize(item)
12
- @item = item
13
- @look_ahead = nil
14
- @not_selected_symbols = []
15
- end
16
-
17
- def rule
18
- @item.rule
19
- end
20
-
21
- def look_ahead=(look_ahead)
22
- @look_ahead = look_ahead.freeze
23
- end
24
-
25
- def add_not_selected_symbol(sym)
26
- @not_selected_symbols << sym
27
- end
28
-
29
- def selected_look_ahead
30
- if @look_ahead
31
- @look_ahead - @not_selected_symbols
32
- else
33
- []
34
- end
35
- end
36
- end
37
-
38
- class Shift
39
- attr_reader :next_sym, :next_items
40
- attr_accessor :not_selected
41
-
42
- def initialize(next_sym, next_items)
43
- @next_sym = next_sym
44
- @next_items = next_items
45
- end
46
- end
47
-
48
- # * symbol: A symbol under discussion
49
- # * reduce: A reduce under discussion
50
- # * which: For which a conflict is resolved. :shift, :reduce or :error (for nonassociative)
51
- ResolvedConflict = Struct.new(:symbol, :reduce, :which, :same_prec, keyword_init: true) do
52
- def report_message
53
- s = symbol.display_name
54
- r = reduce.rule.precedence_sym.display_name
55
- case
56
- when which == :shift && same_prec
57
- msg = "resolved as #{which} (%right #{s})"
58
- when which == :shift
59
- msg = "resolved as #{which} (#{r} < #{s})"
60
- when which == :reduce && same_prec
61
- msg = "resolved as #{which} (%left #{s})"
62
- when which == :reduce
63
- msg = "resolved as #{which} (#{s} < #{r})"
64
- when which == :error
65
- msg = "resolved as an #{which} (%nonassoc #{s})"
66
- else
67
- raise "Unknown direction. #{self}"
68
- end
69
-
70
- "Conflict between rule #{reduce.rule.id} and token #{s} #{msg}."
71
- end
72
- end
73
-
74
- Conflict = Struct.new(:symbols, :reduce, :type, keyword_init: true)
75
-
76
- attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts,
77
- :default_reduction_rule, :closure, :items
78
- attr_accessor :shifts, :reduces
79
-
80
- def initialize(id, accessing_symbol, kernels)
81
- @id = id
82
- @accessing_symbol = accessing_symbol
83
- @kernels = kernels.freeze
84
- @items = @kernels
85
- # Manage relationships between items to state
86
- # to resolve next state
87
- @items_to_state = {}
88
- @conflicts = []
89
- @resolved_conflicts = []
90
- @default_reduction_rule = nil
91
- end
92
-
93
- def closure=(closure)
94
- @closure = closure
95
- @items = @kernels + @closure
96
- end
97
-
98
- def non_default_reduces
99
- reduces.select do |reduce|
100
- reduce.rule != @default_reduction_rule
101
- end
102
- end
103
-
104
- def compute_shifts_reduces
105
- _shifts = {}
106
- reduces = []
107
- items.each do |item|
108
- # TODO: Consider what should be pushed
109
- if item.end_of_rule?
110
- reduces << Reduce.new(item)
111
- else
112
- key = item.next_sym
113
- _shifts[key] ||= []
114
- _shifts[key] << item.new_by_next_position
115
- end
116
- end
117
-
118
- # It seems Bison 3.8.2 iterates transitions order by symbol number
119
- shifts = _shifts.sort_by do |next_sym, new_items|
120
- next_sym.number
121
- end.map do |next_sym, new_items|
122
- Shift.new(next_sym, new_items.flatten)
123
- end
124
- self.shifts = shifts.freeze
125
- self.reduces = reduces.freeze
126
- end
127
-
128
- def set_items_to_state(items, next_state)
129
- @items_to_state[items] = next_state
130
- end
131
-
132
- #
133
- def set_look_ahead(rule, look_ahead)
134
- reduce = reduces.find do |r|
135
- r.rule == rule
136
- end
137
-
138
- reduce.look_ahead = look_ahead
139
- end
140
-
141
- # Returns array of [nterm, next_state]
142
- def nterm_transitions
143
- return @nterm_transitions if @nterm_transitions
144
-
145
- @nterm_transitions = []
146
-
147
- shifts.each do |shift|
148
- next if shift.next_sym.term?
149
-
150
- @nterm_transitions << [shift, @items_to_state[shift.next_items]]
151
- end
152
-
153
- @nterm_transitions
154
- end
155
-
156
- # Returns array of [term, next_state]
157
- def term_transitions
158
- return @term_transitions if @term_transitions
159
-
160
- @term_transitions = []
161
-
162
- shifts.each do |shift|
163
- next if shift.next_sym.nterm?
164
-
165
- @term_transitions << [shift, @items_to_state[shift.next_items]]
166
- end
167
-
168
- @term_transitions
169
- end
170
-
171
- def selected_term_transitions
172
- term_transitions.select do |shift, next_state|
173
- !shift.not_selected
174
- end
175
- end
176
-
177
- # Move to next state by sym
178
- def transition(sym)
179
- result = nil
180
-
181
- if sym.term?
182
- term_transitions.each do |shift, next_state|
183
- term = shift.next_sym
184
- result = next_state if term == sym
185
- end
186
- else
187
- nterm_transitions.each do |shift, next_state|
188
- nterm = shift.next_sym
189
- result = next_state if nterm == sym
190
- end
191
- end
192
-
193
- raise "Can not transit by #{sym} #{self}" if result.nil?
194
-
195
- result
196
- end
197
-
198
- def find_reduce_by_item!(item)
199
- reduces.find do |r|
200
- r.item == item
201
- end || (raise "reduce is not found. #{item}, #{state}")
202
- end
203
-
204
- def default_reduction_rule=(default_reduction_rule)
205
- @default_reduction_rule = default_reduction_rule
206
-
207
- reduces.each do |r|
208
- if r.rule == default_reduction_rule
209
- r.default_reduction = true
210
- end
211
- end
212
- end
213
-
214
- def sr_conflicts
215
- @conflicts.select do |conflict|
216
- conflict.type == :shift_reduce
217
- end
218
- end
219
-
220
- def rr_conflicts
221
- @conflicts.select do |conflict|
222
- conflict.type == :reduce_reduce
223
- end
224
- end
225
- end
226
-
227
5
  # States is passed to a template file
228
6
  #
229
7
  # "Efficient Computation of LALR(1) Look-Ahead Sets"
@@ -411,23 +189,13 @@ module Lrama
411
189
  @states.flat_map(&:rr_conflicts)
412
190
  end
413
191
 
414
- def initial_attrs
415
- h = {}
416
-
417
- attrs.each do |attr|
418
- h[attr.id] = false
419
- end
420
-
421
- h
422
- end
423
-
424
192
  def trace_state
425
193
  if @trace_state
426
194
  yield STDERR
427
195
  end
428
196
  end
429
197
 
430
- def create_state(accessing_symbol, kernels, states_creted)
198
+ def create_state(accessing_symbol, kernels, states_created)
431
199
  # A item can appear in some states,
432
200
  # so need to use `kernels` (not `kernels.first`) as a key.
433
201
  #
@@ -464,11 +232,11 @@ module Lrama
464
232
  # string_1: string •
465
233
  # string_2: string • '+'
466
234
  #
467
- return [states_creted[kernels], false] if states_creted[kernels]
235
+ return [states_created[kernels], false] if states_created[kernels]
468
236
 
469
237
  state = State.new(@states.count, accessing_symbol, kernels)
470
238
  @states << state
471
- states_creted[kernels] = state
239
+ states_created[kernels] = state
472
240
 
473
241
  return [state, true]
474
242
  end
@@ -532,9 +300,9 @@ module Lrama
532
300
  def compute_lr0_states
533
301
  # State queue
534
302
  states = []
535
- states_creted = {}
303
+ states_created = {}
536
304
 
537
- state, _ = create_state(symbols.first, [Item.new(rule: @grammar.rules.first, position: 0)], states_creted)
305
+ state, _ = create_state(symbols.first, [Item.new(rule: @grammar.rules.first, position: 0)], states_created)
538
306
  enqueue_state(states, state)
539
307
 
540
308
  while (state = states.shift) do
@@ -550,7 +318,7 @@ module Lrama
550
318
  setup_state(state)
551
319
 
552
320
  state.shifts.each do |shift|
553
- new_state, created = create_state(shift.next_sym, shift.next_items, states_creted)
321
+ new_state, created = create_state(shift.next_sym, shift.next_items, states_created)
554
322
  state.set_items_to_state(shift.next_items, new_state)
555
323
  enqueue_state(states, new_state) if created
556
324
  end
@@ -210,7 +210,7 @@ module Lrama
210
210
  io << "\n"
211
211
 
212
212
 
213
- # Reprot reads_relation
213
+ # Report reads_relation
214
214
  io << " [Reads Relation]\n"
215
215
  @states.nterms.each do |nterm|
216
216
  a = @states.reads_relation[[state.id, nterm.token_id]]
@@ -224,7 +224,7 @@ module Lrama
224
224
  io << "\n"
225
225
 
226
226
 
227
- # Reprot read_sets
227
+ # Report read_sets
228
228
  io << " [Read sets]\n"
229
229
  read_sets = @states.read_sets
230
230
  @states.nterms.each do |nterm|
@@ -239,7 +239,7 @@ module Lrama
239
239
  io << "\n"
240
240
 
241
241
 
242
- # Reprot includes_relation
242
+ # Report includes_relation
243
243
  io << " [Includes Relation]\n"
244
244
  @states.nterms.each do |nterm|
245
245
  a = @states.includes_relation[[state.id, nterm.token_id]]
@@ -267,7 +267,7 @@ module Lrama
267
267
  io << "\n"
268
268
 
269
269
 
270
- # Reprot follow_sets
270
+ # Report follow_sets
271
271
  io << " [Follow sets]\n"
272
272
  follow_sets = @states.follow_sets
273
273
  @states.nterms.each do |nterm|
data/lib/lrama/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Lrama
2
- VERSION = "0.5.0".freeze
2
+ VERSION = "0.5.1".freeze
3
3
  end
data/lib/lrama.rb CHANGED
@@ -7,6 +7,7 @@ require "lrama/lexer"
7
7
  require "lrama/output"
8
8
  require "lrama/parser"
9
9
  require "lrama/report"
10
+ require "lrama/state"
10
11
  require "lrama/states"
11
12
  require "lrama/states_reporter"
12
13
  require "lrama/version"
@@ -0,0 +1,263 @@
1
+ Grammar
2
+
3
+ 0 $accept: list "end of file"
4
+
5
+ 1 list: ε
6
+ 2 | list LF
7
+ 3 | list expr LF
8
+
9
+ 4 expr: NUM
10
+ 5 | expr '+' expr
11
+ 6 | expr '-' expr
12
+ 7 | expr '*' expr
13
+ 8 | expr '/' expr
14
+ 9 | '(' expr ')'
15
+
16
+
17
+ State 0
18
+
19
+ 0 $accept: • list "end of file"
20
+ 1 list: ε •
21
+ 2 | • list LF
22
+ 3 | • list expr LF
23
+
24
+ $default reduce using rule 1 (list)
25
+
26
+ list go to state 1
27
+
28
+
29
+ State 1
30
+
31
+ 0 $accept: list • "end of file"
32
+ 2 list: list • LF
33
+ 3 | list • expr LF
34
+ 4 expr: • NUM
35
+ 5 | • expr '+' expr
36
+ 6 | • expr '-' expr
37
+ 7 | • expr '*' expr
38
+ 8 | • expr '/' expr
39
+ 9 | • '(' expr ')'
40
+
41
+ "end of file" shift, and go to state 2
42
+ LF shift, and go to state 3
43
+ NUM shift, and go to state 4
44
+ '(' shift, and go to state 5
45
+
46
+ expr go to state 6
47
+
48
+
49
+ State 2
50
+
51
+ 0 $accept: list "end of file" •
52
+
53
+ $default accept
54
+
55
+
56
+ State 3
57
+
58
+ 2 list: list LF •
59
+
60
+ $default reduce using rule 2 (list)
61
+
62
+
63
+ State 4
64
+
65
+ 4 expr: NUM •
66
+
67
+ $default reduce using rule 4 (expr)
68
+
69
+
70
+ State 5
71
+
72
+ 4 expr: • NUM
73
+ 5 | • expr '+' expr
74
+ 6 | • expr '-' expr
75
+ 7 | • expr '*' expr
76
+ 8 | • expr '/' expr
77
+ 9 | • '(' expr ')'
78
+ 9 | '(' • expr ')'
79
+
80
+ NUM shift, and go to state 4
81
+ '(' shift, and go to state 5
82
+
83
+ expr go to state 7
84
+
85
+
86
+ State 6
87
+
88
+ 3 list: list expr • LF
89
+ 5 expr: expr • '+' expr
90
+ 6 | expr • '-' expr
91
+ 7 | expr • '*' expr
92
+ 8 | expr • '/' expr
93
+
94
+ LF shift, and go to state 8
95
+ '+' shift, and go to state 9
96
+ '-' shift, and go to state 10
97
+ '*' shift, and go to state 11
98
+ '/' shift, and go to state 12
99
+
100
+
101
+ State 7
102
+
103
+ 5 expr: expr • '+' expr
104
+ 6 | expr • '-' expr
105
+ 7 | expr • '*' expr
106
+ 8 | expr • '/' expr
107
+ 9 | '(' expr • ')'
108
+
109
+ '+' shift, and go to state 9
110
+ '-' shift, and go to state 10
111
+ '*' shift, and go to state 11
112
+ '/' shift, and go to state 12
113
+ ')' shift, and go to state 13
114
+
115
+
116
+ State 8
117
+
118
+ 3 list: list expr LF •
119
+
120
+ $default reduce using rule 3 (list)
121
+
122
+
123
+ State 9
124
+
125
+ 4 expr: • NUM
126
+ 5 | • expr '+' expr
127
+ 5 | expr '+' • expr
128
+ 6 | • expr '-' expr
129
+ 7 | • expr '*' expr
130
+ 8 | • expr '/' expr
131
+ 9 | • '(' expr ')'
132
+
133
+ NUM shift, and go to state 4
134
+ '(' shift, and go to state 5
135
+
136
+ expr go to state 14
137
+
138
+
139
+ State 10
140
+
141
+ 4 expr: • NUM
142
+ 5 | • expr '+' expr
143
+ 6 | • expr '-' expr
144
+ 6 | expr '-' • expr
145
+ 7 | • expr '*' expr
146
+ 8 | • expr '/' expr
147
+ 9 | • '(' expr ')'
148
+
149
+ NUM shift, and go to state 4
150
+ '(' shift, and go to state 5
151
+
152
+ expr go to state 15
153
+
154
+
155
+ State 11
156
+
157
+ 4 expr: • NUM
158
+ 5 | • expr '+' expr
159
+ 6 | • expr '-' expr
160
+ 7 | • expr '*' expr
161
+ 7 | expr '*' • expr
162
+ 8 | • expr '/' expr
163
+ 9 | • '(' expr ')'
164
+
165
+ NUM shift, and go to state 4
166
+ '(' shift, and go to state 5
167
+
168
+ expr go to state 16
169
+
170
+
171
+ State 12
172
+
173
+ 4 expr: • NUM
174
+ 5 | • expr '+' expr
175
+ 6 | • expr '-' expr
176
+ 7 | • expr '*' expr
177
+ 8 | • expr '/' expr
178
+ 8 | expr '/' • expr
179
+ 9 | • '(' expr ')'
180
+
181
+ NUM shift, and go to state 4
182
+ '(' shift, and go to state 5
183
+
184
+ expr go to state 17
185
+
186
+
187
+ State 13
188
+
189
+ 9 expr: '(' expr ')' •
190
+
191
+ $default reduce using rule 9 (expr)
192
+
193
+
194
+ State 14
195
+
196
+ 5 expr: expr • '+' expr
197
+ 5 | expr '+' expr • [LF, '+', '-', ')']
198
+ 6 | expr • '-' expr
199
+ 7 | expr • '*' expr
200
+ 8 | expr • '/' expr
201
+
202
+ '*' shift, and go to state 11
203
+ '/' shift, and go to state 12
204
+
205
+ $default reduce using rule 5 (expr)
206
+
207
+ Conflict between rule 5 and token '+' resolved as reduce (%left '+').
208
+ Conflict between rule 5 and token '-' resolved as reduce (%left '-').
209
+ Conflict between rule 5 and token '*' resolved as shift ('+' < '*').
210
+ Conflict between rule 5 and token '/' resolved as shift ('+' < '/').
211
+
212
+
213
+ State 15
214
+
215
+ 5 expr: expr • '+' expr
216
+ 6 | expr • '-' expr
217
+ 6 | expr '-' expr • [LF, '+', '-', ')']
218
+ 7 | expr • '*' expr
219
+ 8 | expr • '/' expr
220
+
221
+ '*' shift, and go to state 11
222
+ '/' shift, and go to state 12
223
+
224
+ $default reduce using rule 6 (expr)
225
+
226
+ Conflict between rule 6 and token '+' resolved as reduce (%left '+').
227
+ Conflict between rule 6 and token '-' resolved as reduce (%left '-').
228
+ Conflict between rule 6 and token '*' resolved as shift ('-' < '*').
229
+ Conflict between rule 6 and token '/' resolved as shift ('-' < '/').
230
+
231
+
232
+ State 16
233
+
234
+ 5 expr: expr • '+' expr
235
+ 6 | expr • '-' expr
236
+ 7 | expr • '*' expr
237
+ 7 | expr '*' expr • [LF, '+', '-', '*', '/', ')']
238
+ 8 | expr • '/' expr
239
+
240
+ $default reduce using rule 7 (expr)
241
+
242
+ Conflict between rule 7 and token '+' resolved as reduce ('+' < '*').
243
+ Conflict between rule 7 and token '-' resolved as reduce ('-' < '*').
244
+ Conflict between rule 7 and token '*' resolved as reduce (%left '*').
245
+ Conflict between rule 7 and token '/' resolved as reduce (%left '/').
246
+
247
+
248
+ State 17
249
+
250
+ 5 expr: expr • '+' expr
251
+ 6 | expr • '-' expr
252
+ 7 | expr • '*' expr
253
+ 8 | expr • '/' expr
254
+ 8 | expr '/' expr • [LF, '+', '-', '*', '/', ')']
255
+
256
+ $default reduce using rule 8 (expr)
257
+
258
+ Conflict between rule 8 and token '+' resolved as reduce ('+' < '/').
259
+ Conflict between rule 8 and token '-' resolved as reduce ('-' < '/').
260
+ Conflict between rule 8 and token '*' resolved as reduce (%left '*').
261
+ Conflict between rule 8 and token '/' resolved as reduce (%left '/').
262
+
263
+
data/sample/calc.y ADDED
@@ -0,0 +1,98 @@
1
+ /*
2
+ * How to build and run:
3
+ *
4
+ * $ lrama -d calc.y -o calc.c && gcc -Wall calc.c -o calc && ./calc
5
+ * 1
6
+ * => 1
7
+ * 1+2*3
8
+ * => 7
9
+ * (1+2)*3
10
+ * => 9
11
+ *
12
+ */
13
+
14
+ %{
15
+ #include <stdio.h>
16
+ #include <stdlib.h>
17
+ #include <ctype.h>
18
+
19
+ #include "calc.h"
20
+
21
+ static int yylex(YYSTYPE *val, YYLTYPE *loc);
22
+ static int yyerror(YYLTYPE *loc, const char *str);
23
+ %}
24
+
25
+ %union {
26
+ int val;
27
+ }
28
+ %token LF
29
+ %token <val> NUM
30
+ %type <val> expr
31
+ %left '+' '-'
32
+ %left '*' '/'
33
+
34
+ %%
35
+
36
+ list : /* empty */
37
+ | list LF
38
+ | list expr LF { printf("=> %d\n", $2); }
39
+ ;
40
+ expr : NUM
41
+ | expr '+' expr { $$ = $1 + $3; }
42
+ | expr '-' expr { $$ = $1 - $3; }
43
+ | expr '*' expr { $$ = $1 * $3; }
44
+ | expr '/' expr { $$ = $1 / $3; }
45
+ | '(' expr ')' { $$ = $2; }
46
+ ;
47
+
48
+ %%
49
+
50
+ static int yylex(YYSTYPE *yylval, YYLTYPE *loc) {
51
+ int c = getchar();
52
+ int val;
53
+
54
+ switch (c) {
55
+ case ' ': case '\t':
56
+ return yylex(yylval, loc);
57
+
58
+ case '0': case '1': case '2': case '3': case '4':
59
+ case '5': case '6': case '7': case '8': case '9':
60
+ val = c - '0';
61
+ while (1) {
62
+ c = getchar();
63
+ if (isdigit(c)) {
64
+ val = val * 10 + (c - '0');
65
+ }
66
+ else {
67
+ ungetc(c, stdin);
68
+ break;
69
+ }
70
+ }
71
+ yylval->val = val;
72
+ return NUM;
73
+
74
+ case '\n':
75
+ return LF;
76
+
77
+ case '+': case '-': case '*': case '/': case '(': case ')':
78
+ return c;
79
+
80
+ case EOF:
81
+ exit(0);
82
+
83
+ default:
84
+ fprintf(stderr, "unknown character: %c\n", c);
85
+ exit(1);
86
+ }
87
+ }
88
+
89
+ static int yyerror(YYLTYPE *loc, const char *str) {
90
+ fprintf(stderr, "parse error: %s\n", str);
91
+ return 0;
92
+ }
93
+
94
+ int main() {
95
+ printf("Enter the formula:\n");
96
+ yyparse();
97
+ return 0;
98
+ }
@@ -0,0 +1,7 @@
1
+ module Lrama
2
+ module Bitmap
3
+ def self.from_array: (Array[int] ary) -> Integer
4
+
5
+ def self.to_array: (Integer int) -> Array[Integer]
6
+ end
7
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lrama
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuichiro Kaneko
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-15 00:00:00.000000000 Z
11
+ date: 2023-05-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: LALR (1) parser generator written by Ruby
14
14
  email:
@@ -18,6 +18,7 @@ executables:
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
+ - ".github/dependabot.yml"
21
22
  - ".github/workflows/test.yaml"
22
23
  - ".gitignore"
23
24
  - ".rspec"
@@ -26,6 +27,7 @@ files:
26
27
  - MIT
27
28
  - README.md
28
29
  - Rakefile
30
+ - Steepfile
29
31
  - doc/TODO.md
30
32
  - exe/lrama
31
33
  - lib/lrama.rb
@@ -37,13 +39,20 @@ files:
37
39
  - lib/lrama/lexer.rb
38
40
  - lib/lrama/output.rb
39
41
  - lib/lrama/parser.rb
42
+ - lib/lrama/parser/token_scanner.rb
40
43
  - lib/lrama/report.rb
44
+ - lib/lrama/state.rb
45
+ - lib/lrama/state/reduce.rb
46
+ - lib/lrama/state/shift.rb
41
47
  - lib/lrama/states.rb
42
48
  - lib/lrama/states_reporter.rb
43
49
  - lib/lrama/version.rb
44
50
  - lib/lrama/warning.rb
45
51
  - lrama.gemspec
52
+ - sample/calc.output
53
+ - sample/calc.y
46
54
  - sample/parse.y
55
+ - sig/lrama/bitmap.rbs
47
56
  - template/bison/yacc.c
48
57
  - template/bison/yacc.h
49
58
  homepage: https://github.com/yui-knk/lrama
@@ -65,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
74
  - !ruby/object:Gem::Version
66
75
  version: '0'
67
76
  requirements: []
68
- rubygems_version: 3.4.1
77
+ rubygems_version: 3.5.0.dev
69
78
  signing_key:
70
79
  specification_version: 4
71
80
  summary: LALR (1) parser generator written by Ruby