tdevelop/languages/ruby/debugger/rdbparser.cpp

351 lines
9.8 KiB

// **************************************************************************
// begin : Tue Aug 17 1999
// copyright : (C) 1999 by John Birch
// email : jbb@kdevelop.org
//
// Adapted for ruby debugging
// --------------------------
// begin : Mon Nov 1 2004
// copyright : (C) 2004 by Richard Dale
// email : Richard_Dale@tipitina.demon.co.uk
// **************************************************************************
// **************************************************************************
// *
// This program is free software; you can redistribute it and/or modify *
// it under the terms of the GNU General Public License as published by *
// the Free Software Foundation; either version 2 of the License, or *
// (at your option) any later version. *
// *
// **************************************************************************
#include "rdbparser.h"
#include "variablewidget.h"
#include <tqregexp.h>
#include <ctype.h>
#include <stdlib.h>
#include <kdebug.h>
namespace RDBDebugger
{
// **************************************************************************
void RDBParser::parseVariables(LazyFetchItem *parent, char *buf)
{
static const char *unknown = "?";
TQString varName;
TQCString value;
int pos;
Q_ASSERT(parent);
if (buf == 0 || strlen(buf) == 0) {
return;
}
if (buf[0] == 0) {
buf = (char*)unknown;
}
TQRegExp var_re("\\s*([^\\n\\s]+) => ([^\\n]+)");
TQRegExp ref_re("(#<([^:]|::)+:0x[\\da-f]+)\\s*([^=]*)>?");
TQRegExp struct_re("#<struct Struct::(\\w+)");
// Look for 'dataitem => value' pairs. For example:
// a => 1
// m => #<MyClass:0x30093540 @temp={"z"=>"zed", "p"=>"pee"}, @foobar="hello">
//
pos = var_re.search(buf);
if (pos != -1) {
while (pos != -1) {
varName = var_re.cap(1);
if (ref_re.search(var_re.cap(2)) != -1) {
if (var_re.cap(2).contains("=") > 0) {
value = (ref_re.cap(1) + ">").latin1();
} else {
// There are no 'name=value' pairs, as in #<TQt::Color:0x0 #ff0000>
value = var_re.cap(2).latin1();
}
} else if (struct_re.search(var_re.cap(2)) != -1) {
value = (TQString("#<Struct::") + struct_re.cap(1) + ">").latin1();
} else {
value = var_re.cap(2).latin1();
}
DataType dataType = determineType((char *) var_re.cap(2).latin1());
// 'self' variables don't need to be expandable, as their details are
// already shown in the current frame. So always make them VALUE_TYPE's.
if (varName == "self") {
dataType = VALUE_TYPE;
}
setItem(parent, varName, dataType, value);
pos += var_re.matchedLength();
pos = var_re.search(buf, pos);
}
return;
}
}
void RDBParser::parseExpandedVariable(VarItem *parent, char *buf)
{
DataType dataType;
int pos;
TQString varName;
TQCString value;
TQRegExp ppref_re("(#<([^:]|::)+:0x[\\da-f]+)([^\\n>]*)(>?)");
switch (parent->dataType()) {
case REFERENCE_TYPE:
{
// Look for a reference type which has been printed via a 'pp' command, to
// expand its sub items on multiple lines. For example:
// #<MyClass:0x30093540
// @foobar="hello",
// @sleeper=#<Thread:0x3008fd18 sleep>,
// @temp={"z"=>"zed", "p"=>"pee"}>
//
TQRegExp ppvalue_re("\\s*([^\\n\\s=]+)=([^\\n]+)[,>]");
pos = ppref_re.search(buf);
if (pos != -1) {
if (ppref_re.cap(4) != "") {
// The value is all on one line, so match against name=value
// pairs which can't have commas in their values
ppvalue_re = TQRegExp("\\s*([^\\s=]+)=([^,>]+)([,>])");
}
pos = ppvalue_re.search(buf, pos);
while (pos != -1) {
varName = ppvalue_re.cap(1);
if (ppref_re.search(ppvalue_re.cap(2)) != -1) {
if (ppvalue_re.cap(2).contains("=") > 0) {
value = (ppref_re.cap(1) + ">").latin1();
} else {
// There are no 'name=value' pairs, as in #<TQt::Color:0x0 #ff0000>
value = ppvalue_re.cap(2).latin1();
}
} else {
value = ppvalue_re.cap(2).latin1();
}
dataType = determineType((char *) ppvalue_re.cap(2).latin1());
setItem(parent, varName, dataType, value);
pos += ppvalue_re.matchedLength();
pos = ppvalue_re.search(buf, pos);
}
}
return;
}
case ARRAY_TYPE:
{
// Look for a array type which has been printed via a 'pp' command, to
// expand its sub items. For example:
// [0]="hello"
// [1]=#"goodbye"
//
TQRegExp pparray_re("\\s*([^=]+)=([^\\n]+)\\n");
pos = pparray_re.search(buf);
while (pos != -1) {
varName = pparray_re.cap(1);
if (ppref_re.search(pparray_re.cap(2)) != -1) {
value = (ppref_re.cap(1) + ">").latin1();
} else {
value = pparray_re.cap(2).latin1();
}
DataType dataType = determineType((char *) pparray_re.cap(2).latin1());
setItem(parent, varName, dataType, value);
pos += pparray_re.matchedLength();
pos = pparray_re.search(buf, pos);
}
return;
}
case HASH_TYPE:
{
// Look for a hash type which has been printed via a 'pp' command, to
// expand its sub items. For example:
// ["greeting"]="hello"
// ["farewell"]="goodbye"
//
TQRegExp pphash_re("\\s*(\\[[^\\]]+\\])=([^\\n]+)\\n");
pos = pphash_re.search(buf);
while (pos != -1) {
varName = pphash_re.cap(1);
value = pphash_re.cap(2).latin1();
DataType dataType = determineType(value.data());
setItem(parent, varName, dataType, value);
pos += pphash_re.matchedLength();
pos = pphash_re.search(buf, pos);
}
return;
}
case STRUCT_TYPE:
{
// Look for a reference type which has been printed via a 'pp' command, to
// expand its sub items. For example:
// #<Struct::Customer
// @foobar="hello",
// @sleeper=#<Thread:0x3008fd18 sleep>,
// @temp={"z"=>"zed", "p"=>"pee"}>
//
TQRegExp ppstruct_re("(#<Struct::\\w+)\\s([^\\n>]*)(>?)");
TQRegExp ppvalue_re("\\s*([^\\n\\s=]+)=([^\\n]+)[,>]");
pos = ppstruct_re.search(buf);
if (pos != -1) {
if (ppstruct_re.cap(3) != "" && ppvalue_re.search(ppstruct_re.cap(0)) != -1) {
// The line ends with a '>', but we have this case now..
// If there is only one instance variable, pp puts everything
// on a single line:
// #<Struct::Customer @foobar="hello">
// So search for '@foobar="hello"', to use as the
// first name=value pair
pos = 0;
} else {
// Mltiple lines with name=value pairs:
// #<Struct::Customer
// @foobar="hello",
pos = ppvalue_re.search(buf, pos);
}
while (pos != -1) {
varName = ppvalue_re.cap(1);
value = ppvalue_re.cap(2).latin1();
dataType = determineType(value.data());
setItem(parent, varName, dataType, value);
pos += ppvalue_re.matchedLength();
pos = ppvalue_re.search(buf, pos);
}
}
return;
}
case STRING_TYPE:
{
// Look for a long String which has been printed via a 'pp' command, to
// show it as a sequence of 12 bytes slices in hex. For example:
// [0..11]=0x89504e470d0a1a0a0000000d
// [12..23]=0x494844520000001600000016
//
TQRegExp ppstring_re("\\s*(\\[[^\\]]+\\])=([^\\n]+)\\n");
pos = ppstring_re.search(buf);
while (pos != -1) {
varName = ppstring_re.cap(1);
value = ppstring_re.cap(2).latin1();
DataType dataType = determineType(value.data());
setItem(parent, varName, dataType, value);
pos += ppstring_re.matchedLength();
pos = ppstring_re.search(buf, pos);
}
return;
}
default:
Q_ASSERT(false);
}
return;
}
// **************************************************************************
void RDBParser::setItem(LazyFetchItem *parent, const TQString &varName,
DataType dataType, const TQCString &value)
{
VarItem *item = parent->findItem(varName);
if (item == 0) {
item = new VarItem(parent, varName, dataType);
} else {
// The dataType of an item can change, so update it
item->setDataType(dataType);
}
switch (dataType) {
case HASH_TYPE:
case ARRAY_TYPE:
case REFERENCE_TYPE:
case STRUCT_TYPE:
case STRING_TYPE:
item->setText(VALUE_COLUMN, value);
item->setExpandable(true);
item->update();
break;
case COLOR_TYPE:
case VALUE_TYPE:
item->setText(VALUE_COLUMN, value);
item->setExpandable(false);
break;
default:
break;
}
}
// **************************************************************************
DataType RDBParser::determineType(char *buf)
{
TQRegExp array_re("(Array \\(\\d+ element\\(s\\)\\))");
TQRegExp hash_re("(Hash \\(\\d+ element\\(s\\)\\))");
TQRegExp string_re("(String \\(length \\d+\\))");
if (tqstrncmp(buf, "#<struct", strlen("#<struct")) == 0) {
return STRUCT_TYPE;
} else if (tqstrncmp(buf, "#<TQt::Color:0x", strlen("#<TQt::Color:0x")) == 0) {
return COLOR_TYPE;
} else if (tqstrncmp(buf, "#<", strlen("#<")) == 0 && strstr(buf, "=") != 0) {
// An object instance reference is only expandable and a 'REFERENCE_TYPE'
// if it contains an '=' (ie it has at least one '@instance_variable=value').
// Otherwise, treat it as a 'VALUE_TYPE'.
return REFERENCE_TYPE;
} else if (array_re.search(buf) != -1) {
return ARRAY_TYPE;
} else if (hash_re.search(buf) != -1) {
return HASH_TYPE;
} else if (string_re.search(buf) != -1) {
return STRING_TYPE;
} else if (tqstrncmp(buf, "nil", strlen("nil")) == 0) {
// return UNKNOWN_TYPE;
return VALUE_TYPE;
} else {
return VALUE_TYPE;
}
}
// **************************************************************************
// **************************************************************************
// **************************************************************************
}