File indexing completed on 2024-05-12 05:51:07
0001 /* 0002 SPDX-FileCopyrightText: 2022 Héctor Mesa Jiménez <wmj.py@gmx.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "parser.h" 0008 #include "tokens.h" 0009 #include <QJsonArray> 0010 #include <QRegularExpression> 0011 0012 namespace gdbmi 0013 { 0014 0015 int GdbmiParser::parseRecord(const QByteArray &message, int position) 0016 { 0017 /* 0018 * out-of-band-record → async-record | stream-record 0019 */ 0020 position = advanceNewlines(message, position); 0021 0022 if (position >= message.size()) { 0023 return position; 0024 } 0025 0026 // token 0027 const auto token = tryToken(message, position); 0028 if (!token.isEmpty()) { 0029 position = token.position; 0030 } 0031 0032 if (position >= message.size()) { 0033 Q_EMIT parserError(QStringLiteral("unexpected end of line")); 0034 return -1; 0035 } 0036 0037 const auto prefix = message.at(position); 0038 0039 // async-output * + = 0040 switch (prefix) { 0041 case '~': 0042 case '@': 0043 case '&': { 0044 const auto tok = tryStreamOutput(prefix, message, position); 0045 if (!tok.isEmpty()) { 0046 Q_EMIT outputProduced(tok.value.value()); 0047 position = tok.position; 0048 } else if (tok.hasError()) { 0049 Q_EMIT parserError(tok.error.value()); 0050 position = -1; 0051 } else { 0052 position = tok.position; 0053 } 0054 } break; 0055 // stream-output ~ @ & ^ 0056 case '*': 0057 case '+': 0058 case '=': 0059 case '^': { 0060 const auto tok = tryRecord(prefix, message, position, token.value.value_or(-1)); 0061 if (!tok.isEmpty()) { 0062 Q_EMIT recordProduced(tok.value.value()); 0063 position = tok.position; 0064 } else if (tok.hasError()) { 0065 Q_EMIT parserError(tok.error.value()); 0066 position = -1; 0067 } else { 0068 position = tok.position; 0069 } 0070 } break; 0071 case '(': { 0072 const auto tok = tryPrompt(message, position); 0073 if (!tok.hasError()) { 0074 Q_EMIT recordProduced(Record{Record::Prompt, QString(), {}, std::nullopt}); 0075 position = tok.position; 0076 } else { 0077 Q_EMIT parserError(tok.error.value()); 0078 position = -1; 0079 } 0080 break; 0081 } 0082 default: 0083 Q_EMIT parserError(QStringLiteral("unexpected GDB/MI record class: %1").arg(prefix)); 0084 return -1; 0085 } 0086 0087 return advanceNewlines(message, position); 0088 } 0089 0090 GdbmiParser::GdbmiParser(QObject *parent) 0091 : QObject(parent) 0092 { 0093 } 0094 0095 GdbmiParser::ParserHead GdbmiParser::parseResponse(const QByteArray &message) 0096 { 0097 const int size = message.size(); 0098 0099 int pos = advanceNewlines(message, 0); 0100 0101 while (pos < size) { 0102 const int newPos = parseRecord(message, pos); 0103 if (newPos <= pos) { 0104 return {pos, true}; 0105 } 0106 pos = newPos; 0107 } 0108 0109 return {pos, false}; 0110 } 0111 0112 bool GdbmiParser::isMIRequest(const QString &message) 0113 { 0114 static const QRegularExpression rx(QStringLiteral(R"--(^\s*(?:\d+\s*)?\-)--")); 0115 0116 return rx.match(message).hasMatch(); 0117 } 0118 0119 std::optional<QString> GdbmiParser::getMICommand(const QString &message) 0120 { 0121 static const QRegularExpression rx(QStringLiteral(R"--(^\s*(?:\d+\s*)?\-(\S+))--")); 0122 0123 const auto match = rx.match(message); 0124 if (!match.hasMatch()) { 0125 return std::nullopt; 0126 } 0127 return match.captured(1); 0128 } 0129 0130 bool GdbmiParser::isMISeparator(const QString &message) 0131 { 0132 static const QRegularExpression rx(QStringLiteral(R"--(^\s*(gdb)\s*$)--")); 0133 0134 return rx.match(message).hasMatch(); 0135 } 0136 0137 QStringList GdbmiParser::splitCommand(const QString &message) 0138 { 0139 static const QRegularExpression rx(QStringLiteral(R"--(^(\s*(\d+)\s*)?(\-\S+))--")); 0140 0141 const auto match = rx.match(message); 0142 if (!match.hasMatch()) { 0143 return match.capturedTexts(); 0144 } 0145 return {message}; 0146 } 0147 0148 static int indexOf(const QByteArray &stack, char needle, bool lastIndex) 0149 { 0150 if (lastIndex) { 0151 return stack.lastIndexOf(needle); 0152 } 0153 return stack.indexOf(needle); 0154 } 0155 0156 int GdbmiParser::splitLines(const QByteArray &buffer, bool lastIndex) 0157 { 0158 // \r\n 0159 const int r_idx = indexOf(buffer, '\r', lastIndex); 0160 if ((r_idx >= 0) && ((r_idx + 1) < buffer.size()) && (buffer.at(r_idx + 1) == '\n')) { 0161 return r_idx + 1; 0162 } 0163 0164 const int n_idx = indexOf(buffer, '\n', lastIndex); 0165 if (n_idx >= 0) { 0166 // \n 0167 return n_idx; 0168 } else { 0169 // \r 0170 return r_idx; 0171 } 0172 } 0173 0174 } // namespace gdbmi 0175 0176 #include "moc_parser.cpp"