8using namespace QMakeInternal;
11#ifdef PROPARSER_THREAD_SAFE
12# include <qthreadpool.h>
23ProFileCache::ProFileCache()
30 for (
const Entry &ent : std::as_const(parsed_files))
42 if (cid && cid != eid)
48#ifdef PROPARSER_THREAD_SAFE
49 QMutexLocker lck(&mutex);
51 auto it = parsed_files.find(id);
52 if (it != parsed_files.end()) {
53#ifdef PROPARSER_THREAD_SAFE
55 if (!it->locker->done) {
56 ++it->locker->waiters;
57 it->locker->cond.wait(&mutex);
58 if (!--it->locker->waiters) {
67 parsed_files.erase(it);
73#ifdef PROPARSER_THREAD_SAFE
74 QMutexLocker lck(&mutex);
76 auto it = parsed_files.begin(), end = parsed_files.end();
79 QString fn = vfs->fileNameForId(it.key());
80 if (fn.startsWith(prefix)) {
81#ifdef PROPARSER_THREAD_SAFE
83 if (!it->locker->done) {
84 ++it->locker->waiters;
85 it->locker->cond.wait(&mutex);
86 if (!--it->locker->waiters) {
95 it = parsed_files.erase(it);
104#define fL1S(s) QString::fromLatin1(s)
111 QString strdefineTest;
112 QString strdefineReplace;
113 QString strbypassNesting;
118 QString strhost_build;
121 QString strLITERAL_HASH;
122 QString strLITERAL_DOLLAR;
123 QString strLITERAL_WHITESPACE;
130 if (!statics.strelse.isNull())
133 statics.strelse = QLatin1String(
"else");
134 statics.strfor = QLatin1String(
"for");
135 statics.strdefineTest = QLatin1String(
"defineTest");
136 statics.strdefineReplace = QLatin1String(
"defineReplace");
137 statics.strbypassNesting = QLatin1String(
"bypassNesting");
138 statics.stroption = QLatin1String(
"option");
139 statics.strreturn = QLatin1String(
"return");
140 statics.strnext = QLatin1String(
"next");
141 statics.strbreak = QLatin1String(
"break");
142 statics.strhost_build = QLatin1String(
"host_build");
143 statics.strLINE = QLatin1String(
"_LINE_");
144 statics.strFILE = QLatin1String(
"_FILE_");
145 statics.strLITERAL_HASH = QLatin1String(
"LITERAL_HASH");
146 statics.strLITERAL_DOLLAR = QLatin1String(
"LITERAL_DOLLAR");
147 statics.strLITERAL_WHITESPACE = QLatin1String(
"LITERAL_WHITESPACE");
162 QMakeVfs::VfsFlags vfsFlags = ((flags & ParseCumulative) ? QMakeVfs::VfsCumulative
163 : QMakeVfs::VfsExact);
164 int id = m_vfs->idForFileName(fileName, vfsFlags);
167#ifdef PROPARSER_THREAD_SAFE
168 QMutexLocker locker(&m_cache->mutex);
170 auto it = m_cache->parsed_files.find(id);
171 if (it != m_cache->parsed_files.end()) {
173#ifdef PROPARSER_THREAD_SAFE
174 if (ent->locker && !ent->locker->done) {
175 ++ent->locker->waiters;
176 QThreadPool::globalInstance()->releaseThread();
177 ent->locker->cond.wait(locker.mutex());
178 QThreadPool::globalInstance()->reserveThread();
179 if (!--ent->locker->waiters) {
185 if ((pro = ent->pro))
188 ent = &m_cache->parsed_files[id];
189#ifdef PROPARSER_THREAD_SAFE
190 ent->locker =
new ProFileCache::Entry::Locker;
194 if (readFile(id, flags, &contents)) {
195 pro = parsedProBlock(QStringView(contents), id, fileName, 1, FullGrammar);
202#ifdef PROPARSER_THREAD_SAFE
204 if (ent->locker->waiters) {
205 ent->locker->done =
true;
206 ent->locker->cond.wakeAll();
215 if (readFile(id, flags, &contents))
216 pro = parsedProBlock(QStringView(contents), id, fileName, 1, FullGrammar);
224 QStringView contents,
int id,
const QString &name,
int line,
SubGrammar grammar)
227 read(pro, contents, line, grammar);
237bool QMakeParser::readFile(
int id, ParseFlags flags, QString *contents)
242 if (m_handler && ((flags & ParseReportMissing) || result != QMakeVfs::ReadNotFound))
243 m_handler->message(QMakeParserHandler::ParserIoError,
244 fL1S(
"Cannot read %1: %2").arg(m_vfs->fileNameForId(id), errStr));
250void QMakeParser::putTok(ushort *&tokPtr, ushort tok)
255void QMakeParser::putBlockLen(ushort *&tokPtr, uint len)
257 *tokPtr++ = (ushort)len;
258 *tokPtr++ = (ushort)(len >> 16);
261void QMakeParser::putBlock(ushort *&tokPtr,
const ushort *buf, uint len)
263 memcpy(tokPtr, buf, len * 2);
267void QMakeParser::putHashStr(ushort *&pTokPtr,
const ushort *buf, uint len)
269 const size_t hash = ProString::hash((
const QChar *)buf, len);
270 ushort *tokPtr = pTokPtr;
271 *tokPtr++ = (ushort)hash;
272 *tokPtr++ = (ushort)(hash >> 16);
273 *tokPtr++ = (ushort)len;
275 memcpy(tokPtr, buf, len * 2);
276 pTokPtr = tokPtr + len;
279void QMakeParser::finalizeHashStr(ushort *buf, uint len)
283 const size_t hash = ProString::hash((
const QChar *)buf, len);
284 buf[-3] = (ushort)hash;
285 buf[-2] = (ushort)(hash >> 16);
321 tokBuff.reserve((in.size() + 1) * 7);
322 ushort *tokPtr = (ushort *)tokBuff.constData();
326 xprBuff.reserve(tokBuff.capacity());
327 ushort *buf = (ushort *)xprBuff.constData();
330 m_blockstack.clear();
331 m_blockstack.resize(1);
333 QStack<ParseCtx> xprStack;
334 xprStack.reserve(10);
336 const ushort *cur = (
const ushort *)in.data();
337 const ushort *inend = cur + in.size();
342 m_operator = NoOperator;
343 m_markLine = m_lineNo;
349 bool lineMarked =
true;
357 context = CtxPureValue;
363 ushort *xprPtr = ptr;
365#define FLUSH_LHS_LITERAL()
367 if ((tlen = ptr - xprPtr)) {
368 finalizeHashStr(xprPtr, tlen);
378#define FLUSH_RHS_LITERAL()
380 if ((tlen = ptr - xprPtr)) {
381 xprPtr[-2
] = TokLiteral | needSep;
392#define FLUSH_LITERAL()
394 if (context == CtxTest)
400#define FLUSH_VALUE_LIST()
404 if (*xprPtr == TokLine)
406 tokPtr[-1
] = ((*xprPtr & TokMask) == TokLiteral) ? wordCount : 0
;
411 putTok(tokPtr, TokValueTerminator);
419 if (context == CtxPureValue) {
431 for (indent = 0; ; ++cur, ++indent) {
441 if (c !=
' ' && c !=
'\t' && c !=
'\r')
446 for (cptr = cur;; ++cptr) {
454 while (++cptr < inend) {
461 if (m_markLine == m_lineNo)
478 ushort ec = *(end - 1);
484 if (ec !=
' ' && ec !=
'\t' && ec !=
'\r') {
499 }
while (c ==
' ' || c ==
'\t');
508 *ptr++ = (ushort)m_lineNo;
518 }
else if (c ==
'{') {
522 }
else if (c ==
'(') {
532 while ((c & 0xFF00) || c ==
'.' || c ==
'_' ||
533 (c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') ||
534 (c >=
'0' && c <=
'9') || (c ==
'/' && term)) {
542 if (tok == TokVariable && c ==
'(')
546 languageWarning(
fL1S(
"Missing name in expansion"));
554 if (rtok != TokVariable
555 || !resolveVariable(xprPtr, tlen, needSep, &ptr,
556 &buf, &xprBuff, &tokPtr, &tokBuff, cur, in)) {
557 if (rtok == TokVariable || rtok == TokProperty) {
559 const size_t hash = ProString::hash((
const QChar *)xprPtr, tlen);
560 xprPtr[-3] = (ushort)hash;
561 xprPtr[-2] = (ushort)(hash >> 16);
568 if ((tok & TokMask) == TokFuncName) {
572 xprStack.resize(xprStack.size() + 1);
573 ParseCtx &top = xprStack.top();
576 top.terminator = term;
577 top.context = context;
579 top.wordCount = wordCount;
589 ptr += (context == CtxTest) ? 4 : 2;
597 parseError(
fL1S(
"Missing %1 terminator [found %2]")
599 .arg(c ? QString(QChar(c)) : QString::fromLatin1(
"end-of-line")));
607 ptr += (context == CtxTest) ? 4 : 2;
612 }
else if (c ==
'\\') {
613 static const char symbols[] =
"[]{}()$\\'\"";
615 if (cur != end && !((c2 = *cur) & 0xff00) && strchr(symbols, c2)) {
619 deprecationWarning(
fL1S(
"Unescaped backslashes are deprecated"));
625 }
else if (c ==
'!' && ptr == xprPtr && context == CtxTest) {
629 }
else if (c ==
'\'' || c ==
'"') {
632 }
else if (context == CtxArgs) {
634 if (c ==
' ' || c ==
'\t') {
637 }
else if (c ==
'(') {
639 }
else if (c ==
')') {
642 *ptr++ = TokFuncTerminator;
645 ParseCtx &top = xprStack.top();
648 term = top.terminator;
649 context = top.context;
651 wordCount = top.wordCount;
652 xprStack.resize(xprStack.size() - 1);
655 finalizeCall(tokPtr, buf, ptr, theargc);
657 }
else if (term ==
'}') {
658 c = (cur == end) ? 0 : *cur;
665 }
else if (!parens && c ==
',') {
667 *ptr++ = TokArgSeparator;
671 }
else if (context == CtxTest) {
673 if (c ==
' ' || c ==
'\t') {
676 }
else if (c ==
'(') {
678 if (wordCount != 1) {
680 parseError(
fL1S(
"Extra characters after test expression."));
682 parseError(
fL1S(
"Opening parenthesis without prior test name."));
685 *ptr++ = TokTestCall;
688 }
else if (c ==
'!' && ptr == xprPtr) {
691 }
else if (c ==
':') {
693 finalizeCond(tokPtr, buf, ptr, wordCount);
694 warnOperator(
"in front of AND operator");
695 if (m_state == StNew)
696 parseError(
fL1S(
"AND operator without prior condition."));
698 m_operator = AndOperator;
702 }
else if (c ==
'|') {
704 finalizeCond(tokPtr, buf, ptr, wordCount);
705 warnOperator(
"in front of OR operator");
706 if (m_state != StCond)
707 parseError(
fL1S(
"OR operator without prior condition."));
709 m_operator = OrOperator;
711 }
else if (c ==
'{') {
713 finalizeCond(tokPtr, buf, ptr, wordCount);
714 if (m_operator == AndOperator) {
715 languageWarning(
fL1S(
"Excess colon in front of opening brace."));
716 m_operator = NoOperator;
718 failOperator(
"in front of opening brace");
721 ++m_blockstack.top().braceLevel;
722 if (grammar == TestGrammar)
723 parseError(
fL1S(
"Opening scope not permitted in this context."));
725 }
else if (c ==
'}') {
727 finalizeCond(tokPtr, buf, ptr, wordCount);
731 failOperator(
"in front of closing brace");
732 if (!m_blockstack.top().braceLevel) {
733 parseError(
fL1S(
"Excess closing brace."));
734 }
else if (!--m_blockstack.top().braceLevel
735 && m_blockstack.size() != 1) {
739 m_markLine = m_lineNo;
742 }
else if (c ==
'+') {
745 }
else if (c ==
'-') {
748 }
else if (c ==
'*') {
749 tok = TokAppendUnique;
751 }
else if (c ==
'~') {
758 }
else if (c ==
'=') {
763 acceptColon(
"in front of assignment");
764 putLineMarker(tokPtr);
765 if (grammar == TestGrammar) {
766 parseError(
fL1S(
"Assignment not permitted in this context."));
767 }
else if (wordCount != 1) {
768 parseError(
fL1S(
"Assignment needs exactly one word on the left hand side."));
771 putBlock(tokPtr, buf, ptr - buf);
778 }
else if (context == CtxValue) {
779 if (c ==
' ' || c ==
'\t') {
782 }
else if (c ==
'{') {
784 }
else if (c ==
'}') {
792 }
else if (c ==
'=') {
793 if (indent < lastIndent)
794 languageWarning(
fL1S(
"Possible accidental line continuation"));
811 ptr += (context == CtxTest) ? 4 : 2;
819 parseError(
fL1S(
"Missing closing %1 quote").arg(QChar(quote)));
820 if (!xprStack.isEmpty()) {
821 context = xprStack.at(0).context;
825 }
else if (!xprStack.isEmpty()) {
826 parseError(
fL1S(
"Missing closing parenthesis in function call"));
827 context = xprStack.at(0).context;
831 if (context == CtxValue) {
833 putTok(tokPtr, TokValueTerminator);
834 }
else if (context == CtxPureValue) {
835 putTok(tokPtr, TokValueTerminator);
837 bogusTest(tokPtr, QString());
839 }
else if (context == CtxValue) {
842 languageWarning(
fL1S(
"Possible braces mismatch"));
843 }
else if (context == CtxPureValue) {
845 putTok(tokPtr, TokValueTerminator);
847 finalizeCond(tokPtr, buf, ptr, wordCount);
848 warnOperator(
"at end of line");
864 if (m_blockstack.size() > 1 || m_blockstack.top().braceLevel)
865 parseError(
fL1S(
"Missing closing brace(s)."));
866 while (m_blockstack.size())
868 tokBuff.resize(tokPtr - (ushort *)tokBuff.constData());
871#undef FLUSH_VALUE_LIST
873#undef FLUSH_LHS_LITERAL
874#undef FLUSH_RHS_LITERAL
881 *tokPtr++ = (ushort)m_markLine;
886void QMakeParser::enterScope(ushort *&tokPtr,
bool special, ScopeState state)
888 uchar nest = m_blockstack.top().nest;
889 m_blockstack.resize(m_blockstack.size() + 1);
890 m_blockstack.top().special = special;
891 m_blockstack.top().start = tokPtr;
892 m_blockstack.top().nest = nest;
897 m_markLine = m_lineNo;
902 if (m_blockstack.top().inBranch) {
904 putBlockLen(tokPtr, 0);
906 if (ushort *start = m_blockstack.top().start) {
908 uint len = tokPtr - start - 2;
909 start[0] = (ushort)len;
910 start[1] = (ushort)(len >> 16);
912 m_blockstack.resize(m_blockstack.size() - 1);
918 if (m_state == StNew) {
919 while (!m_blockstack.top().braceLevel && m_blockstack.size() > 1)
921 if (m_blockstack.top().inBranch) {
922 m_blockstack.top().inBranch =
false;
924 putBlockLen(tokPtr, 0);
933 if (m_state == StCond) {
935 m_blockstack.top().inBranch =
true;
936 enterScope(tokPtr,
false, StNew);
945 languageWarning(
fL1S(
"Stray NOT operator %1.").arg(
fL1S(msg)));
948 if (m_operator == AndOperator) {
949 languageWarning(
fL1S(
"Stray AND operator %1.").arg(
fL1S(msg)));
950 m_operator = NoOperator;
951 }
else if (m_operator == OrOperator) {
952 languageWarning(
fL1S(
"Stray OR operator %1.").arg(
fL1S(msg)));
953 m_operator = NoOperator;
961 parseError(
fL1S(
"Unexpected NOT operator %1.").arg(
fL1S(msg)));
965 if (m_operator == AndOperator) {
966 parseError(
fL1S(
"Unexpected AND operator %1.").arg(
fL1S(msg)));
967 m_operator = NoOperator;
969 }
else if (m_operator == OrOperator) {
970 parseError(
fL1S(
"Unexpected OR operator %1.").arg(
fL1S(msg)));
971 m_operator = NoOperator;
979 if (m_operator == AndOperator)
980 m_operator = NoOperator;
981 return !failOperator(msg);
986 if (m_operator== AndOperator) {
989 if (m_state == StCond)
991 m_operator = NoOperator;
992 }
else if (m_operator == OrOperator) {
993 putTok(tokPtr,
TokOr);
994 m_operator = NoOperator;
1000 flushScopes(tokPtr);
1001 putLineMarker(tokPtr);
1002 putOperator(tokPtr);
1010void QMakeParser::bogusTest(ushort *&tokPtr,
const QString &msg)
1014 flushScopes(tokPtr);
1015 m_operator = NoOperator;
1021void QMakeParser::finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr,
int wordCount)
1023 if (wordCount != 1) {
1025 bogusTest(tokPtr,
fL1S(
"Extra characters after test expression."));
1032 ushort *uce = uc + 4 + nlen;
1034 m_tmp.setRawData((QChar *)uc + 4, nlen);
1035 if (!m_tmp.compare(statics.strelse, Qt::CaseInsensitive)) {
1036 if (failOperator(
"in front of else"))
1038 BlockScope &top = m_blockstack.top();
1039 if (m_canElse && (!top.special || top.braceLevel)) {
1044 putBlockLen(tokPtr, 0);
1045 enterScope(tokPtr,
false, StCtrl);
1049 BlockScope &top = m_blockstack.top();
1050 if (top.inBranch && (!top.special || top.braceLevel)) {
1051 top.inBranch =
false;
1052 enterScope(tokPtr,
false, StCtrl);
1055 if (top.braceLevel || m_blockstack.size() == 1)
1059 parseError(
fL1S(
"Unexpected 'else'."));
1065 finalizeTest(tokPtr);
1066 putBlock(tokPtr, uc, ptr - uc);
1070void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr,
int argc)
1075 ushort *uce = uc + 4 + nlen;
1078 m_tmp.setRawData((QChar *)uc + 4, nlen);
1079 const QString *defName;
1081 if (m_tmp == statics.strfor) {
1082 if (!acceptColon(
"in front of for()")) {
1083 bogusTest(tokPtr, QString());
1087 putLineMarker(tokPtr);
1092 uc = uce + 2 + nlen;
1096 putHashStr(tokPtr,
nullptr, (uint)0);
1097 putBlockLen(tokPtr, 1 + 3 + nlen + 1);
1099 putHashStr(tokPtr, uce + 2, nlen);
1102 enterScope(tokPtr,
true, StCtrl);
1103 m_blockstack.top().nest |= NestLoop;
1109 putHashStr(tokPtr, uce + 2, nlen);
1112 putBlockLen(tokPtr, nlen + 1);
1113 putBlock(tokPtr, uc, nlen);
1116 }
else if (argc == 1) {
1119 putHashStr(tokPtr,
nullptr, (uint)0);
1123 parseError(
fL1S(
"Syntax is for(var, list), for(var, forever) or for(ever)."));
1125 }
else if (m_tmp == statics.strdefineReplace) {
1126 defName = &statics.strdefineReplace;
1129 }
else if (m_tmp == statics.strdefineTest) {
1130 defName = &statics.strdefineTest;
1134 bogusTest(tokPtr,
fL1S(
"Unexpected NOT operator in front of function definition."));
1137 flushScopes(tokPtr);
1138 putLineMarker(tokPtr);
1142 putOperator(tokPtr);
1143 putTok(tokPtr, defType);
1144 putHashStr(tokPtr, uce + 2, nlen);
1145 enterScope(tokPtr,
true, StCtrl);
1146 m_blockstack.top().nest = NestFunction;
1150 parseError(
fL1S(
"%1(function) requires one literal argument.").arg(*defName));
1152 }
else if (m_tmp == statics.strbypassNesting) {
1154 bogusTest(tokPtr,
fL1S(
"%1() requires zero arguments.").arg(m_tmp));
1157 if (!(m_blockstack.top().nest & NestFunction)) {
1158 bogusTest(tokPtr,
fL1S(
"Unexpected %1().").arg(m_tmp));
1162 bogusTest(tokPtr,
fL1S(
"Unexpected NOT operator in front of %1().").arg(m_tmp));
1165 flushScopes(tokPtr);
1166 putLineMarker(tokPtr);
1167 putOperator(tokPtr);
1169 enterScope(tokPtr,
true, StCtrl);
1171 }
else if (m_tmp == statics.strreturn) {
1172 if (m_blockstack.top().nest & NestFunction) {
1174 bogusTest(tokPtr,
fL1S(
"return() requires zero or one argument."));
1179 bogusTest(tokPtr,
fL1S(
"Top-level return() requires zero arguments."));
1185 }
else if (m_tmp == statics.strnext) {
1188 }
else if (m_tmp == statics.strbreak) {
1192 bogusTest(tokPtr,
fL1S(
"%1() requires zero arguments.").arg(m_tmp));
1195 if (!(m_blockstack.top().nest & NestLoop)) {
1196 bogusTest(tokPtr,
fL1S(
"Unexpected %1().").arg(m_tmp));
1201 bogusTest(tokPtr,
fL1S(
"Unexpected NOT operator in front of %1().").arg(m_tmp));
1204 finalizeTest(tokPtr);
1205 putBlock(tokPtr, uce, ptr - uce - 1);
1206 putTok(tokPtr, defType);
1208 }
else if (m_tmp == statics.stroption) {
1209 if (m_state != StNew || m_blockstack.top().braceLevel || m_blockstack.size() > 1
1210 || m_invert || m_operator != NoOperator) {
1211 bogusTest(tokPtr,
fL1S(
"option() must appear outside any control structures."));
1217 m_tmp.setRawData((QChar *)uce + 2, nlen);
1218 if (m_tmp == statics.strhost_build)
1221 parseError(
fL1S(
"Unknown option() %1.").arg(m_tmp));
1225 parseError(
fL1S(
"option() requires one literal argument."));
1231 finalizeTest(tokPtr);
1232 putBlock(tokPtr, uc, ptr - uc);
1235bool QMakeParser::resolveVariable(ushort *xprPtr,
int tlen,
int needSep, ushort **ptr,
1236 ushort **buf, QString *xprBuff,
1237 ushort **tokPtr, QString *tokBuff,
1238 const ushort *cur, QStringView in)
1241 m_tmp.setRawData((
const QChar *)xprPtr, tlen);
1242 if (m_tmp == statics.strLINE) {
1243 out.setNum(m_lineNo);
1244 }
else if (m_tmp == statics.strFILE) {
1245 out = m_proFile->fileName();
1249 int need = (in.size() - (cur - (
const ushort *)in.constData()) + 2) * 5 + out.size();
1250 int tused = *tokPtr - (ushort *)tokBuff->constData();
1253 bool ptrFinal = xprPtr >= (ushort *)tokBuff->constData()
1254 && xprPtr < (ushort *)tokBuff->constData() + tokBuff->capacity();
1256 xused = xprPtr - (ushort *)tokBuff->constData();
1257 total = xused + need;
1259 xused = xprPtr - *buf;
1260 total = tused + xused + need;
1262 if (tokBuff->capacity() < total) {
1263 tokBuff->reserve(total);
1264 *tokPtr = (ushort *)tokBuff->constData() + tused;
1265 xprBuff->reserve(total);
1266 *buf = (ushort *)xprBuff->constData();
1267 xprPtr = (ptrFinal ? (ushort *)tokBuff->constData() : *buf) + xused;
1269 }
else if (m_tmp == statics.strLITERAL_HASH) {
1270 out = QLatin1String(
"#");
1271 }
else if (m_tmp == statics.strLITERAL_DOLLAR) {
1272 out = QLatin1String(
"$");
1273 }
else if (m_tmp == statics.strLITERAL_WHITESPACE) {
1274 out = QLatin1String(
"\t");
1280 xprPtr[-1] = out.size();
1281 memcpy(xprPtr, out.constData(), out.size() * 2);
1282 *ptr = xprPtr + out.size();
1286void QMakeParser::message(
int type,
const QString &msg)
const
1288 if (!m_inError && m_handler)
1292#ifdef PROPARSER_DEBUG
1294#define BOUNDS_CHECK(need)
1296 int have = limit - offset;
1297 if (have < (int)need) {
1298 *outStr += fL1S("<out of bounds (need %1, got %2)>").arg(need).arg(have);
1303static bool getRawUshort(
const ushort *tokens,
int limit,
int &offset, ushort *outVal, QString *outStr)
1306 uint val = tokens[offset++];
1311static bool getUshort(
const ushort *tokens,
int limit,
int &offset, ushort *outVal, QString *outStr)
1313 *outStr += fL1S(
" << H(");
1314 if (!getRawUshort(tokens, limit, offset, outVal, outStr))
1316 *outStr += QString::number(*outVal) + QLatin1Char(
')');
1320static bool getRawUint(
const ushort *tokens,
int limit,
int &offset, uint *outVal, QString *outStr)
1323 uint val = tokens[offset++];
1324 val |= (uint)tokens[offset++] << 16;
1329static bool getUint(
const ushort *tokens,
int limit,
int &offset, uint *outVal, QString *outStr)
1331 *outStr += fL1S(
" << I(");
1332 if (!getRawUint(tokens, limit, offset, outVal, outStr))
1334 *outStr += QString::number(*outVal) + QLatin1Char(
')');
1338static bool getRawStr(
const ushort *tokens,
int limit,
int &offset,
int strLen, QString *outStr)
1340 BOUNDS_CHECK(strLen);
1341 *outStr += fL1S(
"L\"");
1343 for (
int i = 0; i < strLen; i++) {
1344 ushort val = tokens[offset++];
1346 case '"': *outStr += fL1S(
"\\\"");
break;
1347 case '\n': *outStr += fL1S(
"\\n");
break;
1348 case '\r': *outStr += fL1S(
"\\r");
break;
1349 case '\t': *outStr += fL1S(
"\\t");
break;
1350 case '\\': *outStr += fL1S(
"\\\\");
break;
1352 if (val < 32 || val > 126) {
1353 *outStr += (val > 255 ? fL1S(
"\\u") : fL1S(
"\\x")) + QString::number(val, 16);
1357 if (attn && isxdigit(val))
1358 *outStr += fL1S(
"\"\"");
1359 *outStr += QChar(val);
1364 *outStr += QLatin1Char(
'"');
1368static bool getStr(
const ushort *tokens,
int limit,
int &offset, QString *outStr)
1370 *outStr += fL1S(
" << S(");
1372 if (!getRawUshort(tokens, limit, offset, &len, outStr))
1374 if (!getRawStr(tokens, limit, offset, len, outStr))
1376 *outStr += QLatin1Char(
')');
1380static bool getHashStr(
const ushort *tokens,
int limit,
int &offset, QString *outStr)
1382 *outStr += fL1S(
" << HS(");
1384 if (!getRawUint(tokens, limit, offset, &hash, outStr))
1387 if (!getRawUshort(tokens, limit, offset, &len, outStr))
1389 const QChar *chars = (
const QChar *)tokens + offset;
1390 if (!getRawStr(tokens, limit, offset, len, outStr))
1392 uint realhash = ProString::hash(chars, len);
1393 if (realhash != hash)
1394 *outStr += fL1S(
" /* Bad hash ") + QString::number(hash) + fL1S(
" */");
1395 *outStr += QLatin1Char(
')');
1399static bool getBlock(
const ushort *tokens,
int limit,
int &offset, QString *outStr,
int indent);
1401static bool getSubBlock(
const ushort *tokens,
int limit,
int &offset, QString *outStr,
int indent,
1404 *outStr += fL1S(
"\n /* %1 */ ").arg(offset, 5)
1405 + QString(indent * 4, QLatin1Char(
' '))
1406 + fL1S(
"/* ") + fL1S(scope) + fL1S(
" */");
1408 if (!getUint(tokens, limit, offset, &len, outStr))
1412 int tmpOff = offset;
1415 if (!getBlock(tokens, offset, tmpOff, outStr, indent + 1))
1417 if (tmpOff == offset)
1419 *outStr += QLatin1Char(
'\n') + QString(20 + indent * 4, QLatin1Char(
' '))
1420 + fL1S(
"/* Warning: Excess tokens follow. */");
1426static bool getBlock(
const ushort *tokens,
int limit,
int &offset, QString *outStr,
int indent)
1428 static const char *
const tokNames[] = {
1431 "TokAssign",
"TokAppend",
"TokAppendUnique",
"TokRemove",
"TokReplace",
1432 "TokValueTerminator",
1433 "TokLiteral",
"TokHashLiteral",
"TokVariable",
"TokProperty",
"TokEnvVar",
1434 "TokFuncName",
"TokArgSeparator",
"TokFuncTerminator",
1435 "TokCondition",
"TokTestCall",
1436 "TokReturn",
"TokBreak",
"TokNext",
1437 "TokNot",
"TokAnd",
"TokOr",
1438 "TokBranch",
"TokForLoop",
1439 "TokTestDef",
"TokReplaceDef",
"TokBypassNesting"
1442 while (offset != limit) {
1443 *outStr += fL1S(
"\n /* %1 */").arg(offset, 5)
1444 + QString(indent * 4, QLatin1Char(
' '));
1446 ushort tok = tokens[offset++];
1447 ushort maskedTok = tok & TokMask;
1448 if (maskedTok >=
sizeof(tokNames)/
sizeof(tokNames[0])
1449 || (tok & ~(TokNewStr | TokQuoted | TokMask))) {
1450 *outStr += fL1S(
" << {invalid token %1}").arg(tok);
1453 *outStr += fL1S(
" << H(") + fL1S(tokNames[maskedTok]);
1454 if (tok & TokNewStr)
1455 *outStr += fL1S(
" | TokNewStr");
1456 if (tok & TokQuoted)
1457 *outStr += fL1S(
" | TokQuoted");
1458 *outStr += QLatin1Char(
')');
1460 switch (maskedTok) {
1461 case TokFuncTerminator:
1463 case TokArgSeparator:
1464 case TokValueTerminator:
1476 ok = getBlock(tokens, limit, offset, outStr, indent + 1);
1479 ok = getSubBlock(tokens, limit, offset, outStr, indent,
"then branch");
1481 ok = getSubBlock(tokens, limit, offset, outStr, indent,
"else branch");
1484 switch (maskedTok) {
1487 case TokAppendUnique:
1494 ok = getUshort(tokens, limit, offset, &dummy, outStr);
1498 ok = getStr(tokens, limit, offset, outStr);
1500 case TokHashLiteral:
1503 ok = getHashStr(tokens, limit, offset, outStr);
1506 ok = getHashStr(tokens, limit, offset, outStr);
1508 ok = getBlock(tokens, limit, offset, outStr, indent + 1);
1511 ok = getHashStr(tokens, limit, offset, outStr);
1513 ok = getSubBlock(tokens, limit, offset, outStr, indent,
"iterator");
1515 ok = getSubBlock(tokens, limit, offset, outStr, indent,
"body");
1519 ok = getHashStr(tokens, limit, offset, outStr);
1521 ok = getSubBlock(tokens, limit, offset, outStr, indent,
"body");
1523 case TokBypassNesting:
1524 ok = getSubBlock(tokens, limit, offset, outStr, indent,
"block");
1537QString QMakeParser::formatProBlock(
const QString &block)
1540 outStr += fL1S(
"\n << TS(");
1542 getBlock(
reinterpret_cast<
const ushort *>(block.constData()), block.length(),
1543 offset, &outStr, 0);
1544 outStr += QLatin1Char(
')');
void discardFile(const QString &fileName, QMakeVfs *vfs)
void discardFiles(const QString &prefix, QMakeVfs *vfs)
ProFile(int id, const QString &fileName)
void setHostBuild(bool host_build)
virtual void message(int type, const QString &msg, const QString &fileName=QString(), int lineNo=0)=0
void discardFileFromCache(int id)
ProFile * parsedProBlock(QStringView contents, int id, const QString &name, int line=0, SubGrammar grammar=FullGrammar)
ProFile * parsedProFile(const QString &fileName, ParseFlags flags=ParseDefault)
ReadResult readFile(int id, QString *contents, QString *errStr)
Combined button and popup list for selecting options.
#define FLUSH_VALUE_LIST()
#define FLUSH_LHS_LITERAL()
#define FLUSH_RHS_LITERAL()