lrama 0.5.0 → 0.5.2
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 +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
|