lrama 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,310 @@
1
+ module Lrama
2
+ class StatesReporter
3
+ include Lrama::Report::Duration
4
+
5
+ def initialize(states)
6
+ @states = states
7
+ end
8
+
9
+ def report(io, **options)
10
+ report_duration(:report) do
11
+ _report(io, **options)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def _report(io, grammar: false, states: false, itemsets: false, lookaheads: false, solved: false, verbose: false)
18
+ # TODO: Unused terms
19
+ # TODO: Unused rules
20
+
21
+ report_conflicts(io)
22
+ report_grammar(io) if grammar
23
+ report_states(io, itemsets, lookaheads, solved, verbose)
24
+ end
25
+
26
+ def report_conflicts(io)
27
+ has_conflict = false
28
+
29
+ @states.states.each do |state|
30
+ messages = []
31
+ cs = state.conflicts.group_by(&:type)
32
+ if cs[:shift_reduce]
33
+ messages << "#{cs[:shift_reduce].count} shift/reduce"
34
+ end
35
+
36
+ if cs[:reduce_reduce]
37
+ messages << "#{cs[:reduce_reduce].count} reduce/reduce"
38
+ end
39
+
40
+ if !messages.empty?
41
+ has_conflict = true
42
+ io << "State #{state.id} conflicts: #{messages.join(', ')}\n"
43
+ end
44
+ end
45
+
46
+ if has_conflict
47
+ io << "\n\n"
48
+ end
49
+ end
50
+
51
+ def report_grammar(io)
52
+ io << "Grammar\n"
53
+ last_lhs = nil
54
+
55
+ @states.rules.each do |rule|
56
+ if rule.rhs.empty?
57
+ r = "ε"
58
+ else
59
+ r = rule.rhs.map(&:display_name).join(" ")
60
+ end
61
+
62
+ if rule.lhs == last_lhs
63
+ io << sprintf("%5d %s| %s\n", rule.id, " " * rule.lhs.display_name.length, r)
64
+ else
65
+ io << "\n"
66
+ io << sprintf("%5d %s: %s\n", rule.id, rule.lhs.display_name, r)
67
+ end
68
+
69
+ last_lhs = rule.lhs
70
+ end
71
+ io << "\n\n"
72
+ end
73
+
74
+ def report_states(io, itemsets, lookaheads, solved, verbose)
75
+ @states.states.each do |state|
76
+ # Report State
77
+ io << "State #{state.id}\n\n"
78
+
79
+ # Report item
80
+ last_lhs = nil
81
+ list = itemsets ? state.items : state.kernels
82
+ list.sort_by {|i| [i.rule_id, i.position] }.each do |item|
83
+ rule = item.rule
84
+ position = item.position
85
+ if rule.rhs.empty?
86
+ r = "ε •"
87
+ else
88
+ r = rule.rhs.map(&:display_name).insert(position, "•").join(" ")
89
+ end
90
+ if rule.lhs == last_lhs
91
+ l = " " * rule.lhs.id.s_value.length + "|"
92
+ else
93
+ l = rule.lhs.id.s_value + ":"
94
+ end
95
+ la = ""
96
+ if lookaheads && item.end_of_rule?
97
+ reduce = state.find_reduce_by_item!(item)
98
+ look_ahead = reduce.selected_look_ahead
99
+ if !look_ahead.empty?
100
+ la = " [#{look_ahead.map(&:display_name).join(", ")}]"
101
+ end
102
+ end
103
+ last_lhs = rule.lhs
104
+
105
+ io << sprintf("%5i %s %s%s\n", rule.id, l, r, la)
106
+ end
107
+ io << "\n"
108
+
109
+
110
+ # Report shifts
111
+ tmp = state.term_transitions.select do |shift, _|
112
+ !shift.not_selected
113
+ end.map do |shift, next_state|
114
+ [shift.next_sym, next_state.id]
115
+ end
116
+ max_len = tmp.map(&:first).map(&:display_name).map(&:length).max
117
+ tmp.each do |term, state_id|
118
+ io << " #{term.display_name.ljust(max_len)} shift, and go to state #{state_id}\n"
119
+ end
120
+ io << "\n" if !tmp.empty?
121
+
122
+
123
+ # Report error caused by %nonassoc
124
+ nl = false
125
+ tmp = state.resolved_conflicts.select do |resolved|
126
+ resolved.which == :error
127
+ end.map do |error|
128
+ error.symbol.display_name
129
+ end
130
+ max_len = tmp.map(&:length).max
131
+ tmp.each do |name|
132
+ nl = true
133
+ io << " #{name.ljust(max_len)} error (nonassociative)\n"
134
+ end
135
+ io << "\n" if !tmp.empty?
136
+
137
+
138
+ # Report reduces
139
+ nl = false
140
+ max_len = state.non_default_reduces.flat_map(&:look_ahead).compact.map(&:display_name).map(&:length).max || 0
141
+ max_len = [max_len, "$default".length].max if state.default_reduction_rule
142
+ ary = []
143
+
144
+ state.non_default_reduces.each do |reduce|
145
+ reduce.look_ahead.each do |term|
146
+ ary << [term, reduce]
147
+ end
148
+ end
149
+
150
+ ary.sort_by do |term, reduce|
151
+ term.number
152
+ end.each do |term, reduce|
153
+ rule = reduce.item.rule
154
+ io << " #{term.display_name.ljust(max_len)} reduce using rule #{rule.id} (#{rule.lhs.display_name})\n"
155
+ nl = true
156
+ end
157
+
158
+ if r = state.default_reduction_rule
159
+ nl = true
160
+ s = "$default".ljust(max_len)
161
+
162
+ if r.initial_rule?
163
+ io << " #{s} accept\n"
164
+ else
165
+ io << " #{s} reduce using rule #{r.id} (#{r.lhs.display_name})\n"
166
+ end
167
+ end
168
+ io << "\n" if nl
169
+
170
+
171
+ # Report nonterminal transitions
172
+ tmp = []
173
+ max_len = 0
174
+ state.nterm_transitions.each do |shift, next_state|
175
+ nterm = shift.next_sym
176
+ tmp << [nterm, next_state.id]
177
+ max_len = [max_len, nterm.id.s_value.length].max
178
+ end
179
+ tmp.uniq!
180
+ tmp.sort_by! do |nterm, state_id|
181
+ nterm.number
182
+ end
183
+ tmp.each do |nterm, state_id|
184
+ io << " #{nterm.id.s_value.ljust(max_len)} go to state #{state_id}\n"
185
+ end
186
+ io << "\n" if !tmp.empty?
187
+
188
+
189
+ if solved
190
+ # Report conflict resolutions
191
+ state.resolved_conflicts.each do |resolved|
192
+ io << " #{resolved.report_message}\n"
193
+ end
194
+ io << "\n" if !state.resolved_conflicts.empty?
195
+ end
196
+
197
+
198
+ if verbose
199
+ # Report direct_read_sets
200
+ io << " [Direct Read sets]\n"
201
+ direct_read_sets = @states.direct_read_sets
202
+ @states.nterms.each do |nterm|
203
+ terms = direct_read_sets[[state.id, nterm.token_id]]
204
+ next if !terms
205
+ next if terms.empty?
206
+
207
+ str = terms.map {|sym| sym.id.s_value }.join(", ")
208
+ io << " read #{nterm.id.s_value} shift #{str}\n"
209
+ end
210
+ io << "\n"
211
+
212
+
213
+ # Reprot reads_relation
214
+ io << " [Reads Relation]\n"
215
+ @states.nterms.each do |nterm|
216
+ a = @states.reads_relation[[state.id, nterm.token_id]]
217
+ next if !a
218
+
219
+ a.each do |state_id2, nterm_id2|
220
+ n = @states.nterms.find {|n| n.token_id == nterm_id2 }
221
+ io << " (State #{state_id2}, #{n.id.s_value})\n"
222
+ end
223
+ end
224
+ io << "\n"
225
+
226
+
227
+ # Reprot read_sets
228
+ io << " [Read sets]\n"
229
+ read_sets = @states.read_sets
230
+ @states.nterms.each do |nterm|
231
+ terms = read_sets[[state.id, nterm.token_id]]
232
+ next if !terms
233
+ next if terms.empty?
234
+
235
+ terms.each do |sym|
236
+ io << " #{sym.id.s_value}\n"
237
+ end
238
+ end
239
+ io << "\n"
240
+
241
+
242
+ # Reprot includes_relation
243
+ io << " [Includes Relation]\n"
244
+ @states.nterms.each do |nterm|
245
+ a = @states.includes_relation[[state.id, nterm.token_id]]
246
+ next if !a
247
+
248
+ a.each do |state_id2, nterm_id2|
249
+ n = @states.nterms.find {|n| n.token_id == nterm_id2 }
250
+ io << " (State #{state.id}, #{nterm.id.s_value}) -> (State #{state_id2}, #{n.id.s_value})\n"
251
+ end
252
+ end
253
+ io << "\n"
254
+
255
+
256
+ # Report lookback_relation
257
+ io << " [Lookback Relation]\n"
258
+ @states.rules.each do |rule|
259
+ a = @states.lookback_relation[[state.id, rule.id]]
260
+ next if !a
261
+
262
+ a.each do |state_id2, nterm_id2|
263
+ n = @states.nterms.find {|n| n.token_id == nterm_id2 }
264
+ io << " (Rule: #{rule.to_s}) -> (State #{state_id2}, #{n.id.s_value})\n"
265
+ end
266
+ end
267
+ io << "\n"
268
+
269
+
270
+ # Reprot follow_sets
271
+ io << " [Follow sets]\n"
272
+ follow_sets = @states.follow_sets
273
+ @states.nterms.each do |nterm|
274
+ terms = follow_sets[[state.id, nterm.token_id]]
275
+
276
+ next if !terms
277
+
278
+ terms.each do |sym|
279
+ io << " #{nterm.id.s_value} -> #{sym.id.s_value}\n"
280
+ end
281
+ end
282
+ io << "\n"
283
+
284
+
285
+ # Report LA
286
+ io << " [Look-Ahead Sets]\n"
287
+ tmp = []
288
+ max_len = 0
289
+ @states.rules.each do |rule|
290
+ syms = @states.la[[state.id, rule.id]]
291
+ next if !syms
292
+
293
+ tmp << [rule, syms]
294
+ max_len = ([max_len] + syms.map {|s| s.id.s_value.length }).max
295
+ end
296
+ tmp.each do |rule, syms|
297
+ syms.each do |sym|
298
+ io << " #{sym.id.s_value.ljust(max_len)} reduce using rule #{rule.id} (#{rule.lhs.id.s_value})\n"
299
+ end
300
+ end
301
+ io << "\n" if !tmp.empty?
302
+ end
303
+
304
+
305
+ # End of Report State
306
+ io << "\n"
307
+ end
308
+ end
309
+ end
310
+ end
data/lib/lrama/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Lrama
2
- VERSION = "0.1.0".freeze
2
+ VERSION = "0.2.1".freeze
3
3
  end
