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);