Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cppgenerator.cpp
Go to the documentation of this file.
1// REUSE-IgnoreStart
2// Copyright (C) 2016 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
4// REUSE-IgnoreEnd
5
6#include "cppgenerator.h"
7
8#include "lalr.h"
9#include "recognizer.h"
10
11#include <QtCore/qbitarray.h>
12#include <QtCore/qtextstream.h>
13#include <QtCore/qfile.h>
14#include <QtCore/qmap.h>
15#include <QtCore/private/qconfig_p.h>
16
17#include <iterator>
18
19using namespace Qt::StringLiterals;
20
21namespace {
22
23void generateSeparator(int i, QTextStream &out)
24{
25 if (!(i % 10)) {
26 if (i)
27 out << ",";
28 out << Qt::endl << " ";
29 } else {
30 out << ", ";
31 }
32}
33
34void generateList(const QList<int> &list, QTextStream &out)
35{
36 for (int i = 0; i < list.size(); ++i) {
37 generateSeparator(i, out);
38
39 out << list[i];
40 }
41}
42
43} // unnamed namespace
44
45
46// REUSE-IgnoreStart
47QString CppGenerator::copyrightHeader() const
48{
49 return
50 "// " QT_COPYRIGHT "\n"
51 "// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0\n"
52 "\n"_L1;
53}
54// REUSE-IgnoreEnd
55
56QString CppGenerator::privateCopyrightHeader() const
57{
58 return
59 "//\n"
60 "// W A R N I N G\n"
61 "// -------------\n"
62 "//\n"
63 "// This file is not part of the Qt API. It exists for the convenience\n"
64 "// of other Qt classes. This header file may change from version to\n"
65 "// version without notice, or even be removed.\n"
66 "//\n"
67 "// We mean it.\n"
68 "//\n"_L1;
69}
70
71static QString includeGuard(const QString &fileName)
72{
73 return fileName.toUpper().replace(u'.', u'_');
74}
75
76QString CppGenerator::startIncludeGuard(const QString &fileName)
77{
78 const QString normalized = includeGuard(fileName);
79
80 return QString::fromLatin1("#ifndef %1\n"
81 "#define %2\n").arg(normalized, normalized);
82}
83
84QString CppGenerator::endIncludeGuard(const QString &fileName)
85{
86 const QString normalized = includeGuard(fileName);
87
88 return QString::fromLatin1("#endif // %1\n").arg(normalized);
89}
90
92{
93 // action table...
94 state_count = static_cast<int>(aut.states.size());
95 terminal_count = static_cast<int>(grammar.terminals.size());
96 non_terminal_count = static_cast<int>(grammar.non_terminals.size());
97
98#define ACTION(i, j) table [(i) * terminal_count + (j)]
99#define GOTO(i, j) pgoto [(i) * non_terminal_count + (j)]
100
101 int *table = new int [state_count * terminal_count];
102 ::memset (table, 0, state_count * terminal_count * sizeof (int));
103
104 int *pgoto = new int [state_count * non_terminal_count];
105 ::memset (pgoto, 0, state_count * non_terminal_count * sizeof (int));
106
107 accept_state = -1;
108 int shift_reduce_conflict_count = 0;
109 int reduce_reduce_conflict_count = 0;
110
111 for (StatePointer state = aut.states.begin (); state != aut.states.end (); ++state)
112 {
113 int q = aut.id (state);
114
115 for (Bundle::iterator a = state->bundle.begin (); a != state->bundle.end (); ++a)
116 {
117 int symbol = aut.id (a.key ());
118 int r = aut.id (a.value ());
119
120 Q_ASSERT (r < state_count);
121
122 if (grammar.isNonTerminal (a.key ()))
123 {
124 Q_ASSERT(symbol >= terminal_count && symbol < static_cast<int>(grammar.names.size()));
125 GOTO (q, symbol - terminal_count) = r;
126 }
127
128 else
129 ACTION (q, symbol) = r;
130 }
131
132 for (ItemPointer item = state->closure.begin (); item != state->closure.end (); ++item)
133 {
134 if (item->dot != item->end_rhs ())
135 continue;
136
137 int r = aut.id (item->rule);
138
139 const NameSet lookaheads = aut.lookaheads.value (item);
140
141 if (item->rule == grammar.goal)
142 accept_state = q;
143
144 for (const Name &s : lookaheads)
145 {
146 int &u = ACTION (q, aut.id (s));
147
148 if (u == 0)
149 u = - r;
150
151 else if (u < 0)
152 {
153 if (verbose)
154 qout() << "*** Warning. Found a reduce/reduce conflict in state " << q << " on token ``" << s << "'' between rule "
155 << r << " and " << -u << Qt::endl;
156
157 ++reduce_reduce_conflict_count;
158
159 u = qMax (u, -r);
160
161 if (verbose)
162 qout() << "\tresolved using rule " << -u << Qt::endl;
163 }
164
165 else if (u > 0)
166 {
167 if (item->rule->prec != grammar.names.end() && grammar.token_info.contains (s))
168 {
169 Grammar::TokenInfo info_r = grammar.token_info.value (item->rule->prec);
170 Grammar::TokenInfo info_s = grammar.token_info.value (s);
171
172 if (info_r.prec > info_s.prec)
173 u = -r;
174 else if (info_r.prec == info_s.prec)
175 {
176 switch (info_r.assoc) {
177 case Grammar::Left:
178 u = -r;
179 break;
180 case Grammar::Right:
181 // shift... nothing to do
182 break;
183 case Grammar::NonAssoc:
184 u = 0;
185 break;
186 } // switch
187 }
188 }
189
190 else
191 {
192 ++shift_reduce_conflict_count;
193
194 if (verbose)
195 qout() << "*** Warning. Found a shift/reduce conflict in state " << q << " on token ``" << s << "'' with rule " << r << Qt::endl;
196 }
197 }
198 }
199 }
200 }
201
202 if (shift_reduce_conflict_count || reduce_reduce_conflict_count)
203 {
204 if (shift_reduce_conflict_count != grammar.expected_shift_reduce
205 || reduce_reduce_conflict_count != grammar.expected_reduce_reduce)
206 {
207 qerr() << "*** Conflicts: " << shift_reduce_conflict_count << " shift/reduce, " << reduce_reduce_conflict_count << " reduce/reduce" << Qt::endl;
208 if (warnings_are_errors)
209 {
210 qerr() << "qlalr: error: warning occurred, treating as error due to "
211 "--exit-on-warn." << Qt::endl;
212 exit(2);
213 }
214 }
215
216 if (verbose)
217 qout() << Qt::endl << "*** Conflicts: " << shift_reduce_conflict_count << " shift/reduce, " << reduce_reduce_conflict_count << " reduce/reduce" << Qt::endl
218 << Qt::endl;
219 }
220
221 QBitArray used_rules{static_cast<int>(grammar.rules.size())};
222
223 int q = 0;
224 for (StatePointer state = aut.states.begin (); state != aut.states.end (); ++state, ++q)
225 {
226 for (int j = 0; j < terminal_count; ++j)
227 {
228 int &u = ACTION (q, j);
229
230 if (u < 0)
231 used_rules.setBit (-u - 1);
232 }
233 }
234
235 auto rule = grammar.rules.begin();
236 for (int i = 0; i < used_rules.size(); ++i, ++rule)
237 {
238 if (! used_rules.testBit (i))
239 {
240 if (rule != grammar.goal)
241 {
242 qerr() << "*** Warning: Rule ``" << *rule << "'' is useless!" << Qt::endl;
243 if (warnings_are_errors)
244 {
245 qerr() << "qlalr: error: warning occurred, treating as error due to "
246 "--exit-on-warn." << Qt::endl;
247 exit(2);
248 }
249 }
250 }
251 }
252
253 q = 0;
254 for (StatePointer state = aut.states.begin (); state != aut.states.end (); ++state, ++q)
255 {
256 for (int j = 0; j < terminal_count; ++j)
257 {
258 int &u = ACTION (q, j);
259
260 if (u >= 0)
261 continue;
262
263 RulePointer rule = std::next(grammar.rules.begin(), - u - 1);
264
265 if (state->defaultReduce == rule)
266 u = 0;
267 }
268 }
269
270 // ... compress the goto table
271 defgoto.resize (non_terminal_count);
272 for (int j = 0; j < non_terminal_count; ++j)
273 {
274 count.fill (0, state_count);
275
276 int &mx = defgoto [j];
277
278 for (int i = 0; i < state_count; ++i)
279 {
280 int r = GOTO (i, j);
281
282 if (! r)
283 continue;
284
285 ++count [r];
286
287 if (count [r] > count [mx])
288 mx = r;
289 }
290 }
291
292 for (int i = 0; i < state_count; ++i)
293 {
294 for (int j = 0; j < non_terminal_count; ++j)
295 {
296 int &r = GOTO (i, j);
297
298 if (r == defgoto [j])
299 r = 0;
300 }
301 }
302
303 compressed_action (table, state_count, terminal_count);
304 compressed_goto (pgoto, state_count, non_terminal_count);
305
306 delete[] table;
307 table = nullptr;
308
309 delete[] pgoto;
310 pgoto = nullptr;
311
312#undef ACTION
313#undef GOTO
314
315 if (! grammar.merged_output.isEmpty())
316 {
317 QFile f(grammar.merged_output);
318 if (! f.open (QFile::WriteOnly))
319 {
320 fprintf (stderr, "*** cannot create %s\n", qPrintable(grammar.merged_output));
321 return;
322 }
323
324 QTextStream out (&f);
325
326 // copyright headers must come first, otherwise the headers tests will fail
327 if (copyright)
328 {
329 out << copyrightHeader()
330 << privateCopyrightHeader()
331 << Qt::endl;
332 }
333
334 out << "// This file was generated by qlalr - DO NOT EDIT!\n";
335
336 out << startIncludeGuard(grammar.merged_output) << Qt::endl;
337
338 if (copyright) {
339 out << "#if defined(ERROR)" << Qt::endl
340 << "# undef ERROR" << Qt::endl
341 << "#endif" << Qt::endl << Qt::endl;
342 }
343
344 generateDecl (out);
345 generateImpl (out);
346 out << p.decls();
347 out << p.impls();
348 out << Qt::endl;
349
350 out << endIncludeGuard(grammar.merged_output) << Qt::endl;
351
352 return;
353 }
354
355 // default behaviour
356 QString declFileName = grammar.table_name.toLower () + "_p.h"_L1;
357 QString bitsFileName = grammar.table_name.toLower () + ".cpp"_L1;
358
359 { // decls...
360 QFile f (declFileName);
361 if (! f.open (QFile::WriteOnly))
362 {
363 fprintf (stderr, "*** cannot create %s: %s\n",
364 qPrintable(declFileName), qPrintable(f.errorString()));
365 return;
366 }
367 QTextStream out (&f);
368
369 // copyright headers must come first, otherwise the headers tests will fail
370 if (copyright)
371 {
372 out << copyrightHeader()
373 << privateCopyrightHeader()
374 << Qt::endl;
375 }
376
377 out << "// This file was generated by qlalr - DO NOT EDIT!\n";
378
379 out << startIncludeGuard(declFileName) << Qt::endl;
380
381 if (copyright) {
382 out << "#include <QtCore/qglobal.h>" << Qt::endl << Qt::endl;
383 out << "QT_BEGIN_NAMESPACE" << Qt::endl << Qt::endl;
384 }
385 generateDecl (out);
386 if (copyright)
387 out << "QT_END_NAMESPACE" << Qt::endl;
388
389 out << endIncludeGuard(declFileName) << Qt::endl;
390 } // end decls
391
392 { // bits...
393 QFile f (bitsFileName);
394 if (! f.open (QFile::WriteOnly))
395 {
396 fprintf (stderr, "*** cannot create %s: %s\n",
397 qPrintable(bitsFileName), qPrintable(f.errorString()));
398 return;
399 }
400 QTextStream out (&f);
401
402 // copyright headers must come first, otherwise the headers tests will fail
403 if (copyright)
404 out << copyrightHeader();
405
406 out << "// This file was generated by qlalr - DO NOT EDIT!\n";
407
408 out << "#include \"" << declFileName << "\"" << Qt::endl << Qt::endl;
409 if (copyright)
410 out << "QT_BEGIN_NAMESPACE" << Qt::endl << Qt::endl;
411 generateImpl(out);
412 if (copyright)
413 out << "QT_END_NAMESPACE" << Qt::endl;
414
415 } // end bits
416
417 if (! grammar.decl_file_name.isEmpty ())
418 {
419 QFile f (grammar.decl_file_name);
420 if (! f.open (QFile::WriteOnly))
421 {
422 fprintf (stderr, "*** cannot create %s: %s\n",
423 qPrintable(grammar.decl_file_name), qPrintable(f.errorString()));
424 return;
425 }
426 QTextStream out (&f);
427 out << p.decls();
428 }
429
430 if (! grammar.impl_file_name.isEmpty ())
431 {
432 QFile f (grammar.impl_file_name);
433 if (! f.open (QFile::WriteOnly))
434 {
435 fprintf (stderr, "*** cannot create %s: %s\n",
436 qPrintable(grammar.impl_file_name), qPrintable(f.errorString()));
437 return;
438 }
439 QTextStream out (&f);
440 out << p.impls();
441 }
442}
443
444QString CppGenerator::debugInfoProt() const
445{
446 QString prot = "QLALR_NO_"_L1;
447 prot += grammar.table_name.toUpper();
448 prot += "_DEBUG_INFO"_L1;
449 return prot;
450}
451
452void CppGenerator::generateDecl (QTextStream &out)
453{
454 out << "class " << grammar.table_name << Qt::endl
455 << "{" << Qt::endl
456 << "public:" << Qt::endl
457 << " enum VariousConstants {" << Qt::endl;
458
459 for (const Name &t : std::as_const(grammar.terminals))
460 {
461 QString name = *t;
462 int value = std::distance (grammar.names.begin (), t);
463
464 if (name == "$end"_L1)
465 name = "EOF_SYMBOL"_L1;
466
467 else if (name == "$accept"_L1)
468 name = "ACCEPT_SYMBOL"_L1;
469
470 else
471 name.prepend (grammar.token_prefix);
472
473 out << " " << name << " = " << value << "," << Qt::endl;
474 }
475
476 out << Qt::endl
477 << " ACCEPT_STATE = " << accept_state << "," << Qt::endl
478 << " RULE_COUNT = " << grammar.rules.size () << "," << Qt::endl
479 << " STATE_COUNT = " << state_count << "," << Qt::endl
480 << " TERMINAL_COUNT = " << terminal_count << "," << Qt::endl
481 << " NON_TERMINAL_COUNT = " << non_terminal_count << "," << Qt::endl
482 << Qt::endl
483 << " GOTO_INDEX_OFFSET = " << compressed_action.index.size () << "," << Qt::endl
484 << " GOTO_INFO_OFFSET = " << compressed_action.info.size () << "," << Qt::endl
485 << " GOTO_CHECK_OFFSET = " << compressed_action.check.size () << Qt::endl
486 << " };" << Qt::endl
487 << Qt::endl
488 << " static const char *const spell[];" << Qt::endl
489 << " static const short lhs[];" << Qt::endl
490 << " static const short rhs[];" << Qt::endl;
491
492 if (debug_info)
493 {
494 QString prot = debugInfoProt();
495
496 out << Qt::endl << "#ifndef " << prot << Qt::endl
497 << " static const int rule_index[];" << Qt::endl
498 << " static const int rule_info[];" << Qt::endl
499 << "#endif // " << prot << Qt::endl << Qt::endl;
500 }
501
502 out << " static const short goto_default[];" << Qt::endl
503 << " static const short action_default[];" << Qt::endl
504 << " static const short action_index[];" << Qt::endl
505 << " static const short action_info[];" << Qt::endl
506 << " static const short action_check[];" << Qt::endl
507 << Qt::endl
508 << " static inline int nt_action (int state, int nt)" << Qt::endl
509 << " {" << Qt::endl
510 << " const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt;" << Qt::endl
511 << " if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt)" << Qt::endl
512 << " return goto_default [nt];" << Qt::endl
513 << Qt::endl
514 << " return action_info [GOTO_INFO_OFFSET + yyn];" << Qt::endl
515 << " }" << Qt::endl
516 << Qt::endl
517 << " static inline int t_action (int state, int token)" << Qt::endl
518 << " {" << Qt::endl
519 << " const int yyn = action_index [state] + token;" << Qt::endl
520 << Qt::endl
521 << " if (yyn < 0 || action_check [yyn] != token)" << Qt::endl
522 << " return - action_default [state];" << Qt::endl
523 << Qt::endl
524 << " return action_info [yyn];" << Qt::endl
525 << " }" << Qt::endl
526 << "};" << Qt::endl
527 << Qt::endl
528 << Qt::endl;
529}
530
531void CppGenerator::generateImpl (QTextStream &out)
532{
533 int idx = 0;
534
535 out << "const char *const " << grammar.table_name << "::spell [] = {";
536 idx = 0;
537
538 QMap<Name, int> name_ids;
539 bool first_nt = true;
540
541 for (Name t = grammar.names.begin (); t != grammar.names.end (); ++t, ++idx)
542 {
543 bool terminal = grammar.isTerminal (t);
544
545 if (! (debug_info || terminal))
546 break;
547
548 name_ids.insert (t, idx);
549
550 generateSeparator(idx, out);
551
552 if (terminal)
553 {
554 QString spell = grammar.spells.value (t);
555
556 if (spell.isEmpty ())
557 out << "nullptr";
558 else
559 out << "\"" << spell << "\"";
560 }
561 else
562 {
563 if (first_nt)
564 {
565 first_nt = false;
566 QString prot = debugInfoProt();
567 out << Qt::endl << "#ifndef " << prot << Qt::endl;
568 }
569 out << "\"" << *t << "\"";
570 }
571 }
572
573 if (debug_info)
574 out << Qt::endl << "#endif // " << debugInfoProt() << Qt::endl;
575
576 out << Qt::endl << "};" << Qt::endl << Qt::endl;
577
578 out << "const short " << grammar.table_name << "::lhs [] = {";
579 idx = 0;
580 for (RulePointer rule = grammar.rules.begin (); rule != grammar.rules.end (); ++rule, ++idx)
581 {
582 generateSeparator(idx, out);
583
584 out << aut.id (rule->lhs);
585 }
586 out << Qt::endl << "};" << Qt::endl << Qt::endl;
587
588 out << "const short " << grammar.table_name << "::rhs [] = {";
589 idx = 0;
590 for (RulePointer rule = grammar.rules.begin (); rule != grammar.rules.end (); ++rule, ++idx)
591 {
592 generateSeparator(idx, out);
593
594 out << rule->rhs.size ();
595 }
596 out << Qt::endl << "};" << Qt::endl << Qt::endl;
597
598 if (debug_info)
599 {
600 QString prot = debugInfoProt();
601
602 out << Qt::endl << "#ifndef " << prot << Qt::endl;
603 out << "const int " << grammar.table_name << "::rule_info [] = {";
604 idx = 0;
605 for (auto rule = grammar.rules.cbegin (); rule != grammar.rules.cend (); ++rule, ++idx)
606 {
607 generateSeparator(idx, out);
608
609 out << name_ids.value(rule->lhs);
610
611 for (const Name &n : rule->rhs)
612 out << ", " << name_ids.value (n);
613 }
614 out << Qt::endl << "};" << Qt::endl << Qt::endl;
615
616 out << "const int " << grammar.table_name << "::rule_index [] = {";
617 idx = 0;
618 size_t offset = 0;
619 for (RulePointer rule = grammar.rules.begin (); rule != grammar.rules.end (); ++rule, ++idx)
620 {
621 generateSeparator(idx, out);
622
623 out << offset;
624 offset += rule->rhs.size () + 1;
625 }
626 out << Qt::endl << "};" << Qt::endl
627 << "#endif // " << prot << Qt::endl << Qt::endl;
628 }
629
630 out << "const short " << grammar.table_name << "::action_default [] = {";
631 idx = 0;
632 for (StatePointer state = aut.states.begin (); state != aut.states.end (); ++state, ++idx)
633 {
634 generateSeparator(idx, out);
635
636 if (state->defaultReduce != grammar.rules.end ())
637 out << aut.id (state->defaultReduce);
638 else
639 out << "0";
640 }
641 out << Qt::endl << "};" << Qt::endl << Qt::endl;
642
643 out << "const short " << grammar.table_name << "::goto_default [] = {";
644 generateList(defgoto, out);
645 out << Qt::endl << "};" << Qt::endl << Qt::endl;
646
647 out << "const short " << grammar.table_name << "::action_index [] = {";
648 generateList(compressed_action.index, out);
649 out << "," << Qt::endl;
650 generateList(compressed_goto.index, out);
651 out << Qt::endl << "};" << Qt::endl << Qt::endl;
652
653 out << "const short " << grammar.table_name << "::action_info [] = {";
654 generateList(compressed_action.info, out);
655 out << "," << Qt::endl;
656 generateList(compressed_goto.info, out);
657 out << Qt::endl << "};" << Qt::endl << Qt::endl;
658
659 out << "const short " << grammar.table_name << "::action_check [] = {";
660 generateList(compressed_action.check, out);
661 out << "," << Qt::endl;
662 generateList(compressed_goto.check, out);
663 out << Qt::endl << "};" << Qt::endl << Qt::endl;
664}
int id(RulePointer rule)
Definition lalr.cpp:242
StateList states
Definition lalr.h:352
int expected_reduce_reduce
Definition lalr.h:249
NameSet terminals
Definition lalr.h:239
debug_infot rules
Definition lalr.h:242
bool isNonTerminal(Name name) const
Definition lalr.h:225
RulePointer goal
Definition lalr.h:244
NameSet non_terminals
Definition lalr.h:240
int expected_shift_reduce
Definition lalr.h:248
#define GOTO(i, j)
#define ACTION(i, j)
static QString includeGuard(const QString &fileName)
QT_FORWARD_DECLARE_CLASS(QTextStream)
std::set< Name > NameSet
Definition lalr.h:30
ItemList::iterator ItemPointer
Definition lalr.h:34
StateList::iterator StatePointer
Definition lalr.h:43
debug_infot::iterator RulePointer
Definition lalr.h:38