JSON parser: Improve by using exceptions to get exact error

Also strip redundant "_Parser_" prefix from private methods.
This commit is contained in:
Stephan Aßmus
2014-08-30 23:42:02 +02:00
parent 7f9beaf1f8
commit ffd7c6110b
2 changed files with 102 additions and 44 deletions
+5 -4
View File
@@ -22,10 +22,11 @@ public:
static status_t Parse(BMessage& message, BString& JSON); static status_t Parse(BMessage& message, BString& JSON);
private: private:
static BString _Parser_ParseString(BString& JSON, int32& pos); static void _Parse(BMessage& message, BString& JSON);
static double _Parser_ParseNumber(BString& JSON, int32& pos); static BString _ParseString(BString& JSON, int32& pos);
static bool _Parser_ParseConstant(BString& JSON, int32& pos, static double _ParseNumber(BString& JSON, int32& pos);
const char* constant); static bool _ParseConstant(BString& JSON, int32& pos,
const char* constant);
}; };
} // namespace BPrivate } // namespace BPrivate
+97 -40
View File
@@ -1,5 +1,6 @@
/* /*
* Copyright 2014, Augustin Cavalier (waddlesplash) * Copyright 2014, Augustin Cavalier (waddlesplash)
* Copyright 2014, Stephan Aßmus <[email protected]>
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -17,6 +18,46 @@
namespace BPrivate { namespace BPrivate {
class ParseException {
public:
ParseException(int32 position, BString error)
:
fPosition(position),
fError(error),
fReturnCode(B_BAD_DATA)
{
}
ParseException(int32 position, status_t returnCode)
:
fPosition(position),
fError(""),
fReturnCode(returnCode)
{
}
void PrintToStream() const {
const char* error;
if (fError.Length() > 0)
error = fError.String();
else
error = strerror(fReturnCode);
printf("Parse error at %ld: %s\n", fPosition, error);
}
status_t ReturnCode() const
{
return fReturnCode;
}
private:
int32 fPosition;
BString fError;
status_t fReturnCode;
};
status_t status_t
BJson::Parse(BMessage& message, const char* JSON) BJson::Parse(BMessage& message, const char* JSON)
{ {
@@ -27,6 +68,23 @@ BJson::Parse(BMessage& message, const char* JSON)
status_t status_t
BJson::Parse(BMessage& message, BString& JSON) BJson::Parse(BMessage& message, BString& JSON)
{
try {
_Parse(message, JSON);
return B_OK;
} catch (ParseException e) {
e.PrintToStream();
return e.ReturnCode();
}
return B_ERROR;
}
// #pragma mark - Private methods
void
BJson::_Parse(BMessage& message, BString& JSON)
{ {
BMessageBuilder builder(message); BMessageBuilder builder(message);
int32 pos = 0; int32 pos = 0;
@@ -59,13 +117,13 @@ BJson::Parse(BMessage& message, BString& JSON)
break; break;
case '}': case '}':
if (hierarchy.EndsWith("{") && (hierarchy.Length() != 1)) { if (hierarchy.EndsWith("{") && hierarchy.Length() != 1) {
hierarchy.Truncate(hierarchy.Length() - 1); hierarchy.Truncate(hierarchy.Length() - 1);
builder.PopObject(); builder.PopObject();
} else if (hierarchy.Length() == 1) } else if (hierarchy.Length() == 1)
return B_OK; // End of the JSON data return; // End of the JSON data
else else
return B_BAD_DATA; // Unmatched closebrace throw ParseException(pos, "Unmatched closebrace }");
break; break;
@@ -86,78 +144,80 @@ BJson::Parse(BMessage& message, BString& JSON)
if (hierarchy.EndsWith("[")) { if (hierarchy.EndsWith("[")) {
hierarchy.Truncate(hierarchy.Length() - 1); hierarchy.Truncate(hierarchy.Length() - 1);
builder.PopObject(); builder.PopObject();
} else } else {
return B_BAD_DATA; // Unmatched closebracket BString error("Unmatched closebrace ] hierarchy: ");
error << hierarchy;
throw ParseException(pos, error);
}
break; break;
case 't': case 't':
{ {
if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) {
return B_BAD_DATA; throw ParseException(pos,
// 'true' cannot be a key, it can only be a value "'true' cannot be a key, it can only be a value");
}
if (_Parser_ParseConstant(JSON, pos, "true")) { if (_ParseConstant(JSON, pos, "true")) {
if (builder.What() == JSON_TYPE_ARRAY) if (builder.What() == JSON_TYPE_ARRAY)
key.SetToFormat("%" B_PRIu32, builder.CountNames()); key.SetToFormat("%" B_PRIu32, builder.CountNames());
builder.AddBool(key.String(), true); builder.AddBool(key.String(), true);
key = ""; key = "";
} else } else
return B_BAD_DATA; throw ParseException(pos, "Unexpected 't'");
// "t" out in the middle of nowhere!?
break; break;
} }
case 'f': case 'f':
{ {
if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) {
return B_BAD_DATA; throw ParseException(pos,
// 'false' cannot be a key, it can only be a value "'false' cannot be a key, it can only be a value");
}
if (_Parser_ParseConstant(JSON, pos, "false")) { if (_ParseConstant(JSON, pos, "false")) {
if (builder.What() == JSON_TYPE_ARRAY) if (builder.What() == JSON_TYPE_ARRAY)
key.SetToFormat("%" B_PRIu32, builder.CountNames()); key.SetToFormat("%" B_PRIu32, builder.CountNames());
builder.AddBool(key.String(), false); builder.AddBool(key.String(), false);
key = ""; key = "";
} else } else
return B_BAD_DATA; throw ParseException(pos, "Unexpected 'f'");
// "f" out in the middle of nowhere!?
break; break;
} }
case 'n': case 'n':
{ {
if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) {
return B_BAD_DATA; throw ParseException(pos,
// 'null' cannot be a key, it can only be a value "'null' cannot be a key, it can only be a value");
}
if (_Parser_ParseConstant(JSON, pos, "null")) { if (_ParseConstant(JSON, pos, "null")) {
if (builder.What() == JSON_TYPE_ARRAY) if (builder.What() == JSON_TYPE_ARRAY)
key.SetToFormat("%" B_PRIu32, builder.CountNames()); key.SetToFormat("%" B_PRIu32, builder.CountNames());
builder.AddPointer(key.String(), (void*)NULL); builder.AddPointer(key.String(), (void*)NULL);
key = ""; key = "";
} else } else
return B_BAD_DATA; throw ParseException(pos, "Unexpected 'n'");
// "n" out in the middle of nowhere!?
break; break;
} }
case '"': case '"':
if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0)
key = _Parser_ParseString(JSON, pos); key = _ParseString(JSON, pos);
else if (builder.What() != JSON_TYPE_ARRAY && key.Length() > 0) { else if (builder.What() != JSON_TYPE_ARRAY && key.Length() > 0) {
builder.AddString(key, _Parser_ParseString(JSON, pos)); builder.AddString(key, _ParseString(JSON, pos));
key = ""; key = "";
} else if (builder.What() == JSON_TYPE_ARRAY) { } else if (builder.What() == JSON_TYPE_ARRAY) {
key << builder.CountNames(); key << builder.CountNames();
builder.AddString(key, _Parser_ParseString(JSON, pos)); builder.AddString(key, _ParseString(JSON, pos));
key = ""; key = "";
} else } else
return B_BAD_DATA; throw ParseException(pos, "Internal error at encountering \"");
// Pretty sure it's impossible to get here, but you never know
break; break;
@@ -174,14 +234,15 @@ BJson::Parse(BMessage& message, BString& JSON)
case '8': case '8':
case '9': case '9':
{ {
if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0) {
return B_BAD_DATA; throw ParseException(pos,
// Numbers cannot be keys, they can only be values "Numbers cannot be keys, they can only be values");
}
if (builder.What() == JSON_TYPE_ARRAY) if (builder.What() == JSON_TYPE_ARRAY)
key << builder.CountNames(); key << builder.CountNames();
double number = _Parser_ParseNumber(JSON, pos); double number = _ParseNumber(JSON, pos);
builder.AddDouble(key.String(), number); builder.AddDouble(key.String(), number);
key = ""; key = "";
@@ -197,16 +258,12 @@ BJson::Parse(BMessage& message, BString& JSON)
pos++; pos++;
} }
// Reached the end of the BString without reaching the end of the document throw ParseException(pos, "Unexpected end of document");
return B_BAD_DATA;
} }
// #pragma mark - Private methods
BString BString
BJson::_Parser_ParseString(BString& JSON, int32& pos) BJson::_ParseString(BString& JSON, int32& pos)
{ {
if (JSON[pos] != '"') // Verify we're at the start of a string. if (JSON[pos] != '"') // Verify we're at the start of a string.
return BString(""); return BString("");
@@ -270,7 +327,7 @@ BJson::_Parser_ParseString(BString& JSON, int32& pos)
double double
BJson::_Parser_ParseNumber(BString& JSON, int32& pos) BJson::_ParseNumber(BString& JSON, int32& pos)
{ {
BString value; BString value;
@@ -309,7 +366,7 @@ BJson::_Parser_ParseNumber(BString& JSON, int32& pos)
bool bool
BJson::_Parser_ParseConstant(BString& JSON, int32& pos, const char* constant) BJson::_ParseConstant(BString& JSON, int32& pos, const char* constant)
{ {
BString value; BString value;
JSON.CopyInto(value, pos, strlen(constant)); JSON.CopyInto(value, pos, strlen(constant));