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