summaryrefslogtreecommitdiffstats
path: root/languages/cpp/debugger/mi
diff options
context:
space:
mode:
Diffstat (limited to 'languages/cpp/debugger/mi')
-rw-r--r--languages/cpp/debugger/mi/Makefile.am12
-rw-r--r--languages/cpp/debugger/mi/gdbmi.cpp128
-rw-r--r--languages/cpp/debugger/mi/gdbmi.h221
-rw-r--r--languages/cpp/debugger/mi/milexer.cpp290
-rw-r--r--languages/cpp/debugger/mi/milexer.h147
-rw-r--r--languages/cpp/debugger/mi/miparser.cpp345
-rw-r--r--languages/cpp/debugger/mi/miparser.h82
-rw-r--r--languages/cpp/debugger/mi/tokens.h34
8 files changed, 1259 insertions, 0 deletions
diff --git a/languages/cpp/debugger/mi/Makefile.am b/languages/cpp/debugger/mi/Makefile.am
new file mode 100644
index 00000000..d6cdf5f7
--- /dev/null
+++ b/languages/cpp/debugger/mi/Makefile.am
@@ -0,0 +1,12 @@
+
+# We need exceptions since they are used to report all MI access errors.
+KDE_CXXFLAGS = $(USE_EXCEPTIONS)
+
+METASOURCES = AUTO
+INCLUDES = $(all_includes)
+
+lib_LTLIBRARIES = libgdbmi_parser.la
+libgdbmi_parser_la_LDFLAGS = $(all_libraries)
+libgdbmi_parser_la_LIBADD = $(LIB_QT)
+libgdbmi_parser_la_SOURCES = gdbmi.cpp miparser.cpp milexer.cpp
+
diff --git a/languages/cpp/debugger/mi/gdbmi.cpp b/languages/cpp/debugger/mi/gdbmi.cpp
new file mode 100644
index 00000000..534a0cad
--- /dev/null
+++ b/languages/cpp/debugger/mi/gdbmi.cpp
@@ -0,0 +1,128 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * Copyright (C) 2005-2006 by Vladimir Prus *
+ * ghost@cs.msu.su *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "gdbmi.h"
+
+using namespace GDBMI;
+
+
+type_error::type_error()
+: std::logic_error("MI type error")
+{}
+
+QString Value::literal() const
+{
+ throw type_error();
+}
+
+int Value::toInt(int /*base*/) const
+{
+ throw type_error();
+}
+
+bool Value::hasField(const QString&) const
+{
+ throw type_error();
+}
+
+const Value& Value::operator[](const QString&) const
+{
+ throw type_error();
+}
+
+bool Value::empty() const
+{
+ throw type_error();
+}
+
+unsigned Value::size() const
+{
+ throw type_error();
+}
+
+
+const Value& Value::operator[](unsigned) const
+{
+ throw type_error();
+}
+
+QString StringLiteralValue::literal() const
+{
+ return literal_;
+}
+
+int StringLiteralValue::toInt(int base) const
+{
+ bool ok;
+ int result = literal_.toInt(&ok, base);
+ if (!ok)
+ throw type_error();
+ return result;
+}
+
+TupleValue::~TupleValue()
+{
+ for (QValueListIterator<Result*> it=results.begin(); it!=results.end(); ++it)
+ delete *it;
+}
+
+bool TupleValue::hasField(const QString& variable) const
+{
+ return results_by_name.count(variable);
+}
+
+const Value& TupleValue::operator[](const QString& variable) const
+{
+ if (results_by_name.count(variable))
+ return *results_by_name[variable]->value;
+ else
+ throw type_error();
+}
+
+ListValue::~ListValue()
+{
+ for (QValueListIterator<Result*> it=results.begin(); it!=results.end(); ++it)
+ delete *it;
+}
+
+bool ListValue::empty() const
+{
+ return results.isEmpty();
+}
+
+unsigned ListValue::size() const
+{
+ return results.size();
+}
+
+const Value& ListValue::operator[](unsigned index) const
+{
+ if (index < results.size())
+ {
+ return *results[index]->value;
+ }
+ else
+ throw type_error();
+}
+
+
+
+
diff --git a/languages/cpp/debugger/mi/gdbmi.h b/languages/cpp/debugger/mi/gdbmi.h
new file mode 100644
index 00000000..7a193e91
--- /dev/null
+++ b/languages/cpp/debugger/mi/gdbmi.h
@@ -0,0 +1,221 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * Copyright (C) 2005-2006 by Vladimir Prus *
+ * ghost@cs.msu.su *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef GDBMI_H
+#define GDBMI_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <qmap.h>
+
+#include <stdexcept>
+
+/**
+@author Roberto Raggi
+@author Vladimir Prus
+*/
+namespace GDBMI
+{
+ /** Exception that is thrown when we're trying to invoke an
+ operation that is not supported by specific MI value. For
+ example, trying to index a string literal.
+
+ Such errors are conceptually the same as assert, but in GUI
+ we can't use regular assert, and Q_ASSERT, which only prints
+ a message, is not suitable either. We need to break processing,
+ and the higher-level code can report "Internal parsing error",
+ or something.
+
+ Being glorified assert, this exception does not cary any
+ useful information.
+ */
+ class type_error : public std::logic_error
+ {
+ public:
+ type_error();
+ };
+
+ /** Base class for all MI values.
+ MI values are of three kinds:
+ - String literals
+ - Lists (indexed by integer)
+ - Tuple (set of named values, indexed by name)
+
+ The structure of response to a specific gdb command is fixed.
+ While any tuples in response may omit certain named fields, the
+ kind of each item never changes. That is, response to specific
+ command can't contains sometimes string and sometimes tuple in
+ specific position.
+
+ Because of that static structure, it's almost never needed to query
+ dynamic type of a MI value. Most often we know it's say, tuple, and
+ can subscripts it.
+
+ So, the Value class has methods for accessing all kinds of values.
+ Attempting to call a method that is not applicable to specific value
+ will result in exception. The client code will almost never need to
+ cast from 'Value' to its derived classes.
+
+ Note also that all methods in this class are const and return
+ const Value&. That's by design -- there's no need to modify gdb
+ responses in GUI.
+ */
+ struct Value
+ {
+ Value() {}
+ private: // Copy disabled to prevent slicing.
+ Value(const Value&);
+ Value& operator=(const Value&);
+
+ public:
+
+ virtual ~Value() {}
+
+ enum { StringLiteral, Tuple, List } kind;
+
+ /** If this value is a string literals, returns the string value.
+ Othewise, throws type_error.
+ */
+ virtual QString literal() const;
+
+ /** If the value is a string literal, converts it to int and
+ returns. If conversion fails, or the value cannot be
+ converted to int, throws type_error.
+ */
+ virtual int toInt(int base = 10) const;
+
+ /** If this value is a tuple, returns true if the tuple
+ has a field named 'variable'. Otherwise,
+ throws type_error.
+ */
+ virtual bool hasField(const QString& variable) const;
+
+ /** If this value is a tuple, and contains named field 'variable',
+ returns it. Otherwise, throws 'type_error'.
+ This method is virtual, and derived in base class, so that
+ we can save on casting, when we know for sure that instance
+ is TupleValue, or ListValue.
+ */
+ virtual const Value& operator[](const QString& variable) const;
+
+ /** If this value is a list, returns true if the list is empty.
+ If this value is not a list, throws 'type_error'.
+ */
+ virtual bool empty() const;
+
+ /** If this value is a list, returns it's size.
+ Otherwise, throws 'type_error'.
+ */
+ virtual unsigned size() const;
+
+ /** If this value is a list, returns the element at
+ 'index'. Otherwise, throws 'type_error'.
+ */
+ virtual const Value& operator[](unsigned index) const;
+ };
+
+ /** @internal
+ Internal class to represent name-value pair in tuples.
+ */
+ struct Result
+ {
+ Result() : value(0) {}
+ ~Result() { delete value; value = 0; }
+
+ QString variable;
+ Value *value;
+ };
+
+ struct StringLiteralValue : public Value
+ {
+ StringLiteralValue(const QString &lit)
+ : literal_(lit) { Value::kind = StringLiteral; }
+
+ public: // Value overrides
+
+ QString literal() const;
+ int toInt(int base) const;
+
+ private:
+ QString literal_;
+ };
+
+ struct TupleValue : public Value
+ {
+ TupleValue() { Value::kind = Tuple; }
+ ~TupleValue();
+
+ bool hasField(const QString&) const;
+ const Value& operator[](const QString& variable) const;
+
+
+ QValueList<Result*> results;
+ QMap<QString, GDBMI::Result*> results_by_name;
+ };
+
+ struct ListValue : public Value
+ {
+ ListValue() { Value::kind = List; }
+ ~ListValue();
+
+ bool empty() const;
+
+ unsigned size() const;
+
+ const Value& operator[](unsigned index) const;
+
+ QValueList<Result*> results;
+
+ };
+
+ struct Record
+ {
+ virtual ~Record() {}
+ virtual QString toString() const { Q_ASSERT( 0 ); return QString::null; }
+
+ enum { Prompt, Stream, Result } kind;
+ };
+
+ struct ResultRecord : public Record, public TupleValue
+ {
+ ResultRecord() { Record::kind = Result; }
+
+ QString reason;
+ };
+
+ struct PromptRecord : public Record
+ {
+ inline PromptRecord() { Record::kind = Prompt; }
+
+ virtual QString toString() const
+ { return "(prompt)\n"; }
+ };
+
+ struct StreamRecord : public Record
+ {
+ inline StreamRecord() : reason(0) { Record::kind = Stream; }
+
+ char reason;
+ QString message;
+ };
+}
+
+#endif
diff --git a/languages/cpp/debugger/mi/milexer.cpp b/languages/cpp/debugger/mi/milexer.cpp
new file mode 100644
index 00000000..ecf18373
--- /dev/null
+++ b/languages/cpp/debugger/mi/milexer.cpp
@@ -0,0 +1,290 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "milexer.h"
+#include "tokens.h"
+#include <cctype>
+#include <iostream>
+
+bool MILexer::s_initialized = false;
+scan_fun_ptr MILexer::s_scan_table[];
+
+
+MILexer::MILexer()
+{
+ if (!s_initialized)
+ setupScanTable();
+}
+
+MILexer::~MILexer()
+{
+}
+
+void MILexer::setupScanTable()
+{
+ s_initialized = true;
+
+ for (int i=0; i<128; ++i) {
+ switch (i) {
+ case '\n':
+ s_scan_table[i] = &MILexer::scanNewline;
+ break;
+
+ case '"':
+ s_scan_table[i] = &MILexer::scanStringLiteral;
+ break;
+
+ default:
+ if (isspace(i))
+ s_scan_table[i] = &MILexer::scanWhiteSpaces;
+ else if (isalpha(i) || i == '_')
+ s_scan_table[i] = &MILexer::scanIdentifier;
+ else if (isdigit(i))
+ s_scan_table[i] = &MILexer::scanNumberLiteral;
+ else
+ s_scan_table[i] = &MILexer::scanChar;
+ }
+ }
+
+ s_scan_table[128] = &MILexer::scanUnicodeChar;
+}
+
+/*
+
+ m_firstToken = m_tokens.data();
+ m_currentToken = 0;
+
+ m_firstToken = m_tokens.data();
+ m_currentToken = m_firstToken;
+ */
+
+TokenStream *MILexer::tokenize(const FileSymbol *fileSymbol)
+{
+ m_tokensCount = 0;
+ m_tokens.resize(64);
+
+ m_contents = fileSymbol->contents;
+ m_length = m_contents.length();
+ m_ptr = 0;
+
+ m_lines.resize(8);
+ m_line = 0;
+
+ m_lines[m_line++] = 0;
+
+ m_cursor = 0;
+
+ // tokenize
+ int pos, len;
+
+ for (;;) {
+ if (m_tokensCount == (int)m_tokens.size())
+ m_tokens.resize(m_tokensCount * 2);
+
+ Token &tk = m_tokens[m_tokensCount++];
+ tk.kind = nextToken(pos, len);
+ tk.position = pos;
+ tk.length = len;
+
+ if (tk.kind == 0)
+ break;
+ }
+
+ TokenStream *tokenStream = new TokenStream;
+ tokenStream->m_contents = m_contents;
+
+ tokenStream->m_lines = m_lines;
+ tokenStream->m_line = m_line;
+
+ tokenStream->m_tokens = m_tokens;
+ tokenStream->m_tokensCount = m_tokensCount;
+
+ tokenStream->m_firstToken = tokenStream->m_tokens.data();
+ tokenStream->m_currentToken = tokenStream->m_firstToken;;
+
+ tokenStream->m_cursor = m_cursor;
+
+ return tokenStream;
+}
+
+int MILexer::nextToken(int &pos, int &len)
+{
+ int start = 0;
+ int kind = 0;
+ unsigned char ch = 0;
+
+ while (m_ptr < m_length) {
+ start = m_ptr;
+
+ ch = (unsigned char)m_contents[m_ptr];
+ (this->*s_scan_table[ch < 128 ? ch : 128])(&kind);
+
+ switch (kind) {
+ case Token_whitespaces:
+ case '\n':
+ break;
+
+ default:
+ pos = start;
+ len = m_ptr - start;
+ return kind;
+ }
+
+ if (kind == 0)
+ break;
+ }
+
+ return 0;
+}
+
+void MILexer::scanChar(int *kind)
+{
+ *kind = m_contents[m_ptr++];
+}
+
+void MILexer::scanWhiteSpaces(int *kind)
+{
+ *kind = Token_whitespaces;
+
+ char ch;
+ while (m_ptr < m_length) {
+ ch = m_contents[m_ptr];
+ if (!(isspace(ch) && ch != '\n'))
+ break;
+
+ ++m_ptr;
+ }
+}
+
+void MILexer::scanNewline(int *kind)
+{
+ if (m_line == (int)m_lines.size())
+ m_lines.resize(m_lines.size() * 2);
+
+ if (m_lines.at(m_line) < m_ptr)
+ m_lines[m_line++] = m_ptr;
+
+ *kind = m_contents[m_ptr++];
+}
+
+void MILexer::scanUnicodeChar(int *kind)
+{
+ *kind = m_contents[m_ptr++];
+}
+
+void MILexer::scanStringLiteral(int *kind)
+{
+ ++m_ptr;
+ while (char c = m_contents[m_ptr]) {
+ switch (c) {
+ case '\n':
+ // ### error
+ *kind = Token_string_literal;
+ return;
+ case '\\':
+ {
+ char next = m_contents.at(m_ptr+1);
+ if (next == '"' || next == '\\')
+ m_ptr += 2;
+ else
+ ++m_ptr;
+ }
+ break;
+ case '"':
+ ++m_ptr;
+ *kind = Token_string_literal;
+ return;
+ default:
+ ++m_ptr;
+ break;
+ }
+ }
+
+ // ### error
+ *kind = Token_string_literal;
+}
+
+void MILexer::scanIdentifier(int *kind)
+{
+ char ch;
+ while (m_ptr < m_length) {
+ ch = m_contents[m_ptr];
+ if (!(isalnum(ch) || ch == '-' || ch == '_'))
+ break;
+
+ ++m_ptr;
+ }
+
+ *kind = Token_identifier;
+}
+
+void MILexer::scanNumberLiteral(int *kind)
+{
+ char ch;
+ while (m_ptr < m_length) {
+ ch = m_contents[m_ptr];
+ if (!(isalnum(ch) || ch == '.'))
+ break;
+
+ ++m_ptr;
+ }
+
+ // ### finish to implement me!!
+ *kind = Token_number_literal;
+}
+
+void TokenStream::positionAt(int position, int *line, int *column) const
+{
+ if (!(line && column && !m_lines.isEmpty()))
+ return;
+
+ int first = 0;
+ int len = m_line;
+ int half;
+ int middle;
+
+ while (len > 0) {
+ half = len >> 1;
+ middle = first;
+
+ middle += half;
+
+ if (m_lines[middle] < position) {
+ first = middle;
+ ++first;
+ len = len - half - 1;
+ }
+ else
+ len = half;
+ }
+
+ *line = QMAX(first - 1, 0);
+ *column = position - m_lines.at(*line);
+
+ Q_ASSERT( *column >= 0 );
+}
+
+QCString TokenStream::tokenText(int index) const
+{
+ Token *t = index < 0 ? m_currentToken : m_firstToken + index;
+ const char* data = m_contents;
+ return QCString(data + t->position, t->length+1);
+}
+
diff --git a/languages/cpp/debugger/mi/milexer.h b/languages/cpp/debugger/mi/milexer.h
new file mode 100644
index 00000000..bc0deead
--- /dev/null
+++ b/languages/cpp/debugger/mi/milexer.h
@@ -0,0 +1,147 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef MILEXER_H
+#define MILEXER_H
+
+#include <qmemarray.h>
+#include <qmap.h>
+#include <qstring.h>
+
+class MILexer;
+class TokenStream;
+
+typedef void (MILexer::*scan_fun_ptr)(int *kind);
+
+struct Token
+{
+ int kind;
+ int position;
+ int length;
+};
+
+struct FileSymbol
+{
+ QCString contents;
+ TokenStream *tokenStream;
+
+ inline FileSymbol()
+ : tokenStream(0) {}
+
+ inline ~FileSymbol();
+};
+
+struct TokenStream
+{
+ inline int lookAhead(int n = 0) const
+ { return (m_currentToken + n)->kind; }
+
+ inline int currentToken() const
+ { return m_currentToken->kind; }
+
+ inline QCString currentTokenText() const
+ { return tokenText(-1); }
+
+ QCString tokenText(int index = 0) const;
+
+ inline int lineOffset(int line) const
+ { return m_lines.at(line); }
+
+ void positionAt(int position, int *line, int *column) const;
+
+ inline void getTokenStartPosition(int index, int *line, int *column) const
+ { positionAt((m_firstToken + index)->position, line, column); }
+
+ inline void getTokenEndPosition(int index, int *line, int *column) const
+ {
+ Token *tk = m_firstToken + index;
+ positionAt(tk->position + tk->length, line, column);
+ }
+
+ inline void rewind(int index)
+ { m_currentToken = m_firstToken + index; }
+
+ inline int cursor() const
+ { return m_currentToken - m_firstToken; }
+
+ inline void nextToken()
+ { m_currentToken++; m_cursor++; }
+
+//private:
+ QCString m_contents;
+
+ QMemArray<int> m_lines;
+ int m_line;
+
+ QMemArray<Token> m_tokens;
+ int m_tokensCount;
+
+ Token *m_firstToken;
+ Token *m_currentToken;
+
+ int m_cursor;
+};
+
+class MILexer
+{
+public:
+ MILexer();
+ ~MILexer();
+
+ TokenStream *tokenize(const FileSymbol *fileSymbol);
+
+private:
+ int nextToken(int &position, int &len);
+
+ void scanChar(int *kind);
+ void scanUnicodeChar(int *kind);
+ void scanNewline(int *kind);
+ void scanWhiteSpaces(int *kind);
+ void scanStringLiteral(int *kind);
+ void scanNumberLiteral(int *kind);
+ void scanIdentifier(int *kind);
+
+ void setupScanTable();
+
+private:
+ static bool s_initialized;
+ static scan_fun_ptr s_scan_table[128 + 1];
+
+ QCString m_contents;
+ int m_ptr;
+ // Cached 'm_contents.length()'
+ int m_length;
+
+ QMemArray<int> m_lines;
+ int m_line;
+
+ QMemArray<Token> m_tokens;
+ int m_tokensCount;
+
+ int m_cursor;
+};
+
+inline FileSymbol::~FileSymbol()
+{
+ delete tokenStream;
+ tokenStream = 0;
+}
+
+
+#endif
diff --git a/languages/cpp/debugger/mi/miparser.cpp b/languages/cpp/debugger/mi/miparser.cpp
new file mode 100644
index 00000000..876bfa47
--- /dev/null
+++ b/languages/cpp/debugger/mi/miparser.cpp
@@ -0,0 +1,345 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * Copyright (C) 2005-2006 by Vladimir Prus *
+ * ghost@cs.msu.su *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "miparser.h"
+#include "tokens.h"
+#include <memory>
+
+using namespace GDBMI;
+
+#define MATCH(tok) \
+ do { \
+ if (lex->lookAhead(0) != (tok)) \
+ return false; \
+ } while (0)
+
+#define ADVANCE(tok) \
+ do { \
+ MATCH(tok); \
+ lex->nextToken(); \
+ } while (0)
+
+MIParser::MIParser()
+ : lex(0)
+{
+}
+
+MIParser::~MIParser()
+{
+}
+
+Record *MIParser::parse(FileSymbol *file)
+{
+ lex = 0;
+
+ Record *record = 0;
+
+ TokenStream *tokenStream = lexer.tokenize(file);
+ if (!tokenStream)
+ return false;
+
+ lex = file->tokenStream = tokenStream;
+
+ switch (lex->lookAhead()) {
+ case '~':
+ case '@':
+ case '&':
+ parseStreamRecord(record);
+ break;
+ case '(':
+ parsePrompt(record);
+ break;
+ case '^':
+ parseResultRecord(record);
+ break;
+ case '*':
+ // Same as result, only differs in start
+ // marker.
+ parseResultRecord(record);
+ break;
+ default:
+ break;
+ }
+
+ return record;
+}
+
+bool MIParser::parsePrompt(Record *&record)
+{
+ ADVANCE('(');
+ MATCH(Token_identifier);
+ if (lex->currentTokenText() != "gdb")
+ return false;
+ lex->nextToken();
+ ADVANCE(')');
+
+ record = new PromptRecord;
+ return true;
+}
+
+bool MIParser::parseStreamRecord(Record *&record)
+{
+ std::auto_ptr<StreamRecord> stream(new StreamRecord);
+
+ switch (lex->lookAhead()) {
+ case '~':
+ case '@':
+ case '&': {
+ stream->reason = lex->lookAhead();
+ lex->nextToken();
+ MATCH(Token_string_literal);
+ stream->message = parseStringLiteral();
+ record = stream.release();
+ }
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool MIParser::parseResultRecord(Record *&record)
+{
+ if (lex->lookAhead() != '^' && lex->lookAhead() != '*')
+ return false;
+ lex->nextToken();
+
+ MATCH(Token_identifier);
+ QString reason = lex->currentTokenText();
+ lex->nextToken();
+
+ std::auto_ptr<ResultRecord> res(new ResultRecord);
+ res->reason = reason;
+
+ if (lex->lookAhead() != ',') {
+ record = res.release();
+ return true;
+ }
+
+ lex->nextToken();
+
+ if (!parseCSV(*res))
+ return false;
+
+ record = res.release();
+ return true;
+}
+
+bool MIParser::parseResult(Result *&result)
+{
+ MATCH(Token_identifier);
+ QString variable = lex->currentTokenText();
+ lex->nextToken();
+
+ std::auto_ptr<Result> res(new Result);
+ res->variable = variable;
+
+ if (lex->lookAhead() != '=')
+ return true;
+
+ lex->nextToken();
+
+ Value *value = 0;
+ if (!parseValue(value))
+ return false;
+
+ res->value = value;
+ result = res.release();
+
+ return true;
+}
+
+bool MIParser::parseValue(Value *&value)
+{
+ value = 0;
+
+ switch (lex->lookAhead()) {
+ case Token_string_literal: {
+ value = new StringLiteralValue(parseStringLiteral());
+ }
+ return true;
+
+ case '{':
+ return parseTuple(value);
+
+ case '[':
+ return parseList(value);
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool MIParser::parseTuple(Value *&value)
+{
+ TupleValue* val;
+
+ if (!parseCSV(&val, '{', '}'))
+ return false;
+
+ value = val;
+ return true;
+}
+
+bool MIParser::parseList(Value *&value)
+{
+ ADVANCE('[');
+
+ std::auto_ptr<ListValue> lst(new ListValue);
+
+ // Note: can't use parseCSV here because of nested
+ // "is this Value or Result" guessing. Too lazy to factor
+ // that out too using function pointers.
+ int tok = lex->lookAhead();
+ while (tok && tok != ']') {
+ Result *result = 0;
+ Value *val = 0;
+
+ if (tok == Token_identifier)
+ {
+ if (!parseResult(result))
+ return false;
+ }
+ else if (!parseValue(val))
+ return false;
+
+ Q_ASSERT(result || val);
+
+ if (!result) {
+ result = new Result;
+ result->value = val;
+ }
+ lst->results.append(result);
+
+ if (lex->lookAhead() == ',')
+ lex->nextToken();
+
+ tok = lex->lookAhead();
+ }
+ ADVANCE(']');
+
+ value = lst.release();
+
+ return true;
+}
+
+bool MIParser::parseCSV(TupleValue** value,
+ char start, char end)
+{
+ std::auto_ptr<TupleValue> tuple(new TupleValue);
+
+ if (!parseCSV(*tuple, start, end))
+ return false;
+
+ *value = tuple.get();
+ tuple.release();
+ return true;
+}
+
+bool MIParser::parseCSV(GDBMI::TupleValue& value,
+ char start, char end)
+{
+ if (start)
+ ADVANCE(start);
+
+ int tok = lex->lookAhead();
+ while (tok) {
+ if (end && tok == end)
+ break;
+
+ Result *result;
+ if (!parseResult(result))
+ return false;
+
+ value.results.append(result);
+ value.results_by_name.insert(result->variable, result);
+
+ if (lex->lookAhead() == ',')
+ lex->nextToken();
+
+ tok = lex->lookAhead();
+ }
+
+ if (end)
+ ADVANCE(end);
+
+ return true;
+}
+
+
+QString MIParser::parseStringLiteral()
+{
+ QCString message = lex->currentTokenText();
+
+ unsigned int length = message.length();
+ QString message2;
+ message2.setLength(length);
+ // The [1,length-1] range removes quotes without extra
+ // call to 'mid'
+ unsigned target_index = 0;
+ for(unsigned i = 1, e = length-1; i != e; ++i)
+ {
+ int translated = -1;
+ if (message[i] == '\\')
+ {
+ if (i+1 < length)
+ {
+ // TODO: implement all the other escapes, maybe
+ if (message[i+1] == 'n')
+ {
+ translated = '\n';
+ }
+ else if (message[i+1] == '\\')
+ {
+ translated = '\\';
+ }
+ else if (message[i+1] == '"')
+ {
+ translated = '"';
+ }
+ else if (message[i+1] == 't')
+ {
+ translated = '\t';
+ }
+
+ }
+ }
+
+ if (translated != -1)
+ {
+ message2[target_index++] = (char)translated;
+ ++i;
+ }
+ else
+ {
+ message2[target_index++] = message[i];
+ }
+ }
+ message2.setLength(target_index);
+
+ lex->nextToken();
+ return message2;
+}
+
diff --git a/languages/cpp/debugger/mi/miparser.h b/languages/cpp/debugger/mi/miparser.h
new file mode 100644
index 00000000..524dba7c
--- /dev/null
+++ b/languages/cpp/debugger/mi/miparser.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef MIPARSER_H
+#define MIPARSER_H
+
+#include "milexer.h"
+#include "gdbmi.h"
+
+#include <qstring.h>
+#include <qvaluelist.h>
+
+/**
+@author Roberto Raggi
+*/
+class MIParser
+{
+public:
+ MIParser();
+ ~MIParser();
+
+ GDBMI::Record *parse(FileSymbol *file);
+
+protected: // rules
+ bool parseResultRecord(GDBMI::Record *&record);
+ bool parsePrompt(GDBMI::Record *&record);
+ bool parseStreamRecord(GDBMI::Record *&record);
+
+ bool parseResult(GDBMI::Result *&result);
+ bool parseValue(GDBMI::Value *&value);
+ bool parseTuple(GDBMI::Value *&value);
+ bool parseList(GDBMI::Value *&value);
+
+ /** Creates new TupleValue object, writes its address
+ into *value, parses a comma-separated set of values,
+ and adds each new value into (*value)->results.
+ If 'start' and 'end' are not zero, they specify
+ start and end delimiter of the list.
+ Parsing stops when we see 'end' character, or, if
+ 'end' is zero, at the end of input.
+ */
+ bool parseCSV(GDBMI::TupleValue** value,
+ char start = 0, char end = 0);
+
+ /** @overload
+ Same as above, but writes into existing tuple.
+ */
+ bool parseCSV(GDBMI::TupleValue& value,
+ char start = 0, char end = 0);
+
+
+ /** Parses a string literal and returns it. Advances
+ the lexer past the literal. Processes C escape sequences
+ in the string.
+ @pre lex->lookAhead(0) == Token_string_literal
+ */
+ QString parseStringLiteral();
+
+
+
+private:
+ MILexer lexer;
+ TokenStream *lex;
+};
+
+#endif
diff --git a/languages/cpp/debugger/mi/tokens.h b/languages/cpp/debugger/mi/tokens.h
new file mode 100644
index 00000000..c9ab283d
--- /dev/null
+++ b/languages/cpp/debugger/mi/tokens.h
@@ -0,0 +1,34 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef TOKENS_H
+#define TOKENS_H
+
+enum Type
+{
+ Token_eof = 0,
+ Token_identifier = 1000,
+ Token_number_literal,
+ Token_string_literal,
+ Token_whitespaces
+};
+
+#endif
+