lrama 0.4.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: ef1a46b0de83840d8353cf0e832898b7567a6a546b5ab8402cee59be5178d3af
4
- data.tar.gz: 81e546a5f907ed2a1a2ca4e1028f5be05df8251bd59fb8c7d8a3a7565e0f9dce
3
+ metadata.gz: 20a0927bedeb14281abb8a5c59daf643fdfe556b5899cbe4872878b022804da2
4
+ data.tar.gz: 731fdd0201266db9724a2ebcc1162427a2cfaa9d0d94543e1298b3f2683eb14f
5
5
  SHA512:
6
- metadata.gz: d388d548999a8e96d08698d191a44ea5c71893060eef624a1fd7845cfe23f96f27720646897fc12930f948f3dc27771a0e69ec26f57b604711ebe537984cb752
7
- data.tar.gz: afe54b54f3852fd65c243bff4a800a2186bc89cdfc92a83bae85eabf10aae7b4989d9daea9904477bff109fd48ae38af0aefaf1e10d245cb74f107397db5ae55
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:
@@ -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 template ../ruby/tool/lrama
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 YACC=$(readlink -f $(pwd)/../tool/lrama/exe/lrama)
87
+ - run: make
72
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/command.rb CHANGED
@@ -57,14 +57,18 @@ module Lrama
57
57
  end
58
58
 
59
59
  if !grammar_file
60
- puts "File should be specified\n"
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
- y = File.read(grammar_file)
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. #{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
data/lib/lrama/output.rb CHANGED
@@ -53,9 +53,7 @@ module Lrama
53
53
  if @header_out
54
54
  @header_out << tmp
55
55
  else
56
- File.open(@header_file_path, "w+") do |f|
57
- f << tmp
58
- end
56
+ File.write(@header_file_path, tmp)
59
57
  end
60
58
  end
61
59
  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