@@ -0,0 +1,25 @@
1
+ module Lrama
2
+ class Warning
3
+ attr_reader :errors, :warns
4
+
5
+ def initialize(out = STDERR)
6
+ @out = out
7
+ @errors = []
8
+ @warns = []
9
+ end
10
+
11
+ def error(message)
12
+ @out << message << "\n"
13
+ @errors << message
14
+ end
15
+
16
+ def warn(message)
17
+ @out << message << "\n"
18
+ @warns << message
19
+ end
20
+
21
+ def has_error?
22
+ !@errors.empty?
23
+ end
24
+ end
25
+ end
data/lib/lrama.rb CHANGED
@@ -1,9 +1,13 @@
1
+ require "lrama/bitmap"
1
2
  require "lrama/command"
2
3
  require "lrama/context"
4
+ require "lrama/digraph"
3
5
  require "lrama/grammar"
4
6
  require "lrama/lexer"
5
7
  require "lrama/output"
6
8
  require "lrama/parser"
7
9
  require "lrama/report"
8
10
  require "lrama/states"
11
+ require "lrama/states_reporter"
9
12
  require "lrama/version"
13
+ require "lrama/warning"
data/sample/parse.y ADDED
@@ -0,0 +1,58 @@
1
+ /*
2
+ * This is comment for this file.
3
+ */
4
+
5
+ %{
6
+ // Prologue
7
+
8
+ #include "y.tab.h"
9
+
10
+ static enum yytokentype yylex(YYSTYPE *lval, YYLTYPE *yylloc);
11
+ static void yyerror(YYLTYPE *yylloc, const char *msg);
12
+
13
+ %}
14
+
15
+ %expect 0
16
+ %define api.pure
17
+ %define parse.error verbose
18
+
19
+ %union {
20
+ int i;
21
+ }
22
+
23
+ %token <i> number
24
+
25
+ %%
26
+
27
+ program : expr
28
+ ;
29
+
30
+ expr : term '+' expr
31
+ | term
32
+ ;
33
+
34
+ term : factor '*' term
35
+ | factor
36
+ ;
37
+
38
+ factor : number
39
+ ;
40
+
41
+ %%
42
+
43
+ // Epilogue
44
+
45
+ static enum yytokentype
46
+ yylex(YYSTYPE *lval, YYLTYPE *yylloc)
47
+ {
48
+ return 0;
49
+ }
50
+
51
+ static void yyerror(YYLTYPE *yylloc, const char *msg)
52
+ {
53
+ (void) msg;
54
+ }
55
+
56
+ int main(int argc, char *argv[])
57
+ {
58
+ }
@@ -668,7 +668,7 @@ enum { YYENOMEM = -2 };
668
668
  } \
