7#include <qstringlist.h>
11#include <qvarlengtharray.h>
15using namespace QtMiscUtils;
26 result.resize(input.size());
27 const char *data = input.constData();
28 const char *end = input.constData() + input.size();
29 char *output = result.data();
35 bool takeLine = (*data ==
'#');
36 if (*data ==
'%' && *(data+1) ==
':') {
43 do ++data;
while (data != end &&
is_space(*data
));
48 if (*(data + 1) ==
'\r') {
51 if (data != end && (*(data + 1) ==
'\n' || (*data) ==
'\r')) {
54 if (data != end && *data !=
'\r')
58 }
else if (*data ==
'\r' && *(data + 1) ==
'\n') {
84 result.resize(output - result.constData());
91 while(index < symbols.size() - 1 && symbols.at(index).token != PP_ENDIF){
92 switch (symbols.at(index).token) {
108 while (index < symbols.size() - 1
109 && (symbols.at(index).token != PP_ENDIF
110 && symbols.at(index).token != PP_ELIF
111 && symbols.at(index).token != PP_ELSE)
113 switch (symbols.at(index).token) {
125 return (index < symbols.size() - 1);
136 symbols.reserve(input.size() / 16);
137 const char *begin = input.constData();
138 const char *data = begin;
143 const char *lexem = data;
145 Token token = NOTOKEN;
147 if (
static_cast<
signed char>(*data) < 0) {
151 int nextindex = keywords[state]
.next;
153 if (*data == keywords[state]
.defchar)
155 else if (!state || nextindex)
160 token = keywords[state]
.token;
166 token = keywords[state]
.ident;
168 if (token == NOTOKEN) {
180 if (token > SPECIAL_TREATMENT_MARK) {
184 token = STRING_LITERAL;
188 && !symbols.isEmpty()
189 && symbols.constLast().token == STRING_LITERAL) {
191 const QByteArray newString
193 + symbols.constLast().unquotedLexemView()
194 + input.mid(lexem - begin + 1, data - lexem - 2)
196 symbols.last() =
Symbol(symbols.constLast().lineNum,
203 while (*data && (*data !=
'\''
205 && *(data-2)!=
'\\')))
209 token = CHARACTER_LITERAL;
218 bool hasSeenTokenSeparator =
false;;
219 while (isAsciiDigit(*data) || (hasSeenTokenSeparator = *data ==
'\''))
221 if (!*data || *data !=
'.') {
222 token = INTEGER_LITERAL;
223 if (data - lexem == 1 &&
224 (*data ==
'x' || *data ==
'X'
225 || *data ==
'b' || *data ==
'B')
228 while (isHexDigit(*data) || (hasSeenTokenSeparator = *data ==
'\''))
230 }
else if (*data ==
'L')
232 if (!hasSeenTokenSeparator) {
240 token = FLOATING_LITERAL;
244 case FLOATING_LITERAL:
245 while (isAsciiDigit(*data) || *data ==
'\'')
247 if (*data ==
'+' || *data ==
'-')
249 if (*data ==
'e' || *data ==
'E') {
251 while (isAsciiDigit(*data) || *data ==
'\'')
254 if (*data ==
'f' || *data ==
'F'
255 || *data ==
'l' || *data ==
'L')
261 while (*data && (*data ==
' ' || *data ==
'\t'))
282 const char *rewind = data;
283 while (*data && (*data ==
' ' || *data ==
'\t'))
285 if (*data && *data ==
'\n') {
307 while (*data && (*(data-1) !=
'/' || *(data-2) !=
'*')) {
317 while (*data && (*data ==
' ' || *data ==
'\t'))
323 while (*data && *data !=
'\n')
330 symbols +=
Symbol(lineNum, token, input, lexem-begin, data-lexem);
334 const char *lexem = data;
336 Token token = NOTOKEN;
342 if (
static_cast<
signed char>(*data) < 0) {
346 int nextindex = pp_keywords[state]
.next;
348 if (*data == pp_keywords[state]
.defchar)
350 else if (!state || nextindex)
355 token = pp_keywords[state]
.token;
360 token = pp_keywords[state]
.ident;
384 token = PP_STRING_LITERAL;
387 while (*data && (*data !=
'\''
389 && *(data-2)!=
'\\')))
393 token = PP_CHARACTER_LITERAL;
396 while (isAsciiDigit(*data) || *data ==
'\'')
398 if (!*data || *data !=
'.') {
399 token = PP_INTEGER_LITERAL;
400 if (data - lexem == 1 &&
401 (*data ==
'x' || *data ==
'X')
404 while (isHexDigit(*data) || *data ==
'\'')
406 }
else if (*data ==
'L')
410 token = PP_FLOATING_LITERAL;
413 case PP_FLOATING_LITERAL:
414 while (isAsciiDigit(*data) || *data ==
'\'')
416 if (*data ==
'+' || *data ==
'-')
418 if (*data ==
'e' || *data ==
'E') {
420 while (isAsciiDigit(*data) || *data ==
'\'')
423 if (*data ==
'f' || *data ==
'F'
424 || *data ==
'l' || *data ==
'L')
436 token = PP_IDENTIFIER;
439 symbols +=
Symbol(lineNum, token, input, lexem-begin, data-lexem);
460 while (*data && (*(data-1) !=
'/' || *(data-2) !=
'*')) {
465 token = PP_WHITESPACE;
468 while (*data && (*data ==
' ' || *data ==
'\t'))
472 while (*data && *data !=
'\n')
481 const char *rewind = data;
482 while (*data && (*data ==
' ' || *data ==
'\t'))
484 if (*data && *data ==
'\n') {
493 token = PP_STRING_LITERAL;
494 while (*data && *data !=
'\n' && *(data-1) !=
'>')
502 symbols +=
Symbol(lineNum, token, input, lexem-begin, data-lexem);
510 int lineNum,
bool one,
const QSet<QByteArray> &excludeSymbols)
515 sf.symbols = toExpand;
517 sf.excludedSymbols = excludeSymbols;
518 symbols.push(
std::move(sf));
520 if (toExpand.isEmpty())
525 Symbols newSyms = macroExpandIdentifier(that, symbols, lineNum, ¯o);
527 if (macro.isEmpty()) {
534 sf.symbols = newSyms;
536 sf.expandedMacro = macro;
537 symbols.push(
std::move(sf));
539 if (!symbols
.hasNext() || (one && symbols.size() == 1))
545 index = symbols.top().index;
547 index = toExpand.size();
556 if (s
.token != PP_IDENTIFIER || !that
->macros.contains(s) || symbols.dontReplaceSymbol(s.lexem())) {
561 *macroName = s.lexem();
565 expansion = macro.symbols;
567 bool haveSpace =
false;
568 while (symbols
.test(PP_WHITESPACE
)) { haveSpace =
true; }
569 if (!symbols
.test(PP_LPAREN
)) {
570 *macroName = QByteArray();
575 syms.last().lineNum = lineNum;
582 while (symbols
.test(PP_WHITESPACE
)) {}
584 bool vararg = macro
.isVariadic && (arguments.size() == macro.arguments.size() - 1);
587 if (t == PP_LPAREN) {
589 }
else if (t == PP_RPAREN) {
593 }
else if (t == PP_COMMA && nesting == 0) {
599 arguments += argument;
604 that->error(
"missing ')' in macro usage");
608 if (macro
.isVariadic && arguments.size() == macro.arguments.size() - 1)
609 arguments += Symbols();
618 const auto end = macro.symbols.cend();
619 auto it = macro.symbols.cbegin();
620 const auto lastSym =
std::prev(macro.symbols.cend(), !macro.symbols.isEmpty() ? 1 : 0);
621 for (; it != end; ++it) {
624 mode = (s
.token == HASH ? Hash : HashHash);
627 const qsizetype index = macro.arguments.indexOf(s);
628 if (mode == Normal) {
629 if (index >= 0 && index < arguments.size()) {
631 if (it == lastSym ||
std::next(it)->token != PP_HASHHASH) {
632 Symbols arg = arguments.at(index);
634 macroExpand(&expansion, that, arg, idx, lineNum,
false, symbols.excludeSymbols());
636 expansion += arguments.at(index);
641 }
else if (mode == Hash) {
643 that->error(
"'#' is not followed by a macro parameter");
645 }
else if (index >= arguments.size()) {
646 that->error(
"Macro invoked with too few parameters for a use of '#'");
650 const Symbols &arg = arguments.at(index);
651 QByteArray stringified;
653 stringified = arg.front().lexem();
654 for (
auto it = arg.cbegin();
std::next(it) != arg.cend(); ++it) {
655 const auto next =
std::next(it);
656 if (next->from - (it->from + it->len) > 0)
657 stringified +=
' ' + next->lexem();
659 stringified += next->lexem();
662 stringified.replace(
'"',
"\\\"");
663 stringified.prepend(
'"');
664 stringified.append(
'"');
667 expansion +=
Symbol(lineNum, STRING_LITERAL, stringified);
668 }
else if (mode == HashHash){
669 if (s
.token == WHITESPACE)
672 while (expansion.size() && expansion.constLast().token == PP_WHITESPACE)
673 expansion.pop_back();
676 if (index >= 0 && index < arguments.size()) {
677 const Symbols &arg = arguments.at(index);
678 if (arg.size() == 0) {
685 if (!expansion.isEmpty() && expansion.constLast().token == s
.token
686 && expansion.constLast().token != STRING_LITERAL) {
687 Symbol last = expansion.takeLast();
689 QByteArray lexem = last.lexem() + next.lexem();
695 if (index >= 0 && index < arguments.size()) {
696 const Symbols &arg = arguments.at(index);
698 expansion.append(arg.cbegin() + 1, arg.cend());
704 that->error(
"'#' or '##' found at the end of a macro argument");
714 Token token = next();
715 if (token == PP_IDENTIFIER) {
716 macroExpand(&substituted,
this, symbols, index, symbol().lineNum,
true);
717 }
else if (token == PP_DEFINED) {
718 bool braces = test(PP_LPAREN);
719 if (test(PP_HAS_INCLUDE)) {
721 Symbol definedOrNotDefined = symbol();
722 definedOrNotDefined
.token = PP_MOC_TRUE;
723 substituted += definedOrNotDefined;
726 Symbol definedOrNotDefined = symbol();
727 definedOrNotDefined
.token =
macros.contains(definedOrNotDefined)? PP_MOC_TRUE : PP_MOC_FALSE;
728 substituted += definedOrNotDefined;
733 }
else if (token == PP_NEWLINE) {
734 substituted += symbol();
736 }
else if (token == PP_HAS_INCLUDE) {
739 bool usesAngleInclude =
false;
740 QByteArray includeAsString;
742 if (tok == PP_LANGLE) {
743 usesAngleInclude =
true;
746 Symbol currentSymbol = symbol();
747 includeAsString += currentSymbol.lexem();
748 if (currentSymbol
.token == PP_IDENTIFIER)
749 macroExpand(&innerSymbols,
this, symbols, index, symbol().lineNum,
true);
751 innerSymbols.append(currentSymbol);
752 }
while (next() != PP_RANGLE);
754 includeAsString = unquotedLexem();
757 const QByteArray &relative = usesAngleInclude ? QByteArray() : currentFilenames.top();
758 bool result = !resolveInclude(includeAsString, relative).isNull();
759 if (usesAngleInclude && !result) {
761 includeAsString = {};
762 for (
const auto &innerSymbol: innerSymbols)
763 includeAsString.append(innerSymbol.lexem());
764 result = !resolveInclude(includeAsString, relative).isNull();
766 Symbol definedOrNotDefined = symbol();
767 definedOrNotDefined
.token = result ? PP_MOC_TRUE : PP_MOC_FALSE;
768 substituted += definedOrNotDefined;
770 substituted += symbol();
801 if (test(PP_QUESTION)) {
804 return value ? alt1 : alt2;
926 return remainder ? value % remainder : 0;
931 return div ? value / div : 0;
974 if (test(PP_LPAREN)) {
979 auto lexView = lexemView();
980 if (lexView.endsWith(
'L'))
982 value = lexView.toInt(
nullptr, 0);
990 return (t == PP_IDENTIFIER
991 || t == PP_INTEGER_LITERAL
992 || t == PP_FLOATING_LITERAL
1001 expression.currentFilenames = currentFilenames;
1010 const qint64 size = file->size();
1011 char *rawInput =
reinterpret_cast<
char*>(file->map(0, size));
1012 return rawInput ? QByteArray::fromRawData(rawInput, size) : file->readAll();
1018 Q_ASSERT(from + len <= lex.size());
1019 Q_ASSERT(next.len >= 2);
1020 Q_ASSERT(next.from + next.len <= next.lex.size());
1022 if (len != lex.size()) {
1024 QByteArray l = lexemView().chopped(1) % next.lexemView().sliced(1);
1029 const auto unquoted = next.unquotedLexemView();
1030 lex.insert(from + len - 1,
1040 const auto mergeable = [](
const Symbol &lhs,
const Symbol &rhs) {
1041 return lhs
.token == STRING_LITERAL && rhs
.token == STRING_LITERAL;
1044 auto end = symbols.end();
1045 auto it =
std::adjacent_find(symbols.begin(), symbols.end(), mergeable);
1055 lit->mergeStringLiteral(*it);
1057 while (++it != end) {
1063 if (it->token == STRING_LITERAL) {
1065 lit->mergeStringLiteral(*it);
1067 *++dst =
std::move(*it);
1071 *++dst =
std::move(*it);
1077 symbols.erase(dst, end);
1081 const QByteArray &include,
1082 const bool debugIncludes)
1086 if (Q_UNLIKELY(debugIncludes)) {
1087 fprintf(stderr,
"debug-includes: searching for '%s'\n", include.constData());
1090 for (
const Parser::IncludePath &p : includepaths) {
1094 if (p.isFrameworkPath) {
1095 const qsizetype slashPos = include.indexOf(
'/');
1098 fi.setFile(QString::fromLocal8Bit(p.path +
'/' + include.left(slashPos) +
".framework/Headers/"),
1099 QString::fromLocal8Bit(include.mid(slashPos + 1)));
1101 fi.setFile(QString::fromLocal8Bit(p.path), QString::fromLocal8Bit(include));
1104 if (Q_UNLIKELY(debugIncludes)) {
1105 const auto candidate = fi.filePath().toLocal8Bit();
1106 fprintf(stderr,
"debug-includes: considering '%s'\n", candidate.constData());
1117 if (!fi.exists() || fi.isDir()) {
1118 if (Q_UNLIKELY(debugIncludes)) {
1119 fprintf(stderr,
"debug-includes: can't find '%s'\n", include.constData());
1121 return QByteArray();
1124 const auto result = fi.canonicalFilePath().toLocal8Bit();
1126 if (Q_UNLIKELY(debugIncludes)) {
1127 fprintf(stderr,
"debug-includes: found '%s'\n", result.constData());
1135 if (!relativeTo.isEmpty()) {
1137 fi.setFile(QFileInfo(QString::fromLocal8Bit(relativeTo)).dir(), QString::fromLocal8Bit(include));
1138 if (fi.exists() && !fi.isDir())
1139 return fi.canonicalFilePath().toLocal8Bit();
1142 auto it = nonlocalIncludePathResolutionCache.find(include);
1143 if (it == nonlocalIncludePathResolutionCache.end())
1144 it = nonlocalIncludePathResolutionCache.insert(include,
1154 currentFilenames.push(filename);
1155 preprocessed.reserve(preprocessed.size() + symbols.size());
1157 Token token = next();
1162 int lineNum = symbol().lineNum;
1165 if (test(PP_STRING_LITERAL)) {
1166 local = lexemView().startsWith(
'\"');
1167 include = unquotedLexem();
1172 include = resolveInclude(include, local ? filename : QByteArray());
1173 if (include.isNull())
1176 if (Preprocessor::preprocessedIncludes.contains(include))
1178 Preprocessor::preprocessedIncludes.insert(include);
1180 QFile file(QString::fromLocal8Bit(include.constData()));
1181 if (!file.open(QFile::ReadOnly))
1184 QByteArray input = readOrMapFile(&file);
1187 if (input.isEmpty())
1190 Symbols saveSymbols = symbols;
1191 qsizetype saveIndex = index;
1194 input = cleaned(input);
1197 symbols = tokenize(input);
1203 preprocessed +=
Symbol(0, MOC_INCLUDE_BEGIN, include);
1204 preprocess(include, preprocessed);
1205 preprocessed +=
Symbol(lineNum, MOC_INCLUDE_END, include);
1207 symbols = saveSymbols;
1214 QByteArray name = lexem();
1215 if (name.isEmpty() || !is_ident_start(name[0]))
1226 qsizetype start = index;
1228 macro.symbols.reserve(index - start - 1);
1232 Token lastToken = HASH;
1233 for (qsizetype i = start; i < index - 1; ++i) {
1234 Token token = symbols.at(i).token;
1235 if (token == WHITESPACE) {
1236 if (lastToken == PP_HASH || lastToken == HASH ||
1237 lastToken == PP_HASHHASH ||
1238 lastToken == WHITESPACE)
1240 }
else if (token == PP_HASHHASH) {
1241 if (!macro.symbols.isEmpty() &&
1242 lastToken == WHITESPACE)
1243 macro.symbols.pop_back();
1245 macro.symbols.append(symbols.at(i));
1249 while (!macro.symbols.isEmpty() &&
1250 (macro.symbols.constLast().token == PP_WHITESPACE || macro.symbols.constLast().token == WHITESPACE))
1251 macro.symbols.pop_back();
1253 if (!macro.symbols.isEmpty()) {
1254 if (macro.symbols.constFirst().token == PP_HASHHASH ||
1255 macro.symbols.constLast().token == PP_HASHHASH) {
1256 error(
"'##' cannot appear at either end of a macro expansion");
1259 macros.insert(name, macro);
1264 QByteArray name = lexem();
1269 case PP_IDENTIFIER: {
1271 macroExpand(&preprocessed,
this, symbols, index, symbol().lineNum,
true);
1283 if (test(PP_ELIF)) {
1302 if (
macros.contains(
"QT_NO_KEYWORDS"))
1305 sym
.token = (token == SIGNALS ? Q_SIGNALS_TOKEN : Q_SLOTS_TOKEN);
1306 preprocessed += sym;
1311 preprocessed += symbol();
1314 currentFilenames.pop();
1319 QByteArray input = readOrMapFile(file);
1321 if (input.isEmpty())
1325 input = cleaned(input);
1329 symbols = tokenize(input);
1332 for (
int j = 0; j < symbols.size(); ++j)
1333 fprintf(stderr,
"line %d: %s(%s)\n",
1335 symbols[j].lexem().constData(),
1336 tokenTypeName(symbols[j].token));
1344 result.reserve(file->size() / 300000);
1345 preprocess(filename, result);
1346 mergeStringLiterals(result);
1349 for (
int j = 0; j < result.size(); ++j)
1350 fprintf(stderr,
"line %d: %s(%s)\n",
1352 result[j].lexem().constData(),
1353 tokenTypeName(result[j].token));
1363 while (test(PP_WHITESPACE)) {}
1367 if (t != PP_IDENTIFIER) {
1368 QByteArrayView l = lexemView();
1371 arguments +=
Symbol(symbol().lineNum, PP_IDENTIFIER,
"__VA_ARGS__");
1372 while (test(PP_WHITESPACE)) {}
1373 if (!test(PP_RPAREN))
1374 error(
"missing ')' in macro argument list");
1376 }
else if (!is_identifier(l.constData(), l.size())) {
1377 error(
"Unexpected character in macro argument list.");
1382 if (arguments.contains(arg))
1383 error(
"Duplicate macro parameter.");
1384 arguments += symbol();
1386 while (test(PP_WHITESPACE)) {}
1392 if (lexemView() ==
"...") {
1396 while (test(PP_WHITESPACE)) {}
1397 if (!test(PP_RPAREN))
1398 error(
"missing ')' in macro argument list");
1401 error(
"Unexpected character in macro argument list.");
1403 m->arguments = arguments;
1404 while (test(PP_WHITESPACE)) {}
1409 while(hasNext() && next() != t)
1415 debugIncludes = value;
int relational_expression()
int exclusive_OR_expression()
bool unary_expression_lookup()
int logical_OR_expression()
int equality_expression()
int logical_AND_expression()
int additive_expression()
int multiplicative_expression()
int conditional_expression()
bool primary_expression_lookup()
int inclusive_OR_expression()
void setDebugIncludes(bool value)
void parseDefineArguments(Macro *m)
Symbols preprocessed(const QByteArray &filename, QFile *device)
void substituteUntilNewline(Symbols &substituted)
static bool preprocessOnly
QByteArray resolveInclude(const QByteArray &filename, const QByteArray &relativeTo)
@ PreparePreprocessorStatement
@ TokenizePreprocessorStatement
const Symbol & symbol() const
static const short keyword_trans[][128]
Combined button and popup list for selecting options.
static const short pp_keyword_trans[][128]
static QByteArray readOrMapFile(QFile *file)
static QByteArray searchIncludePaths(const QList< Parser::IncludePath > &includepaths, const QByteArray &include, const bool debugIncludes)
static QByteArray cleaned(const QByteArray &input)
static void mergeStringLiterals(Symbols &symbols)
Simple structure used by the Doc and DocParser classes.
Symbol(int lineNum, Token token)
void mergeStringLiteral(const Symbol &next)