lrama 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/test.yaml +19 -3
- data/Gemfile +2 -0
- data/README.md +29 -2
- data/Steepfile +7 -0
- data/lib/lrama/command.rb +7 -3
- data/lib/lrama/grammar.rb +5 -5
- data/lib/lrama/lexer.rb +23 -3
- data/lib/lrama/output.rb +1 -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
- data/template/bison/yacc.c +1 -1
- data/template/bison/yacc.h +1 -1
- metadata +12 -3
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
|
+
}
|
data/template/bison/yacc.c
CHANGED