File indexing completed on 2024-05-12 04:39:43
0001 /* 0002 SPDX-FileCopyrightText: 2004 Roberto Raggi <roberto@kdevelop.org> 0003 SPDX-FileCopyrightText: 2005-2006 Vladimir Prus <ghost@cs.msu.su> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "miparser.h" 0009 #include "tokens.h" 0010 0011 using namespace KDevMI::MI; 0012 0013 #define MATCH(tok) \ 0014 do { \ 0015 if (m_lex->lookAhead(0) != (tok)) \ 0016 return false; \ 0017 } while (0) 0018 0019 #define ADVANCE(tok) \ 0020 do { \ 0021 MATCH(tok); \ 0022 m_lex->nextToken(); \ 0023 } while (0) 0024 0025 #define MATCH_PTR(tok) \ 0026 do { \ 0027 if (m_lex->lookAhead(0) != (tok)) \ 0028 return {}; \ 0029 } while (0) 0030 0031 #define ADVANCE_PTR(tok) \ 0032 do { \ 0033 MATCH_PTR(tok); \ 0034 m_lex->nextToken(); \ 0035 } while (0) 0036 0037 MIParser::MIParser() 0038 { 0039 } 0040 0041 MIParser::~MIParser() 0042 { 0043 } 0044 0045 std::unique_ptr<Record> MIParser::parse(FileSymbol *file) 0046 { 0047 m_lex = nullptr; 0048 0049 TokenStream *tokenStream = m_lexer.tokenize(file); 0050 if (!tokenStream) 0051 return nullptr; 0052 0053 m_lex = file->tokenStream = tokenStream; 0054 0055 uint32_t token = 0; 0056 if (m_lex->lookAhead() == Token_number_literal) { 0057 token = QString::fromUtf8(m_lex->currentTokenText()).toUInt(); 0058 m_lex->nextToken(); 0059 } 0060 0061 std::unique_ptr<Record> record; 0062 0063 switch (m_lex->lookAhead()) { 0064 case '~': 0065 case '@': 0066 case '&': 0067 record = parseStreamRecord(); 0068 break; 0069 case '(': 0070 record = parsePrompt(); 0071 break; 0072 case '^': 0073 case '*': 0074 case '=': 0075 case '+': 0076 record = parseResultOrAsyncRecord(); 0077 break; 0078 default: 0079 break; 0080 } 0081 0082 if (record && record->kind == Record::Result) { 0083 auto * result = static_cast<ResultRecord *>(record.get()); 0084 result->token = token; 0085 } else { 0086 Q_ASSERT(token == 0); 0087 } 0088 0089 return record; 0090 } 0091 0092 std::unique_ptr<Record> MIParser::parsePrompt() 0093 { 0094 ADVANCE_PTR('('); 0095 MATCH_PTR(Token_identifier); 0096 if (m_lex->currentTokenText() != "gdb") 0097 return {}; 0098 m_lex->nextToken(); 0099 ADVANCE_PTR(')'); 0100 0101 return std::unique_ptr<Record>(new PromptRecord); 0102 } 0103 0104 std::unique_ptr<Record> MIParser::parseStreamRecord() 0105 { 0106 StreamRecord::Subkind subkind; 0107 0108 switch (m_lex->lookAhead()) { 0109 case '~': subkind = StreamRecord::Console; break; 0110 case '@': subkind = StreamRecord::Target; break; 0111 case '&': subkind = StreamRecord::Log; break; 0112 default: 0113 Q_ASSERT(false); 0114 return {}; 0115 } 0116 0117 std::unique_ptr<StreamRecord> stream(new StreamRecord(subkind)); 0118 0119 m_lex->nextToken(); 0120 MATCH_PTR(Token_string_literal); 0121 stream->message = parseStringLiteral(); 0122 return stream; 0123 } 0124 0125 std::unique_ptr<Record> MIParser::parseResultOrAsyncRecord() 0126 { 0127 std::unique_ptr<TupleRecord> result; 0128 0129 char c = m_lex->lookAhead(); 0130 m_lex->nextToken(); 0131 MATCH_PTR(Token_identifier); 0132 QString reason = QString::fromUtf8(m_lex->currentTokenText()); 0133 m_lex->nextToken(); 0134 0135 if (c == '^') { 0136 result.reset(new ResultRecord(reason)); 0137 } else { 0138 AsyncRecord::Subkind subkind; 0139 switch (c) { 0140 case '*': subkind = AsyncRecord::Exec; break; 0141 case '=': subkind = AsyncRecord::Notify; break; 0142 case '+': subkind = AsyncRecord::Status; break; 0143 default: 0144 Q_ASSERT(false); 0145 return {}; 0146 } 0147 result.reset(new AsyncRecord(subkind, reason)); 0148 } 0149 0150 if (m_lex->lookAhead() == ',') { 0151 m_lex->nextToken(); 0152 0153 if (!parseCSV(*result)) 0154 return {}; 0155 } 0156 0157 return result; 0158 } 0159 0160 bool MIParser::parseResult(Result *&result) 0161 { 0162 // be less strict about the format, see e.g.: 0163 // https://bugs.kde.org/show_bug.cgi?id=304730 0164 // https://sourceware.org/bugzilla/show_bug.cgi?id=9659 0165 0166 std::unique_ptr<Result> res(new Result); 0167 0168 if (m_lex->lookAhead() == Token_identifier) { 0169 res->variable = QString::fromUtf8(m_lex->currentTokenText()); 0170 m_lex->nextToken(); 0171 0172 if (m_lex->lookAhead() != '=') { 0173 result = res.release(); 0174 return true; 0175 } 0176 0177 m_lex->nextToken(); 0178 } 0179 0180 Value *value = nullptr; 0181 if (!parseValue(value)) 0182 return false; 0183 0184 res->value = value; 0185 result = res.release(); 0186 0187 return true; 0188 } 0189 0190 bool MIParser::parseValue(Value *&value) 0191 { 0192 value = nullptr; 0193 0194 switch (m_lex->lookAhead()) { 0195 case Token_string_literal: { 0196 value = new StringLiteralValue(parseStringLiteral()); 0197 } 0198 return true; 0199 0200 case '{': 0201 return parseTuple(value); 0202 0203 case '[': 0204 return parseList(value); 0205 0206 default: 0207 break; 0208 } 0209 0210 return false; 0211 } 0212 0213 bool MIParser::parseTuple(Value *&value) 0214 { 0215 TupleValue* val; 0216 0217 if (!parseCSV(&val, '{', '}')) 0218 return false; 0219 0220 value = val; 0221 return true; 0222 } 0223 0224 bool MIParser::parseList(Value *&value) 0225 { 0226 ADVANCE('['); 0227 0228 std::unique_ptr<ListValue> lst(new ListValue); 0229 0230 // Note: can't use parseCSV here because of nested 0231 // "is this Value or Result" guessing. Too lazy to factor 0232 // that out too using function pointers. 0233 int tok = m_lex->lookAhead(); 0234 while (tok && tok != ']') { 0235 Result *result = nullptr; 0236 Value *val = nullptr; 0237 0238 if (tok == Token_identifier) 0239 { 0240 if (!parseResult(result)) 0241 return false; 0242 } 0243 else if (!parseValue(val)) 0244 return false; 0245 0246 Q_ASSERT(result || val); 0247 0248 if (!result) { 0249 result = new Result; 0250 result->value = val; 0251 } 0252 lst->results.append(result); 0253 0254 if (m_lex->lookAhead() == ',') 0255 m_lex->nextToken(); 0256 0257 tok = m_lex->lookAhead(); 0258 } 0259 ADVANCE(']'); 0260 0261 value = lst.release(); 0262 0263 return true; 0264 } 0265 0266 bool MIParser::parseCSV(TupleValue** value, 0267 char start, char end) 0268 { 0269 std::unique_ptr<TupleValue> tuple(new TupleValue); 0270 0271 if (!parseCSV(*tuple, start, end)) 0272 return false; 0273 0274 *value = tuple.get(); 0275 tuple.release(); 0276 return true; 0277 } 0278 0279 bool MIParser::parseCSV(TupleValue& value, 0280 char start, char end) 0281 { 0282 if (start) 0283 ADVANCE(start); 0284 0285 int tok = m_lex->lookAhead(); 0286 while (tok) { 0287 if (end && tok == end) 0288 break; 0289 0290 Result *result; 0291 if (!parseResult(result)) 0292 return false; 0293 0294 value.results.append(result); 0295 value.results_by_name.insert(result->variable, result); 0296 0297 if (m_lex->lookAhead() == ',') 0298 m_lex->nextToken(); 0299 0300 tok = m_lex->lookAhead(); 0301 } 0302 0303 if (end) 0304 ADVANCE(end); 0305 0306 return true; 0307 } 0308 0309 QString MIParser::parseStringLiteral() 0310 { 0311 QByteArray messageByteArray = m_lex->currentTokenText(); 0312 QString message = QString::fromUtf8(messageByteArray.constData()); 0313 0314 int length = message.length(); 0315 QString message2; 0316 message2.reserve(length); 0317 // The [1,length-1] range removes quotes without extra 0318 // call to 'mid' 0319 for(int i = 1, e = length-1; i != e; ++i) 0320 { 0321 int translated = -1; 0322 if (message[i] == QLatin1Char('\\')) { 0323 if (i+1 < length) 0324 { 0325 // TODO: implement all the other escapes, maybe 0326 if (message[i+1] == QLatin1Char('n')) { 0327 translated = '\n'; 0328 } 0329 else if (message[i+1] == QLatin1Char('\\')) { 0330 translated = '\\'; 0331 } 0332 else if (message[i+1] == QLatin1Char('"')) { 0333 translated = '"'; 0334 } 0335 else if (message[i+1] == QLatin1Char('t')) { 0336 translated = '\t'; 0337 } 0338 else if (message[i+1] == QLatin1Char('r')) { 0339 translated = '\r'; 0340 } 0341 } 0342 } 0343 0344 if (translated != -1) 0345 { 0346 message2.append(translated); 0347 ++i; 0348 } 0349 else 0350 { 0351 message2.append(message[i]); 0352 } 0353 } 0354 0355 m_lex->nextToken(); 0356 return message2; 0357 } 0358