ultimatepp/uppsrc/ide/Debuggers/MIValue.cpp
cxl 5edae0e110 .reorganizing
git-svn-id: svn://ultimatepp.org/upp/trunk@9759 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2016-05-01 20:21:16 +00:00

806 lines
14 KiB
C++

#include "MIValue.h"
//#define MITUPLE_DUMP_MARKERS
static MIValue &NullMIValue(void)
{
static MIValue v;
return v;
}
static MIValue &ErrorMIValue(String const &msg)
{
static MIValue v;
v.SetError(msg);
return v;
}
bool MIValue::expect(String const &where, char exp, int i, String const &s)
{
if(s[i] == exp)
return true;
int start = i - 30;
if(start < 0)
start = 0;
if(!s[i])
SetError(Format(where + " : Expected '%c', got end of string at pos %d around '%s'", exp, i, s.Mid(start, 60)));
else
SetError(Format(where + " : Expected '%c', got '%c' at pos %d in '%s'", exp, s[i], i, s.Mid(start, 60)));
return false;
}
static char backslash(String const &s, int &i)
{
i++;
if(IsDigit(s[i]))
{
char c = (s[i]-'0')*64 + (s[i+1]-'0')*8 + s[i+2]-'0';
i += 2;
return c;
}
// control chars and octals
switch(s[i])
{
case 'a' :
return '\a';
case 'b' :
return '\b';
case 'f' :
return '\f';
case 'n' :
return '\n';
case 'r' :
return '\r';
case 't' :
return '\t';
case 'v' :
return '\v';
default:
return s[i];
}
}
int MIValue::ParsePair(String &name, MIValue &val, String const &s, int i)
{
name.Clear();
val.Clear();
while(s[i] && isspace(s[i]))
i++;
if(!s[i])
{
SetError("ParsePair:Unexpected end of string");
return i;
}
// is starting wirh '[' or '{' take it as a value with empty name
if(s[i] == '{' || s[i] == '[')
{
name = "<UNNAMED>";
return val.ParseTuple(s, i);
}
else
{
int aCount = 0;
while(s[i] && ((s[i] != '=' && s[i] != '}' && s[i] != ']' && s[i] != ',') || aCount))
{
if(s[i] == '<')
aCount++;
else if(s[i] == '>')
aCount--;
if(s[i] == '\\')
name.Cat(backslash(s, i));
else
name.Cat(s[i]);
i++;
// skip blanks if not inside <>
/*
if(!aCount)
while(s[i] && isspace(s[i]))
i++;
*/
}
while(s[i] && isspace(s[i]))
i++;
if(s[i] != '=')
{
// we take the data without = as the value part
// of keyless tuple...
val.Set(name);
name = "<UNNAMED>";
return i;
}
i++;
while(s[i] && isspace(s[i]))
i++;
}
// skip address part before a tuple start, if any... it's useless and confuses the parser
if(s[i] == '@')
{
int j = i;
while(s[j] && s[j] != ':')
j++;
if(s[j] == ':')
j++;
while(s[j] && IsSpace(s[j]))
j++;
if(s[j] == '{')
i = j;
}
switch(s[i])
{
case '"':
i = val.ParseString(s, i);
break;
break;
case '[':
i = val.ParseArray(s, i);
break;
case '{':
i = val.ParseTuple(s, i);
break;
default:
i = val.ParseUnquotedString(s, i);
break;
}
return i;
}
int MIValue::ParseTuple(String const &s, int i)
{
Clear();
type = MITuple;
// drop opening delimiter
if(!expect("ParseTuple", '{', i, s))
return s.GetCount();
i++;
while(s[i] && s[i] != '}')
{
while(s[i] && isspace(s[i]))
i++;
String name;
MIValue val;
i = ParsePair(name, val, s, i);
tuple.AddPick(name, pick(val));
while(s[i] && isspace(s[i]))
i++;
if(s[i] == '}')
break;
if(!expect("ParseTuple", ',', i, s))
return s.GetCount();
i++;
}
return i + 1;
}
int MIValue::ParseArray(String const &s, int i)
{
Clear();
type = MIArray;
// drop opening delimiter
if(!expect("ParseArray", '[', i, s))
return s.GetCount();
i++;
while(s[i] && isspace(s[i]))
i++;
while(s[i] && s[i] != ']')
{
while(s[i] && isspace(s[i]))
i++;
String name;
MIValue val;
if(s[i] == '[')
i = val.ParseArray(s, i);
else if(s[i] == '{')
i = val.ParseTuple(s, i);
else if(s[i] == '"')
i = val.ParseString(s, i);
else if(s[i] == '<')
i = val.ParseAngle(s, i);
else
i = ParsePair(name, val, s, i);
array.Add() = pick(val);
while(s[i] && isspace(s[i]))
i++;
if(s[i] == ']')
break;
if(!expect("ParseArray", ',', i, s))
return s.GetCount();
i++;
}
return i + 1;
}
int MIValue::ParseString(String const &s, int i)
{
Clear();
type = MIString;
if(!expect("ParseString", '"', i, s))
return s.GetCount();
i++;
while(s[i])
{
// verbatim if escaped
if(s[i] == '\\')
string.Cat(backslash(s, i));
else if(s[i] == '"')
{
i++;
break;
}
else
string.Cat(s[i]);
i++;
}
if(!expect("ParseString", '"', i-1, s))
return s.GetCount();
return i;
}
int MIValue::ParseAngle(String const &s, int i)
{
Clear();
type = MIString;
int aCount = 0;
if(!expect("ParseAngle", '<', i, s))
return s.GetCount();
string = "<";
aCount++;
i++;
while(s[i])
{
// verbatim if escaped
if(s[i] == '\\')
string.Cat(backslash(s, i));
else if(s[i] == '>' && !--aCount)
{
i++;
break;
}
else
{
string.Cat(s[i]);
if(s[i] == '<')
aCount++;
}
i++;
}
if(!expect("ParseAngle", '>', i-1, s))
return s.GetCount();
string.Cat('>');
return i;
}
// sigh
static bool comma(String const &s, int i)
{
if(s[i] != ',')
return false;
if(!i)
return true;
if(IsDigit(s[i-1]) && IsDigit(s[i+1]))
return false;
return true;
}
// we can hava a non-quoted string... so we read up
// to terminator, which can be '}', ']' or ','
int MIValue::ParseUnquotedString(String const &s, int i)
{
String valStr;
int aCount = 0;
bool inQuote = false;
while(s[i] && ((s[i] != '=' && s[i] != '}' && s[i] != ']' && !comma(s, i)) || inQuote || aCount))
{
valStr.Cat(s[i]);
if(s[i] == '\\')
{
i++;
if(s[i])
valStr.Cat(s[i++]);
continue;
}
if(s[i] == '<' && !inQuote)
aCount++;
else if(s[i] == '>' && !inQuote)
aCount--;
else if(s[i] == '"' && !aCount)
inQuote = !inQuote;
i++;
}
type = MIString;
string = valStr;
return i;
}
int MIValue::Parse(String const &s, int i)
{
// if starts with '"', it's a string
// if starts with '[', it's an array
// if starts with '{', it's a tuple
// otherwise, it can be a sequence of pair name="value" which is stored like a tuple
// latter case is an example o bad design of MI interface....
Clear();
while(s[i] && isspace(s[i]))
i++;
if(s[i] == '"')
return ParseString(s, i);
else if(s[i] == '<')
return ParseAngle(s, i);
else if(s[i] == '[')
return ParseArray(s, i);
else if(s[i] == '{')
return ParseTuple(s, i);
else
{
String name;
MIValue val;
type = MITuple;
while(s[i])
{
i = ParsePair(name, val, s, i);
tuple.AddPick(name, pick(val));
while(s[i] && isspace(s[i]))
i++;
if(s[i] != ',')
break;
i++;
}
return i;
}
}
MIValue &MIValue::operator=(MIValue&& v)
{
Clear();
type = pick(v.type);
switch(type)
{
case MIString:
string = v.string;
break;
case MIArray:
array = pick(v.array);
break;
case MITuple:
tuple = pick(v.tuple);
break;
default:
SetError("Unknown MIValue type");
}
return *this;
}
MIValue::MIValue()
{
Clear();
}
MIValue::MIValue(MIValue&& v)
{
Clear();
type = v.type;
switch(type)
{
case MIString:
string = v.string;
break;
case MIArray:
array = pick(v.array);
break;
case MITuple:
tuple = pick(v.tuple);
break;
default:
SetError("Unknown MIValue type");
}
}
MIValue::MIValue(String const &s)
{
Parse(s);
// tuple with 1 element and unnamed key is a string
if(IsTuple() && tuple.GetCount() == 1 && tuple.GetKey(0) == "<UNNAMED>")
{
type = MIString;
string = tuple[0];
tuple.Clear();
}
}
MIValue &MIValue::operator=(String const &s)
{
Parse(s);
// tuple with 1 element and unnamed key is a string
if(IsTuple() && tuple.GetCount() == 1 && tuple.GetKey(0) == "<UNNAMED>")
{
type = MIString;
string = tuple[0];
tuple.Clear();
}
return *this;
}
void MIValue::Clear()
{
type = MIString;
string = "";
array.Clear();
tuple.Clear();
}
// sets value to an error condition
MIValue &MIValue::SetError(String const &msg)
{
type = MIString;
string = "error:" + msg;
return *this;
}
// check if value contains an error
bool MIValue::IsError(void) const
{
return type == MIString && string.StartsWith("error:");
}
// check for emptyness
bool MIValue::IsEmpty(void) const
{
return type == MIString && string == "";
}
// simple accessors
int MIValue::GetCount(void) const
{
if(IsError())
return 0;
if(type == MIArray)
return array.GetCount();
else if(type == MITuple)
return tuple.GetCount();
else
return string.GetCount();
}
int MIValue::Find(const char *key) const
{
if(type != MITuple)
return -1;
return tuple.Find(key);
}
MIValue &MIValue::Get(int i)
{
if(IsError())
return *this;
if(type == MIArray)
return array[i];
if(type == MITuple)
return tuple[i];
return ErrorMIValue("Not an Array value type");
}
MIValue const &MIValue::Get(int i) const
{
if(IsError())
return *this;
if(type == MIArray)
return array[i];
if(type == MITuple)
return tuple[i];
return ErrorMIValue("Not an Array value type");
}
MIValue &MIValue::Get(const char *key)
{
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
if(tuple.Find(key) < 0)
return ErrorMIValue(String("key '") + key + "' not found");
return tuple.Get(key);
}
MIValue const &MIValue::Get(const char *key) const
{
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
if(tuple.Find(key) < 0)
return ErrorMIValue(String("key '") + key + "' not found");
return tuple.Get(key);
}
String &MIValue::Get(void)
{
if(type != MIString)
return ErrorMIValue("Not a String value type");
return string;
}
String const &MIValue::Get(void) const
{
if(type != MIString)
return ErrorMIValue("Not a String value type");
return string;
}
// tuple string member accessor with default value if not found
String MIValue::Get(const char *key, const char *def) const
{
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
int i = tuple.Find(key);
if(i >= 0)
{
if(tuple[i].type != MIString)
return def;
return tuple[i].Get();
}
else
return def;
}
// gets key by index for tuple values
String MIValue::GetKey(int idx) const
{
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
return tuple.GetKey(idx);
}
void MIValue::Set(String const &s)
{
Clear();
type = MIString;
string = s;
}
// data dump
#ifdef MITUPLE_DUMP_MARKERS
#define MARK_STRING "<STRING>"
#define MARK_ARRAY "<ARRAY>"
#define MARK_TUPLE "<TUPLE>"
#else
#define MARK_STRING ""
#define MARK_ARRAY ""
#define MARK_TUPLE ""
#endif
// dumps a string with special chars inside
String MIValue::Dump(String const &s)
{
String res;
for(int i = 0; i < s.GetCharCount(); i++)
{
byte c = s[i];
if(isprint(c))
res << c;
else
res += Format("\\%03o", c);
}
return res;
}
String MIValue::Dump(int level) const
{
String spacer(' ', level);
switch(type)
{
case MIString:
return spacer + MARK_STRING + Dump(string);
case MITuple:
{
String s = spacer + MARK_TUPLE + "{\n";
level += 4;
spacer = String(' ', level);
for(int i = 0; i < tuple.GetCount(); i++)
{
String s1 = spacer + tuple.GetKey(i) + "=";
s += s1;
MIValue const &val = tuple[i];
if(val.type == MIString)
s += val.Dump();
else
{
s += '\n' + val.Dump(level + 4);
s = s.Left(s.GetCount()-1);
}
if(i < tuple.GetCount() - 1)
s += ',';
s += '\n';
}
level -= 4;
spacer = String(' ', level);
s += spacer + "}\n";
return s;
}
case MIArray:
{
String s = spacer + MARK_ARRAY + "[ \n";
level += 4;
for(int i = 0; i < array.GetCount(); i++)
{
MIValue const &val = array[i];
s += val.Dump(level);
if(val.type != MIString)
s = s.Left(s.GetCount()-1);
if(i < array.GetCount() - 1)
s += ',';
s += '\n';
}
s += spacer + "]\n";
return s;
}
default:
return spacer + "*UNKNOWN MIVALUE TYPE*";
}
}
// finds breakpoint data given file and line
MIValue &MIValue::FindBreakpoint(String const &file, int line)
{
MIValue &body = Get("body");
if(body.IsError() || !body.IsArray())
return NullMIValue();
for(int i = 0; i < body.GetCount(); i++)
{
MIValue &bp = body[i];
if(bp.IsError() || !bp.IsTuple())
return NullMIValue();
if(bp["file"] == file && atoi(bp["line"].Get()) == line)
return bp;
}
return NullMIValue();
}
static String PackName(String const &name)
{
String res;
const char *c = ~name;
while(*c)
{
if(!IsSpace(*c))
res.Cat(*c);
c++;
}
return res;
}
// packs names inside tuples -- to make type recognition easy
void MIValue::PackNames(void)
{
if(type == MITuple)
{
for(int i = 0; i < tuple.GetCount(); i++)
{
tuple.SetKey(i, PackName(tuple.GetKey(i)));
tuple[i].PackNames();
}
}
else if(type == MIArray)
{
for(int i = 0; i < array.GetCount(); i++)
array[i].PackNames();
}
}
// fix arrays -- i.e. replace a tuple containing ALL unnamed elements
// with the corresponding array
// ( gdb evaluator array data is returned as tuple with {} instead []=
void MIValue::FixArrays(void)
{
bool named = false;
if(IsTuple())
{
for(int iVal = 0; iVal < tuple.GetCount(); iVal++)
if(tuple.GetKey(iVal) != "<UNNAMED>")
{
named = true;
break;
}
if(!named)
{
array.Clear();
for(int iVal = 0; iVal < tuple.GetCount(); iVal++)
array.Add() = pick(tuple[iVal]);
tuple.Clear();
type = MIArray;
}
}
if(IsTuple() || IsArray())
for(int i = 0; i < GetCount(); i++)
Get(i).FixArrays();
}
// add an item to a tuple
MIValue &MIValue::Add(String const &key, MIValue&& v)
{
if(IsEmpty())
{
Clear();
type = MITuple;
}
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
tuple.AddPick(key, pick(v));
return *this;
}
MIValue &MIValue::Add(String const &key, String const &data)
{
MIValue v;
v.Set(data);
return Add(key, pick(v));
}
MIValue &MIValue::FindAdd(String const &key, String const &data)
{
if(IsEmpty())
{
Clear();
type = MITuple;
}
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
int idx = tuple.Find(key);
MIValue v;
v.Set(data);
if(idx >= 0)
tuple[idx] = pick(v);
else
tuple.AddPick(key, pick(v));
return *this;
}
// add an item to an array
MIValue &MIValue::Add(MIValue&& v)
{
if(IsEmpty())
{
Clear();
type = MIArray;
}
if(type != MIArray)
return ErrorMIValue("Not a Array value type");
array.AddPick(pick(v));
return *this;
}
MIValue &MIValue::Add(String const &data)
{
MIValue v;
v.Set(data);
return Add(pick(v));
}
// remove a tuple key
MIValue &MIValue::Remove(String const &key)
{
if(type != MITuple)
return *this;
tuple.RemoveKey(key);
return *this;
}