669
669
  else \
670
670
  { \
671
- yyerror (&yylloc, p, YY_("syntax error: cannot back up")); \
671
+ yyerror (<%= output.yyerror_args %>, YY_("syntax error: cannot back up")); \
672
672
  YYERROR; \
673
673
  } \
674
674
  while (0)
@@ -802,9 +802,7 @@ yy_symbol_value_print (FILE *yyo,
802
802
  yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp<%= output.user_formals %>)
803
803
  {
804
804
  FILE *yyoutput = yyo;
805
- YY_USE (yyoutput);
806
- YY_USE (yylocationp);
807
- YY_USE (p);
805
+ <%= output.parse_param_use("yyoutput", "yylocationp") %>
808
806
  if (!yyvaluep)
809
807
  return;
810
808
  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
@@ -1199,9 +1197,7 @@ static void
1199
1197
  yydestruct (const char *yymsg,
1200
1198
  yysymbol_kind_t yykind, YYSTYPE *yyvaluep, YYLTYPE *yylocationp<%= output.user_formals %>)
1201
1199
  {
1202
- YY_USE (yyvaluep);
1203
- YY_USE (yylocationp);
1204
- YY_USE (p);
1200
+ <%= output.parse_param_use("yyvaluep", "yylocationp") %>
1205
1201
  if (!yymsg)
1206
1202
  yymsg = "Deleting";
1207
1203
  YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp);
@@ -1301,10 +1297,7 @@ YYLTYPE yylloc = yyloc_default;
1301
1297
 
1302
1298
 
1303
1299
  <%# b4_user_initial_action -%>
1304
- /* User initialization code. */
1305
- #line <%= output.grammar.initial_action.line %> "<%= output.grammar_file_path %>"
1306
- <%= output.grammar.initial_action.translated_code %>
1307
-
1300
+ <%= output.user_initial_action("/* User initialization code. */") %>
1308
1301
  #line [@oline@] [@ofile@]
1309
1302
 
1310
1303
  yylsp[0] = yylloc;
@@ -1424,7 +1417,7 @@ yybackup:
1424
1417
  if (yychar == YYEMPTY)
1425
1418
  {
1426
1419
  YYDPRINTF ((stderr, "Reading a token\n"));
1427
- yychar = yylex (&yylval, &yylloc, p);
1420
+ yychar = yylex <%= output.yylex_formals %>;
1428
1421
  }
1429
1422
 
1430
1423
  if (yychar <= <%= output.eof_symbol.id.s_value %>)
@@ -1590,7 +1583,7 @@ yyerrlab:
1590
1583
  yysyntax_error_status = YYENOMEM;
1591
1584
  }
