lrama 0.5.0 → 0.5.1
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 +15 -1
- data/Gemfile +2 -0
- data/README.md +29 -2
- data/Steepfile +7 -0
- data/lib/lrama/grammar.rb +5 -5
- data/lib/lrama/lexer.rb +23 -3
- data/lib/lrama/parser/token_scanner.rb +55 -0
- data/lib/lrama/parser.rb +1 -52
- 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/sample/calc.output +263 -0
- data/sample/calc.y +98 -0
- data/sig/lrama/bitmap.rbs +7 -0
- metadata +12 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20a0927bedeb14281abb8a5c59daf643fdfe556b5899cbe4872878b022804da2
|
4
|
+
data.tar.gz: 731fdd0201266db9724a2ebcc1162427a2cfaa9d0d94543e1298b3f2683eb14f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a6e7c0ef4e7266dae41dba15e3a5d7609cb7171b82e0dbd5b75595e8e3cf8a4a402c5b710cc0f714c54357ef9fc20fd0935e55b8c529c99cd3c09ea173efb87
|
7
|
+
data.tar.gz: fa867c9be43384c7f282bf22ecd2b0939cd0dca263a51914871dba42f409ddc01fb00c34d8696288c3668177fdc450ff1bc9fb5e90fdf053e2d56a5bf31bbe04
|
data/.github/workflows/test.yaml
CHANGED
@@ -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
|
87
|
+
- run: make
|
74
88
|
- run: make test-all
|
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 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
@@ -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. #{
|
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. #{
|
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. #{
|
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
|
-
#
|
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
|
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 (
|
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
|
data/lib/lrama/state.rb
ADDED
@@ -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,
|
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 [
|
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
|
-
|
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
|
-
|
303
|
+
states_created = {}
|
536
304
|
|
537
|
-
state, _ = create_state(symbols.first, [Item.new(rule: @grammar.rules.first, position: 0)],
|
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,
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
data/lib/lrama.rb
CHANGED
data/sample/calc.output
ADDED
@@ -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
|
+
}
|
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.
|
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-
|
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.
|
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
|