35 int line = 1, column = 1;
37 String getDescription()
const {
return String (line) +
":" +
String (column) +
": error: " + message; }
44 e.message = std::move (message);
46 for (
auto i = startLocation; i < location && ! i.
isEmpty(); ++i)
49 if (*i ==
'\n') { e.column = 1; e.line++; }
56 juce_wchar readChar() {
return currentLocation.
getAndAdvance(); }
57 juce_wchar peekChar()
const {
return *currentLocation; }
58 bool matchIf (
char c) {
if (peekChar() == (juce_wchar) c) { ++currentLocation;
return true; }
return false; }
59 bool isEOF()
const {
return peekChar() == 0; }
61 bool matchString (
const char* t)
70 var parseObjectOrArray()
74 if (matchIf (
'{'))
return parseObject();
75 if (matchIf (
'['))
return parseArray();
78 throwError (
"Expected '{' or '['", currentLocation);
83 String parseString (
const juce_wchar quoteChar)
96 auto errorLocation = currentLocation;
106 case 'a': c =
'\a';
break;
107 case 'b': c =
'\b';
break;
108 case 'f': c =
'\f';
break;
109 case 'n': c =
'\n';
break;
110 case 'r': c =
'\r';
break;
111 case 't': c =
'\t';
break;
117 for (
int i = 4; --i >= 0;)
122 throwError (
"Syntax error in unicode escape sequence", errorLocation);
124 c = (juce_wchar) ((c << 4) +
static_cast<juce_wchar
> (digitValue));
133 throwError (
"Unexpected EOF in string constant", currentLocation);
144 auto originalLocation = currentLocation;
148 case '{':
return parseObject();
149 case '[':
return parseArray();
150 case '"':
return parseString (
'"');
151 case '\'':
return parseString (
'\'');
155 return parseNumber (
true);
157 case '0':
case '1':
case '2':
case '3':
case '4':
158 case '5':
case '6':
case '7':
case '8':
case '9':
159 currentLocation = originalLocation;
160 return parseNumber (
false);
163 if (matchString (
"rue"))
169 if (matchString (
"alse"))
175 if (matchString (
"ull"))
184 throwError (
"Syntax error", originalLocation);
187 var parseNumber (
bool isNegative)
189 auto originalPos = currentLocation;
191 int64 intValue = readChar() -
'0';
192 jassert (intValue >= 0 && intValue < 10);
196 auto lastPos = currentLocation;
198 auto digit = ((int) c) -
'0';
200 if (isPositiveAndBelow (digit, 10))
202 intValue = intValue * 10 + digit;
206 if (c ==
'e' || c ==
'E' || c ==
'.')
208 currentLocation = originalPos;
210 return var (isNegative ? -asDouble : asDouble);
214 || c ==
',' || c ==
'}' || c ==
']' || c == 0)
216 currentLocation = lastPos;
220 throwError (
"Syntax error in number", lastPos);
223 auto correctedValue = isNegative ? -intValue : intValue;
225 return (intValue >> 31) != 0 ?
var (correctedValue)
226 :
var ((
int) correctedValue);
232 var result (resultObject);
233 auto& resultProperties = resultObject->getProperties();
234 auto startOfObjectDecl = currentLocation;
239 auto errorLocation = currentLocation;
246 throwError (
"Unexpected EOF in object declaration", startOfObjectDecl);
249 throwError (
"Expected a property name in double-quotes", errorLocation);
251 errorLocation = currentLocation;
255 throwError (
"Invalid property name", errorLocation);
258 errorLocation = currentLocation;
260 if (readChar() !=
':')
261 throwError (
"Expected ':'", errorLocation);
263 resultProperties.set (propertyName, parseAny());
266 if (matchIf (
','))
continue;
267 if (matchIf (
'}'))
break;
269 throwError (
"Expected ',' or '}'", currentLocation);
278 auto destArray = result.getArray();
279 auto startOfArrayDecl = currentLocation;
289 throwError (
"Unexpected EOF in array declaration", startOfArrayDecl);
291 destArray->add (parseAny());
294 if (matchIf (
','))
continue;
295 if (matchIf (
']'))
break;
297 throwError (
"Expected ',' or ']'", currentLocation);
308 int indentLevel,
bool allOnOneLine,
int maximumDecimalPlaces)
320 else if (v.isUndefined())
326 out << (static_cast<bool> (v) ?
"true" :
"false");
328 else if (v.isDouble())
330 auto d =
static_cast<double> (v);
332 if (juce_isfinite (d))
334 out << serialiseDouble (d);
341 else if (v.isArray())
343 writeArray (out, *v.
getArray(), indentLevel, allOnOneLine, maximumDecimalPlaces);
345 else if (v.isObject())
347 if (
auto*
object = v.getDynamicObject())
348 object->writeAsJSON (out, indentLevel, allOnOneLine, maximumDecimalPlaces);
355 jassert (! (v.isMethod() || v.isBinaryData()));
361 static void writeEscapedChar (
OutputStream& out,
const unsigned short value)
376 case '\"': out <<
"\\\"";
break;
377 case '\\': out <<
"\\\\";
break;
378 case '\a': out <<
"\\a";
break;
379 case '\b': out <<
"\\b";
break;
380 case '\f': out <<
"\\f";
break;
381 case '\t': out <<
"\\t";
break;
382 case '\r': out <<
"\\r";
break;
383 case '\n': out <<
"\\n";
break;
386 if (c >= 32 && c < 127)
394 CharPointer_UTF16::CharType chars[2];
398 for (
int i = 0; i < 2; ++i)
399 writeEscapedChar (out, (
unsigned short) chars[i]);
403 writeEscapedChar (out, (
unsigned short) c);
412 static void writeSpaces (
OutputStream& out,
int numSpaces)
418 int indentLevel,
bool allOnOneLine,
int maximumDecimalPlaces)
427 for (
int i = 0; i < array.
size(); ++i)
430 writeSpaces (out, indentLevel + indentSize);
432 write (out, array.
getReference(i), indentLevel + indentSize, allOnOneLine, maximumDecimalPlaces);
434 if (i < array.
size() - 1)
439 out <<
',' << newLine;
441 else if (! allOnOneLine)
446 writeSpaces (out, indentLevel);
452 enum { indentSize = 2 };
460 if (parse (text, result))
495 return error.getResult();
504 JSONFormatter::write (mo, data, 0, allOnOneLine, maximumDecimalPlaces);
510 JSONFormatter::write (output, data, 0, allOnOneLine, maximumDecimalPlaces);
516 JSONFormatter::writeString (mo, s.
text);
525 auto quote = parser.readChar();
527 if (quote !=
'"' && quote !=
'\'')
530 result = parser.parseString (quote);
531 t = parser.currentLocation;
535 return error.getResult();
550 :
UnitTest (
"JSON", UnitTestCategories::json)
555 juce_wchar buffer[40] = { 0 };
557 for (
int i = 0; i < numElementsInArray (buffer) - 1; ++i)
563 buffer[i] = (juce_wchar) (1 + r.
nextInt (0x10ffff - 1));
568 buffer[i] = (juce_wchar) (1 + r.
nextInt (0xff));
576 char buffer[30] = { 0 };
578 for (
int i = 0; i < numElementsInArray (buffer) - 1; ++i)
580 static const char chars[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:";
581 buffer[i] = chars [r.
nextInt (
sizeof (chars) - 1)];
589 static var createRandomDouble (
Random& r)
594 static var createRandomVar (
Random& r,
int depth)
596 switch (r.
nextInt (depth > 3 ? 6 : 8))
602 case 4:
return createRandomDouble (r);
603 case 5:
return createRandomWideCharString (r);
607 var v (createRandomVar (r, depth + 1));
609 for (
int i = 1 + r.
nextInt (30); --i >= 0;)
610 v.
append (createRandomVar (r, depth + 1));
619 for (
int i = r.
nextInt (30); --i >= 0;)
620 o->setProperty (createRandomIdentifier (r), createRandomVar (r, depth + 1));
630 void runTest()
override 635 auto r = getRandom();
641 expect (
JSON::parse (
"[ 12345678901234 ]")[0].isInt64());
642 expect (
JSON::parse (
"[ 1.123e3 ]")[0].isDouble());
644 expect (
JSON::parse (
"[-12345678901234]")[0].isInt64());
647 for (
int i = 100; --i >= 0;)
652 v = createRandomVar (r, 0);
658 expect (asString.
isNotEmpty() && parsedString == asString);
663 beginTest (
"Float formatting");
665 std::map<double, String> tests;
668 tests[1.01] =
"1.01";
669 tests[0.76378] =
"0.76378";
670 tests[-10] =
"-10.0";
671 tests[10.01] =
"10.01";
672 tests[0.0123] =
"0.0123";
673 tests[-3.7e-27] =
"-3.7e-27";
674 tests[1e+40] =
"1.0e40";
675 tests[-12345678901234567.0] =
"-1.234567890123457e16";
676 tests[192000] =
"192000.0";
677 tests[1234567] =
"1.234567e6";
678 tests[0.00006] =
"0.00006";
679 tests[0.000006] =
"6.0e-6";
681 for (
auto& test : tests)
687 static JSONTests JSONUnitTests;
Wraps a pointer to a null-terminated ASCII character string, and provides various methods to operate ...
static String toString(const var &objectToFormat, bool allOnOneLine=false, int maximumDecimalPlaces=15)
Returns a string which contains a JSON-formatted representation of the var object.
static String escapeString(StringRef)
Returns a version of a string with any extended characters escaped.
static Result ok() noexcept
Creates and returns a 'successful' result.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
Represents a string identifier, designed for accessing properties by name.
int nextInt() noexcept
Returns the next random 32 bit integer.
A simple class for holding temporary references to a string literal or String.
A variant class, that can be used to hold a range of primitive values.
bool nextBool() noexcept
Returns the next random boolean value.
static bool canRepresent(juce_wchar character) noexcept
Returns true if the given unicode character can be represented in this encoding.
static double readDoubleValue(CharPointerType &text) noexcept
Parses a character string to read a floating-point number.
static void writeToStream(OutputStream &output, const var &objectToFormat, bool allOnOneLine=false, int maximumDecimalPlaces=15)
Writes a JSON-formatted representation of the var object to the given stream.
static var fromString(StringRef)
Parses a string that was created with the toString() method.
CharPointerType getCharPointer() const noexcept
Returns the character pointer currently being used to store this string.
bool isEmpty() const noexcept
Returns true if this pointer is pointing to a null character.
bool isValid() const noexcept
Returns true if this Identifier is not null.
static Result parse(const String &text, var &parsedResult)
Parses a string of JSON-formatted text, and returns a result code containing any parse errors...
int64 nextInt64() noexcept
Returns the next 64-bit random number.
static bool isWhitespace(char character) noexcept
Checks whether a character is whitespace.
CharPointer_UTF8 findEndOfWhitespace() const noexcept
Returns the first non-whitespace character in the string.
This is a base class for classes that perform a unit test.
static Result fail(const String &errorMessage) noexcept
Creates a 'failure' result.
Represents a dynamically implemented object.
static Result parseQuotedString(String::CharPointerType &text, var &result)
Parses a quoted string-literal in JSON format, returning the un-escaped result in the result paramete...
Wraps a pointer to a null-terminated UTF-32 character string, and provides various methods to operate...
String loadFileAsString() const
Reads a file into memory as a string.
double nextDouble() noexcept
Returns the next random floating-point number.
void write(juce_wchar charToWrite) noexcept
Writes a unicode character to this string, and advances this pointer to point to the next position...
static int getHexDigitValue(juce_wchar digit) noexcept
Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legal hex digit.
juce_wchar getAndAdvance() noexcept
Returns the character that this pointer is currently pointing to, and then advances the pointer to po...
static String toHexString(IntegerType number)
Returns a string representing this numeric value in hexadecimal.
Represents the 'success' or 'failure' of an operation, and holds an associated error message to descr...
Array< var > * getArray() const noexcept
If this variant holds an array, this provides access to it.
Represents a local file or directory.
The base class for streams that write data to some kind of destination.
Holds a resizable array of primitive or copy-by-value objects.
String paddedLeft(juce_wchar padCharacter, int minimumLength) const
Returns a copy of this string with the specified character repeatedly added to its beginning until th...
int size() const noexcept
Returns the current number of elements in the array.
String::CharPointerType text
The text that is referenced.
static size_t getBytesRequiredFor(juce_wchar charToWrite) noexcept
Returns the number of bytes that would be needed to represent the given unicode character in this enc...
void append(const var &valueToAppend)
Appends an element to the var, converting it to an array if it isn't already one. ...
virtual bool writeRepeatedByte(uint8 byte, size_t numTimesToRepeat)
Writes a byte to the output stream a given number of times.
Wraps a pointer to a null-terminated UTF-16 character string, and provides various methods to operate...
ElementType & getReference(int index) noexcept
Returns a direct reference to one of the elements in the array, without checking the index passed in...
bool isEmpty() const noexcept
Returns true if the array is empty, false otherwise.
A random number generator.
bool appendUTF8Char(juce_wchar character)
Appends the utf-8 bytes for a unicode character.
String toUTF8() const
Returns a String created from the (UTF8) data that has been written to the stream.
Writes data to an internal memory buffer, which grows as required.
String toString() const
Attempts to detect the encoding of the data and convert it to a string.
Wraps a pointer to a null-terminated UTF-8 character string, and provides various methods to operate ...