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