You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdevelop/languages/cpp/debugger/mi/miparser.cpp

346 lines
8.0 KiB

/***************************************************************************
* 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., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 0;
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::unique_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);
TQString reason = lex->currentTokenText();
lex->nextToken();
std::unique_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);
TQString variable = lex->currentTokenText();
lex->nextToken();
std::unique_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::unique_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::unique_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;
}
TQString MIParser::parseStringLiteral()
{
TQCString message = lex->currentTokenText();
unsigned int length = message.length();
TQString 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;
}