diff options
Diffstat (limited to 'languages/cpp/debugger/mi')
-rw-r--r-- | languages/cpp/debugger/mi/Makefile.am | 12 | ||||
-rw-r--r-- | languages/cpp/debugger/mi/gdbmi.cpp | 128 | ||||
-rw-r--r-- | languages/cpp/debugger/mi/gdbmi.h | 221 | ||||
-rw-r--r-- | languages/cpp/debugger/mi/milexer.cpp | 290 | ||||
-rw-r--r-- | languages/cpp/debugger/mi/milexer.h | 147 | ||||
-rw-r--r-- | languages/cpp/debugger/mi/miparser.cpp | 345 | ||||
-rw-r--r-- | languages/cpp/debugger/mi/miparser.h | 82 | ||||
-rw-r--r-- | languages/cpp/debugger/mi/tokens.h | 34 |
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 + |