lrama 0.5.0 → 0.5.1

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 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