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().unquotedLexem()
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)
514 sf.symbols = toExpand;
516 sf.excludedSymbols = excludeSymbols;
519 if (toExpand.isEmpty())
524 Symbols newSyms = macroExpandIdentifier(that, symbols, lineNum, ¯o);
526 if (macro.isEmpty()) {
533 sf.symbols = newSyms;
535 sf.expandedMacro = macro;
538 if (!symbols
.hasNext() || (one && symbols.size() == 1))
544 index = symbols.top().index;
546 index = toExpand.size();
555 if (s
.token != PP_IDENTIFIER || !that
->macros.contains(s) || symbols.dontReplaceSymbol(s.lexem())) {
560 *macroName = s.lexem();
564 expansion = macro.symbols;
566 bool haveSpace =
false;
567 while (symbols
.test(PP_WHITESPACE
)) { haveSpace =
true; }
568 if (!symbols
.test(PP_LPAREN
)) {
569 *macroName = QByteArray();
574 syms.last().lineNum = lineNum;
581 while (symbols
.test(PP_WHITESPACE
)) {}
583 bool vararg = macro
.isVariadic && (arguments.size() == macro.arguments.size() - 1);
586 if (t == PP_LPAREN) {
588 }
else if (t == PP_RPAREN) {
592 }
else if (t == PP_COMMA && nesting == 0) {
598 arguments += argument;
603 that->error(
"missing ')' in macro usage");
607 if (macro
.isVariadic && arguments.size() == macro.arguments.size() - 1)
608 arguments += Symbols();
617 const auto end = macro.symbols.cend();
618 auto it = macro.symbols.cbegin();
619 const auto lastSym =
std::prev(macro.symbols.cend(), !macro.symbols.isEmpty() ? 1 : 0);
620 for (; it != end; ++it) {
623 mode = (s
.token == HASH ? Hash : HashHash);
626 const qsizetype index = macro.arguments.indexOf(s);
627 if (mode == Normal) {
628 if (index >= 0 && index < arguments.size()) {
630 if (it == lastSym ||
std::next(it)->token != PP_HASHHASH) {
631 Symbols arg = arguments.at(index);
633 macroExpand(&expansion, that, arg, idx, lineNum,
false, symbols.excludeSymbols());
635 expansion += arguments.at(index);
640 }
else if (mode == Hash) {
642 that->error(
"'#' is not followed by a macro parameter");
644 }
else if (index >= arguments.size()) {
645 that->error(
"Macro invoked with too few parameters for a use of '#'");
649 const Symbols &arg = arguments.at(index);
650 QByteArray stringified;
651 for (
const Symbol &sym : arg)
652 stringified += sym.lexem();
654 stringified.replace(
'"',
"\\\"");
655 stringified.prepend(
'"');
656 stringified.append(
'"');
657 expansion +=
Symbol(lineNum, STRING_LITERAL, stringified);
658 }
else if (mode == HashHash){
659 if (s
.token == WHITESPACE)
662 while (expansion.size() && expansion.constLast().token == PP_WHITESPACE)
663 expansion.pop_back();
666 if (index >= 0 && index < arguments.size()) {
667 const Symbols &arg = arguments.at(index);
668 if (arg.size() == 0) {
675 if (!expansion.isEmpty() && expansion.constLast().token == s
.token
676 && expansion.constLast().token != STRING_LITERAL) {
677 Symbol last = expansion.takeLast();
679 QByteArray lexem = last.lexem() + next.lexem();
685 if (index >= 0 && index < arguments.size()) {
686 const Symbols &arg = arguments.at(index);
688 expansion.append(arg.cbegin() + 1, arg.cend());
694 that->error(
"'#' or '##' found at the end of a macro argument");
704 Token token = next();
705 if (token == PP_IDENTIFIER) {
706 macroExpand(&substituted,
this, symbols, index, symbol().lineNum,
true);
707 }
else if (token == PP_DEFINED) {
708 bool braces = test(PP_LPAREN);
710 Symbol definedOrNotDefined = symbol();
711 definedOrNotDefined
.token =
macros.contains(definedOrNotDefined)? PP_MOC_TRUE : PP_MOC_FALSE;
712 substituted += definedOrNotDefined;
716 }
else if (token == PP_NEWLINE) {
717 substituted += symbol();
720 substituted += symbol();
751 if (test(PP_QUESTION)) {
754 return value ? alt1 : alt2;
876 return remainder ? value % remainder : 0;
881 return div ? value / div : 0;
924 if (test(PP_LPAREN)) {
929 const QByteArray &lex = lexem();
930 auto lexView = QByteArrayView(lex);
931 if (lex.endsWith(
'L'))
933 value = lexView.toInt(
nullptr, 0);
941 return (t == PP_IDENTIFIER
942 || t == PP_INTEGER_LITERAL
943 || t == PP_FLOATING_LITERAL
952 expression.currentFilenames = currentFilenames;
961 const qint64 size = file->size();
962 char *rawInput =
reinterpret_cast<
char*>(file->map(0, size));
963 return rawInput ? QByteArray::fromRawData(rawInput, size) : file->readAll();
969 for (Symbols::iterator i = symbols.begin(); i != symbols.end(); ++i) {
970 if (i->token == STRING_LITERAL) {
971 Symbols::Iterator mergeSymbol = i;
972 qsizetype literalsLength = mergeSymbol->len;
973 while (++i != symbols.end() && i->token == STRING_LITERAL)
974 literalsLength += i->len - 2;
976 if (literalsLength != mergeSymbol->len) {
977 QByteArray mergeSymbolOriginalLexem = mergeSymbol->unquotedLexem();
978 QByteArray &mergeSymbolLexem = mergeSymbol->lex;
979 mergeSymbolLexem.resize(0);
980 mergeSymbolLexem.reserve(literalsLength);
981 mergeSymbolLexem.append(
'"');
982 mergeSymbolLexem.append(mergeSymbolOriginalLexem);
983 for (Symbols::iterator j = mergeSymbol + 1; j != i; ++j)
984 mergeSymbolLexem.append(j->lex.constData() + j->from + 1, j->len - 2);
985 mergeSymbolLexem.append(
'"');
986 mergeSymbol->len = mergeSymbol->lex.size();
987 mergeSymbol->from = 0;
988 i = symbols.erase(mergeSymbol + 1, i);
990 if (i == symbols.end())
997 const QByteArray &include,
998 const bool debugIncludes)
1002 if (Q_UNLIKELY(debugIncludes)) {
1003 fprintf(stderr,
"debug-includes: searching for '%s'\n", include.constData());
1006 for (
const Parser::IncludePath &p : includepaths) {
1010 if (p.isFrameworkPath) {
1011 const qsizetype slashPos = include.indexOf(
'/');
1014 fi.setFile(QString::fromLocal8Bit(p.path +
'/' + include.left(slashPos) +
".framework/Headers/"),
1015 QString::fromLocal8Bit(include.mid(slashPos + 1)));
1017 fi.setFile(QString::fromLocal8Bit(p.path), QString::fromLocal8Bit(include));
1020 if (Q_UNLIKELY(debugIncludes)) {
1021 const auto candidate = fi.filePath().toLocal8Bit();
1022 fprintf(stderr,
"debug-includes: considering '%s'\n", candidate.constData());
1033 if (!fi.exists() || fi.isDir()) {
1034 if (Q_UNLIKELY(debugIncludes)) {
1035 fprintf(stderr,
"debug-includes: can't find '%s'\n", include.constData());
1037 return QByteArray();
1040 const auto result = fi.canonicalFilePath().toLocal8Bit();
1042 if (Q_UNLIKELY(debugIncludes)) {
1043 fprintf(stderr,
"debug-includes: found '%s'\n", result.constData());
1051 if (!relativeTo.isEmpty()) {
1053 fi.setFile(QFileInfo(QString::fromLocal8Bit(relativeTo)).dir(), QString::fromLocal8Bit(include));
1054 if (fi.exists() && !fi.isDir())
1055 return fi.canonicalFilePath().toLocal8Bit();
1058 auto it = nonlocalIncludePathResolutionCache.find(include);
1059 if (it == nonlocalIncludePathResolutionCache.end())
1060 it = nonlocalIncludePathResolutionCache.insert(include,
1070 currentFilenames.push(filename);
1071 preprocessed.reserve(preprocessed.size() + symbols.size());
1073 Token token = next();
1078 int lineNum = symbol().lineNum;
1081 if (test(PP_STRING_LITERAL)) {
1082 local = lexem().startsWith(
'\"');
1083 include = unquotedLexem();
1088 include = resolveInclude(include, local ? filename : QByteArray());
1089 if (include.isNull())
1092 if (Preprocessor::preprocessedIncludes.contains(include))
1094 Preprocessor::preprocessedIncludes.insert(include);
1096 QFile file(QString::fromLocal8Bit(include.constData()));
1097 if (!file.open(QFile::ReadOnly))
1100 QByteArray input = readOrMapFile(&file);
1103 if (input.isEmpty())
1106 Symbols saveSymbols = symbols;
1107 qsizetype saveIndex = index;
1110 input = cleaned(input);
1113 symbols = tokenize(input);
1119 preprocessed +=
Symbol(0, MOC_INCLUDE_BEGIN, include);
1120 preprocess(include, preprocessed);
1121 preprocessed +=
Symbol(lineNum, MOC_INCLUDE_END, include);
1123 symbols = saveSymbols;
1130 QByteArray name = lexem();
1131 if (name.isEmpty() || !is_ident_start(name[0]))
1142 qsizetype start = index;
1144 macro.symbols.reserve(index - start - 1);
1148 Token lastToken = HASH;
1149 for (qsizetype i = start; i < index - 1; ++i) {
1150 Token token = symbols.at(i).token;
1151 if (token == WHITESPACE) {
1152 if (lastToken == PP_HASH || lastToken == HASH ||
1153 lastToken == PP_HASHHASH ||
1154 lastToken == WHITESPACE)
1156 }
else if (token == PP_HASHHASH) {
1157 if (!macro.symbols.isEmpty() &&
1158 lastToken == WHITESPACE)
1159 macro.symbols.pop_back();
1161 macro.symbols.append(symbols.at(i));
1165 while (!macro.symbols.isEmpty() &&
1166 (macro.symbols.constLast().token == PP_WHITESPACE || macro.symbols.constLast().token == WHITESPACE))
1167 macro.symbols.pop_back();
1169 if (!macro.symbols.isEmpty()) {
1170 if (macro.symbols.constFirst().token == PP_HASHHASH ||
1171 macro.symbols.constLast().token == PP_HASHHASH) {
1172 error(
"'##' cannot appear at either end of a macro expansion");
1175 macros.insert(name, macro);
1180 QByteArray name = lexem();
1185 case PP_IDENTIFIER: {
1187 macroExpand(&preprocessed,
this, symbols, index, symbol().lineNum,
true);
1199 if (test(PP_ELIF)) {
1218 if (
macros.contains(
"QT_NO_KEYWORDS"))
1221 sym
.token = (token == SIGNALS ? Q_SIGNALS_TOKEN : Q_SLOTS_TOKEN);
1222 preprocessed += sym;
1227 preprocessed += symbol();
1230 currentFilenames.pop();
1235 QByteArray input = readOrMapFile(file);
1237 if (input.isEmpty())
1241 input = cleaned(input);
1245 symbols = tokenize(input);
1248 for (
int j = 0; j < symbols.size(); ++j)
1249 fprintf(stderr,
"line %d: %s(%s)\n",
1251 symbols[j].lexem().constData(),
1252 tokenTypeName(symbols[j].token));
1260 result.reserve(file->size() / 300000);
1261 preprocess(filename, result);
1262 mergeStringLiterals(&result);
1265 for (
int j = 0; j < result.size(); ++j)
1266 fprintf(stderr,
"line %d: %s(%s)\n",
1268 result[j].lexem().constData(),
1269 tokenTypeName(result[j].token));
1279 while (test(PP_WHITESPACE)) {}
1283 if (t != PP_IDENTIFIER) {
1284 QByteArray l = lexem();
1287 arguments +=
Symbol(symbol().lineNum, PP_IDENTIFIER,
"__VA_ARGS__");
1288 while (test(PP_WHITESPACE)) {}
1289 if (!test(PP_RPAREN))
1290 error(
"missing ')' in macro argument list");
1292 }
else if (!is_identifier(l.constData(), l.size())) {
1293 error(
"Unexpected character in macro argument list.");
1298 if (arguments.contains(arg))
1299 error(
"Duplicate macro parameter.");
1300 arguments += symbol();
1302 while (test(PP_WHITESPACE)) {}
1308 if (lexem() ==
"...") {
1312 while (test(PP_WHITESPACE)) {}
1313 if (!test(PP_RPAREN))
1314 error(
"missing ')' in macro argument list");
1317 error(
"Unexpected character in macro argument list.");
1319 m->arguments = arguments;
1320 while (test(PP_WHITESPACE)) {}
1325 while(hasNext() && next() != t)
1331 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)
Combined button and popup list for selecting options.
static const short pp_keyword_trans[][128]
static QByteArray readOrMapFile(QFile *file)
static void mergeStringLiterals(Symbols *_symbols)
static QByteArray searchIncludePaths(const QList< Parser::IncludePath > &includepaths, const QByteArray &include, const bool debugIncludes)
static QByteArray cleaned(const QByteArray &input)
Simple structure used by the Doc and DocParser classes.
Symbol(int lineNum, Token token)