1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the utils of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "generator.h"
43 
44 #include <QFile>
45 
printDeclaration(CodeBlock & block,const QString & funcNamePrefix) const46 void Function::printDeclaration(CodeBlock &block, const QString &funcNamePrefix) const
47 {
48     block << (iline ? "inline " : "") << signature(funcNamePrefix) << (iline ? QLatin1String(" {") : QLatin1String(";"));
49     if (!iline)
50         return;
51 
52     block.indent();
53     QString tmp = body;
54     if (tmp.endsWith(QLatin1Char('\n')))
55         tmp.chop(1);
56     foreach (QString line, tmp.split(QLatin1Char('\n')))
57         block << line;
58     block.outdent();
59     block << "}";
60 }
61 
signature(const QString & funcNamePrefix) const62 QString Function::signature(const QString &funcNamePrefix) const
63 {
64    QString sig;
65    if (!rtype.isEmpty()) {
66        sig += rtype;
67        sig += QLatin1Char(' ');
68    }
69    sig += funcNamePrefix;
70    sig += fname;
71    if (cnst)
72        sig += " const";
73    return sig;
74 }
75 
definition() const76 QString Function::definition() const
77 {
78     if (iline)
79         return QString();
80 
81     QString result;
82     result += signature();
83     result += QLatin1String("\n{\n");
84 
85     QString tmp = body;
86 
87     if (tmp.endsWith(QLatin1Char('\n')))
88         tmp.chop(1);
89     if (!tmp.startsWith(QLatin1Char('\n')))
90         tmp.prepend("    ");
91 
92     tmp.replace(QLatin1Char('\n'), QLatin1String("\n    "));
93 
94     result += tmp;
95 
96     result += QLatin1String("\n}\n");
97 
98     return result;
99 }
100 
printDeclaration(const Class * klass,CodeBlock & block) const101 void Class::Section::printDeclaration(const Class *klass, CodeBlock &block) const
102 {
103     foreach (Function ctor, constructors)
104         ctor.printDeclaration(block, klass->name());
105 
106     if (!constructors.isEmpty())
107         block.addNewLine();
108 
109     foreach (Function func, functions)
110         func.printDeclaration(block);
111 
112     if (!functions.isEmpty())
113         block.addNewLine();
114 
115     foreach (QString var, variables)
116         block << var << ';';
117 }
118 
addConstructor(Access access,const QString & body,const QString & _args)119 void Class::addConstructor(Access access, const QString &body, const QString &_args)
120 {
121     Function ctor;
122     QString args = _args;
123     if (!args.startsWith(QLatin1Char('('))
124         && !args.endsWith(QLatin1Char(')'))) {
125         args.prepend('(');
126         args.append(')');
127     }
128     ctor.setName(args);
129     ctor.addBody(body);
130     sections[access].constructors.append(ctor);
131 }
132 
definition(const Class * klass) const133 QString Class::Section::definition(const Class *klass) const
134 {
135     QString result;
136 
137     foreach (Function ctor, constructors) {
138         ctor.setName(klass->name() + "::" + klass->name() + ctor.name());
139         result += ctor.definition();
140         result += QLatin1Char('\n');
141     }
142 
143     foreach (Function func, functions) {
144         if (!func.hasBody()) continue;
145         func.setName(klass->name() + "::" + func.name());
146         result += func.definition();
147         result += QLatin1Char('\n');
148     }
149 
150     return result;
151 }
152 
declaration() const153 QString Class::declaration() const
154 {
155     CodeBlock block;
156 
157     block << QLatin1String("class ") << cname;
158     block << "{";
159 
160     if (!sections[PublicMember].isEmpty()) {
161         block << "public:";
162         block.indent();
163         sections[PublicMember].printDeclaration(this, block);
164         block.outdent();
165     }
166 
167     if (!sections[ProtectedMember].isEmpty()) {
168         block << "protected:";
169         block.indent();
170         sections[ProtectedMember].printDeclaration(this, block);
171         block.outdent();
172     }
173 
174     if (!sections[PrivateMember].isEmpty()) {
175         block << "private:";
176         block.indent();
177         sections[PrivateMember].printDeclaration(this, block);
178         block.outdent();
179     }
180 
181     block << "};";
182     block.addNewLine();
183 
184     return block.toString();
185 }
186 
definition() const187 QString Class::definition() const
188 {
189     return sections[PrivateMember].definition(this)
190            + sections[ProtectedMember].definition(this)
191            + sections[PublicMember].definition(this);
192 }
193 
Generator(const DFA & _dfa,const Config & config)194 Generator::Generator(const DFA &_dfa, const Config &config)
195      : dfa(_dfa), cfg(config)
196 {
197     QList<InputType> lst = cfg.maxInputSet.toList();
198     qSort(lst);
199     minInput = lst.first();
200     maxInput = lst.last();
201 
202     ConfigFile::Section section = config.configSections.value("Code Generator Options");
203 
204     foreach (ConfigFile::Entry entry, section) {
205         if (!entry.key.startsWith(QLatin1String("MapToCode["))
206             || !entry.key.endsWith(QLatin1Char(']')))
207             continue;
208         QString range = entry.key;
209         range.remove(0, qstrlen("MapToCode["));
210         range.chop(1);
211         if (range.length() != 3
212             || range.at(1) != QLatin1Char('-')) {
213             qWarning("Invalid range for char mapping function: %s", qPrintable(range));
214             continue;
215         }
216         TransitionSequence seq;
217         seq.first = range.at(0).unicode();
218         seq.last = range.at(2).unicode();
219         seq.testFunction = entry.value;
220         charFunctionRanges.append(seq);
221     }
222 
223     QString tokenPrefix = section.value("TokenPrefix");
224     if (!tokenPrefix.isEmpty()) {
225         for (int i = 0; i < dfa.count(); ++i)
226             if (!dfa.at(i).symbol.isEmpty()
227                 && !dfa.at(i).symbol.endsWith(QLatin1String("()")))
228                 dfa[i].symbol.prepend(tokenPrefix);
229     }
230 
231     headerFileName = section.value("FileHeader");
232 }
233 
adjacentKeys(int left,int right)234 static inline bool adjacentKeys(int left, int right) { return left + 1 == right; }
235 //static inline bool adjacentKeys(const InputType &left, const InputType &right)
236 //{ return left.val + 1 == right.val; }
237 
convertToSequences(const TransitionMap & transitions)238 static QVector<Generator::TransitionSequence> convertToSequences(const TransitionMap &transitions)
239 {
240     QVector<Generator::TransitionSequence> sequences;
241     if (transitions.isEmpty())
242         return sequences;
243 
244     QList<InputType> keys = transitions.keys();
245     qSort(keys);
246     int i = 0;
247     Generator::TransitionSequence sequence;
248     sequence.first = keys.at(0);
249     ++i;
250     for (; i < keys.count(); ++i) {
251         if (adjacentKeys(keys.at(i - 1), keys.at(i))
252             && transitions.value(keys.at(i)) == transitions.value(keys.at(i - 1))) {
253             continue;
254         }
255         sequence.last = keys.at(i - 1);
256         sequence.transition = transitions.value(sequence.last);
257         sequences.append(sequence);
258 
259         sequence.first = keys.at(i);
260     }
261     sequence.last = keys.at(i - 1);
262     sequence.transition = transitions.value(sequence.last);
263     sequences.append(sequence);
264 
265     return sequences;
266 }
267 
operator <<(QDebug & debug,const Generator::TransitionSequence & seq)268 QDebug &operator<<(QDebug &debug, const Generator::TransitionSequence &seq)
269 {
270     return debug << "[first:" << seq.first << "; last:" << seq.last << "; transition:" << seq.transition
271                  << (seq.testFunction.isEmpty() ? QString() : QString(QString("; testfunction:" + seq.testFunction)))
272                  << "]";
273 }
274 
isSingleReferencedFinalState(int i) const275 bool Generator::isSingleReferencedFinalState(int i) const
276 {
277     return backReferenceMap.value(i) == 1
278            && dfa.at(i).transitions.isEmpty()
279            && !dfa.at(i).symbol.isEmpty();
280 }
281 
generateTransitions(CodeBlock & body,const TransitionMap & transitions)282 void Generator::generateTransitions(CodeBlock &body, const TransitionMap &transitions)
283 {
284     if (transitions.isEmpty())
285         return;
286 
287     QVector<TransitionSequence> sequences = convertToSequences(transitions);
288 
289     bool needsCharFunction = false;
290     if (!charFunctionRanges.isEmpty()) {
291         int i = 0;
292         while (i < sequences.count()) {
293             const TransitionSequence &seq = sequences.at(i);
294             if (!seq.testFunction.isEmpty()) {
295                 ++i;
296                 continue;
297             }
298 
299             foreach (TransitionSequence range, charFunctionRanges)
300                 if (range.first >= seq.first && range.last <= seq.last) {
301                     needsCharFunction = true;
302 
303                     TransitionSequence left, middle, right;
304 
305                     left.first = seq.first;
306                     left.last = range.first - 1;
307                     left.transition = seq.transition;
308 
309                     middle = range;
310                     middle.transition = seq.transition;
311 
312                     right.first = range.last + 1;
313                     right.last = seq.last;
314                     right.transition = seq.transition;
315 
316                     sequences.remove(i);
317                     if (left.last >= left.first) {
318                         sequences.insert(i, left);
319                         ++i;
320                     }
321                     sequences.insert(i, middle);
322                     ++i;
323                     if (right.last >= right.first) {
324                         sequences.insert(i, right);
325                         ++i;
326                     }
327 
328                     i = -1;
329                     break;
330                 }
331 
332             ++i;
333         }
334     }
335 
336     //qDebug() << "sequence count" << sequences.count();
337     //qDebug() << sequences;
338 
339     if (sequences.count() < 10
340         || sequences.last().last == maxInput
341         || needsCharFunction) {
342         foreach (TransitionSequence seq, sequences) {
343             const bool embedFinalState = isSingleReferencedFinalState(seq.transition);
344 
345             QString brace;
346             if (embedFinalState)
347                 brace = " {";
348 
349             if (!seq.testFunction.isEmpty()) {
350                 body << "if (" << seq.testFunction << ")" << brace;
351             } else if (seq.first == seq.last) {
352                 body << "if (ch.unicode() == " << seq.first << ")" << brace;
353             } else {
354                 if (seq.last < maxInput)
355                     body << "if (ch.unicode() >= " << seq.first
356                          << " && ch.unicode() <= " << seq.last << ")" << brace;
357                 else
358                     body << "if (ch.unicode() >= " << seq.first << ")" << brace;
359             }
360             body.indent();
361             if (embedFinalState) {
362                 body << "token = " << dfa.at(seq.transition).symbol << ";";
363                 body << "goto found;";
364 
365                 body.outdent();
366                 body << "}";
367             } else {
368                 body << "goto state_" << seq.transition << ";";
369                 body.outdent();
370             }
371         }
372     } else {
373         QList<InputType> keys = transitions.keys();
374         qSort(keys);
375 
376         body << "switch (ch.unicode()) {";
377         body.indent();
378         for (int k = 0; k < keys.count(); ++k) {
379             const InputType key = keys.at(k);
380             const int trans = transitions.value(key);
381 
382             QString keyStr;
383             if (key == '\\')
384                 keyStr = QString("\'\\\\\'");
385             else if (key >= 48 && key < 127)
386                 keyStr = QString('\'') + QChar::fromLatin1(char(key)) + QChar('\'');
387             else
388                 keyStr = QString::number(key);
389 
390             if (k < keys.count() - 1
391                 && transitions.value(keys.at(k + 1)) == trans) {
392                 body << "case " << keyStr << ":";
393             } else {
394                 if (isSingleReferencedFinalState(trans)) {
395                     body << "case " << keyStr << ": token = " << dfa.at(trans).symbol << "; goto found;";
396                 } else {
397                     body << "case " << keyStr << ": goto state_" << trans << ";";
398                 }
399             }
400         }
401         body.outdent();
402         body << "}";
403     }
404 }
405 
generate()406 QString Generator::generate()
407 {
408     Class klass(cfg.className);
409 
410     klass.addMember(Class::PublicMember, "QString input");
411     klass.addMember(Class::PublicMember, "int pos");
412     klass.addMember(Class::PublicMember, "int lexemStart");
413     klass.addMember(Class::PublicMember, "int lexemLength");
414 
415     {
416         CodeBlock body;
417         body << "input = inp;";
418         body << "pos = 0;";
419         body << "lexemStart = 0;";
420         body << "lexemLength = 0;";
421         klass.addConstructor(Class::PublicMember, body, "const QString &inp");
422     }
423 
424     {
425         Function next("QChar", "next()");
426         next.setInline(true);
427         if (cfg.caseSensitivity == Qt::CaseSensitive)
428             next.addBody("return (pos < input.length()) ? input.at(pos++) : QChar();");
429         else
430             next.addBody("return (pos < input.length()) ? input.at(pos++).toLower() : QChar();");
431         klass.addMember(Class::PublicMember, next);
432     }
433 
434     /*
435     {
436         Function lexem("QString", "lexem()");
437         lexem.setConst(true);
438         lexem.setInline(true);
439         lexem.addBody("return input.mid(lexemStart, lexemLength);");
440         klass.addMember(Class::PublicMember, lexem);
441     }
442     */
443 
444     for (int i = 0; i < dfa.count(); ++i)
445         if (dfa.at(i).symbol.endsWith(QLatin1String("()"))) {
446             Function handlerFunc("int", dfa.at(i).symbol);
447             klass.addMember(Class::PublicMember, handlerFunc);
448         }
449 
450     Function lexFunc;
451     lexFunc.setReturnType("int");
452     lexFunc.setName("lex()");
453 
454     CodeBlock body;
455     body << "lexemStart = pos;";
456     body << "lexemLength = 0;";
457     body << "int lastAcceptingPos = -1;";
458     body << "int token = -1;";
459     body << "QChar ch;";
460     body.addNewLine();
461 
462     backReferenceMap.clear();
463     foreach (State s, dfa)
464         foreach (int state, s.transitions)
465             backReferenceMap[state]++;
466 
467     bool haveSingleReferencedFinalState = false;
468 
469     for (int i = 0; i < dfa.count(); ++i) {
470         if (isSingleReferencedFinalState(i)) {
471             haveSingleReferencedFinalState = true;
472             continue;
473         }
474 
475         if (i > 0)
476             body << "state_" << i << ":";
477         else
478             body << "// initial state";
479 
480         body.indent();
481 
482         if (!dfa.at(i).symbol.isEmpty()) {
483             body << "lastAcceptingPos = pos;";
484             body << "token = " << dfa.at(i).symbol << ";";
485         }
486 
487         body.outdent();
488 
489         body.indent();
490 
491         if (!dfa.at(i).transitions.isEmpty()) {
492             body << "ch = next();";
493             generateTransitions(body, dfa.at(i).transitions);
494         }
495 
496         body << "goto out;";
497 
498         body.outdent();
499     }
500 
501     if (haveSingleReferencedFinalState) {
502         body << "found:";
503         body << "lastAcceptingPos = pos;";
504         body.addNewLine();
505     }
506 
507     body << "out:";
508     body << "if (lastAcceptingPos != -1) {";
509     body.indent();
510     body << "lexemLength = lastAcceptingPos - lexemStart;";
511     body << "pos = lastAcceptingPos;";
512     body.outdent();
513     body << "}";
514     body << "return token;";
515 
516     lexFunc.addBody(body);
517 
518     klass.addMember(Class::PublicMember, lexFunc);
519 
520     QString header;
521     QFile headerFile(headerFileName);
522     if (!headerFileName.isEmpty()
523         && headerFile.exists()
524         && headerFile.open(QIODevice::ReadOnly)) {
525         header = QString::fromUtf8(headerFile.readAll());
526     }
527 
528     header += QLatin1String("// auto generated. DO NOT EDIT.\n");
529 
530     return header + klass.declaration() + klass.definition();
531 }
532 
533