1592
1585
  }
1593
- yyerror (&yylloc, p, yymsgp);
1586
+ yyerror (<%= output.yyerror_args %>, yymsgp);
1594
1587
  if (yysyntax_error_status == YYENOMEM)
1595
1588
  YYNOMEM;
1596
1589
  }
@@ -1611,7 +1604,7 @@ yyerrlab:
1611
1604
  else
1612
1605
  {
1613
1606
  yydestruct ("Error: discarding",
1614
- yytoken, &yylval, &yylloc, p);
1607
+ yytoken, &yylval, &yylloc<%= output.user_args %>);
1615
1608
  yychar = YYEMPTY;
1616
1609
  }
1617
1610
  }
@@ -1667,7 +1660,7 @@ yyerrlab1:
1667
1660
 
1668
1661
  yyerror_range[1] = *yylsp;
1669
1662
  yydestruct ("Error: popping",
1670
- YY_ACCESSING_SYMBOL (yystate), yyvsp, yylsp, p);
1663
+ YY_ACCESSING_SYMBOL (yystate), yyvsp, yylsp<%= output.user_args %>);
1671
1664
  YYPOPSTACK (1);
1672
1665
  yystate = *yyssp;
1673
1666
  YY_STACK_PRINT (yyss, yyssp);
@@ -1708,7 +1701,7 @@ yyabortlab:
1708
1701
  | yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. |
