From 07da6f6f1bfa590d58b2582c75cf88af0881a158 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 22 Dec 2011 21:12:25 +0000 Subject: [PATCH] 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. --- aegisub/libaegisub/common/cajun/elements.cpp | 19 +- aegisub/libaegisub/common/cajun/reader.cpp | 575 ++++++++---------- aegisub/libaegisub/common/cajun/writer.cpp | 172 +++--- aegisub/libaegisub/common/option.cpp | 140 ++--- aegisub/libaegisub/common/option_visit.cpp | 24 +- aegisub/libaegisub/common/option_visit.h | 7 +- .../include/libaegisub/cajun/elements.h | 16 +- .../include/libaegisub/cajun/reader.h | 164 +++-- .../include/libaegisub/cajun/visitor.h | 40 +- .../include/libaegisub/cajun/writer.h | 57 +- .../libaegisub/include/libaegisub/option.h | 6 - aegisub/reporter/report.cpp | 125 ++-- aegisub/tests/libaegisub_cajun.cpp | 52 +- 13 files changed, 658 insertions(+), 739 deletions(-) diff --git a/aegisub/libaegisub/common/cajun/elements.cpp b/aegisub/libaegisub/common/cajun/elements.cpp index c12389fae..3d2f32eeb 100644 --- a/aegisub/libaegisub/common/cajun/elements.cpp +++ b/aegisub/libaegisub/common/cajun/elements.cpp @@ -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) ) {} UnknownElement::UnknownElement(const Array& array) : m_pImp( new Imp_T(array) ) {} -UnknownElement::UnknownElement(double number) : m_pImp( new Imp_T(number) ) {} -UnknownElement::UnknownElement(int number) : m_pImp( new Imp_T(number) ) {} -UnknownElement::UnknownElement(long number) : m_pImp( new Imp_T(number) ) {} +UnknownElement::UnknownElement(double number) : m_pImp( new Imp_T(number) ) {} +UnknownElement::UnknownElement(int number) : m_pImp( new Imp_T(number) ) {} +UnknownElement::UnknownElement(int64_t number) : m_pImp( new Imp_T(number) ) {} +UnknownElement::UnknownElement(long number) : m_pImp( new Imp_T(number) ) {} UnknownElement::UnknownElement(bool boolean) : m_pImp( new Imp_T(boolean) ) {} UnknownElement::UnknownElement(const char *string) : m_pImp( new Imp_T(string) ) {} UnknownElement::UnknownElement(const String& string) : m_pImp( new Imp_T(string) ) {} @@ -98,14 +101,16 @@ UnknownElement::~UnknownElement() { delete m_pImp; } UnknownElement::operator Object const&() const { return CastTo(); } UnknownElement::operator Array const&() const { return CastTo(); } -UnknownElement::operator Number const&() const { return CastTo(); } +UnknownElement::operator Integer const&() const { return CastTo(); } +UnknownElement::operator Double const&() const { return CastTo(); } UnknownElement::operator Boolean const&() const { return CastTo(); } UnknownElement::operator String const&() const { return CastTo(); } UnknownElement::operator Null const&() const { return CastTo(); } UnknownElement::operator Object&() { return CastTo(); } UnknownElement::operator Array&() { return CastTo(); } -UnknownElement::operator Number&() { return CastTo(); } +UnknownElement::operator Integer&() { return CastTo(); } +UnknownElement::operator Double&() { return CastTo(); } UnknownElement::operator Boolean&() { return CastTo(); } UnknownElement::operator String&() { return CastTo(); } UnknownElement::operator Null&() { return CastTo(); } diff --git a/aegisub/libaegisub/common/cajun/reader.cpp b/aegisub/libaegisub/common/cajun/reader.cpp index d45b99099..ddf3556b2 100644 --- a/aegisub/libaegisub/common/cajun/reader.cpp +++ b/aegisub/libaegisub/common/cajun/reader.cpp @@ -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 -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 numericChars; + numericChars.insert(sNumericChars, sNumericChars + sizeof(sNumericChars)); -void Reader::MatchNumber(std::string& sNumber, InputStream& inputStream) -{ - const char sNumericChars[] = "0123456789.eE-+"; - std::set 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 +} diff --git a/aegisub/libaegisub/common/cajun/writer.cpp b/aegisub/libaegisub/common/cajun/writer.cpp index a9b939330..940996c9c 100644 --- a/aegisub/libaegisub/common/cajun/writer.cpp +++ b/aegisub/libaegisub/common/cajun/writer.cpp @@ -9,6 +9,7 @@ Author: Terry Caton #include "libaegisub/cajun/writer.h" #ifndef LAGI_PRE +#include #include #include #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 diff --git a/aegisub/libaegisub/common/option.cpp b/aegisub/libaegisub/common/option.cpp index ef96580cc..72cb404f7 100644 --- a/aegisub/libaegisub/common/option.cpp +++ b/aegisub/libaegisub/common/option.cpp @@ -21,10 +21,11 @@ #include "libaegisub/option.h" #ifndef LAGI_PRE +#include #include -#include #include #include +#include #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 + void put_array(json::Object &obj, const std::string &path, const char *element_key, std::vector const& value) { + json::Array array; + for (typename std::vector::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 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::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 const& array_int(i->second->GetListInt()); - - json::Array array; - - for (std::vector::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 const& array_double(i->second->GetListDouble()); - - json::Array array; - - for (std::vector::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 const& array_colour(i->second->GetListColour()); - - json::Array array; - for (std::vector::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 const& array_bool(i->second->GetListBool()); - - json::Array array; - for (std::vector::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 diff --git a/aegisub/libaegisub/common/option_visit.cpp b/aegisub/libaegisub/common/option_visit.cpp index 7205255eb..fd42bc366 100644 --- a/aegisub/libaegisub/common/option_visit.cpp +++ b/aegisub/libaegisub/common/option_visit.cpp @@ -22,6 +22,7 @@ #include "option_visit.h" #ifndef LAGI_PRE +#include #include #include #endif @@ -59,16 +60,6 @@ void ConfigVisitor::Visit(const json::Object& object) { } } -template -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 OptionValue *ConfigVisitor::ReadArray(json::Array const& src, std::string const& array_type, void (OptionValueType::*set_list)(const std::vector&)) { std::vector arr; @@ -86,7 +77,7 @@ OptionValue *ConfigVisitor::ReadArray(json::Array const& src, std::string const& return 0; } - arr.push_back(convert_unknown(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("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) { diff --git a/aegisub/libaegisub/common/option_visit.h b/aegisub/libaegisub/common/option_visit.h index 588293b40..918953e0f 100644 --- a/aegisub/libaegisub/common/option_visit.h +++ b/aegisub/libaegisub/common/option_visit.h @@ -23,6 +23,10 @@ #include "libaegisub/cajun/elements.h" #include "libaegisub/cajun/visitor.h" +#ifndef LAGI_PRE +#include +#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); diff --git a/aegisub/libaegisub/include/libaegisub/cajun/elements.h b/aegisub/libaegisub/include/libaegisub/cajun/elements.h index 7dda1a4a1..0862da173 100644 --- a/aegisub/libaegisub/include/libaegisub/cajun/elements.h +++ b/aegisub/libaegisub/include/libaegisub/cajun/elements.h @@ -14,6 +14,8 @@ Author: Terry Caton #include #include #include + +#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 class TrivialType_T; -typedef double Number; +typedef int64_t Integer; +typedef double Double; typedef bool Boolean; typedef std::string String; typedef std::deque 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&(); diff --git a/aegisub/libaegisub/include/libaegisub/cajun/reader.h b/aegisub/libaegisub/include/libaegisub/cajun/reader.h index af329b61b..a8f9dfcc0 100644 --- a/aegisub/libaegisub/include/libaegisub/cajun/reader.h +++ b/aegisub/libaegisub/include/libaegisub/cajun/reader.h @@ -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 Tokens; + class InputStream; + class TokenStream; + typedef std::vector Tokens; - template - static void Read_i(ElementTypeT& element, std::istream& istr); + template + 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 +} diff --git a/aegisub/libaegisub/include/libaegisub/cajun/visitor.h b/aegisub/libaegisub/include/libaegisub/cajun/visitor.h index 78b7b8367..4552f448f 100644 --- a/aegisub/libaegisub/include/libaegisub/cajun/visitor.h +++ b/aegisub/libaegisub/include/libaegisub/cajun/visitor.h @@ -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 diff --git a/aegisub/libaegisub/include/libaegisub/cajun/writer.h b/aegisub/libaegisub/include/libaegisub/cajun/writer.h index 1cf700acd..3e5a5e9a4 100644 --- a/aegisub/libaegisub/include/libaegisub/cajun/writer.h +++ b/aegisub/libaegisub/include/libaegisub/cajun/writer.h @@ -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 - 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 + static void Write(const ElementTypeT& element, std::ostream& ostr) { + Writer writer(ostr); + writer.Write(element); + ostr.flush(); // all done + } }; diff --git a/aegisub/libaegisub/include/libaegisub/option.h b/aegisub/libaegisub/include/libaegisub/option.h index fdc2a51bf..4313bd3c0 100644 --- a/aegisub/libaegisub/include/libaegisub/option.h +++ b/aegisub/libaegisub/include/libaegisub/option.h @@ -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. diff --git a/aegisub/reporter/report.cpp b/aegisub/reporter/report.cpp index c02350bab..e847eb2cd 100644 --- a/aegisub/reporter/report.cpp +++ b/aegisub/reporter/report.cpp @@ -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"); diff --git a/aegisub/tests/libaegisub_cajun.cpp b/aegisub/tests/libaegisub_cajun.cpp index a02f85a95..2153e866f 100644 --- a/aegisub/tests/libaegisub_cajun.cpp +++ b/aegisub/tests/libaegisub_cajun.cpp @@ -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(Integer)); + EXPECT_NO_THROW(static_cast(Integer)); + EXPECT_NO_THROW(static_cast(Double)); EXPECT_NO_THROW(static_cast(String)); EXPECT_NO_THROW(static_cast(Boolean)); EXPECT_NO_THROW(static_cast(Array)); EXPECT_NO_THROW(static_cast(Object)); - EXPECT_NO_THROW(static_cast(Integer)); + EXPECT_NO_THROW(static_cast(Integer)); + EXPECT_NO_THROW(static_cast(Double)); EXPECT_NO_THROW(static_cast(String)); EXPECT_NO_THROW(static_cast(Boolean)); EXPECT_NO_THROW(static_cast(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(Integer)); + EXPECT_NO_THROW(static_cast(Integer)); + EXPECT_NO_THROW(static_cast(Double)); EXPECT_NO_THROW(static_cast(String)); EXPECT_NO_THROW(static_cast(Boolean)); EXPECT_NO_THROW(static_cast(Array)); EXPECT_NO_THROW(static_cast(Object)); */ - EXPECT_NO_THROW(static_cast(Integer)); + EXPECT_NO_THROW(static_cast(Integer)); + EXPECT_NO_THROW(static_cast(Double)); EXPECT_NO_THROW(static_cast(String)); EXPECT_NO_THROW(static_cast(Boolean)); EXPECT_NO_THROW(static_cast(Array)); EXPECT_NO_THROW(static_cast(Object)); - EXPECT_EQ(10, static_cast(Integer)); + EXPECT_EQ(10, static_cast(Integer)); + EXPECT_EQ(10, static_cast(Double)); EXPECT_STREQ("1", static_cast(String).c_str()); EXPECT_EQ(false, static_cast(Boolean)); EXPECT_EQ(true, static_cast(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(obj["Integer"])); + EXPECT_EQ(1, static_cast(obj["Integer"])); EXPECT_THROW(static_cast(obj["Integer"]), json::Exception); EXPECT_THROW(static_cast(obj["Integer"]), json::Exception); @@ -185,12 +192,24 @@ TEST_F(lagi_cajun, ObjectStoreNumber) { EXPECT_THROW(static_cast(obj["Integer"]), json::Exception); } +TEST_F(lagi_cajun, ObjectStoreDouble) { + json::Object obj; + obj["Double"] = 1.0; + EXPECT_EQ(1.0, static_cast(obj["Double"])); + + EXPECT_THROW(static_cast(obj["Double"]), json::Exception); + EXPECT_THROW(static_cast(obj["Double"]), json::Exception); + EXPECT_THROW(static_cast(obj["Double"]), json::Exception); + EXPECT_THROW(static_cast(obj["Double"]), json::Exception); + EXPECT_THROW(static_cast(obj["Double"]), json::Exception); +} + TEST_F(lagi_cajun, ObjectStoreString) { json::Object obj; obj["String"] = "test"; EXPECT_STREQ("test", static_cast(obj["String"]).c_str()); - EXPECT_THROW(static_cast(obj["String"]), json::Exception); + EXPECT_THROW(static_cast(obj["String"]), json::Exception); EXPECT_THROW(static_cast(obj["String"]), json::Exception); EXPECT_THROW(static_cast(obj["String"]), json::Exception); EXPECT_THROW(static_cast(obj["String"]), json::Exception); @@ -203,7 +222,7 @@ TEST_F(lagi_cajun, ObjectStoreBoolean) { EXPECT_EQ(true, static_cast(obj["Boolean"])); EXPECT_THROW(static_cast(obj["Boolean"]), json::Exception); - EXPECT_THROW(static_cast(obj["Boolean"]), json::Exception); + EXPECT_THROW(static_cast(obj["Boolean"]), json::Exception); EXPECT_THROW(static_cast(obj["Boolean"]), json::Exception); EXPECT_THROW(static_cast(obj["Boolean"]), json::Exception); EXPECT_THROW(static_cast(obj["Boolean"]), json::Exception); @@ -219,7 +238,10 @@ TEST_F(lagi_cajun, ObjectStoreNull) { EXPECT_NO_THROW(static_cast(obj["Null"])); obj["Null"] = json::Null(); - EXPECT_NO_THROW(static_cast(obj["Null"])); + EXPECT_NO_THROW(static_cast(obj["Null"])); + + obj["Null"] = json::Null(); + EXPECT_NO_THROW(static_cast(obj["Null"])); obj["Null"] = json::Null(); EXPECT_NO_THROW(static_cast(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);