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
recognizer.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:insignificant reason:build-tool
4
5#include "recognizer.h"
6
7#include <QtCore/qdir.h>
8#include <QtCore/qstringlist.h>
9
10#include <cstdlib>
11#include <cstring>
12#include <cctype>
13
14using namespace Qt::StringLiterals;
15
16Recognizer::Recognizer (Grammar *grammar, bool no_lines):
17 tos(0),
18 stack_size(0),
19 state_stack(nullptr),
20 _M_line(1),
21 _M_action_line(0),
22 _M_grammar(grammar),
23 _M_no_lines(no_lines)
24{
25}
26
27Recognizer::~Recognizer()
28{
29 if (stack_size)
30 ::free(state_stack);
31}
32
33inline void Recognizer::reallocateStack()
34{
35 if (! stack_size)
36 stack_size = 128;
37 else
38 stack_size <<= 1;
39
40 sym_stack.resize (stack_size);
41
42 if (! state_stack)
43 state_stack = reinterpret_cast<int*> (::malloc(stack_size * sizeof(int)));
44 else
45 state_stack = reinterpret_cast<int*> (::realloc(state_stack, stack_size * sizeof(int)));
46}
47
48int Recognizer::nextToken()
49{
50 QString text;
51
52 Lagain:
53 while (ch.isSpace ())
54 inp ();
55
56 if (ch.isNull ())
57 return EOF_SYMBOL;
58
59 int token = ch.unicode ();
60
61 if (token == '"')
62 {
63 inp(); // skip "
64 text.clear ();
65 while (!ch.isNull () && ch != u'"')
66 {
67 if (ch == u'\\')
68 {
69 text += ch;
70 inp();
71 }
72 text += ch;
73 inp ();
74 }
75
76 if (ch == u'"')
77 inp ();
78 else
79 qerr() << _M_input_file << ":" << _M_line << ": Warning. Expected `\"'" << Qt::endl;
80
81 _M_current_value = text;
82 return (token = STRING_LITERAL);
83 }
84
85 else if (ch.isLetterOrNumber () || ch == u'_')
86 {
87 text.clear ();
88 do { text += ch; inp (); }
89 while (ch.isLetterOrNumber () || ch == u'_' || ch == u'.');
90 _M_current_value = text;
91 return (token = ID);
92 }
93
94 else if (token == '%')
95 {
96 text.clear ();
97
98 do { inp (); }
99 while (ch.isSpace ());
100
101 do { text += ch; inp (); }
102 while (ch.isLetterOrNumber () || ch == u'_' || ch == u'-');
103
104 if (text == "token_prefix"_L1)
105 return (token = TOKEN_PREFIX);
106 else if (text == "merged_output"_L1)
107 return (token = MERGED_OUTPUT);
108 else if (text == "token"_L1)
109 return (token = TOKEN);
110 else if (text == "start"_L1)
111 return (token = START);
112 else if (text == "parser"_L1)
113 return (token = PARSER);
114 else if (text == "decl"_L1)
115 return (token = DECL_FILE);
116 else if (text == "impl"_L1)
117 return (token = IMPL_FILE);
118 else if (text == "expect"_L1)
119 return (token = EXPECT);
120 else if (text == "expect-rr"_L1)
121 return (token = EXPECT_RR);
122 else if (text == "left"_L1)
123 return (token = LEFT);
124 else if (text == "right"_L1)
125 return (token = RIGHT);
126 else if (text == "nonassoc"_L1)
127 return (token = NONASSOC);
128 else if (text == "prec"_L1)
129 return (token = PREC);
130 else
131 {
132 qerr() << _M_input_file << ":" << _M_line << ": Unknown keyword `" << text << "'" << Qt::endl;
133 exit (EXIT_FAILURE);
134 return (token = ERROR);
135 }
136 }
137
138 inp ();
139
140 if (token == '-' && ch == u'-')
141 {
142 do { inp (); }
143 while (!ch.isNull () && ch != u'\n');
144 goto Lagain;
145 }
146
147 else if (token == ':' && ch == u':')
148 {
149 inp ();
150 if (ch != u'=')
151 return (token = ERROR);
152 inp ();
153 return (token = COLON);
154 }
155
156 else if (token == '/' && ch == u':')
157 {
158 _M_action_line = _M_line;
159
160 text.clear ();
161 if (! _M_no_lines)
162 text += "\n#line "_L1 + QString::number(_M_action_line) +
163 " \""_L1 + QDir::fromNativeSeparators(_M_input_file) + "\"\n"_L1;
164 inp (); // skip ':'
165
166 forever
167 {
168 while (! ch.isNull ())
169 {
170 token = ch.unicode ();
171 inp ();
172
173 if (token == ':' && ch == u'/')
174 break;
175
176 text += QLatin1Char (token);
177 }
178
179 if (ch != u'/')
180 return (token = ERROR);
181
182 inp ();
183
184 if (ch.isNull () || ch.isSpace ())
185 {
186 _M_current_value = text;
187 return (token = DECL);
188 }
189 else
190 text += ":/"_L1;
191 }
192 }
193
194 else if (token == '/' && ch == u'.')
195 {
196 _M_action_line = _M_line;
197
198 text.clear ();
199 if (! _M_no_lines)
200 text += "\n#line "_L1 + QString::number(_M_action_line) +
201 " \""_L1 + QDir::fromNativeSeparators(_M_input_file) + "\"\n"_L1;
202
203 inp (); // skip ':'
204
205 forever
206 {
207 while (! ch.isNull ())
208 {
209 token = ch.unicode ();
210 inp ();
211
212 if (token == '.' && ch == u'/')
213 break;
214
215 text += QLatin1Char (token);
216 }
217
218 if (ch != u'/')
219 return (token = ERROR);
220
221 inp ();
222
223 if (ch.isNull () || ch.isSpace ())
224 {
225 _M_current_value = text;
226 return (token = IMPL);
227 }
228 else
229 text += "./"_L1;
230 }
231 }
232
233 switch (token) {
234 case ':':
235 return (token = COLON);
236
237 case ';':
238 return (token = SEMICOLON);
239
240 case '|':
241 return (token = OR);
242
243 default:
244 break;
245 }
246
247 return token;
248}
249
250bool Recognizer::parse (const QString &input_file)
251{
252 _M_input_file = input_file;
253
254 QFile file(_M_input_file);
255 if (! file.open(QFile::ReadOnly))
256 {
257 qerr() << "qlalr: no input file\n";
258 return false;
259 }
260
261 QTextStream stream(&file);
262
263 // Extract leading copyright header (-- comment lines) from the input file
264 {
265 QStringList copyrightLines;
266 QString accumulated;
267 bool done = false;
268
269 while (!stream.atEnd() && !done) {
270 QString line = stream.readLine();
271 auto trimmed = QStringView(line).trimmed();
272 if (trimmed.startsWith("--"_L1)) {
273 copyrightLines.append(line);
274 accumulated += line + '\n'_L1;
275 } else if (trimmed.isEmpty() && !copyrightLines.isEmpty()) {
276 accumulated += line + '\n'_L1;
277 } else {
278 accumulated += line + '\n'_L1;
279 done = true;
280 }
281 }
282
283 _M_contents = done ? accumulated + stream.readAll() : accumulated;
284 _M_firstChar = _M_contents.constBegin();
285 _M_lastChar = _M_contents.constEnd();
286 _M_currentChar = _M_firstChar;
287 _M_line = 1;
288
289 if (!copyrightLines.isEmpty()) {
290 // Convert -- comments to C++ // comments
291 QString header;
292 for (const QString &line : copyrightLines) {
293 QString cppLine = line;
294 int idx = cppLine.indexOf("--"_L1);
295 Q_ASSERT(idx >= 0);
296 cppLine.replace(idx, 2, "//"_L1);
297 header += cppLine;
298 header += '\n'_L1;
299 }
300 _M_grammar->inputCopyright = header;
301 }
302 }
303
304 int yytoken = -1;
305 inp ();
306
307 reallocateStack();
308
309 _M_current_rule = _M_grammar->rules.end ();
310 _M_decls.clear ();
311 _M_impls.clear ();
312
313 tos = 0;
314 state_stack[++tos] = 0;
315
316 while (true)
317 {
318 if (yytoken == -1 && - TERMINAL_COUNT != action_index [state_stack [tos]])
319 yytoken = nextToken();
320
321 int act = t_action (state_stack [tos], yytoken);
322
323 if (act == ACCEPT_STATE)
324 return true;
325
326 else if (act > 0)
327 {
328 if (++tos == stack_size)
329 reallocateStack();
330
331 sym_stack [tos] = _M_current_value;
332 state_stack [tos] = act;
333 yytoken = -1;
334 }
335
336 else if (act < 0)
337 {
338 int r = - act - 1;
339
340 tos -= rhs [r];
341 act = state_stack [tos++];
342
343 switch (r) {
344
345case 3: {
346 Name name = _M_grammar->intern (sym(2));
347 _M_grammar->start = name;
348 _M_grammar->non_terminals.insert (name);
349} break;
350
351case 5: {
352 _M_grammar->table_name = sym(2);
353} break;
354
355case 6: {
356 _M_grammar->merged_output = sym(2);
357} break;
358
359case 7: {
360 _M_grammar->decl_file_name = sym(2);
361} break;
362
363case 8: {
364 _M_grammar->impl_file_name = sym(2);
365} break;
366
367case 9: {
368 _M_grammar->expected_shift_reduce = sym(2).toInt();
369} break;
370
371case 10: {
372 _M_grammar->expected_reduce_reduce = sym(2).toInt();
373} break;
374
375case 11: {
376 _M_grammar->token_prefix = sym(2);
377} break;
378case 17:case 18: {
379 Name name = _M_grammar->intern (sym(1));
380 _M_grammar->terminals.insert (name);
381 _M_grammar->spells.insert (name, sym(2));
382} break;
383
384case 19: {
385 _M_grammar->current_assoc = Grammar::Left;
386 ++_M_grammar->current_prec;
387} break;
388
389case 20: {
391 ++_M_grammar->current_prec;
392} break;
393
394case 21: {
396 ++_M_grammar->current_prec;
397} break;
398
399case 25: {
400 Name name = _M_grammar->intern (sym(1));
401 _M_grammar->terminals.insert (name);
402
403 Grammar::TokenInfo info;
404 info.prec = _M_grammar->current_prec;
405 info.assoc = _M_grammar->current_assoc;
406 _M_grammar->token_info.insert (name, info);
407} break;
408
409case 26: {
410 _M_decls += expand (sym(1));
411} break;
412
413case 27: {
414 _M_impls += expand (sym(1));
415} break;
416
417case 34: {
418 _M_current_rule = _M_grammar->rules.insert (_M_grammar->rules.end (), Rule ());
419 _M_current_rule->lhs = _M_grammar->intern (sym(1));
420 _M_grammar->declared_lhs.insert (_M_current_rule->lhs);
421
422 if (_M_grammar->terminals.find (_M_current_rule->lhs) != _M_grammar->terminals.end ())
423 {
424 qerr() << _M_input_file << ":" << _M_line << ": Invalid non terminal `" << *_M_current_rule->lhs << "'" << Qt::endl;
425 return false;
426 }
427
428 _M_grammar->non_terminals.insert (_M_current_rule->lhs);
429} break;
430
431case 38: {
432 Name lhs = _M_current_rule->lhs;
433 _M_current_rule = _M_grammar->rules.insert (_M_grammar->rules.end (), Rule ());
434 _M_current_rule->lhs = lhs;
435 _M_grammar->declared_lhs.insert (_M_current_rule->lhs);
436
437 if (_M_grammar->terminals.find (_M_current_rule->lhs) != _M_grammar->terminals.end ())
438 {
439 qerr() << _M_input_file << ":" << _M_line << ": Invalid non terminal `" << *_M_current_rule->lhs << "'" << Qt::endl;
440 return false;
441 }
442
443 _M_grammar->non_terminals.insert (_M_current_rule->lhs);
444} break;
445
446case 39: {
447 _M_current_rule->prec = _M_grammar->names.end ();
448
449 for (NameList::iterator it = _M_current_rule->rhs.begin (); it != _M_current_rule->rhs.end (); ++it)
450 {
451 if (! _M_grammar->isTerminal (*it))
452 continue;
453
454 _M_current_rule->prec = *it;
455 }
456} break;
457
458case 40: {
459 Name tok = _M_grammar->intern (sym(2));
460 if (! _M_grammar->isTerminal (tok))
461 {
462 qerr() << _M_input_file << ":" << _M_line << ": `" << *tok << " is not a terminal symbol" << Qt::endl;
463 _M_current_rule->prec = _M_grammar->names.end ();
464 }
465 else
466 _M_current_rule->prec = tok;
467} break;
468
469case 42: {
470 Name name = _M_grammar->intern (sym(2));
471
472 if (_M_grammar->terminals.find (name) == _M_grammar->terminals.end ())
473 _M_grammar->non_terminals.insert (name);
474
475 _M_current_rule->rhs.push_back (name);
476} break;
477
478case 43: {
479 sym(1) = QString();
480} break;
481
482 } // switch
483
484 state_stack [tos] = nt_action (act, lhs [r] - TERMINAL_COUNT);
485 }
486
487 else
488 {
489 break;
490 }
491 }
492
493 qerr() << _M_input_file << ":" << _M_line << ": Syntax error" << Qt::endl;
494 return false;
495}
@ Left
Definition lalr.h:254
@ Right
Definition lalr.h:255
@ NonAssoc
Definition lalr.h:253
Name intern(const char *id)
Definition lalr.h:220
Assoc current_assoc
Definition lalr.h:264
NameSet terminals
Definition lalr.h:239
NameSet declared_lhs
Definition lalr.h:247
debug_infot rules
Definition lalr.h:242
Name start
Definition lalr.h:238
int current_prec
Definition lalr.h:265
bool isTerminal(Name name) const
Definition lalr.h:222
NameSet non_terminals
Definition lalr.h:240
Definition lalr.h:49
QT_FORWARD_DECLARE_CLASS(QTextStream)
std::list< QString >::iterator Name
Definition lalr.h:28