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;
652 for (
const Symbol &sym : arg)
653 stringified += sym.lexemView();
655 stringified.replace(
'"',
"\\\"");
656 stringified.prepend(
'"');
657 stringified.append(
'"');
658 expansion +=
Symbol(lineNum, STRING_LITERAL, stringified);
659 }
else if (mode == HashHash){
660 if (s
.token == WHITESPACE)
663 while (expansion.size() && expansion.constLast().token == PP_WHITESPACE)
664 expansion.pop_back();
667 if (index >= 0 && index < arguments.size()) {
668 const Symbols &arg = arguments.at(index);
669 if (arg.size() == 0) {
676 if (!expansion.isEmpty() && expansion.constLast().token == s
.token
677 && expansion.constLast().token != STRING_LITERAL) {
678 Symbol last = expansion.takeLast();
680 QByteArray lexem = last.lexem() + next.lexem();
686 if (index >= 0 && index < arguments.size()) {
687 const Symbols &arg = arguments.at(index);
689 expansion.append(arg.cbegin() + 1, arg.cend());
695 that->error(
"'#' or '##' found at the end of a macro argument");
705 Token token = next();
706 if (token == PP_IDENTIFIER) {
707 macroExpand(&substituted,
this, symbols, index, symbol().lineNum,
true);
708 }
else if (token == PP_DEFINED) {
709 bool braces = test(PP_LPAREN);
710 if (test(PP_HAS_INCLUDE)) {
712 Symbol definedOrNotDefined = symbol();
713 definedOrNotDefined
.token = PP_MOC_TRUE;
714 substituted += definedOrNotDefined;
717 Symbol definedOrNotDefined = symbol();
718 definedOrNotDefined
.token =
macros.contains(definedOrNotDefined)? PP_MOC_TRUE : PP_MOC_FALSE;
719 substituted += definedOrNotDefined;
724 }
else if (token == PP_NEWLINE) {
725 substituted += symbol();
727 }
else if (token == PP_HAS_INCLUDE) {
730 bool usesAngleInclude =
false;
731 QByteArray includeAsString;
733 if (tok == PP_LANGLE) {
734 usesAngleInclude =
true;
737 Symbol currentSymbol = symbol();
738 includeAsString += currentSymbol.lexem();
739 if (currentSymbol
.token == PP_IDENTIFIER)
740 macroExpand(&innerSymbols,
this, symbols, index, symbol().lineNum,
true);
742 innerSymbols.append(currentSymbol);
743 }
while (next() != PP_RANGLE);
745 includeAsString = unquotedLexem();
748 const QByteArray &relative = usesAngleInclude ? QByteArray() : currentFilenames.top();
749 bool result = !resolveInclude(includeAsString, relative).isNull();
750 if (usesAngleInclude && !result) {
752 includeAsString = {};
753 for (
const auto &innerSymbol: innerSymbols)
754 includeAsString.append(innerSymbol.lexem());
755 result = !resolveInclude(includeAsString, relative).isNull();
757 Symbol definedOrNotDefined = symbol();
758 definedOrNotDefined
.token = result ? PP_MOC_TRUE : PP_MOC_FALSE;
759 substituted += definedOrNotDefined;
761 substituted += symbol();
792 if (test(PP_QUESTION)) {
795 return value ? alt1 : alt2;
917 return remainder ? value % remainder : 0;
922 return div ? value / div : 0;
965 if (test(PP_LPAREN)) {
970 auto lexView = lexemView();
971 if (lexView.endsWith(
'L'))
973 value = lexView.toInt(
nullptr, 0);
981 return (t == PP_IDENTIFIER
982 || t == PP_INTEGER_LITERAL
983 || t == PP_FLOATING_LITERAL
992 expression.currentFilenames = currentFilenames;
1001 const qint64 size = file->size();
1002 char *rawInput =
reinterpret_cast<
char*>(file->map(0, size));
1003 return rawInput ? QByteArray::fromRawData(rawInput, size) : file->readAll();
1009 Q_ASSERT(from + len <= lex.size());
1010 Q_ASSERT(next.len >= 2);
1011 Q_ASSERT(next.from + next.len <= next.lex.size());
1013 if (len != lex.size()) {
1015 QByteArray l = lexemView().chopped(1) % next.lexemView().sliced(1);
1020 const auto unquoted = next.unquotedLexemView();
1021 lex.insert(from + len - 1,
1031 const auto mergeable = [](
const Symbol &lhs,
const Symbol &rhs) {
1032 return lhs
.token == STRING_LITERAL && rhs
.token == STRING_LITERAL;
1035 auto end = symbols.end();
1036 auto it =
std::adjacent_find(symbols.begin(), symbols.end(), mergeable);
1046 lit->mergeStringLiteral(*it);
1048 while (++it != end) {
1054 if (it->token == STRING_LITERAL) {
1056 lit->mergeStringLiteral(*it);
1058 *++dst =
std::move(*it);
1062 *++dst =
std::move(*it);
1068 symbols.erase(dst, end);
1072 const QByteArray &include,
1073 const bool debugIncludes)
1077 if (Q_UNLIKELY(debugIncludes)) {
1078 fprintf(stderr,
"debug-includes: searching for '%s'\n", include.constData());
1081 for (
const Parser::IncludePath &p : includepaths) {
1085 if (p.isFrameworkPath) {
1086 const qsizetype slashPos = include.indexOf(
'/');
1089 fi.setFile(QString::fromLocal8Bit(p.path +
'/' + include.left(slashPos) +
".framework/Headers/"),
1090 QString::fromLocal8Bit(include.mid(slashPos + 1)));
1092 fi.setFile(QString::fromLocal8Bit(p.path), QString::fromLocal8Bit(include));
1095 if (Q_UNLIKELY(debugIncludes)) {
1096 const auto candidate = fi.filePath().toLocal8Bit();
1097 fprintf(stderr,
"debug-includes: considering '%s'\n", candidate.constData());
1108 if (!fi.exists() || fi.isDir()) {
1109 if (Q_UNLIKELY(debugIncludes)) {
1110 fprintf(stderr,
"debug-includes: can't find '%s'\n", include.constData());
1112 return QByteArray();
1115 const auto result = fi.canonicalFilePath().toLocal8Bit();
1117 if (Q_UNLIKELY(debugIncludes)) {
1118 fprintf(stderr,
"debug-includes: found '%s'\n", result.constData());
1126 if (!relativeTo.isEmpty()) {
1128 fi.setFile(QFileInfo(QString::fromLocal8Bit(relativeTo)).dir(), QString::fromLocal8Bit(include));
1129 if (fi.exists() && !fi.isDir())
1130 return fi.canonicalFilePath().toLocal8Bit();
1133 auto it = nonlocalIncludePathResolutionCache.find(include);
1134 if (it == nonlocalIncludePathResolutionCache.end())
1135 it = nonlocalIncludePathResolutionCache.insert(include,
1145 currentFilenames.push(filename);
1146 preprocessed.reserve(preprocessed.size() + symbols.size());
1148 Token token = next();
1153 int lineNum = symbol().lineNum;
1156 if (test(PP_STRING_LITERAL)) {
1157 local = lexemView().startsWith(
'\"');
1158 include = unquotedLexem();
1163 include = resolveInclude(include, local ? filename : QByteArray());
1164 if (include.isNull())
1167 if (Preprocessor::preprocessedIncludes.contains(include))
1169 Preprocessor::preprocessedIncludes.insert(include);
1171 QFile file(QString::fromLocal8Bit(include.constData()));
1172 if (!file.open(QFile::ReadOnly))
1175 QByteArray input = readOrMapFile(&file);
1178 if (input.isEmpty())
1181 Symbols saveSymbols = symbols;
1182 qsizetype saveIndex = index;
1185 input = cleaned(input);
1188 symbols = tokenize(input);
1194 preprocessed +=
Symbol(0, MOC_INCLUDE_BEGIN, include);
1195 preprocess(include, preprocessed);
1196 preprocessed +=
Symbol(lineNum, MOC_INCLUDE_END, include);
1198 symbols = saveSymbols;
1205 QByteArray name = lexem();
1206 if (name.isEmpty() || !is_ident_start(name[0]))
1217 qsizetype start = index;
1219 macro.symbols.reserve(index - start - 1);
1223 Token lastToken = HASH;
1224 for (qsizetype i = start; i < index - 1; ++i) {
1225 Token token = symbols.at(i).token;
1226 if (token == WHITESPACE) {
1227 if (lastToken == PP_HASH || lastToken == HASH ||
1228 lastToken == PP_HASHHASH ||
1229 lastToken == WHITESPACE)
1231 }
else if (token == PP_HASHHASH) {
1232 if (!macro.symbols.isEmpty() &&
1233 lastToken == WHITESPACE)
1234 macro.symbols.pop_back();
1236 macro.symbols.append(symbols.at(i));
1240 while (!macro.symbols.isEmpty() &&
1241 (macro.symbols.constLast().token == PP_WHITESPACE || macro.symbols.constLast().token == WHITESPACE))
1242 macro.symbols.pop_back();
1244 if (!macro.symbols.isEmpty()) {
1245 if (macro.symbols.constFirst().token == PP_HASHHASH ||
1246 macro.symbols.constLast().token == PP_HASHHASH) {
1247 error(
"'##' cannot appear at either end of a macro expansion");
1250 macros.insert(name, macro);
1255 QByteArray name = lexem();
1260 case PP_IDENTIFIER: {
1262 macroExpand(&preprocessed,
this, symbols, index, symbol().lineNum,
true);
1274 if (test(PP_ELIF)) {
1293 if (
macros.contains(
"QT_NO_KEYWORDS"))
1296 sym
.token = (token == SIGNALS ? Q_SIGNALS_TOKEN : Q_SLOTS_TOKEN);
1297 preprocessed += sym;
1302 preprocessed += symbol();
1305 currentFilenames.pop();
1310 QByteArray input = readOrMapFile(file);
1312 if (input.isEmpty())
1316 input = cleaned(input);
1320 symbols = tokenize(input);
1323 for (
int j = 0; j < symbols.size(); ++j)
1324 fprintf(stderr,
"line %d: %s(%s)\n",
1326 symbols[j].lexem().constData(),
1327 tokenTypeName(symbols[j].token));
1335 result.reserve(file->size() / 300000);
1336 preprocess(filename, result);
1337 mergeStringLiterals(result);
1340 for (
int j = 0; j < result.size(); ++j)
1341 fprintf(stderr,
"line %d: %s(%s)\n",
1343 result[j].lexem().constData(),
1344 tokenTypeName(result[j].token));
1354 while (test(PP_WHITESPACE)) {}
1358 if (t != PP_IDENTIFIER) {
1359 QByteArrayView l = lexemView();
1362 arguments +=
Symbol(symbol().lineNum, PP_IDENTIFIER,
"__VA_ARGS__");
1363 while (test(PP_WHITESPACE)) {}
1364 if (!test(PP_RPAREN))
1365 error(
"missing ')' in macro argument list");
1367 }
else if (!is_identifier(l.constData(), l.size())) {
1368 error(
"Unexpected character in macro argument list.");
1373 if (arguments.contains(arg))
1374 error(
"Duplicate macro parameter.");
1375 arguments += symbol();
1377 while (test(PP_WHITESPACE)) {}
1383 if (lexemView() ==
"...") {
1387 while (test(PP_WHITESPACE)) {}
1388 if (!test(PP_RPAREN))
1389 error(
"missing ')' in macro argument list");
1392 error(
"Unexpected character in macro argument list.");
1394 m->arguments = arguments;
1395 while (test(PP_WHITESPACE)) {}
1400 while(hasNext() && next() != t)
1406 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]
bool is_ident_char(char s)
const char * skipQuote(const char *data)
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)