1709
1702
  `-----------------------------------------------------------*/
1710
1703
  yyexhaustedlab:
1711
- yyerror (&yylloc, p, YY_("memory exhausted"));
1704
+ yyerror (<%= output.yyerror_args %>, YY_("memory exhausted"));
1712
1705
  yyresult = 2;
1713
1706
  goto yyreturnlab;
1714
1707
 
@@ -1723,7 +1716,7 @@ yyreturnlab:
1723
1716
  user semantic actions for why this is necessary. */
1724
1717
  yytoken = YYTRANSLATE (yychar);
1725
1718
  yydestruct ("Cleanup: discarding lookahead",
1726
- yytoken, &yylval, &yylloc, p);
1719
+ yytoken, &yylval, &yylloc<%= output.user_args %>);
1727
1720
  }
1728
1721
  /* Do not reclaim the symbols of the rule whose action triggered
1729
1722
  this YYABORT or YYACCEPT. */
@@ -1732,7 +1725,7 @@ yyreturnlab:
1732
1725
  while (yyssp != yyss)
1733
1726
  {
1734
1727
  yydestruct ("Cleanup: popping",
1735
- YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, yylsp, p);
1728
+ YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, yylsp<%= output.user_args %>);
1736
1729
  YYPOPSTACK (1);
1737
1730
  }
1738
1731
  #ifndef yyoverflow
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.1.0
4
+ version: 0.2.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-01-31 00:00:00.000000000 Z
11
+ date: 2023-05-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: LALR (1) parser generator written by Ruby
14
14
  email:
@@ -29,16 +29,21 @@ files:
29
29
  - doc/TODO.md
30
30
  - exe/lrama
31
31
  - lib/lrama.rb
32
+ - lib/lrama/bitmap.rb
32
33
  - lib/lrama/command.rb
33
34
  - lib/lrama/context.rb
35
+ - lib/lrama/digraph.rb
34
36
  - lib/lrama/grammar.rb
35
37
  - lib/lrama/lexer.rb
36
38
  - lib/lrama/output.rb
37
39
  - lib/lrama/parser.rb
38
40
  - lib/lrama/report.rb
39
41
  - lib/lrama/states.rb
42
+ - lib/lrama/states_reporter.rb
40
43
  - lib/lrama/version.rb
44
+ - lib/lrama/warning.rb
41
45
  - lrama.gemspec
46
+ - sample/parse.y
42
47
  - template/bison/yacc.c
43
48
  - template/bison/yacc.h
44
49
  homepage: https://github.com/yui-knk/lrama