Split json::Number into json::Double and json::Integer

Trying to decide whether an option should be an int or double after
discarding the differences between "1.0" and "1" simply isn't possible,
and even if an option was initialized correctly, if it was changed to a
round number it could get written as an int and break later.

Also convert cajun to tabs because three spaces to indent is terrible.

Originally committed to SVN as r6018.
This commit is contained in:
Thomas Goyne 2011-12-22 21:12:25 +00:00
parent daff67b150
commit 07da6f6f1b
13 changed files with 658 additions and 739 deletions

View file

@ -20,13 +20,15 @@ namespace {
class CastVisitorBase : public Visitor, public ConstVisitor {
void Visit(Array&) { }
void Visit(Object&) { }
void Visit(Number&) { }
void Visit(Integer&) { }
void Visit(Double&) { }
void Visit(String&) { }
void Visit(Boolean&) { }
void Visit(Null&) { is_null = true; }
void Visit(Array const&) { }
void Visit(Object const&) { }
void Visit(Number const&) { }
void Visit(Integer const&) { }
void Visit(Double const&) { }
void Visit(String const&) { }
void Visit(Boolean const&) { }
void Visit(Null const&) { is_null = true; }
@ -86,9 +88,10 @@ UnknownElement::UnknownElement() : m_pImp( new Imp
UnknownElement::UnknownElement(const UnknownElement& unknown) : m_pImp( unknown.m_pImp->Clone()) {}
UnknownElement::UnknownElement(const Object& object) : m_pImp( new Imp_T<Object>(object) ) {}
UnknownElement::UnknownElement(const Array& array) : m_pImp( new Imp_T<Array>(array) ) {}
UnknownElement::UnknownElement(double number) : m_pImp( new Imp_T<Number>(number) ) {}
UnknownElement::UnknownElement(int number) : m_pImp( new Imp_T<Number>(number) ) {}
UnknownElement::UnknownElement(long number) : m_pImp( new Imp_T<Number>(number) ) {}
UnknownElement::UnknownElement(double number) : m_pImp( new Imp_T<Double>(number) ) {}
UnknownElement::UnknownElement(int number) : m_pImp( new Imp_T<Integer>(number) ) {}
UnknownElement::UnknownElement(int64_t number) : m_pImp( new Imp_T<Integer>(number) ) {}
UnknownElement::UnknownElement(long number) : m_pImp( new Imp_T<Integer>(number) ) {}
UnknownElement::UnknownElement(bool boolean) : m_pImp( new Imp_T<Boolean>(boolean) ) {}
UnknownElement::UnknownElement(const char *string) : m_pImp( new Imp_T<String>(string) ) {}
UnknownElement::UnknownElement(const String& string) : m_pImp( new Imp_T<String>(string) ) {}
@ -98,14 +101,16 @@ UnknownElement::~UnknownElement() { delete m_pImp; }
UnknownElement::operator Object const&() const { return CastTo<Object>(); }
UnknownElement::operator Array const&() const { return CastTo<Array>(); }
UnknownElement::operator Number const&() const { return CastTo<Number>(); }
UnknownElement::operator Integer const&() const { return CastTo<Integer>(); }
UnknownElement::operator Double const&() const { return CastTo<Double>(); }
UnknownElement::operator Boolean const&() const { return CastTo<Boolean>(); }
UnknownElement::operator String const&() const { return CastTo<String>(); }
UnknownElement::operator Null const&() const { return CastTo<Null>(); }
UnknownElement::operator Object&() { return CastTo<Object>(); }
UnknownElement::operator Array&() { return CastTo<Array>(); }
UnknownElement::operator Number&() { return CastTo<Number>(); }
UnknownElement::operator Integer&() { return CastTo<Integer>(); }
UnknownElement::operator Double&() { return CastTo<Double>(); }
UnknownElement::operator Boolean&() { return CastTo<Boolean>(); }
UnknownElement::operator String&() { return CastTo<String>(); }
UnknownElement::operator Null&() { return CastTo<Null>(); }

View file

@ -22,402 +22,357 @@ TODO:
*/
namespace json
{
namespace json {
std::istream& operator >> (std::istream& istr, UnknownElement& elementRoot) {
Reader::Read(elementRoot, istr);
return istr;
Reader::Read(elementRoot, istr);
return istr;
}
Reader::Location::Location() :
m_nLine(0),
m_nLineOffset(0),
m_nDocOffset(0)
{}
//////////////////////
// Reader::InputStream
// wrapper around istream to keep track of document/line offsets
class Reader::InputStream
{
std::istream& m_iStr;
Location m_Location;
/// Wrapper around istream to keep track of document/line offsets
class Reader::InputStream {
std::istream& m_iStr;
Location m_Location;
public:
InputStream(std::istream& iStr) : m_iStr(iStr) { }
InputStream(std::istream& iStr) : m_iStr(iStr) { }
int Get() {
assert(!m_iStr.eof());
int c = m_iStr.get();
int Get() {
assert(!m_iStr.eof());
int c = m_iStr.get();
++m_Location.m_nDocOffset;
if (c == '\n') {
++m_Location.m_nLine;
m_Location.m_nLineOffset = 0;
}
else {
++m_Location.m_nLineOffset;
}
++m_Location.m_nDocOffset;
if (c == '\n') {
++m_Location.m_nLine;
m_Location.m_nLineOffset = 0;
}
else {
++m_Location.m_nLineOffset;
}
return c;
}
int Peek() {
assert(!m_iStr.eof());
return m_iStr.peek();
}
return c;
}
bool EOS() {
m_iStr.peek(); // apparently eof flag isn't set until a character read is attempted. whatever.
return m_iStr.eof();
}
int Peek() {
assert(!m_iStr.eof());
return m_iStr.peek();
}
const Location& GetLocation() const { return m_Location; }
bool EOS() {
m_iStr.peek(); // apparently eof flag isn't set until a character read is attempted. whatever.
return m_iStr.eof();
}
Location const& GetLocation() const { return m_Location; }
};
//////////////////////
// Reader::TokenStream
class Reader::TokenStream
{
const Tokens& m_Tokens;
Tokens::const_iterator m_itCurrent;
class Reader::TokenStream {
Tokens const& m_Tokens;
Tokens::const_iterator m_itCurrent;
public:
TokenStream(const Tokens& tokens)
: m_Tokens(tokens), m_itCurrent(tokens.begin())
{ }
TokenStream(Tokens const& tokens) : m_Tokens(tokens), m_itCurrent(tokens.begin())
{ }
const Token& Peek() {
assert(!EOS());
return *m_itCurrent;
}
const Token& Get() {
assert(!EOS());
return *m_itCurrent++;
}
Token const& Peek() {
assert(!EOS());
return *m_itCurrent;
}
Token const& Get() {
assert(!EOS());
return *m_itCurrent++;
}
bool EOS() const {
return m_itCurrent == m_Tokens.end();
}
bool EOS() const { return m_itCurrent == m_Tokens.end(); }
};
///////////////////
// Reader (finally)
void Reader::Read(Object& object, std::istream& istr) { Read_i(object, istr); }
void Reader::Read(Array& array, std::istream& istr) { Read_i(array, istr); }
void Reader::Read(String& string, std::istream& istr) { Read_i(string, istr); }
void Reader::Read(Number& number, std::istream& istr) { Read_i(number, istr); }
void Reader::Read(Boolean& boolean, std::istream& istr) { Read_i(boolean, istr); }
void Reader::Read(Null& null, std::istream& istr) { Read_i(null, istr); }
void Reader::Read(UnknownElement& unknown, std::istream& istr) { Read_i(unknown, istr); }
void Reader::Read(Object& object, std::istream& istr) { Read_i(object, istr); }
void Reader::Read(Array& array, std::istream& istr) { Read_i(array, istr); }
void Reader::Read(String& string, std::istream& istr) { Read_i(string, istr); }
void Reader::Read(Integer& number, std::istream& istr) { Read_i(number, istr); }
void Reader::Read(Double& number, std::istream& istr) { Read_i(number, istr); }
void Reader::Read(Boolean& boolean, std::istream& istr) { Read_i(boolean, istr); }
void Reader::Read(Null& null, std::istream& istr) { Read_i(null, istr); }
void Reader::Read(UnknownElement& unknown, std::istream& istr) { Read_i(unknown, istr); }
template <typename ElementTypeT>
void Reader::Read_i(ElementTypeT& element, std::istream& istr)
{
Reader reader;
void Reader::Read_i(ElementTypeT& element, std::istream& istr) {
Reader reader;
Tokens tokens;
InputStream inputStream(istr);
reader.Scan(tokens, inputStream);
Tokens tokens;
InputStream inputStream(istr);
reader.Scan(tokens, inputStream);
TokenStream tokenStream(tokens);
element = reader.Parse(tokenStream);
TokenStream tokenStream(tokens);
element = reader.Parse(tokenStream);
if (!tokenStream.EOS())
{
const Token& token = tokenStream.Peek();
throw ParseException("Expected End of token stream; found " + token.sValue, token.locBegin, token.locEnd);
}
if (!tokenStream.EOS()) {
Token const& token = tokenStream.Peek();
throw ParseException("Expected End of token stream; found " + token.sValue, token.locBegin, token.locEnd);
}
}
void Reader::Scan(Tokens& tokens, InputStream& inputStream)
{
while (EatWhiteSpace(inputStream), !inputStream.EOS())
{
// if all goes well, we'll create a token each pass
Token token;
token.locBegin = inputStream.GetLocation();
void Reader::Scan(Tokens& tokens, InputStream& inputStream) {
while (EatWhiteSpace(inputStream), !inputStream.EOS()) {
// if all goes well, we'll create a token each pass
Token token;
token.locBegin = inputStream.GetLocation();
// gives us null-terminated string
std::string sChar;
sChar.push_back(inputStream.Peek());
// gives us null-terminated string
std::string sChar;
sChar.push_back(inputStream.Peek());
switch (sChar[0])
{
case '{':
token.sValue = sChar[0];
MatchExpectedString(sChar, inputStream);
token.nType = Token::TOKEN_OBJECT_BEGIN;
break;
switch (sChar[0]) {
case '{':
token.sValue = sChar[0];
MatchExpectedString(sChar, inputStream);
token.nType = Token::TOKEN_OBJECT_BEGIN;
break;
case '}':
token.sValue = sChar[0];
MatchExpectedString(sChar, inputStream);
token.nType = Token::TOKEN_OBJECT_END;
break;
case '}':
token.sValue = sChar[0];
MatchExpectedString(sChar, inputStream);
token.nType = Token::TOKEN_OBJECT_END;
break;
case '[':
token.sValue = sChar[0];
MatchExpectedString(sChar, inputStream);
token.nType = Token::TOKEN_ARRAY_BEGIN;
break;
case '[':
token.sValue = sChar[0];
MatchExpectedString(sChar, inputStream);
token.nType = Token::TOKEN_ARRAY_BEGIN;
break;
case ']':
token.sValue = sChar[0];
MatchExpectedString(sChar, inputStream);
token.nType = Token::TOKEN_ARRAY_END;
break;
case ']':
token.sValue = sChar[0];
MatchExpectedString(sChar, inputStream);
token.nType = Token::TOKEN_ARRAY_END;
break;
case ',':
token.sValue = sChar[0];
MatchExpectedString(sChar, inputStream);
token.nType = Token::TOKEN_NEXT_ELEMENT;
break;
case ',':
token.sValue = sChar[0];
MatchExpectedString(sChar, inputStream);
token.nType = Token::TOKEN_NEXT_ELEMENT;
break;
case ':':
token.sValue = sChar[0];
MatchExpectedString(sChar, inputStream);
token.nType = Token::TOKEN_MEMBER_ASSIGN;
break;
case ':':
token.sValue = sChar[0];
MatchExpectedString(sChar, inputStream);
token.nType = Token::TOKEN_MEMBER_ASSIGN;
break;
case '"':
MatchString(token.sValue, inputStream);
token.nType = Token::TOKEN_STRING;
break;
case '"':
MatchString(token.sValue, inputStream);
token.nType = Token::TOKEN_STRING;
break;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
MatchNumber(token.sValue, inputStream);
token.nType = Token::TOKEN_NUMBER;
break;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
MatchNumber(token.sValue, inputStream);
token.nType = Token::TOKEN_NUMBER;
break;
case 't':
token.sValue = "true";
MatchExpectedString(token.sValue, inputStream);
token.nType = Token::TOKEN_BOOLEAN;
break;
case 't':
token.sValue = "true";
MatchExpectedString(token.sValue, inputStream);
token.nType = Token::TOKEN_BOOLEAN;
break;
case 'f':
token.sValue = "false";
MatchExpectedString(token.sValue, inputStream);
token.nType = Token::TOKEN_BOOLEAN;
break;
case 'f':
token.sValue = "false";
MatchExpectedString(token.sValue, inputStream);
token.nType = Token::TOKEN_BOOLEAN;
break;
case 'n':
token.sValue = "null";
MatchExpectedString(token.sValue, inputStream);
token.nType = Token::TOKEN_NULL;
break;
case 'n':
token.sValue = "null";
MatchExpectedString(token.sValue, inputStream);
token.nType = Token::TOKEN_NULL;
break;
default:
throw ScanException("Unexpected character in stream: " + sChar, inputStream.GetLocation());
}
default:
throw ScanException("Unexpected character in stream: " + sChar, inputStream.GetLocation());
}
token.locEnd = inputStream.GetLocation();
tokens.push_back(token);
}
token.locEnd = inputStream.GetLocation();
tokens.push_back(token);
}
}
void Reader::EatWhiteSpace(InputStream& inputStream)
{
while (!inputStream.EOS() && ::isspace(inputStream.Peek()))
inputStream.Get();
void Reader::EatWhiteSpace(InputStream& inputStream) {
while (!inputStream.EOS() && ::isspace(inputStream.Peek()))
inputStream.Get();
}
void Reader::MatchExpectedString(const std::string& sExpected, InputStream& inputStream)
{
std::string::const_iterator it(sExpected.begin()),
itEnd(sExpected.end());
for ( ; it != itEnd; ++it) {
if (inputStream.EOS() || // did we reach the end before finding what we're looking for...
inputStream.Get() != *it) // ...or did we find something different?
{
throw ScanException("Expected string: " + sExpected, inputStream.GetLocation());
}
}
// all's well if we made it here, return quietly
void Reader::MatchExpectedString(std::string const& sExpected, InputStream& inputStream) {
std::string::const_iterator it(sExpected.begin()), itEnd(sExpected.end());
for ( ; it != itEnd; ++it) {
if (inputStream.EOS() || // did we reach the end before finding what we're looking for...
inputStream.Get() != *it) // ...or did we find something different?
{
throw ScanException("Expected string: " + sExpected, inputStream.GetLocation());
}
}
}
void Reader::MatchString(std::string& string, InputStream& inputStream) {
MatchExpectedString("\"", inputStream);
void Reader::MatchString(std::string& string, InputStream& inputStream)
{
MatchExpectedString("\"", inputStream);
while (!inputStream.EOS() && inputStream.Peek() != '"') {
char c = inputStream.Get();
while (inputStream.EOS() == false &&
inputStream.Peek() != '"')
{
char c = inputStream.Get();
// escape?
if (c == '\\' && !inputStream.EOS()) { // shouldn't have reached the end yet
c = inputStream.Get();
switch (c) {
case '/': string.push_back('/'); break;
case '"': string.push_back('"'); break;
case '\\': string.push_back('\\'); break;
case 'b': string.push_back('\b'); break;
case 'f': string.push_back('\f'); break;
case 'n': string.push_back('\n'); break;
case 'r': string.push_back('\r'); break;
case 't': string.push_back('\t'); break;
case 'u': // TODO: what do we do with this?
default:
throw ScanException("Unrecognized escape sequence found in string: \\" + c, inputStream.GetLocation());
}
}
else {
string.push_back(c);
}
}
// escape?
if (c == '\\' &&
inputStream.EOS() == false) // shouldn't have reached the end yet
{
c = inputStream.Get();
switch (c) {
case '/': string.push_back('/'); break;
case '"': string.push_back('"'); break;
case '\\': string.push_back('\\'); break;
case 'b': string.push_back('\b'); break;
case 'f': string.push_back('\f'); break;
case 'n': string.push_back('\n'); break;
case 'r': string.push_back('\r'); break;
case 't': string.push_back('\t'); break;
case 'u': // TODO: what do we do with this?
default:
throw ScanException("Unrecognized escape sequence found in string: \\" + c, inputStream.GetLocation());
}
}
else {
string.push_back(c);
}
}
// eat the last '"' that we just peeked
MatchExpectedString("\"", inputStream);
// eat the last '"' that we hopefully just peeked
MatchExpectedString("\"", inputStream);
}
void Reader::MatchNumber(std::string& sNumber, InputStream& inputStream) {
const char sNumericChars[] = "0123456789.eE-+";
std::set<char> numericChars;
numericChars.insert(sNumericChars, sNumericChars + sizeof(sNumericChars));
void Reader::MatchNumber(std::string& sNumber, InputStream& inputStream)
{
const char sNumericChars[] = "0123456789.eE-+";
std::set<char> numericChars;
numericChars.insert(sNumericChars, sNumericChars + sizeof(sNumericChars));
while (inputStream.EOS() == false &&
numericChars.find(inputStream.Peek()) != numericChars.end())
{
sNumber.push_back(inputStream.Get());
}
while (!inputStream.EOS()&& numericChars.count(inputStream.Peek()))
sNumber.push_back(inputStream.Get());
}
UnknownElement Reader::Parse(Reader::TokenStream& tokenStream) {
if (tokenStream.EOS())
throw ParseException("Unexpected end of token stream", Location(), Location()); // nowhere to point to
UnknownElement Reader::Parse(Reader::TokenStream& tokenStream)
{
if (tokenStream.EOS())
throw ParseException("Unexpected end of token stream", Location(), Location()); // nowhere to point to
Token const& token = tokenStream.Peek();
switch (token.nType) {
case Token::TOKEN_OBJECT_BEGIN: return ParseObject(tokenStream);
case Token::TOKEN_ARRAY_BEGIN: return ParseArray(tokenStream);
case Token::TOKEN_STRING: return ParseString(tokenStream);
case Token::TOKEN_NUMBER: return ParseNumber(tokenStream);
case Token::TOKEN_BOOLEAN: return ParseBoolean(tokenStream);
case Token::TOKEN_NULL: return ParseNull(tokenStream);
default:
throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
}
Token const& token = tokenStream.Peek();
switch (token.nType) {
case Token::TOKEN_OBJECT_BEGIN: return ParseObject(tokenStream);
case Token::TOKEN_ARRAY_BEGIN: return ParseArray(tokenStream);
case Token::TOKEN_STRING: return ParseString(tokenStream);
case Token::TOKEN_NUMBER: return ParseNumber(tokenStream);
case Token::TOKEN_BOOLEAN: return ParseBoolean(tokenStream);
case Token::TOKEN_NULL: return ParseNull(tokenStream);
default:
throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
}
}
Object Reader::ParseObject(Reader::TokenStream& tokenStream)
{
MatchExpectedToken(Token::TOKEN_OBJECT_BEGIN, tokenStream);
UnknownElement Reader::ParseObject(Reader::TokenStream& tokenStream) {
MatchExpectedToken(Token::TOKEN_OBJECT_BEGIN, tokenStream);
Object object;
Object object;
while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END)
{
// first the member name. save the token in case we have to throw an exception
const Token& tokenName = tokenStream.Peek();
std::string const& name = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END) {
// first the member name. save the token in case we have to throw an exception
Token const& tokenName = tokenStream.Peek();
std::string const& name = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
if (object.count(name))
throw ParseException("Duplicate object member token: " + name, tokenName.locBegin, tokenName.locEnd);
if (object.count(name))
throw ParseException("Duplicate object member token: " + name, tokenName.locBegin, tokenName.locEnd);
// ...then the key/value separator...
MatchExpectedToken(Token::TOKEN_MEMBER_ASSIGN, tokenStream);
// ...then the key/value separator...
MatchExpectedToken(Token::TOKEN_MEMBER_ASSIGN, tokenStream);
// ...then the value itself (can be anything).
object[name] = Parse(tokenStream);
// ...then the value itself (can be anything).
object[name] = Parse(tokenStream);
if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END)
MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
}
if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END)
MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
}
MatchExpectedToken(Token::TOKEN_OBJECT_END, tokenStream);
MatchExpectedToken(Token::TOKEN_OBJECT_END, tokenStream);
return object;
return object;
}
Array Reader::ParseArray(Reader::TokenStream& tokenStream)
{
MatchExpectedToken(Token::TOKEN_ARRAY_BEGIN, tokenStream);
UnknownElement Reader::ParseArray(Reader::TokenStream& tokenStream) {
MatchExpectedToken(Token::TOKEN_ARRAY_BEGIN, tokenStream);
Array array;
Array array;
while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
{
array.push_back(Parse(tokenStream));
while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
{
array.push_back(Parse(tokenStream));
if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
}
if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
}
MatchExpectedToken(Token::TOKEN_ARRAY_END, tokenStream);
MatchExpectedToken(Token::TOKEN_ARRAY_END, tokenStream);
return array;
return array;
}
String Reader::ParseString(Reader::TokenStream& tokenStream)
{
return MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
UnknownElement Reader::ParseString(Reader::TokenStream& tokenStream) {
return MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
}
Number Reader::ParseNumber(Reader::TokenStream& tokenStream)
{
const Token& currentToken = tokenStream.Peek(); // might need this later for throwing exception
const std::string& sValue = MatchExpectedToken(Token::TOKEN_NUMBER, tokenStream);
UnknownElement Reader::ParseNumber(Reader::TokenStream& tokenStream) {
Token const& currentToken = tokenStream.Peek(); // might need this later for throwing exception
std::string const& sValue = MatchExpectedToken(Token::TOKEN_NUMBER, tokenStream);
std::istringstream iStr(sValue);
double dValue;
iStr >> dValue;
// First try to parse it as an int
std::istringstream iStr(sValue);
int64_t iValue;
iStr >> iValue;
// did we consume all characters in the token?
if (!iStr.eof())
throw ParseException("Unexpected character in NUMBER token: " + iStr.peek(), currentToken.locBegin, currentToken.locEnd);
// If the entire token was consumed then it's not a double
if (iStr.eof())
return iValue;
return dValue;
// Try again as a double
iStr.seekg(0, std::ios::beg);
double dValue;
iStr >> dValue;
// If there's still stuff left in the token then it's malformed
if (!iStr.eof())
throw ParseException("Unexpected character in NUMBER token: " + iStr.peek(), currentToken.locBegin, currentToken.locEnd);
return dValue;
}
Boolean Reader::ParseBoolean(Reader::TokenStream& tokenStream)
{
return MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream) == "true";
UnknownElement Reader::ParseBoolean(Reader::TokenStream& tokenStream) {
return MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream) == "true";
}
Null Reader::ParseNull(Reader::TokenStream& tokenStream)
{
MatchExpectedToken(Token::TOKEN_NULL, tokenStream);
return Null();
UnknownElement Reader::ParseNull(Reader::TokenStream& tokenStream) {
MatchExpectedToken(Token::TOKEN_NULL, tokenStream);
return Null();
}
std::string const& Reader::MatchExpectedToken(Token::Type nExpected, Reader::TokenStream& tokenStream) {
if (tokenStream.EOS())
throw ParseException("Unexpected End of token stream", Location(), Location()); // nowhere to point to
const std::string& Reader::MatchExpectedToken(Token::Type nExpected, Reader::TokenStream& tokenStream)
{
if (tokenStream.EOS())
{
throw ParseException("Unexpected End of token stream", Location(), Location()); // nowhere to point to
}
Token const& token = tokenStream.Get();
if (token.nType != nExpected)
throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
const Token& token = tokenStream.Get();
if (token.nType != nExpected)
{
throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
}
return token.sValue;
return token.sValue;
}
} // End namespace
}

View file

@ -9,6 +9,7 @@ Author: Terry Caton
#include "libaegisub/cajun/writer.h"
#ifndef LAGI_PRE
#include <cmath>
#include <iostream>
#include <iomanip>
#endif
@ -24,110 +25,107 @@ TODO:
namespace json
{
Writer::Writer(std::ostream& ostr) :
m_ostr(ostr),
m_nTabDepth(0)
{}
void Writer::Write(const Array& array)
Writer::Writer(std::ostream& ostr)
: m_ostr(ostr)
, tab_depth(0)
{
if (array.empty())
m_ostr << "[]";
else
{
m_ostr << '[' << std::endl;
++m_nTabDepth;
Array::const_iterator it(array.begin()), itend(array.end());
while (it != itend) {
m_ostr << std::string(m_nTabDepth, '\t');
Write(*it);
if (++it != itend)
m_ostr << ',';
m_ostr << std::endl;
}
--m_nTabDepth;
m_ostr << std::string(m_nTabDepth, '\t') << ']';
}
}
void Writer::Write(const Object& object)
{
if (object.empty())
m_ostr << "{}";
else
{
m_ostr << '{' << std::endl;
++m_nTabDepth;
void Writer::Write(Array const& array) {
if (array.empty())
m_ostr << "[]";
else {
m_ostr << '[' << std::endl;
++tab_depth;
Object::const_iterator it(object.begin()), itend(object.end());
while (it != itend) {
m_ostr << std::string(m_nTabDepth, '\t') << '"' << it->first << "\" : ";
Write(it->second);
Array::const_iterator it(array.begin()), itend(array.end());
while (it != itend) {
m_ostr << std::string(tab_depth, '\t');
if (++it != itend)
m_ostr << ',';
m_ostr << std::endl;
}
Write(*it);
--m_nTabDepth;
m_ostr << std::string(m_nTabDepth, '\t') << '}';
}
if (++it != itend)
m_ostr << ',';
m_ostr << std::endl;
}
--tab_depth;
m_ostr << std::string(tab_depth, '\t') << ']';
}
}
void Writer::Write(const Number& numberElement)
{
m_ostr << std::setprecision(20) << numberElement;
void Writer::Write(Object const& object) {
if (object.empty())
m_ostr << "{}";
else {
m_ostr << '{' << std::endl;
++tab_depth;
Object::const_iterator it(object.begin()), itend(object.end());
while (it != itend) {
m_ostr << std::string(tab_depth, '\t') << '"' << it->first << "\" : ";
Write(it->second);
if (++it != itend)
m_ostr << ',';
m_ostr << std::endl;
}
--tab_depth;
m_ostr << std::string(tab_depth, '\t') << '}';
}
}
void Writer::Write(const Boolean& booleanElement)
{
m_ostr << (booleanElement ? "true" : "false");
void Writer::Write(Double const& numberElement) {
m_ostr << std::setprecision(20) << numberElement;
double unused;
if (!std::modf(numberElement, &unused))
m_ostr << ".0";
}
void Writer::Write(const String& stringElement)
{
m_ostr << '"';
const std::string& s = stringElement;
std::string::const_iterator it(s.begin()), itend(s.end());
for (; it != itend; ++it)
{
switch (*it)
{
case '"': m_ostr << "\\\""; break;
case '\\': m_ostr << "\\\\"; break;
case '\b': m_ostr << "\\b"; break;
case '\f': m_ostr << "\\f"; break;
case '\n': m_ostr << "\\n"; break;
case '\r': m_ostr << "\\r"; break;
case '\t': m_ostr << "\\t"; break;
//case '\u': m_ostr << ""; break; ??
default: m_ostr << *it; break;
}
}
m_ostr << '"';
void Writer::Write(Integer const& numberElement) {
m_ostr << numberElement;
}
void Writer::Write(const Null& )
{
m_ostr << "null";
void Writer::Write(Boolean const& booleanElement) {
m_ostr << (booleanElement ? "true" : "false");
}
void Writer::Write(const UnknownElement& unknown)
{
unknown.Accept(*this);
void Writer::Write(String const& stringElement) {
m_ostr << '"';
std::string::const_iterator it(stringElement.begin()), itend(stringElement.end());
for (; it != itend; ++it) {
switch (*it) {
case '"': m_ostr << "\\\""; break;
case '\\': m_ostr << "\\\\"; break;
case '\b': m_ostr << "\\b"; break;
case '\f': m_ostr << "\\f"; break;
case '\n': m_ostr << "\\n"; break;
case '\r': m_ostr << "\\r"; break;
case '\t': m_ostr << "\\t"; break;
default: m_ostr << *it; break;
}
}
m_ostr << '"';
}
void Writer::Visit(const Array& array) { Write(array); }
void Writer::Visit(const Object& object) { Write(object); }
void Writer::Visit(const Number& number) { Write(number); }
void Writer::Visit(const String& string) { Write(string); }
void Writer::Visit(const Boolean& boolean) { Write(boolean); }
void Writer::Visit(const Null& null) { Write(null); }
void Writer::Write(Null const&) {
m_ostr << "null";
}
void Writer::Write(UnknownElement const& unknown) {
unknown.Accept(*this);
}
void Writer::Visit(Array const& array) { Write(array); }
void Writer::Visit(Object const& object) { Write(object); }
void Writer::Visit(Integer const& integer) { Write(integer); }
void Writer::Visit(Double const& dbl) { Write(dbl); }
void Writer::Visit(String const& string) { Write(string); }
void Writer::Visit(Boolean const& boolean) { Write(boolean); }
void Writer::Visit(Null const& null) { Write(null); }
} // end namespace

View file

@ -21,10 +21,11 @@
#include "libaegisub/option.h"
#ifndef LAGI_PRE
#include <cassert>
#include <fstream>
#include <sstream>
#include <map>
#include <memory>
#include <sstream>
#endif
#include "libaegisub/cajun/reader.h"
@ -38,6 +39,39 @@
#include "option_visit.h"
namespace {
/// @brief Write an option to a json object
/// @param[out] obj Parent object
/// @param[in] path Path option should be stored in.
/// @param[in] value Value to write.
void put_option(json::Object &obj, const std::string &path, const json::UnknownElement &value) {
std::string::size_type pos = path.find('/');
// Not having a '/' denotes it is a leaf.
if (pos == std::string::npos) {
assert(obj.find(path) == obj.end());
obj[path] = value;
}
else {
put_option(
obj[path.substr(0, pos)],
path.substr(pos + 1),
value);
}
}
template<class T>
void put_array(json::Object &obj, const std::string &path, const char *element_key, std::vector<T> const& value) {
json::Array array;
for (typename std::vector<T>::const_iterator it = value.begin(); it != value.end(); ++it) {
json::Object obj;
obj[element_key] = *it;
array.push_back(obj);
}
put_option(obj, path, array);
}
}
namespace agi {
Options::Options(const std::string &file, const std::string& default_config, const OptionSetting setting)
@ -108,93 +142,44 @@ void Options::Flush() {
for (OptionValueMap::const_iterator i = values.begin(); i != values.end(); ++i) {
switch (i->second->GetType()) {
case OptionValue::Type_String:
PutOption(obj_out, i->first, i->second->GetString());
put_option(obj_out, i->first, i->second->GetString());
break;
case OptionValue::Type_Int:
PutOption(obj_out, i->first, (double)i->second->GetInt());
put_option(obj_out, i->first, i->second->GetInt());
break;
case OptionValue::Type_Double:
PutOption(obj_out, i->first, i->second->GetDouble());
put_option(obj_out, i->first, i->second->GetDouble());
break;
case OptionValue::Type_Colour:
PutOption(obj_out, i->first, i->second->GetColour());
put_option(obj_out, i->first, i->second->GetColour());
break;
case OptionValue::Type_Bool:
PutOption(obj_out, i->first, i->second->GetBool());
put_option(obj_out, i->first, i->second->GetBool());
break;
case OptionValue::Type_List_String: {
std::vector<std::string> const& array_string(i->second->GetListString());
case OptionValue::Type_List_String:
put_array(obj_out, i->first, "string", i->second->GetListString());
break;
json::Array array;
case OptionValue::Type_List_Int:
put_array(obj_out, i->first, "int", i->second->GetListInt());
break;
for (std::vector<std::string>::const_iterator i_str = array_string.begin(); i_str != array_string.end(); ++i_str) {
json::Object obj;
obj["string"] = *i_str;
array.push_back(obj);
}
case OptionValue::Type_List_Double:
put_array(obj_out, i->first, "double", i->second->GetListDouble());
break;
PutOption(obj_out, i->first, array);
}
break;
case OptionValue::Type_List_Colour:
put_array(obj_out, i->first, "colour", i->second->GetListColour());
break;
case OptionValue::Type_List_Int: {
std::vector<int64_t> const& array_int(i->second->GetListInt());
json::Array array;
for (std::vector<int64_t>::const_iterator i_int = array_int.begin(); i_int != array_int.end(); ++i_int) {
json::Object obj;
obj["int"] = (double)*i_int;
array.push_back(obj);
}
PutOption(obj_out, i->first, array);
}
break;
case OptionValue::Type_List_Double: {
std::vector<double> const& array_double(i->second->GetListDouble());
json::Array array;
for (std::vector<double>::const_iterator i_double = array_double.begin(); i_double != array_double.end(); ++i_double) {
json::Object obj;
obj["double"] = *i_double;
array.push_back(obj);
}
PutOption(obj_out, i->first, array);
}
break;
case OptionValue::Type_List_Colour: {
std::vector<Colour> const& array_colour(i->second->GetListColour());
json::Array array;
for (std::vector<Colour>::const_iterator i_colour = array_colour.begin(); i_colour != array_colour.end(); ++i_colour) {
json::Object obj;
obj["colour"] = *i_colour;
array.push_back(obj);
}
PutOption(obj_out, i->first, array);
}
break;
case OptionValue::Type_List_Bool: {
std::vector<bool> const& array_bool(i->second->GetListBool());
json::Array array;
for (std::vector<bool>::const_iterator i_bool = array_bool.begin(); i_bool != array_bool.end(); ++i_bool) {
json::Object obj;
obj["bool"] = *i_bool;
array.push_back(obj);
}
PutOption(obj_out, i->first, array);
}
break;
case OptionValue::Type_List_Bool:
put_array(obj_out, i->first, "bool", i->second->GetListBool());
break;
}
}
@ -202,19 +187,4 @@ void Options::Flush() {
json::Writer::Write(obj_out, file.Get());
}
void Options::PutOption(json::Object &obj, const std::string &path, const json::UnknownElement &value) {
std::string::size_type pos = path.find('/');
// Not having a '/' denotes it is a leaf.
if (pos == std::string::npos) {
assert(obj.find(path) == obj.end());
obj[path] = value;
}
else {
PutOption(
obj[path.substr(0, pos)],
path.substr(pos + 1),
value);
}
}
} // namespace agi

View file

@ -22,6 +22,7 @@
#include "option_visit.h"
#ifndef LAGI_PRE
#include <cassert>
#include <cmath>
#include <memory>
#endif
@ -59,16 +60,6 @@ void ConfigVisitor::Visit(const json::Object& object) {
}
}
template<class T>
static inline T convert_unknown(json::UnknownElement const& ue) {
return ue;
}
template<>
inline int64_t convert_unknown(json::UnknownElement const& ue) {
return (int64_t)(double)ue;
}
template<class OptionValueType, class ValueType>
OptionValue *ConfigVisitor::ReadArray(json::Array const& src, std::string const& array_type, void (OptionValueType::*set_list)(const std::vector<ValueType>&)) {
std::vector<ValueType> arr;
@ -86,7 +77,7 @@ OptionValue *ConfigVisitor::ReadArray(json::Array const& src, std::string const&
return 0;
}
arr.push_back(convert_unknown<ValueType>(obj.begin()->second));
arr.push_back(obj.begin()->second);
}
OptionValueType *ret = new OptionValueType(name);
@ -122,13 +113,12 @@ void ConfigVisitor::Visit(const json::Array& array) {
Error<OptionJsonValueArray>("Array type not handled");
}
void ConfigVisitor::Visit(const json::Integer& number) {
AddOptionValue(new OptionValueInt(name, number));
}
void ConfigVisitor::Visit(const json::Number& number) {
if (int64_t(number) == number) {
AddOptionValue(new OptionValueInt(name, int64_t(number)));
} else {
AddOptionValue(new OptionValueDouble(name, number));
}
void ConfigVisitor::Visit(const json::Double& number) {
AddOptionValue(new OptionValueDouble(name, number));
}
void ConfigVisitor::Visit(const json::String& string) {

View file

@ -23,6 +23,10 @@
#include "libaegisub/cajun/elements.h"
#include "libaegisub/cajun/visitor.h"
#ifndef LAGI_PRE
#include <vector>
#endif
namespace agi {
DEFINE_BASE_EXCEPTION_NOINNER(OptionJsonValueError, Exception)
@ -47,7 +51,8 @@ public:
void Visit(const json::Array& array);
void Visit(const json::Object& object);
void Visit(const json::Number& number);
void Visit(const json::Integer& number);
void Visit(const json::Double& number);
void Visit(const json::String& string);
void Visit(const json::Boolean& boolean);
void Visit(const json::Null& null);

View file

@ -14,6 +14,8 @@ Author: Terry Caton
#include <map>
#include <string>
#include <stdexcept>
#include "stdint.h"
#endif
namespace json
@ -21,14 +23,15 @@ namespace json
/////////////////////////////////////////////////
// forward declarations (more info further below)
class Visitor;
class ConstVisitor;
struct Visitor;
struct ConstVisitor;
class UnknownElement;
template <typename ValueTypeT>
class TrivialType_T;
typedef double Number;
typedef int64_t Integer;
typedef double Double;
typedef bool Boolean;
typedef std::string String;
typedef std::deque<UnknownElement> Array;
@ -69,6 +72,7 @@ public:
UnknownElement(double number);
UnknownElement(int number);
UnknownElement(long number);
UnknownElement(int64_t number);
UnknownElement(bool boolean);
UnknownElement(const char *string);
UnknownElement(const String& string);
@ -81,13 +85,15 @@ public:
// implicit cast to actual element type. throws on failure
operator Object const&() const;
operator Array const&() const;
operator Number const&() const;
operator Integer const&() const;
operator Double const&() const;
operator Boolean const&() const;
operator String const&() const;
operator Null const&() const;
operator Object&();
operator Array&();
operator Number&();
operator Integer&();
operator Double&();
operator Boolean&();
operator String&();
operator Null&();

View file

@ -18,107 +18,103 @@ Author: Terry Caton
namespace json
{
class Reader
{
class Reader {
public:
// this structure will be reported in one of the exceptions defined below
struct Location
{
Location();
// this structure will be reported in one of the exceptions defined below
struct Location {
Location() : m_nLine(0), m_nLineOffset(0), m_nDocOffset(0) { }
unsigned int m_nLine; // document line, zero-indexed
unsigned int m_nLineOffset; // character offset from beginning of line, zero indexed
unsigned int m_nDocOffset; // character offset from entire document, zero indexed
};
unsigned int m_nLine; // document line, zero-indexed
unsigned int m_nLineOffset; // character offset from beginning of line, zero indexed
unsigned int m_nDocOffset; // character offset from entire document, zero indexed
};
// thrown during the first phase of reading. generally catches low-level problems such
// as errant characters or corrupt/incomplete documents
class ScanException : public Exception
{
public:
ScanException(const std::string& sMessage, const Reader::Location& locError) :
Exception(sMessage),
m_locError(locError) {}
// thrown during the first phase of reading. generally catches low-level
// problems such as errant characters or corrupt/incomplete documents
class ScanException : public Exception {
public:
ScanException(std::string const& sMessage, Reader::Location const& locError)
: Exception(sMessage)
, m_locError(locError)
{ }
Reader::Location m_locError;
};
Reader::Location m_locError;
};
// thrown during the second phase of reading. generally catches higher-level problems such
// as missing commas or brackets
class ParseException : public Exception
{
public:
ParseException(const std::string& sMessage, const Reader::Location& locTokenBegin, const Reader::Location& locTokenEnd) :
Exception(sMessage),
m_locTokenBegin(locTokenBegin),
m_locTokenEnd(locTokenEnd) {}
// thrown during the second phase of reading. generally catches
// higher-level problems such as missing commas or brackets
class ParseException : public Exception {
public:
ParseException(std::string const& sMessage, Reader::Location const& locTokenBegin, Reader::Location const& locTokenEnd)
: Exception(sMessage)
, m_locTokenBegin(locTokenBegin)
, m_locTokenEnd(locTokenEnd)
{ }
Reader::Location m_locTokenBegin;
Reader::Location m_locTokenEnd;
};
Reader::Location m_locTokenBegin;
Reader::Location m_locTokenEnd;
};
// if you know what the document looks like, call one of these...
static void Read(Object& object, std::istream& istr);
static void Read(Array& array, std::istream& istr);
static void Read(String& string, std::istream& istr);
static void Read(Integer& number, std::istream& istr);
static void Read(Double& number, std::istream& istr);
static void Read(Boolean& boolean, std::istream& istr);
static void Read(Null& null, std::istream& istr);
// if you know what the document looks like, call one of these...
static void Read(Object& object, std::istream& istr);
static void Read(Array& array, std::istream& istr);
static void Read(String& string, std::istream& istr);
static void Read(Number& number, std::istream& istr);
static void Read(Boolean& boolean, std::istream& istr);
static void Read(Null& null, std::istream& istr);
// ...otherwise, if you don't know, call this & visit it
static void Read(UnknownElement& elementRoot, std::istream& istr);
// ...otherwise, if you don't know, call this & visit it
static void Read(UnknownElement& elementRoot, std::istream& istr);
private:
struct Token
{
enum Type
{
TOKEN_OBJECT_BEGIN, // {
TOKEN_OBJECT_END, // }
TOKEN_ARRAY_BEGIN, // [
TOKEN_ARRAY_END, // ]
TOKEN_NEXT_ELEMENT, // ,
TOKEN_MEMBER_ASSIGN, // :
TOKEN_STRING, // "xxx"
TOKEN_NUMBER, // [+/-]000.000[e[+/-]000]
TOKEN_BOOLEAN, // true -or- false
TOKEN_NULL // null
};
struct Token {
enum Type {
TOKEN_OBJECT_BEGIN, // {
TOKEN_OBJECT_END, // }
TOKEN_ARRAY_BEGIN, // [
TOKEN_ARRAY_END, // ]
TOKEN_NEXT_ELEMENT, // ,
TOKEN_MEMBER_ASSIGN, // :
TOKEN_STRING, // "xxx"
TOKEN_NUMBER, // [+/-]000.000[e[+/-]000]
TOKEN_BOOLEAN, // true -or- false
TOKEN_NULL // null
};
Type nType;
std::string sValue;
Type nType;
std::string sValue;
// for malformed file debugging
Reader::Location locBegin;
Reader::Location locEnd;
};
// for malformed file debugging
Reader::Location locBegin;
Reader::Location locEnd;
};
class InputStream;
class TokenStream;
typedef std::vector<Token> Tokens;
class InputStream;
class TokenStream;
typedef std::vector<Token> Tokens;
template <typename ElementTypeT>
static void Read_i(ElementTypeT& element, std::istream& istr);
template <typename ElementTypeT>
static void Read_i(ElementTypeT& element, std::istream& istr);
// scanning istream into token sequence
void Scan(Tokens& tokens, InputStream& inputStream);
// scanning istream into token sequence
void Scan(Tokens& tokens, InputStream& inputStream);
void EatWhiteSpace(InputStream& inputStream);
void MatchString(std::string& sValue, InputStream& inputStream);
void MatchNumber(std::string& sNumber, InputStream& inputStream);
void MatchExpectedString(const std::string& sExpected, InputStream& inputStream);
void EatWhiteSpace(InputStream& inputStream);
void MatchString(std::string& sValue, InputStream& inputStream);
void MatchNumber(std::string& sNumber, InputStream& inputStream);
void MatchExpectedString(std::string const& sExpected, InputStream& inputStream);
// parsing token sequence into element structure
UnknownElement Parse(TokenStream& tokenStream);
Object ParseObject(TokenStream& tokenStream);
Array ParseArray(TokenStream& tokenStream);
String ParseString(TokenStream& tokenStream);
Number ParseNumber(TokenStream& tokenStream);
Boolean ParseBoolean(TokenStream& tokenStream);
Null ParseNull(TokenStream& tokenStream);
// parsing token sequence into element structure
UnknownElement Parse(TokenStream& tokenStream);
UnknownElement ParseObject(TokenStream& tokenStream);
UnknownElement ParseArray(TokenStream& tokenStream);
UnknownElement ParseString(TokenStream& tokenStream);
UnknownElement ParseNumber(TokenStream& tokenStream);
UnknownElement ParseBoolean(TokenStream& tokenStream);
UnknownElement ParseNull(TokenStream& tokenStream);
const std::string& MatchExpectedToken(Token::Type nExpected, TokenStream& tokenStream);
std::string const& MatchExpectedToken(Token::Type nExpected, TokenStream& tokenStream);
};
} // End namespace
}

View file

@ -13,32 +13,28 @@ Author: Terry Caton
namespace json
{
struct Visitor {
virtual ~Visitor() { }
class Visitor
{
public:
virtual ~Visitor() {}
virtual void Visit(Array& array) = 0;
virtual void Visit(Object& object) = 0;
virtual void Visit(Number& number) = 0;
virtual void Visit(String& string) = 0;
virtual void Visit(Boolean& boolean) = 0;
virtual void Visit(Null& null) = 0;
virtual void Visit(Array& array) = 0;
virtual void Visit(Object& object) = 0;
virtual void Visit(Integer& number) = 0;
virtual void Visit(Double& number) = 0;
virtual void Visit(String& string) = 0;
virtual void Visit(Boolean& boolean) = 0;
virtual void Visit(Null& null) = 0;
};
class ConstVisitor
{
public:
virtual ~ConstVisitor() {}
struct ConstVisitor {
virtual ~ConstVisitor() { }
virtual void Visit(const Array& array) = 0;
virtual void Visit(const Object& object) = 0;
virtual void Visit(const Number& number) = 0;
virtual void Visit(const String& string) = 0;
virtual void Visit(const Boolean& boolean) = 0;
virtual void Visit(const Null& null) = 0;
virtual void Visit(const Array& array) = 0;
virtual void Visit(const Object& object) = 0;
virtual void Visit(const Integer& number) = 0;
virtual void Visit(const Double& number) = 0;
virtual void Visit(const String& string) = 0;
virtual void Visit(const Boolean& boolean) = 0;
virtual void Visit(const Null& null) = 0;
};
} // End namespace

View file

@ -14,36 +14,35 @@ Author: Terry Caton
namespace json
{
class Writer : private ConstVisitor
{
class Writer : private ConstVisitor {
Writer(std::ostream& ostr);
void Write(const Object& object);
void Write(const Array& array);
void Write(const String& string);
void Write(const Integer& number);
void Write(const Double& number);
void Write(const Boolean& boolean);
void Write(const Null& null);
void Write(const UnknownElement& unknown);
void Visit(const Array& array);
void Visit(const Object& object);
void Visit(const Integer& number);
void Visit(const Double& number);
void Visit(const String& string);
void Visit(const Boolean& boolean);
void Visit(const Null& null);
std::ostream& m_ostr;
int tab_depth;
public:
template <typename ElementTypeT>
static void Write(const ElementTypeT& element, std::ostream& ostr)
{
Writer writer(ostr);
writer.Write(element);
ostr.flush(); // all done
}
private:
Writer(std::ostream& ostr);
void Write(const Object& object);
void Write(const Array& array);
void Write(const String& string);
void Write(const Number& number);
void Write(const Boolean& boolean);
void Write(const Null& null);
void Write(const UnknownElement& unknown);
void Visit(const Array& array);
void Visit(const Object& object);
void Visit(const Number& number);
void Visit(const String& string);
void Visit(const Boolean& boolean);
void Visit(const Null& null);
std::ostream& m_ostr;
int m_nTabDepth;
template <typename ElementTypeT>
static void Write(const ElementTypeT& element, std::ostream& ostr) {
Writer writer(ostr);
writer.Write(element);
ostr.flush(); // all done
}
};

View file

@ -78,12 +78,6 @@ private:
/// @param ignore_errors Log invalid entires in the option file and continue rather than throwing an exception
void LoadConfig(std::istream& stream, bool ignore_errors = false);
/// @brief Write an option to file.
/// @param[out] obj Parent object
/// @param[in] path Path option should be stored in.
/// @param[in] value Value to write.
static void PutOption(::json::Object &obj, const std::string &path, const ::json::UnknownElement &value);
public:
/// @brief Constructor
/// @param file User config that will be loaded from and written back to.

View file

@ -40,100 +40,83 @@ Report::Report() {
Platform *p = Platform::GetPlatform();
Aegisub a;
json::Object general;
general["Signature"] = json::String(p->Signature());
general["Date"] = json::String(p->Date());
general["Architecture"] = json::String(p->ArchName());
general["OS Family"] = json::String(p->OSFamily());
general["OS Name"] = json::String(p->OSName());
general["Endian"] = json::String(p->Endian());
general["OS Version"] = json::String(p->OSVersion());
general["wx Version"] = json::String(p->wxVersion());
general["Locale"] = json::String(p->Locale());
general["Language"] = json::String(p->Language());
general["System Language"] = json::String(p->SystemLanguage());
root["General"] = general;
json::Object& general = root["General"];
general["Signature"] = p->Signature();
general["Date"] = p->Date();
general["Architecture"] = p->ArchName();
general["OS Family"] = p->OSFamily();
general["OS Name"] = p->OSName();
general["Endian"] = p->Endian();
general["OS Version"] = p->OSVersion();
general["wx Version"] = p->wxVersion();
general["Locale"] = p->Locale();
general["Language"] = p->Language();
general["System Language"] = p->SystemLanguage();
try {
json::Object aegisub;
aegisub["Last Version"] = json::String(a.GetString("Version/Last Version"));
aegisub["Spelling Language"] = json::String(a.GetString("Tool/Spell Checker/Language"));
aegisub["Thesaurus Language"] = json::String(a.GetString("Tool/Thesaurus/Language"));
aegisub["Audio Player"] = json::String(a.GetString("Audio/Player"));
aegisub["Audio Provider"] = json::String(a.GetString("Audio/Provider"));
aegisub["Video Provider"] = json::String(a.GetString("Video/Provider"));
aegisub["Subtitles Provider"] = json::String(a.GetString("Subtitle/Provider"));
aegisub["Save Charset"] = json::String(a.GetString("App/Save Charset"));
aegisub["Grid Font Size"] = json::Number(a.GetInt("Grid/Font Size"));
aegisub["Edit Font Size"] = json::Number(a.GetInt("Subtitle/Edit Box/Font Size"));
aegisub["Spectrum Enabled"] = json::Boolean(a.GetBool("Audio/Spectrum"));
aegisub["Spectrum Quality"] = json::Number(a.GetInt("Audio/Renderer/Spectrum/Quality"));
aegisub["Call Tips Enabled"] = json::Boolean(a.GetBool("App/Call Tips"));
aegisub["Medusa Hotkeys Enabled"] = json::Boolean(a.GetBool("Audio/Medusa Timing Hotkeys"));
root["Aegisub"] = aegisub;
json::Object& aegisub = root["Aegisub"];
aegisub["Last Version"] = a.GetString("Version/Last Version");
aegisub["Spelling Language"] = a.GetString("Tool/Spell Checker/Language");
aegisub["Thesaurus Language"] = a.GetString("Tool/Thesaurus/Language");
aegisub["Audio Player"] = a.GetString("Audio/Player");
aegisub["Audio Provider"] = a.GetString("Audio/Provider");
aegisub["Video Provider"] = a.GetString("Video/Provider");
aegisub["Subtitles Provider"] = a.GetString("Subtitle/Provider");
aegisub["Save Charset"] = a.GetString("App/Save Charset");
aegisub["Grid Font Size"] = a.GetInt("Grid/Font Size");
aegisub["Edit Font Size"] = a.GetInt("Subtitle/Edit Box/Font Size");
aegisub["Spectrum Enabled"] = a.GetBool("Audio/Spectrum");
aegisub["Spectrum Quality"] = a.GetInt("Audio/Renderer/Spectrum/Quality");
aegisub["Call Tips Enabled"] = a.GetBool("App/Call Tips");
aegisub["Medusa Hotkeys Enabled"] = a.GetBool("Audio/Medusa Timing Hotkeys");
} catch(...) {
root["Aegisub"]["Error"] = json::String("Config file is corrupted");
}
json::Object& hardware = root["Hardware"];
hardware["Memory Size"] = 0;
json::Object& cpu = root["CPU"];
cpu["Id"] = p->CPUId();
cpu["Speed"] = p->CPUSpeed();
cpu["Count"] = p->CPUCount();
cpu["Cores"] = p->CPUCores();
cpu["Features"] = p->CPUFeatures();
cpu["Features2"] = p->CPUFeatures2();
json::Object hardware;
hardware["Memory Size"] = json::Number();
root["Hardware"] = hardware;
json::Object cpu;
cpu["Id"] = json::String(p->CPUId());
cpu["Speed"] = json::String(p->CPUSpeed());
cpu["Count"] = json::Number(p->CPUCount());
cpu["Cores"] = json::Number(p->CPUCores());
cpu["Features"] = json::String(p->CPUFeatures());
cpu["Features2"] = json::String(p->CPUFeatures2());
root["CPU"] = cpu;
json::Object display;
display["Depth"] = json::Number(p->DisplayDepth());
display["Size"] = json::String(p->DisplaySize());
display["Pixels Per Inch"] = json::String(p->DisplayPPI());
json::Object gl;
gl["Vendor"] = json::String(p->OpenGLVendor());
gl["Renderer"] = json::String(p->OpenGLRenderer());
gl["Version"] = json::String(p->OpenGLVersion());
gl["Extensions"] = json::String(p->OpenGLExt());
display["OpenGL"] = gl;
root["Display"] = display;
json::Object& display = root["Display"];
display["Depth"] = p->DisplayDepth();
display["Size"] = p->DisplaySize();
display["Pixels Per Inch"] = p->DisplayPPI();
json::Object& gl = display["OpenGL"];
gl["Vendor"] = p->OpenGLVendor();
gl["Renderer"] = p->OpenGLRenderer();
gl["Version"] = p->OpenGLVersion();
gl["Extensions"] = p->OpenGLExt();
display["OpenGL"] = gl;
#ifdef __WINDOWS__
json::Object windows;
json::Object& windows = root["Windows"];
windows["Service Pack"] = json::String();
windows["Graphics Driver Version"] = json::String();
windows["DirectShow Filters"] = json::String();
windows["AntiVirus Installed"] = json::Boolean();
windows["Firewall Installed"] = json::Boolean();
windows["DLL"] = json::String();
root["Windows"] = windows;
#endif
#ifdef __UNIX__
json::Object u_nix;
u_nix["Desktop Environment"] = json::String(p->DesktopEnvironment());
u_nix["Libraries"] = json::String(p->UnixLibraries());
root["Unix"] = u_nix;
json::Object& u_nix = root["Unix"];
u_nix["Desktop Environment"] = p->DesktopEnvironment();
u_nix["Libraries"] = p->UnixLibraries();
#endif
#ifdef __APPLE__
json::Object osx;
osx["Patch"] = json::String(p->PatchLevel());
osx["QuickTime Extensions"] = json::String(p->QuickTimeExt());
osx["Model"] = json::String(p->HardwareModel());
root["OS X"] = osx;
json::Object& osx = root["OS X"];
osx["Patch"] = p->PatchLevel();
osx["QuickTime Extensions"] = p->QuickTimeExt();
osx["Model"] = p->HardwareModel();
#endif
agi::io::Save file("./t.json");

View file

@ -98,18 +98,21 @@ TEST_F(lagi_cajun, Compare) {
TEST_F(lagi_cajun, CastNonConst) {
json::UnknownElement Integer = 0;
json::UnknownElement Double = 0.0;
json::UnknownElement String = "1";
json::UnknownElement Boolean = false;
json::UnknownElement Array = json::Array();
json::UnknownElement Object = json::Object();
EXPECT_NO_THROW(static_cast<json::Number&>(Integer));
EXPECT_NO_THROW(static_cast<json::Integer&>(Integer));
EXPECT_NO_THROW(static_cast<json::Double&>(Double));
EXPECT_NO_THROW(static_cast<json::String&>(String));
EXPECT_NO_THROW(static_cast<json::Boolean&>(Boolean));
EXPECT_NO_THROW(static_cast<json::Array&>(Array));
EXPECT_NO_THROW(static_cast<json::Object&>(Object));
EXPECT_NO_THROW(static_cast<json::Number const&>(Integer));
EXPECT_NO_THROW(static_cast<json::Integer const&>(Integer));
EXPECT_NO_THROW(static_cast<json::Double const&>(Double));
EXPECT_NO_THROW(static_cast<json::String const&>(String));
EXPECT_NO_THROW(static_cast<json::Boolean const&>(Boolean));
EXPECT_NO_THROW(static_cast<json::Array const&>(Array));
@ -118,26 +121,30 @@ TEST_F(lagi_cajun, CastNonConst) {
TEST_F(lagi_cajun, CastConst) {
const json::UnknownElement Integer = 10;
const json::UnknownElement Double = 10.0;
const json::UnknownElement String = "1";
const json::UnknownElement Boolean = false;
const json::UnknownElement Array = json::Array();
const json::UnknownElement Object = json::Object();
/* these shouldn't compile
EXPECT_NO_THROW(static_cast<json::Number&>(Integer));
EXPECT_NO_THROW(static_cast<json::Integer&>(Integer));
EXPECT_NO_THROW(static_cast<json::Double&>(Double));
EXPECT_NO_THROW(static_cast<json::String&>(String));
EXPECT_NO_THROW(static_cast<json::Boolean&>(Boolean));
EXPECT_NO_THROW(static_cast<json::Array&>(Array));
EXPECT_NO_THROW(static_cast<json::Object&>(Object));
*/
EXPECT_NO_THROW(static_cast<json::Number const&>(Integer));
EXPECT_NO_THROW(static_cast<json::Integer const&>(Integer));
EXPECT_NO_THROW(static_cast<json::Double const&>(Double));
EXPECT_NO_THROW(static_cast<json::String const&>(String));
EXPECT_NO_THROW(static_cast<json::Boolean const&>(Boolean));
EXPECT_NO_THROW(static_cast<json::Array const&>(Array));
EXPECT_NO_THROW(static_cast<json::Object const&>(Object));
EXPECT_EQ(10, static_cast<json::Number const&>(Integer));
EXPECT_EQ(10, static_cast<json::Integer const&>(Integer));
EXPECT_EQ(10, static_cast<json::Double const&>(Double));
EXPECT_STREQ("1", static_cast<json::String const&>(String).c_str());
EXPECT_EQ(false, static_cast<json::Boolean const&>(Boolean));
EXPECT_EQ(true, static_cast<json::Array const&>(Array).empty());
@ -150,7 +157,7 @@ TEST_F(lagi_cajun, UnknownIsIndexable) {
json::UnknownElement unk_obj = obj;
EXPECT_NO_THROW(unk_obj["Integer"]);
EXPECT_EQ(1, (json::Number)unk_obj["Integer"]);
EXPECT_EQ(1, (json::Integer)unk_obj["Integer"]);
EXPECT_THROW(unk_obj[0], json::Exception);
EXPECT_NO_THROW(unk_obj["Nonexistent Key"]);
@ -163,20 +170,20 @@ TEST_F(lagi_cajun, UnknownIsIndexable) {
json::UnknownElement unk_arr = arr;
EXPECT_NO_THROW(unk_arr[0]);
EXPECT_EQ(1, (json::Number)unk_arr[0]);
EXPECT_EQ(1, (json::Integer)unk_arr[0]);
EXPECT_THROW(unk_arr["Integer"], json::Exception);
json::Number number = 1;
json::Integer number = 1;
json::UnknownElement const& unk_num = number;
EXPECT_THROW(unk_num[0], json::Exception);
EXPECT_THROW(unk_num[""], json::Exception);
}
TEST_F(lagi_cajun, ObjectStoreNumber) {
TEST_F(lagi_cajun, ObjectStoreInteger) {
json::Object obj;
obj["Integer"] = 1;
EXPECT_EQ(1, static_cast<json::Number>(obj["Integer"]));
EXPECT_EQ(1, static_cast<json::Integer>(obj["Integer"]));
EXPECT_THROW(static_cast<json::String const&>(obj["Integer"]), json::Exception);
EXPECT_THROW(static_cast<json::Boolean>(obj["Integer"]), json::Exception);
@ -185,12 +192,24 @@ TEST_F(lagi_cajun, ObjectStoreNumber) {
EXPECT_THROW(static_cast<json::Object const&>(obj["Integer"]), json::Exception);
}
TEST_F(lagi_cajun, ObjectStoreDouble) {
json::Object obj;
obj["Double"] = 1.0;
EXPECT_EQ(1.0, static_cast<json::Double>(obj["Double"]));
EXPECT_THROW(static_cast<json::String const&>(obj["Double"]), json::Exception);
EXPECT_THROW(static_cast<json::Boolean>(obj["Double"]), json::Exception);
EXPECT_THROW(static_cast<json::Null>(obj["Double"]), json::Exception);
EXPECT_THROW(static_cast<json::Array const&>(obj["Double"]), json::Exception);
EXPECT_THROW(static_cast<json::Object const&>(obj["Double"]), json::Exception);
}
TEST_F(lagi_cajun, ObjectStoreString) {
json::Object obj;
obj["String"] = "test";
EXPECT_STREQ("test", static_cast<std::string>(obj["String"]).c_str());
EXPECT_THROW(static_cast<json::Number>(obj["String"]), json::Exception);
EXPECT_THROW(static_cast<json::Integer>(obj["String"]), json::Exception);
EXPECT_THROW(static_cast<json::Boolean>(obj["String"]), json::Exception);
EXPECT_THROW(static_cast<json::Null>(obj["String"]), json::Exception);
EXPECT_THROW(static_cast<json::Array const&>(obj["String"]), json::Exception);
@ -203,7 +222,7 @@ TEST_F(lagi_cajun, ObjectStoreBoolean) {
EXPECT_EQ(true, static_cast<json::Boolean>(obj["Boolean"]));
EXPECT_THROW(static_cast<json::String const&>(obj["Boolean"]), json::Exception);
EXPECT_THROW(static_cast<json::Number>(obj["Boolean"]), json::Exception);
EXPECT_THROW(static_cast<json::Integer>(obj["Boolean"]), json::Exception);
EXPECT_THROW(static_cast<json::Null>(obj["Boolean"]), json::Exception);
EXPECT_THROW(static_cast<json::Array const&>(obj["Boolean"]), json::Exception);
EXPECT_THROW(static_cast<json::Object const&>(obj["Boolean"]), json::Exception);
@ -219,7 +238,10 @@ TEST_F(lagi_cajun, ObjectStoreNull) {
EXPECT_NO_THROW(static_cast<json::String const&>(obj["Null"]));
obj["Null"] = json::Null();
EXPECT_NO_THROW(static_cast<json::Number>(obj["Null"]));
EXPECT_NO_THROW(static_cast<json::Integer>(obj["Null"]));
obj["Null"] = json::Null();
EXPECT_NO_THROW(static_cast<json::Double>(obj["Null"]));
obj["Null"] = json::Null();
EXPECT_NO_THROW(static_cast<json::Boolean>(obj["Null"]));
@ -303,7 +325,7 @@ TEST_F(lagi_cajun, ReaderParserErrors) {
std::istringstream missing_comma("[1 2]");
EXPECT_THROW(json::Reader::Read(arr, missing_comma), json::Exception);
json::Number num;
json::Double num;
std::istringstream garbage_after_number("123eee");
EXPECT_THROW(json::Reader::Read(num, garbage_after_number), json::Exception);
@ -335,7 +357,7 @@ TEST_F(lagi_cajun, ReaderScanErrors) {
EXPECT_THROW(json::Reader::Read(obj, doc), json::Exception);
json::Number num;
json::Double num;
std::istringstream garbage_after_number("123abc");
EXPECT_THROW(json::Reader::Read(num, garbage_after_number), json::Exception);