lrama 0.1.0 → 0.2.0

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.
@@ -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.0".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.0
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