lrama 0.5.0 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/test.yaml +17 -4
- data/.gitignore +1 -0
- data/Gemfile +2 -0
- data/README.md +29 -2
- data/Steepfile +9 -0
- data/lib/lrama/grammar.rb +34 -26
- data/lib/lrama/lexer.rb +58 -4
- data/lib/lrama/parser/token_scanner.rb +59 -0
- data/lib/lrama/parser.rb +12 -56
- data/lib/lrama/state/reduce.rb +35 -0
- data/lib/lrama/state/shift.rb +13 -0
- data/lib/lrama/state.rb +184 -0
- data/lib/lrama/states.rb +6 -238
- data/lib/lrama/states_reporter.rb +4 -4
- data/lib/lrama/version.rb +1 -1
- data/lib/lrama.rb +1 -0
- data/lrama.gemspec +1 -1
- data/rbs_collection.lock.yaml +26 -0
- data/rbs_collection.yaml +22 -0
- data/sample/calc.output +263 -0
- data/sample/calc.y +98 -0
- data/sig/lrama/bitmap.rbs +7 -0
- data/sig/lrama/report.rbs +15 -0
- data/sig/lrama/warning.rbs +16 -0
- data/template/bison/yacc.c +34 -32
- metadata +17 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26bcef726eebf61de01200b02250b2aef91ca957df43db5d72e99ae30e320f4e
|
4
|
+
data.tar.gz: 751b62d184806c0cff319bffc4990ad0de9dd143accafdf6bdf1652eb02512b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02001b5b137d023c4f5ce48c1d9284ce90c5aad2ec269d7ef7a17e6ffa65be9e553e213400fc63d8256c0209908fc4b314de261993442af4a25772449453fbeb
|
7
|
+
data.tar.gz: 76a9f6bac19c0dcbdc6f9e2834d3c437f9fc5eb999d1b45d20362d52a94c9dc74bca3411b681993e28f388374df7f9b8ea7d0f534e53399efb8cc24112f75ea4
|
data/.github/workflows/test.yaml
CHANGED
@@ -22,6 +22,21 @@ 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 rbs collection install
|
39
|
+
- run: bundle exec steep check
|
25
40
|
test-ruby:
|
26
41
|
runs-on: ubuntu-20.04
|
27
42
|
strategy:
|
@@ -43,9 +58,7 @@ jobs:
|
|
43
58
|
- run: mkdir -p tool/lrama
|
44
59
|
working-directory: ../ruby
|
45
60
|
- name: Copy Lrama to ruby/tool
|
46
|
-
run: cp -r exe lib ../ruby/tool/lrama
|
47
|
-
# TODO: Consider how to manage changes on ruby/ruby master and ruby/lrama
|
48
|
-
# run: cp -r exe lib template ../ruby/tool/lrama
|
61
|
+
run: cp -r exe lib template ../ruby/tool/lrama
|
49
62
|
working-directory:
|
50
63
|
- run: tree tool/lrama
|
51
64
|
working-directory: ../ruby
|
@@ -70,5 +83,5 @@ jobs:
|
|
70
83
|
- run: sudo apt-get --purge remove bison
|
71
84
|
- run: ../autogen.sh
|
72
85
|
- run: ../configure -C --disable-install-doc
|
73
|
-
- run: make
|
86
|
+
- run: make
|
74
87
|
- run: make test-all
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -21,9 +21,11 @@ $ gem install lrama
|
|
21
21
|
From source codes,
|
22
22
|
|
23
23
|
```shell
|
24
|
+
$ cd "$(lrama root)"
|
25
|
+
$ bundle install
|
24
26
|
$ bundle exec rake install
|
25
|
-
$ lrama --version
|
26
|
-
0.
|
27
|
+
$ bundle exec lrama --version
|
28
|
+
0.5.0
|
27
29
|
```
|
28
30
|
|
29
31
|
## Usage
|
@@ -33,11 +35,36 @@ $ lrama --version
|
|
33
35
|
$ lrama -d sample/parse.y
|
34
36
|
```
|
35
37
|
|
38
|
+
```shell
|
39
|
+
# "calc", "calc.c", and "calc.h" are generated
|
40
|
+
$ lrama -d sample/calc.y -o calc.c && gcc -Wall calc.c -o calc && ./calc
|
41
|
+
Enter the formula:
|
42
|
+
1
|
43
|
+
=> 1
|
44
|
+
1+2*3
|
45
|
+
=> 7
|
46
|
+
(1+2)*3
|
47
|
+
=> 9
|
48
|
+
```
|
49
|
+
|
50
|
+
## Versions and Branches
|
51
|
+
|
52
|
+
### v0_4 (`lrama_0_4` branch)
|
53
|
+
|
54
|
+
This branch generates "parse.c" compatible with Bison 3.8.2 for ruby 3.0, 3.1, 3.2. The first version migrated to ruby is ["0.4.0"](https://github.com/ruby/ruby/pull/7798) therefore keep this branch for Bison compatible branch.
|
55
|
+
|
36
56
|
## Build Ruby
|
37
57
|
|
38
58
|
1. Install Lrama
|
39
59
|
2. Run `make YACC=lrama`
|
40
60
|
|
61
|
+
## Release flow
|
62
|
+
|
63
|
+
1. Update `Lrama::VERSION`
|
64
|
+
2. Release as a gem by `rake release`
|
65
|
+
3. Update Lrama in ruby/ruby by `cp -r LEGAL.md MIT exe lib ruby/tool/lrama`
|
66
|
+
4. Create new release on [GitHub](https://github.com/ruby/lrama/releases)
|
67
|
+
|
41
68
|
## License
|
42
69
|
|
43
70
|
See LEGAL.md file.
|
data/Steepfile
ADDED
data/lib/lrama/grammar.rb
CHANGED
@@ -155,18 +155,18 @@ module Lrama
|
|
155
155
|
last_column = ref.last_column
|
156
156
|
|
157
157
|
case
|
158
|
-
when ref.
|
158
|
+
when ref.value == "$" && ref.type == :dollar # $$
|
159
159
|
# Omit "<>"
|
160
160
|
member = tag.s_value[1..-2]
|
161
161
|
str = "((*yyvaluep).#{member})"
|
162
|
-
when ref.
|
162
|
+
when ref.value == "$" && ref.type == :at # @$
|
163
163
|
str = "(*yylocationp)"
|
164
164
|
when ref.type == :dollar # $n
|
165
|
-
raise "$#{ref.
|
165
|
+
raise "$#{ref.value} can not be used in %printer."
|
166
166
|
when ref.type == :at # @n
|
167
|
-
raise "@#{ref.
|
167
|
+
raise "@#{ref.value} can not be used in %printer."
|
168
168
|
else
|
169
|
-
raise "Unexpected. #{
|
169
|
+
raise "Unexpected. #{self}, #{ref}"
|
170
170
|
end
|
171
171
|
|
172
172
|
t_code[first_column..last_column] = str
|
@@ -190,22 +190,22 @@ module Lrama
|
|
190
190
|
last_column = ref.last_column
|
191
191
|
|
192
192
|
case
|
193
|
-
when ref.
|
193
|
+
when ref.value == "$" && ref.type == :dollar # $$
|
194
194
|
# Omit "<>"
|
195
195
|
member = ref.tag.s_value[1..-2]
|
196
196
|
str = "(yyval.#{member})"
|
197
|
-
when ref.
|
197
|
+
when ref.value == "$" && ref.type == :at # @$
|
198
198
|
str = "(yyloc)"
|
199
199
|
when ref.type == :dollar # $n
|
200
|
-
i = -ref.position_in_rhs + ref.
|
200
|
+
i = -ref.position_in_rhs + ref.value
|
201
201
|
# Omit "<>"
|
202
202
|
member = ref.tag.s_value[1..-2]
|
203
203
|
str = "(yyvsp[#{i}].#{member})"
|
204
204
|
when ref.type == :at # @n
|
205
|
-
i = -ref.position_in_rhs + ref.
|
205
|
+
i = -ref.position_in_rhs + ref.value
|
206
206
|
str = "(yylsp[#{i}])"
|
207
207
|
else
|
208
|
-
raise "Unexpected. #{
|
208
|
+
raise "Unexpected. #{self}, #{ref}"
|
209
209
|
end
|
210
210
|
|
211
211
|
t_code[first_column..last_column] = str
|
@@ -226,16 +226,16 @@ module Lrama
|
|
226
226
|
last_column = ref.last_column
|
227
227
|
|
228
228
|
case
|
229
|
-
when ref.
|
229
|
+
when ref.value == "$" && ref.type == :dollar # $$
|
230
230
|
str = "yylval"
|
231
|
-
when ref.
|
231
|
+
when ref.value == "$" && ref.type == :at # @$
|
232
232
|
str = "yylloc"
|
233
233
|
when ref.type == :dollar # $n
|
234
|
-
raise "$#{ref.
|
234
|
+
raise "$#{ref.value} can not be used in initial_action."
|
235
235
|
when ref.type == :at # @n
|
236
|
-
raise "@#{ref.
|
236
|
+
raise "@#{ref.value} can not be used in initial_action."
|
237
237
|
else
|
238
|
-
raise "Unexpected. #{
|
238
|
+
raise "Unexpected. #{self}, #{ref}"
|
239
239
|
end
|
240
240
|
|
241
241
|
t_code[first_column..last_column] = str
|
@@ -247,7 +247,7 @@ module Lrama
|
|
247
247
|
|
248
248
|
# type: :dollar or :at
|
249
249
|
# ex_tag: "$<tag>1" (Optional)
|
250
|
-
Reference = Struct.new(:type, :
|
250
|
+
Reference = Struct.new(:type, :value, :ex_tag, :first_column, :last_column, :referring_symbol, :position_in_rhs, keyword_init: true) do
|
251
251
|
def tag
|
252
252
|
if ex_tag
|
253
253
|
ex_tag
|
@@ -382,8 +382,8 @@ module Lrama
|
|
382
382
|
end
|
383
383
|
|
384
384
|
def build_references(token_code)
|
385
|
-
token_code.references.map! do |type,
|
386
|
-
Reference.new(type: type,
|
385
|
+
token_code.references.map! do |type, value, tag, first_column, last_column|
|
386
|
+
Reference.new(type: type, value: value, ex_tag: tag, first_column: first_column, last_column: last_column)
|
387
387
|
end
|
388
388
|
|
389
389
|
token_code
|
@@ -627,15 +627,23 @@ module Lrama
|
|
627
627
|
ref.position_in_rhs = i - 1
|
628
628
|
next if ref.type == :at
|
629
629
|
# $$, $n, @$, @n can be used in any actions
|
630
|
-
number = ref.number
|
631
630
|
|
632
|
-
if
|
631
|
+
if ref.value == "$"
|
633
632
|
# TODO: Should be postponed after middle actions are extracted?
|
634
633
|
ref.referring_symbol = lhs
|
635
|
-
|
636
|
-
raise "Can not refer following component. #{
|
637
|
-
rhs1[
|
638
|
-
ref.referring_symbol = rhs1[
|
634
|
+
elsif ref.value.is_a?(Integer)
|
635
|
+
raise "Can not refer following component. #{ref.value} >= #{i}. #{token}" if ref.value >= i
|
636
|
+
rhs1[ref.value - 1].referred = true
|
637
|
+
ref.referring_symbol = rhs1[ref.value - 1]
|
638
|
+
elsif ref.value.is_a?(String)
|
639
|
+
target_tokens = ([lhs] + rhs1 + [code]).compact.first(i)
|
640
|
+
referring_symbol_candidate = target_tokens.filter {|token| token.referred_by?(ref.value) }
|
641
|
+
raise "Referring symbol `#{ref.value}` is duplicated. #{token}" if referring_symbol_candidate.size >= 2
|
642
|
+
raise "Referring symbol `#{ref.value}` is not found. #{token}" if referring_symbol_candidate.count == 0
|
643
|
+
|
644
|
+
referring_symbol = referring_symbol_candidate.first
|
645
|
+
referring_symbol.referred = true
|
646
|
+
ref.referring_symbol = referring_symbol
|
639
647
|
end
|
640
648
|
end
|
641
649
|
end
|
@@ -716,7 +724,7 @@ module Lrama
|
|
716
724
|
# If id is Token::Char, it uses ASCII code
|
717
725
|
if sym.term? && sym.token_id.nil?
|
718
726
|
if sym.id.type == Token::Char
|
719
|
-
#
|
727
|
+
# Ignore ' on the both sides
|
720
728
|
case sym.id.s_value[1..-2]
|
721
729
|
when "\\b"
|
722
730
|
sym.token_id = 8
|
@@ -844,7 +852,7 @@ module Lrama
|
|
844
852
|
|
845
853
|
return if invalid.empty?
|
846
854
|
|
847
|
-
raise "Symbol number is
|
855
|
+
raise "Symbol number is duplicated. #{invalid}"
|
848
856
|
end
|
849
857
|
end
|
850
858
|
end
|
data/lib/lrama/lexer.rb
CHANGED
@@ -7,7 +7,7 @@ module Lrama
|
|
7
7
|
include Lrama::Report::Duration
|
8
8
|
|
9
9
|
# s_value is semantic value
|
10
|
-
Token = Struct.new(:type, :s_value, keyword_init: true) do
|
10
|
+
Token = Struct.new(:type, :s_value, :alias, keyword_init: true) do
|
11
11
|
Type = Struct.new(:id, :name, keyword_init: true)
|
12
12
|
|
13
13
|
attr_accessor :line, :column, :referred
|
@@ -18,6 +18,31 @@ module Lrama
|
|
18
18
|
"#{super} line: #{line}, column: #{column}"
|
19
19
|
end
|
20
20
|
|
21
|
+
def referred_by?(string)
|
22
|
+
[self.s_value, self.alias].include?(string)
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other)
|
26
|
+
self.class == other.class && self.type == other.type && self.s_value == other.s_value
|
27
|
+
end
|
28
|
+
|
29
|
+
def numberize_references(lhs, rhs)
|
30
|
+
self.references.map! {|ref|
|
31
|
+
ref_name = ref[1]
|
32
|
+
if ref_name.is_a?(String) && ref_name != '$'
|
33
|
+
value =
|
34
|
+
if lhs.referred_by?(ref_name)
|
35
|
+
'$'
|
36
|
+
else
|
37
|
+
rhs.find_index {|token| token.referred_by?(ref_name) } + 1
|
38
|
+
end
|
39
|
+
[ref[0], value, ref[2], ref[3], ref[4]]
|
40
|
+
else
|
41
|
+
ref
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
21
46
|
@i = 0
|
22
47
|
@types = []
|
23
48
|
|
@@ -47,6 +72,7 @@ module Lrama
|
|
47
72
|
define_type(:Number) # 0
|
48
73
|
define_type(:Ident_Colon) # k_if:, k_if : (spaces can be there)
|
49
74
|
define_type(:Ident) # api.pure, tNUMBER
|
75
|
+
define_type(:Named_Ref) # [foo]
|
50
76
|
define_type(:Semicolon) # ;
|
51
77
|
define_type(:Bar) # |
|
52
78
|
define_type(:String) # "str"
|
@@ -166,10 +192,15 @@ module Lrama
|
|
166
192
|
tokens << create_token(Token::Number, Integer(ss[0]), line, ss.pos - column)
|
167
193
|
when ss.scan(/(<[a-zA-Z0-9_]+>)/)
|
168
194
|
tokens << create_token(Token::Tag, ss[0], line, ss.pos - column)
|
195
|
+
when ss.scan(/([a-zA-Z_.][-a-zA-Z0-9_.]*)\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]\s*:/)
|
196
|
+
tokens << create_token(Token::Ident_Colon, ss[1], line, ss.pos - column)
|
197
|
+
tokens << create_token(Token::Named_Ref, ss[2], line, ss.pos - column)
|
169
198
|
when ss.scan(/([a-zA-Z_.][-a-zA-Z0-9_.]*)\s*:/)
|
170
199
|
tokens << create_token(Token::Ident_Colon, ss[1], line, ss.pos - column)
|
171
200
|
when ss.scan(/([a-zA-Z_.][-a-zA-Z0-9_.]*)/)
|
172
201
|
tokens << create_token(Token::Ident, ss[0], line, ss.pos - column)
|
202
|
+
when ss.scan(/\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/)
|
203
|
+
tokens << create_token(Token::Named_Ref, ss[1], line, ss.pos - column)
|
173
204
|
when ss.scan(/%expect/)
|
174
205
|
tokens << create_token(Token::P_expect, ss[0], line, ss.pos - column)
|
175
206
|
when ss.scan(/%define/)
|
@@ -206,6 +237,8 @@ module Lrama
|
|
206
237
|
when ss.scan(/\/\*/)
|
207
238
|
# TODO: Need to keep comment?
|
208
239
|
line = lex_comment(ss, line, lines, "")
|
240
|
+
when ss.scan(/\/\//)
|
241
|
+
line = lex_line_comment(ss, line, "")
|
209
242
|
when ss.scan(/'(.)'/)
|
210
243
|
tokens << create_token(Token::Char, ss[0], line, ss.pos - column)
|
211
244
|
when ss.scan(/'\\(.)'/) # '\\', '\t'
|
@@ -218,7 +251,7 @@ module Lrama
|
|
218
251
|
l = line - lines.first[1]
|
219
252
|
split = ss.string.split("\n")
|
220
253
|
col = ss.pos - split[0...l].join("\n").length
|
221
|
-
raise "Parse error (
|
254
|
+
raise "Parse error (unknown token): #{split[l]} \"#{ss.string[ss.pos]}\" (#{line}: #{col})"
|
222
255
|
end
|
223
256
|
end
|
224
257
|
end
|
@@ -255,6 +288,9 @@ module Lrama
|
|
255
288
|
when ss.scan(/\$(<[a-zA-Z0-9_]+>)?(\d+)/) # $1, $2, $<long>1
|
256
289
|
tag = ss[1] ? create_token(Token::Tag, ss[1], line, str.length) : nil
|
257
290
|
references << [:dollar, Integer(ss[2]), tag, str.length, str.length + ss[0].length - 1]
|
291
|
+
when ss.scan(/\$(<[a-zA-Z0-9_]+>)?([a-zA-Z_.][-a-zA-Z0-9_.]*)/) # $foo, $expr, $<long>program
|
292
|
+
tag = ss[1] ? create_token(Token::Tag, ss[1], line, str.length) : nil
|
293
|
+
references << [:dollar, ss[2], tag, str.length, str.length + ss[0].length - 1]
|
258
294
|
when ss.scan(/@\$/) # @$
|
259
295
|
references << [:at, "$", nil, str.length, str.length + ss[0].length - 1]
|
260
296
|
when ss.scan(/@(\d)+/) # @1
|
@@ -276,6 +312,9 @@ module Lrama
|
|
276
312
|
when ss.scan(/\/\*/)
|
277
313
|
str << ss[0]
|
278
314
|
line = lex_comment(ss, line, lines, str)
|
315
|
+
when ss.scan(/\/\//)
|
316
|
+
str << ss[0]
|
317
|
+
line = lex_line_comment(ss, line, str)
|
279
318
|
else
|
280
319
|
# noop, just consume char
|
281
320
|
str << ss.getch
|
@@ -314,8 +353,6 @@ module Lrama
|
|
314
353
|
raise "Parse error (quote mismatch): #{ss.string.split("\n")[l]} \"#{ss.string[ss.pos]}\" (#{line}: #{ss.pos})"
|
315
354
|
end
|
316
355
|
|
317
|
-
# TODO: Need to handle // style comment
|
318
|
-
#
|
319
356
|
# /* */ style comment
|
320
357
|
def lex_comment(ss, line, lines, str)
|
321
358
|
while !ss.eos? do
|
@@ -337,6 +374,23 @@ module Lrama
|
|
337
374
|
raise "Parse error (comment mismatch): #{ss.string.split("\n")[l]} \"#{ss.string[ss.pos]}\" (#{line}: #{ss.pos})"
|
338
375
|
end
|
339
376
|
|
377
|
+
# // style comment
|
378
|
+
def lex_line_comment(ss, line, str)
|
379
|
+
while !ss.eos? do
|
380
|
+
case
|
381
|
+
when ss.scan(/\n/)
|
382
|
+
return line + 1
|
383
|
+
else
|
384
|
+
str << ss.getch
|
385
|
+
next
|
386
|
+
end
|
387
|
+
|
388
|
+
str << ss[0]
|
389
|
+
end
|
390
|
+
|
391
|
+
line # Reach to end of input
|
392
|
+
end
|
393
|
+
|
340
394
|
def lex_grammar_rules_tokens
|
341
395
|
lex_common(@grammar_rules, @grammar_rules_tokens)
|
342
396
|
end
|
@@ -0,0 +1,59 @@
|
|
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 previous_token
|
18
|
+
@tokens[@index - 1]
|
19
|
+
end
|
20
|
+
|
21
|
+
def next
|
22
|
+
token = current_token
|
23
|
+
@index += 1
|
24
|
+
return token
|
25
|
+
end
|
26
|
+
|
27
|
+
def consume(*token_types)
|
28
|
+
if token_types.include?(current_type)
|
29
|
+
token = current_token
|
30
|
+
self.next
|
31
|
+
return token
|
32
|
+
end
|
33
|
+
|
34
|
+
return nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def consume!(*token_types)
|
38
|
+
consume(*token_types) || (raise "#{token_types} is expected but #{current_type}. #{current_token}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def consume_multi(*token_types)
|
42
|
+
a = []
|
43
|
+
|
44
|
+
while token_types.include?(current_type)
|
45
|
+
a << current_token
|
46
|
+
self.next
|
47
|
+
end
|
48
|
+
|
49
|
+
raise "No token is consumed. #{token_types}" if a.empty?
|
50
|
+
|
51
|
+
return a
|
52
|
+
end
|
53
|
+
|
54
|
+
def eots?
|
55
|
+
current_token.nil?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
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
|
@@ -226,8 +175,11 @@ module Lrama
|
|
226
175
|
# LHS
|
227
176
|
lhs = ts.consume!(T::Ident_Colon) # class:
|
228
177
|
lhs.type = T::Ident
|
178
|
+
if named_ref = ts.consume(T::Named_Ref)
|
179
|
+
lhs.alias = named_ref.s_value
|
180
|
+
end
|
229
181
|
|
230
|
-
rhs = parse_grammar_rule_rhs(ts, grammar)
|
182
|
+
rhs = parse_grammar_rule_rhs(ts, grammar, lhs)
|
231
183
|
|
232
184
|
grammar.add_rule(lhs: lhs, rhs: rhs, lineno: rhs.first ? rhs.first.line : lhs.line)
|
233
185
|
|
@@ -237,7 +189,7 @@ module Lrama
|
|
237
189
|
# |
|
238
190
|
bar_lineno = ts.current_token.line
|
239
191
|
ts.next
|
240
|
-
rhs = parse_grammar_rule_rhs(ts, grammar)
|
192
|
+
rhs = parse_grammar_rule_rhs(ts, grammar, lhs)
|
241
193
|
grammar.add_rule(lhs: lhs, rhs: rhs, lineno: rhs.first ? rhs.first.line : bar_lineno)
|
242
194
|
when T::Semicolon
|
243
195
|
# ;
|
@@ -256,13 +208,13 @@ module Lrama
|
|
256
208
|
end
|
257
209
|
end
|
258
210
|
|
259
|
-
def parse_grammar_rule_rhs(ts, grammar)
|
211
|
+
def parse_grammar_rule_rhs(ts, grammar, lhs)
|
260
212
|
a = []
|
261
213
|
prec_seen = false
|
262
214
|
code_after_prec = false
|
263
215
|
|
264
216
|
while true do
|
265
|
-
# TODO:
|
217
|
+
# TODO: String can be here
|
266
218
|
case ts.current_type
|
267
219
|
when T::Ident
|
268
220
|
# keyword_class
|
@@ -295,9 +247,13 @@ module Lrama
|
|
295
247
|
end
|
296
248
|
|
297
249
|
code = ts.current_token
|
250
|
+
code.numberize_references(lhs, a)
|
298
251
|
grammar.build_references(code)
|
299
252
|
a << code
|
300
253
|
ts.next
|
254
|
+
when T::Named_Ref
|
255
|
+
ts.previous_token.alias = ts.current_token.s_value
|
256
|
+
ts.next
|
301
257
|
when T::Bar
|
302
258
|
# |
|
303
259
|
break
|
@@ -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
|