File indexing completed on 2024-04-28 04:38:36
0001 /* 0002 SPDX-FileCopyrightText: 2007 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include "stringhelpers.h" 0008 0009 #include "debuglog.h" 0010 0011 #include <language/duchain/stringhelpers.h> 0012 0013 #include <QChar> 0014 #include <QString> 0015 0016 namespace { 0017 0018 enum { T_ACCESS, T_PAREN, T_BRACKET, T_IDE, T_UNKNOWN, T_TEMP }; 0019 0020 } 0021 0022 int Utils::expressionAt( const QString& text, int index ) { 0023 0024 if( index == 0 ) 0025 return 0; 0026 0027 int last = T_UNKNOWN; 0028 int start = index; 0029 --index; 0030 0031 while ( index > 0 ) { 0032 while ( index > 0 && text[ index ].isSpace() ) { 0033 --index; 0034 } 0035 0036 QChar ch = text[ index ]; 0037 const QStringRef ch2 = text.midRef(index - 1, 2); 0038 if ((last != T_IDE) && (ch.isLetterOrNumber() || ch == QLatin1Char('_'))) { 0039 while (index > 0 && (text[index].isLetterOrNumber() || text[index] == QLatin1Char('_'))) { 0040 --index; 0041 } 0042 last = T_IDE; 0043 } else if (last != T_IDE && ch == QLatin1Char(')')) { 0044 int count = 0; 0045 while ( index > 0 ) { 0046 QChar ch = text[ index ]; 0047 if (ch == QLatin1Char('(')) { 0048 ++count; 0049 } else if (ch == QLatin1Char(')')) { 0050 --count; 0051 } 0052 --index; 0053 if ( count == 0 ) { 0054 //index; 0055 last = T_PAREN; 0056 break; 0057 } 0058 } 0059 } else if (last != T_IDE && ch == QLatin1Char('>') && ch2 != QLatin1String("->")) { 0060 int count = 0; 0061 while ( index > 0 ) { 0062 QChar ch = text[ index ]; 0063 if (ch == QLatin1Char('<')) { 0064 ++count; 0065 } else if (ch == QLatin1Char('>')) { 0066 --count; 0067 } else if ( count == 0 ) { 0068 //--index; 0069 last = T_TEMP; 0070 break; 0071 } 0072 --index; 0073 } 0074 } else if (ch == QLatin1Char(']')) { 0075 int count = 0; 0076 while ( index > 0 ) { 0077 QChar ch = text[ index ]; 0078 if (ch == QLatin1Char('[')) { 0079 ++count; 0080 } else if (ch == QLatin1Char(']')) { 0081 --count; 0082 } else if ( count == 0 ) { 0083 //--index; 0084 last = T_BRACKET; 0085 break; 0086 } 0087 --index; 0088 } 0089 } else if (ch == QLatin1Char('.')) { 0090 --index; 0091 last = T_ACCESS; 0092 } else if ( ch2 == QLatin1String("::") ) { 0093 index -= 2; 0094 last = T_ACCESS; 0095 } else if ( ch2 == QLatin1String("->") ) { 0096 index -= 2; 0097 last = T_ACCESS; 0098 } else { 0099 if ( start > index ) { 0100 ++index; 0101 } 0102 last = T_UNKNOWN; 0103 break; 0104 } 0105 } 0106 0107 ///If we're at the first item, the above algorithm cannot be used safely, 0108 ///so just determine whether the sign is valid for the beginning of an expression, if it isn't reject it. 0109 if (index == 0 && start > index && 0110 !(text[index].isLetterOrNumber() || text[index] == QLatin1Char('_') || text[index] == QLatin1Char(':'))) { 0111 ++index; 0112 } 0113 0114 return index; 0115 } 0116 0117 QString Utils::quoteExpression(const QString& expr) 0118 { 0119 return quote(expr, QLatin1Char('"')); 0120 } 0121 0122 QString Utils::unquoteExpression(const QString& expr) 0123 { 0124 return unquote(expr, false); 0125 } 0126 0127 QString Utils::quote(const QString& str, QChar quoteCh) 0128 { 0129 QString res = str; 0130 res.replace(QLatin1Char('\\'), QLatin1String("\\\\")).replace(quoteCh, QLatin1Char('\\') + quoteCh); 0131 return quoteCh + res + quoteCh; 0132 } 0133 0134 QString Utils::unquote(const QString& str, bool unescapeUnicode, QChar quoteCh) 0135 { 0136 if (str.startsWith(quoteCh) && str.endsWith(quoteCh)) { 0137 QString res; 0138 res.reserve(str.length()); 0139 bool esc = false; 0140 int type = 0; 0141 QString escSeq; 0142 escSeq.reserve(4); 0143 // skip beginning and ending quoteCh, no need for str = str.mid(1, str.length() - 2) 0144 for (int i = 1; i != str.length() - 1; i++) { 0145 auto ch = str[i]; 0146 if (esc) { 0147 switch (ch.unicode()) { 0148 case '\\': 0149 if (type != 0) { 0150 escSeq += ch; 0151 qCDebug(DEBUGGERCOMMON) << "Unrecognized escape sequence:" << escSeq; 0152 res += QLatin1Char('\\'); 0153 res += escSeq; 0154 escSeq.clear(); 0155 esc = false; 0156 type = 0; 0157 } else { 0158 res.append(QLatin1Char('\\')); 0159 // escSeq.clear(); // escSeq must be empty. 0160 esc = false; 0161 } 0162 break; 0163 case 'u': 0164 case 'x': 0165 if (type != 0 || !unescapeUnicode) { 0166 escSeq += ch; 0167 qCDebug(DEBUGGERCOMMON) << "Unrecognized escape sequence:" << escSeq; 0168 res += QLatin1Char('\\'); 0169 res += escSeq; 0170 escSeq.clear(); 0171 esc = false; 0172 type = 0; 0173 } else { 0174 type = ch == QLatin1Char('u') ? 1 : 2; 0175 } 0176 break; 0177 case '0': case '1': case '2': case '3': case '4': 0178 case '5': case '6': case '7': case '8': case '9': 0179 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 0180 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 0181 escSeq += ch; 0182 if (type == 0) { 0183 qCDebug(DEBUGGERCOMMON) << "Unrecognized escape sequence:" << escSeq; 0184 res += QLatin1Char('\\'); 0185 res += escSeq; 0186 escSeq.clear(); 0187 esc = false; 0188 type = 0; 0189 } else { 0190 // \uNNNN 0191 // \xNN 0192 if (escSeq.length() == (type == 1 ? 4 : 2)) { 0193 // no need to handle error, we know for sure escSeq is '[0-9a-fA-F]+' 0194 auto code = escSeq.toInt(nullptr, 16); 0195 res += QChar(code); 0196 escSeq.clear(); 0197 esc = false; 0198 type = 0; 0199 } 0200 } 0201 break; 0202 default: 0203 if (type == 0 && ch == quoteCh) { 0204 res += ch; 0205 } else { 0206 escSeq += ch; 0207 qCDebug(DEBUGGERCOMMON) << "Unrecognized escape sequence:" << escSeq; 0208 res += QLatin1Char('\\'); 0209 res += escSeq; 0210 escSeq.clear(); 0211 } 0212 esc = false; 0213 type = 0; 0214 break; 0215 } 0216 } else { 0217 if (ch == QLatin1Char('\\')) { 0218 esc = true; 0219 continue; 0220 } 0221 res += ch; 0222 } 0223 } 0224 return res; 0225 } else { 0226 return str; 0227 } 0228 }