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"