diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2014-12-11 15:26:50 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2014-12-11 15:26:50 +0100 |
commit | 3f62168ed0b088eec3cb2903f03966f7d501f564 (patch) | |
tree | 781f5bd9b304d9eb931827a26f463575d772983d /src/core/variant | |
parent | b74936760e28a92cadfaec47928ea478fe2d72ee (diff) |
moved to CharReader everywhere
Diffstat (limited to 'src/core/variant')
-rw-r--r-- | src/core/variant/Reader.cpp | 624 | ||||
-rw-r--r-- | src/core/variant/Reader.hpp | 169 | ||||
-rw-r--r-- | src/core/variant/Variant.cpp | 155 | ||||
-rw-r--r-- | src/core/variant/Variant.hpp | 766 |
4 files changed, 0 insertions, 1714 deletions
diff --git a/src/core/variant/Reader.cpp b/src/core/variant/Reader.cpp deleted file mode 100644 index 5c167cd..0000000 --- a/src/core/variant/Reader.cpp +++ /dev/null @@ -1,624 +0,0 @@ -/* - Ousía - Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <iostream> - -#include <cmath> -#include <sstream> - -#include <core/Utils.hpp> - -#include "Reader.hpp" - -namespace ousia { -namespace variant { - -// TODO: Better error messages (like "Expected 'x' but got 'y'") -// TODO: Replace delims with single char delim where possible -// TODO: Use custom return value instead of std::pair -// TODO: Allow buffered char reader to "fork" -// TODO: Rename CharReader to shorter CharReader -// TODO: Implement context in CharReader (to allow error messages to extract the -// current line) - -/* Error Messages */ - -static const char *ERR_UNEXPECTED_CHAR = "Unexpected character"; -static const char *ERR_UNEXPECTED_END = "Unexpected literal end"; -static const char *ERR_UNTERMINATED = "Unterminated literal"; -static const char *ERR_INVALID_ESCAPE = "Invalid escape sequence"; -static const char *ERR_INVALID_INTEGER = "Invalid integer value"; -static const char *ERR_TOO_LARGE = "Value too large to represent"; - -/* Class Number */ - -/** - * Class used internally to represent a number (integer or double). The number - * is represented by its components (base value a, nominator n, denominator d, - * exponent e, sign s and exponent sign sE). - */ -class Number { -private: - /** - * Reprsents the part of the number: Base value a, nominator n, exponent e. - */ - enum class Part { A, N, E }; - - /** - * State used in the parser state machine - */ - enum class State { - INIT, - HAS_MINUS, - LEADING_ZERO, - LEADING_POINT, - INT, - HEX, - POINT, - EXP_INIT, - EXP_HAS_MINUS, - EXP - }; - - /** - * Returns the numeric value of the given ASCII character (returns 0 for - * '0', 1 for '1', 10 for 'A' and so on). - * - * @param c is the character for which the numeric value should be returned. - * @return the numeric value the character represents. - */ - static int charValue(char c) - { - if (c >= '0' && c <= '9') { - return c & 0x0F; - } - if ((c >= 'A' && c <= 'O') || (c >= 'a' && c <= 'o')) { - return (c & 0x0F) + 9; - } - return -1; - } - - /** - * Appends the value of the character c to the internal number - * representation and reports any errors that might occur. - */ - bool appendChar(char c, int base, Part p, CharReader &reader, - Logger &logger) - { - // Check whether the given character is valid - int v = charValue(c); - if (v < 0 || v >= base) { - logger.errorAt(ERR_UNEXPECTED_CHAR, reader); - return false; - } - - // Append the number to the specified part - switch (p) { - case Part::A: - a = a * base + v; - break; - case Part::N: - n = n * base + v; - d = d * base; - break; - case Part::E: - e = e * base + v; - break; - } - - // Check for any overflows - if (a < 0 || n < 0 || d < 0 || e < 0) { - logger.errorAt(ERR_TOO_LARGE, reader); - return false; - } - return true; - } - -public: - /** - * Sign and exponent sign. - */ - int8_t s, sE; - - /** - * Exponent - */ - int16_t e; - - /** - * Base value, nominator, denominator - */ - int64_t a, n, d; - - /** - * Constructor of the number class. - */ - Number() : s(1), sE(1), e(0), a(0), n(0), d(1) {} - - /** - * Returns the represented double value. - */ - double doubleValue() - { - return s * (a + ((double)n / (double)d)) * pow(10.0, (double)(sE * e)); - } - - /** - * Returns the represented integer value. Only a lossless operation, if the - * number is an integer (as can be checked via the isInt method), otherwise - * the exponent and the fractional value will be truncated. - */ - int64_t intValue() { return s * a; } - - /** - * Returns true, if the number is an integer (has no fractional or - * exponential part). - */ - bool isInt() { return (n == 0) && (d == 1) && (e == 0); } - - /** - * Tries to parse the number from the given stream and loggs any errors to - * the given logger instance. Numbers are terminated by one of the given - * delimiters. - */ - bool parse(CharReader &reader, Logger &logger, - const std::unordered_set<char> &delims) - { - State state = State::INIT; - char c; - - // Consume the first whitespace characters - reader.consumeWhitespace(); - - // Iterate over the FSM to extract numbers - while (reader.peek(c)) { - // Abort, once a delimiter or whitespace is reached - if (Utils::isWhitespace(c) || delims.count(c)) { - reader.resetPeek(); - break; - } - - // The character is not a whitespace character and not a delimiter - switch (state) { - case State::INIT: - case State::HAS_MINUS: - switch (c) { - case '-': - // Do not allow multiple minus signs - if (state == State::HAS_MINUS) { - logger.errorAt(ERR_UNEXPECTED_CHAR, reader); - return false; - } - state = State::HAS_MINUS; - s = -1; - break; - case '0': - // Remember a leading zero for the detection of "0x" - state = State::LEADING_ZERO; - break; - case '.': - // Remember a leading point as ".eXXX" is invalid - state = State::LEADING_POINT; - break; - default: - state = State::INT; - if (!appendChar(c, 10, Part::A, reader, logger)) { - return false; - } - break; - } - break; - case State::LEADING_ZERO: - if (c == 'x' || c == 'X') { - state = State::HEX; - break; - } - // fallthrough - case State::INT: - switch (c) { - case '.': - state = State::POINT; - break; - case 'e': - case 'E': - state = State::EXP_INIT; - break; - default: - state = State::INT; - if (!appendChar(c, 10, Part::A, reader, logger)) { - return false; - } - break; - } - break; - case State::HEX: - if (!appendChar(c, 16, Part::A, reader, logger)) { - return false; - } - break; - case State::LEADING_POINT: - case State::POINT: - switch (c) { - case 'e': - case 'E': - if (state == State::LEADING_POINT) { - logger.errorAt(ERR_UNEXPECTED_CHAR, reader); - return false; - } - state = State::EXP_INIT; - break; - default: - state = State::POINT; - if (!appendChar(c, 10, Part::N, reader, logger)) { - return false; - } - break; - } - break; - case State::EXP_HAS_MINUS: - case State::EXP_INIT: - if (c == '-') { - if (state == State::EXP_HAS_MINUS) { - logger.errorAt(ERR_UNEXPECTED_CHAR, reader); - return false; - } - state = State::EXP_HAS_MINUS; - sE = -1; - } else { - state = State::EXP; - if (!appendChar(c, 10, Part::E, reader, logger)) { - return false; - } - } - break; - case State::EXP: - if (!appendChar(c, 10, Part::E, reader, logger)) { - return false; - } - break; - } - reader.consumePeek(); - } - - // States in which ending is valid. Log an error in other states - if (state == State::LEADING_ZERO || state == State::HEX || - state == State::INT || state == State::POINT || - state == State::EXP) { - return true; - } - logger.errorAt(ERR_UNEXPECTED_END, reader); - return false; - } -}; - -/* Class Reader */ - -static const int STATE_INIT = 0; -static const int STATE_IN_STRING = 1; -static const int STATE_IN_ARRAY = 2; -static const int STATE_EXPECT_COMMA = 3; -static const int STATE_ESCAPE = 4; -static const int STATE_WHITESPACE = 5; -static const int STATE_RESYNC = 6; - -template <class T> -static std::pair<bool, T> error(CharReader &reader, Logger &logger, - const char *err, T res) -{ - logger.errorAt(err, reader); - return std::make_pair(false, std::move(res)); -} - -std::pair<bool, std::string> Reader::parseString( - CharReader &reader, Logger &logger, - const std::unordered_set<char> *delims) -{ - // Initialize the internal state - int state = STATE_INIT; - char quote = 0; - std::stringstream res; - - // Consume all whitespace - reader.consumeWhitespace(); - - // Statemachine whic iterates over each character in the stream - // TODO: Combination of peeking and consumePeek is stupid as consumePeek is - // the default (read and putBack would obviously be better, yet the latter - // is not trivial to implement in the current CharReader). - char c; - while (reader.peek(c)) { - switch (state) { - case STATE_INIT: - if (c == '"' || c == '\'') { - quote = c; - state = STATE_IN_STRING; - break; - } else if (delims && delims->count(c)) { - return error(reader, logger, ERR_UNEXPECTED_END, res.str()); - } - return error(reader, logger, ERR_UNEXPECTED_CHAR, res.str()); - case STATE_IN_STRING: - if (c == quote) { - reader.consumePeek(); - return std::make_pair(true, res.str()); - } else if (c == '\\') { - state = STATE_ESCAPE; - reader.consumePeek(); - break; - } else if (c == '\n') { - return error(reader, logger, ERR_UNTERMINATED, res.str()); - } - res << c; - reader.consumePeek(); - break; - case STATE_ESCAPE: - // Handle all possible special escape characters - switch (c) { - case 'b': - res << '\b'; - break; - case 'f': - res << '\f'; - break; - case 'n': - res << '\n'; - break; - case 'r': - res << '\r'; - break; - case 't': - res << '\t'; - break; - case 'v': - res << '\v'; - break; - case '\'': - res << '\''; - break; - case '"': - res << '"'; - break; - case '\\': - res << '\\'; - break; - case '\n': - break; - case 'x': - // TODO: Parse Latin-1 sequence hex XX - break; - case 'u': - // TODO: Parse 16-Bit unicode character hex XXXX - break; - default: - if (Utils::isNumeric(c)) { - // TODO: Parse octal 000 sequence - } else { - logger.errorAt(ERR_INVALID_ESCAPE, reader); - } - break; - } - - // Switch back to the "normal" state - state = STATE_IN_STRING; - reader.consumePeek(); - break; - } - } - return error(reader, logger, ERR_UNEXPECTED_END, res.str()); -} - -std::pair<bool, Variant::arrayType> Reader::parseArray( - CharReader &reader, Logger &logger, char delim) -{ - Variant::arrayType res; - bool hadError = false; - int state = delim ? STATE_IN_ARRAY : STATE_INIT; - delim = delim ? delim : ']'; - char c; - - // Consume all whitespace - reader.consumeWhitespace(); - - // Iterate over the characters, use the parseGeneric function to read the - // pairs - while (reader.peek(c)) { - // Generically handle the end of the array - if (state != STATE_INIT && c == delim) { - reader.consumePeek(); - return std::make_pair(!hadError, res); - } - - switch (state) { - case STATE_INIT: - if (c != '[') { - return error(reader, logger, ERR_UNEXPECTED_CHAR, res); - } - state = STATE_IN_ARRAY; - reader.consumePeek(); - break; - case STATE_IN_ARRAY: { - // Try to read an element using the parseGeneric function - reader.resetPeek(); - auto elem = parseGeneric(reader, logger, {',', delim}); - res.push_back(elem.second); - - // If the reader had no error, expect an comma, otherwise skip - // to the next comma in the stream - if (elem.first) { - state = STATE_EXPECT_COMMA; - } else { - state = STATE_RESYNC; - hadError = true; - } - break; - } - case STATE_EXPECT_COMMA: - // Skip whitespace - if (c == ',') { - state = STATE_IN_ARRAY; - } else if (!Utils::isWhitespace(c)) { - hadError = true; - state = STATE_RESYNC; - logger.errorAt(ERR_UNEXPECTED_CHAR, reader); - } - reader.consumePeek(); - break; - case STATE_RESYNC: - // Just wait for another comma to arrive - if (c == ',') { - state = STATE_IN_ARRAY; - } - reader.consumePeek(); - break; - } - } - return error(reader, logger, ERR_UNEXPECTED_END, res); -} - -std::pair<bool, std::string> Reader::parseUnescapedString( - CharReader &reader, Logger &logger, - const std::unordered_set<char> &delims) -{ - std::stringstream res; - std::stringstream buf; - char c; - - // Consume all whitespace - reader.consumeWhitespace(); - - // Copy all characters, skip whitespace at the end - int state = STATE_IN_STRING; - while (reader.peek(c)) { - if (delims.count(c)) { - reader.resetPeek(); - return std::make_pair(true, res.str()); - } else if (Utils::isWhitespace(c)) { - // Do not add whitespace to the output buffer - state = STATE_WHITESPACE; - buf << c; - } else { - // If we just hat a sequence of whitespace, append it to the output - // buffer and continue - if (state == STATE_WHITESPACE) { - res << buf.str(); - buf.str(std::string{}); - buf.clear(); - state = STATE_IN_STRING; - } - res << c; - } - reader.consumePeek(); - } - return std::make_pair(true, res.str()); -} - -std::pair<bool, int64_t> Reader::parseInteger( - CharReader &reader, Logger &logger, - const std::unordered_set<char> &delims) -{ - Number n; - if (n.parse(reader, logger, delims)) { - // Only succeed if the parsed number is an integer, otherwise this is an - // error - if (n.isInt()) { - return std::make_pair(true, n.intValue()); - } else { - return error(reader, logger, ERR_INVALID_INTEGER, n.intValue()); - } - } - return std::make_pair(false, n.intValue()); -} - -std::pair<bool, double> Reader::parseDouble( - CharReader &reader, Logger &logger, - const std::unordered_set<char> &delims) -{ - Number n; - bool res = n.parse(reader, logger, delims); - return std::make_pair(res, n.doubleValue()); -} - -std::pair<bool, Variant> Reader::parseGeneric( - CharReader &reader, Logger &logger, - const std::unordered_set<char> &delims) -{ - char c; - - // Skip all whitespace characters - reader.consumeWhitespace(); - while (reader.peek(c)) { - // Stop if a delimiter is reached - if (delims.count(c)) { - return error(reader, logger, ERR_UNEXPECTED_END, nullptr); - } - - // Parse a string if a quote is reached - if (c == '"' || c == '\'') { - auto res = parseString(reader, logger); - return std::make_pair(res.first, res.second.c_str()); - } - - if (c == '[') { - // TODO: Parse struct descriptor - } - - // Try to parse everything that looks like a number as number - if (Utils::isNumeric(c) || c == '-') { - Number n; - - // Fork the reader - utils::CharReaderFork fork = reader.fork(); - - // TODO: Fork logger - - // Try to parse the number - if (n.parse(fork, logger, delims)) { - // Parsing was successful, advance the reader - fork.commit(); - if (n.isInt()) { - return std::make_pair( - true, - Variant{static_cast<Variant::intType>(n.intValue())}); - } else { - return std::make_pair(true, n.doubleValue()); - } - } - } - - // Parse an unescaped string in any other case - auto res = parseUnescapedString(reader, logger, delims); - - // Handling for special primitive values - if (res.first) { - if (res.second == "true") { - return std::make_pair(true, Variant{true}); - } - if (res.second == "false") { - return std::make_pair(true, Variant{false}); - } - if (res.second == "null") { - return std::make_pair(true, Variant{nullptr}); - } - } - return std::make_pair(res.first, res.second.c_str()); - } - return error(reader, logger, ERR_UNEXPECTED_END, nullptr); -} -} -} - diff --git a/src/core/variant/Reader.hpp b/src/core/variant/Reader.hpp deleted file mode 100644 index 4114d46..0000000 --- a/src/core/variant/Reader.hpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - Ousía - Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file Reader.hpp - * - * Provides parsers for various micro formats. These formats include integers, - * doubles, strings, JSON and the Ousía struct notation. - * - * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) - */ - -#ifndef _OUSIA_VARIANT_READER_HPP_ -#define _OUSIA_VARIANT_READER_HPP_ - -#include <cstdint> -#include <unordered_set> -#include <utility> - -#include <core/utils/CharReader.hpp> -#include <core/Logger.hpp> - -#include "Variant.hpp" - -namespace ousia { -namespace variant { - -class Reader { -private: - /** - * Parses a string which may either be enclosed by " or ', unescapes - * entities in the string as specified for JavaScript. - * - * @param reader is a reference to the CharReader instance which is - * the source for the character data. The reader will be positioned after - * the terminating quote character or at the terminating delimiting - * character. - * @param logger is the logger instance that should be used to log error - * messages and warnings. - * @param delims is an optional set of delimiters after which parsing has to - * be stopped (the delimiters may occur inside the actual string, but not - * outside). If nullptr is given, no delimiter is used and a complete string - * is read. - */ - static std::pair<bool, std::string> parseString( - CharReader &reader, Logger &logger, - const std::unordered_set<char> *delims); - -public: - /** - * Parses a string which may either be enclosed by " or ', unescapes - * entities in the string as specified for JavaScript. - * - * @param reader is a reference to the CharReader instance which is - * the source for the character data. The reader will be positioned after - * the terminating quote character or at the terminating delimiting - * character. - * @param logger is the logger instance that should be used to log error - * messages and warnings. - * @param delims is a set of delimiters after which parsing has to - * be stopped (the delimiters may occur inside the actual string, but not - * outside). - */ - static std::pair<bool, std::string> parseString( - CharReader &reader, Logger &logger, - const std::unordered_set<char> &delims) - { - return parseString(reader, logger, &delims); - } - - /** - * Parses a string which may either be enclosed by " or ', unescapes - * entities in the string as specified for JavaScript. - * - * @param reader is a reference to the CharReader instance which is - * the source for the character data. The reader will be positioned after - * the terminating quote character or at the terminating delimiting - * character. - * @param logger is the logger instance that should be used to log error - * messages and warnings. - */ - static std::pair<bool, std::string> parseString(CharReader &reader, - Logger &logger) - { - return parseString(reader, logger, nullptr); - } - - /** - * Extracts an unescaped string from the given buffered char reader - * instance. This function just reads text until one of the given delimiter - * characters is reached. - * - * @param reader is a reference to the CharReader instance which is - * the source for the character data. The reader will be positioned at the - * terminating delimiting character. - * @param delims is a set of characters which will terminate the string. - * These characters are not included in the result. May not be nullptr. - */ - static std::pair<bool, std::string> parseUnescapedString( - CharReader &reader, Logger &logger, - const std::unordered_set<char> &delims); - - /** - * Parses an integer from the given buffered char reader instance until one - * of the given delimiter characters is reached. - * - * @param reader is a reference to the CharReader instance from - * which the character data should been reader. The reader will be - * positioned at the terminating delimiting character or directly after the - * integer. - */ - static std::pair<bool, int64_t> parseInteger( - CharReader &reader, Logger &logger, - const std::unordered_set<char> &delims); - - /** - * Parses an double from the given buffered char reader instance until one - * of the given delimiter characters is reached. - * - * @param reader is a reference to the CharReader instance from - * which the character data should been reader. The reader will be - * positioned at the terminating delimiting character or directly after the - * integer. - */ - static std::pair<bool, double> parseDouble( - CharReader &reader, Logger &logger, - const std::unordered_set<char> &delims); - - /** - * Parses an array of values. - */ - static std::pair<bool, Variant::arrayType> parseArray( - CharReader &reader, Logger &logger, char delim = 0); - - /** - * Tries to parse the most specific item from the given stream until one of - * the given delimiters is reached or a meaningful literal has been read. - * The resulting variant represents the value that has been read. - * - * @param reader is a reference to the CharReader instance which is - * the source for the character data. The reader will be positioned at the - * terminating delimiting character. - * @param delims is a set of characters which will terminate the string. - * These characters are not included in the result. May not be nullptr. - */ - static std::pair<bool, Variant> parseGeneric( - CharReader &reader, Logger &logger, - const std::unordered_set<char> &delims); -}; -} -} - -#endif /* _OUSIA_VARIANT_READER_HPP_ */ - diff --git a/src/core/variant/Variant.cpp b/src/core/variant/Variant.cpp deleted file mode 100644 index d33cd4f..0000000 --- a/src/core/variant/Variant.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - Ousía - Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <sstream> - -#include <core/Utils.hpp> - -#include "Variant.hpp" - -namespace ousia { - -/* Class Variant::TypeException */ - -Variant::TypeException::TypeException(Type actualType, Type requestedType) - : OusiaException(std::string("Variant: Requested \"") + - Variant::getTypeName(requestedType) + - std::string("\" but is \"") + - Variant::getTypeName(actualType) + std::string("\"")), - actualType(actualType), - requestedType(requestedType) -{ -} - -/* Class Variant */ - -const char *Variant::getTypeName(Type type) -{ - switch (type) { - case Type::NULLPTR: - return "null"; - case Type::BOOL: - return "boolean"; - case Type::INT: - return "integer"; - case Type::DOUBLE: - return "double"; - case Type::STRING: - return "string"; - case Type::ARRAY: - return "array"; - case Type::MAP: - return "map"; - } - return "unknown"; -} - -Variant::boolType Variant::toBool() const -{ - switch (getType()) { - case Type::NULLPTR: - return false; - case Type::BOOL: - return asBool(); - case Type::INT: - return asInt() != 0; - case Type::DOUBLE: - return asDouble() != 0.0; - case Type::STRING: - return true; - case Type::ARRAY: - return true; - case Type::MAP: - return true; - } - return false; -} - -Variant::intType Variant::toInt() const -{ - switch (getType()) { - case Type::NULLPTR: - return 0; - case Type::BOOL: - return asBool() ? 1 : 0; - case Type::INT: - return asInt(); - case Type::DOUBLE: - return asDouble(); - case Type::STRING: - return 0; // TODO: Parse string as int - case Type::ARRAY: { - const arrayType &a = asArray(); - return (a.size() == 1) ? a[0].toInt() : 0; - } - case Type::MAP: - return 0; - } - return false; -} - -Variant::doubleType Variant::toDouble() const -{ - switch (getType()) { - case Type::NULLPTR: - return 0.0; - case Type::BOOL: - return asBool() ? 1.0 : 0.0; - case Type::INT: - return asInt(); - case Type::DOUBLE: - return asDouble(); - case Type::STRING: - return 0.0; // TODO: Parse string as double - case Type::ARRAY: { - const arrayType &a = asArray(); - return (a.size() == 1) ? a[0].toDouble() : 0; - } - case Type::MAP: - return 0; - } - return false; -} - -Variant::stringType Variant::toString(bool escape) const -{ - switch (getType()) { - case Type::NULLPTR: - return "null"; - case Type::BOOL: - return asBool() ? "true" : "false"; - case Type::INT: - return std::to_string(asInt()); - case Type::DOUBLE: - return std::to_string(asDouble()); - case Type::STRING: { - // TODO: Use proper serialization function - std::stringstream ss; - ss << "\"" << asString() << "\""; - return ss.str(); - } - case Type::ARRAY: - return Utils::join(asArray(), ", ", "[", "]"); - case Type::MAP: - return Utils::join(asMap(), ", ", "{", "}"); - } - return ""; -} - -} - diff --git a/src/core/variant/Variant.hpp b/src/core/variant/Variant.hpp deleted file mode 100644 index 1e62644..0000000 --- a/src/core/variant/Variant.hpp +++ /dev/null @@ -1,766 +0,0 @@ -/* - Ousía - Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file Variant.hpp - * - * The Variant class is used to efficiently represent a variables of varying - * type. Variant instances are used to represent data given by the end user and - * to exchange information between the host application and the script clients. - * - * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) - */ - -#ifndef _OUSIA_VARIANT_HPP_ -#define _OUSIA_VARIANT_HPP_ - -#include <cstdint> -#include <map> -#include <string> -#include <vector> -#include <ostream> - -// TODO: Use -// http://nikic.github.io/2012/02/02/Pointer-magic-for-efficient-dynamic-value-representations.html -// later (will allow to use 8 bytes for a variant) - -#include <core/Exceptions.hpp> - -namespace ousia { -namespace variant { - -/** - * Instances of the Variant class represent any kind of data that is exchanged - * between the host application and the script engine. Variants are immutable. - */ -class Variant { -public: - /** - * Enum containing the possible types a variant may have. - */ - enum class Type : int16_t { - NULLPTR, - BOOL, - INT, - DOUBLE, - STRING, - ARRAY, - MAP - }; - - /** - * Exception thrown whenever a variant is accessed via a getter function - * that is not supported for the current variant type. - */ - class TypeException : public OusiaException { - private: - /** - * Internally used string holding the exception message. - */ - const std::string msg; - - public: - /** - * Contains the actual type of the variant. - */ - const Type actualType; - - /** - * Contains the requested type of the variant. - */ - const Type requestedType; - - /** - * Constructor of the TypeException. - * - * @param actualType describes the actual type of the variant. - * @param requestedType describes the type in which the variant was - * requested. - */ - TypeException(Type actualType, Type requestedType); - }; - - using boolType = bool; - using intType = int32_t; - using doubleType = double; - using stringType = std::string; - using arrayType = std::vector<Variant>; - using mapType = std::map<std::string, Variant>; - -private: - /** - * Used to store the actual type of the variant. - */ - Type type = Type::NULLPTR; - - /** - * Anonymous union containing the possible value of the variant. - */ - union { - /** - * The boolean value. Only valid if type is Type::BOOL. - */ - boolType boolVal; - /** - * The integer value. Only valid if type is Type::INT. - */ - intType intVal; - /** - * The number value. Only valid if type is Type::DOUBLE. - */ - doubleType doubleVal; - /** - * Pointer to the more complex data structures on the free store. Only - * valid if type is one of Type::STRING, Type::ARRAY, - * Type::MAP. - */ - void *ptrVal; - }; - - /** - * Internally used to convert the current pointer value to a reference of - * the specified type. - */ - template <typename T> - T &asObj(Type requestedType) const - { - const Type actualType = getType(); - if (actualType == requestedType) { - return *(static_cast<T *>(ptrVal)); - } - throw TypeException{actualType, requestedType}; - } - - /** - * Used internally to assign the value of another Variant instance to this - * instance. - * - * @param v is the Variant instance that should be copied to this instance. - */ - void copy(const Variant &v) - { - destroy(); - type = v.type; - switch (type) { - case Type::NULLPTR: - break; - case Type::BOOL: - boolVal = v.boolVal; - break; - case Type::INT: - intVal = v.intVal; - break; - case Type::DOUBLE: - doubleVal = v.doubleVal; - break; - case Type::STRING: - ptrVal = new stringType(v.asString()); - break; - case Type::ARRAY: - ptrVal = new arrayType(v.asArray()); - break; - case Type::MAP: - ptrVal = new mapType(v.asMap()); - break; - } - } - - /** - * Used internally to move the value of another Variant instance to this - * instance. - * - * @param v is the Variant instance that should be copied to this instance. - */ - void move(Variant &&v) - { - destroy(); - type = v.type; - switch (type) { - case Type::NULLPTR: - break; - case Type::BOOL: - boolVal = v.boolVal; - break; - case Type::INT: - intVal = v.intVal; - break; - case Type::DOUBLE: - doubleVal = v.doubleVal; - break; - case Type::STRING: - case Type::ARRAY: - case Type::MAP: - ptrVal = v.ptrVal; - v.ptrVal = nullptr; - break; - } - v.type = Type::NULLPTR; - } - - /** - * Used internally to destroy any value that was allocated on the heap. - */ - void destroy() - { - if (ptrVal) { - switch (type) { - case Type::STRING: - delete static_cast<stringType *>(ptrVal); - break; - case Type::ARRAY: - delete static_cast<arrayType *>(ptrVal); - break; - case Type::MAP: - delete static_cast<mapType *>(ptrVal); - break; - default: - break; - } - } - } - -public: - /** - * Copy constructor of the Variant class. - * - * @param v is the Variant instance that should be cloned. - */ - Variant(const Variant &v) : ptrVal(nullptr) { copy(v); } - - /** - * Move constructor of the Variant class. - * - * @param v is the reference to the Variant instance that should be moved, - * this instance is invalidated afterwards. - */ - Variant(Variant &&v) : ptrVal(nullptr) { move(std::move(v)); } - - /** - * Default constructor. Type is set to Type:null. - */ - Variant() : ptrVal(nullptr) { setNull(); } - - /** - * Default destructor, frees any memory that was allocated on the heap. - */ - ~Variant() { destroy(); } - - /** - * Constructor for null values. Initializes the variant as null value. - */ - Variant(std::nullptr_t) : ptrVal(nullptr) { setNull(); } - - /** - * Constructor for boolean values. - * - * @param b boolean value. - */ - Variant(boolType b) : ptrVal(nullptr) { setBool(b); } - - /** - * Constructor for integer values. - * - * @param i integer value. - */ - Variant(intType i) : ptrVal(nullptr) { setInt(i); } - - /** - * Constructor for double values. - * - * @param d double value. - */ - Variant(doubleType d) : ptrVal(nullptr) { setDouble(d); } - - /** - * Constructor for string values. The given string is copied and managed by - * the new Variant instance. - * - * @param s is a reference to a C-Style string used as string value. - */ - Variant(const char *s) : ptrVal(nullptr) { setString(s); } - - /** - * Constructor for array values. The given array is copied and managed by - * the new Variant instance. - * - * @param a is a reference to the array - */ - Variant(arrayType a) : ptrVal(nullptr) { setArray(std::move(a)); } - - /** - * Constructor for map values. The given map is copied and managed by the - * new Variant instance. - * - * @param m is a reference to the map. - */ - Variant(mapType m) : ptrVal(nullptr) { setMap(std::move(m)); } - - /** - * Copy assignment operator. - */ - Variant &operator=(const Variant &v) - { - copy(v); - return *this; - } - - /** - * Move assignment operator. - */ - Variant &operator=(Variant &&v) - { - move(std::move(v)); - return *this; - } - - /** - * Assign nullptr_t operator (allows to write Variant v = nullptr). - * - * @param p is an instance of std::nullptr_t. - */ - Variant &operator=(std::nullptr_t) - { - setNull(); - return *this; - } - - /** - * Assign a boolean value. - * - * @param b is the boolean value to which the variant should be set. - */ - Variant &operator=(boolType b) - { - setBool(b); - return *this; - } - - /** - * Assign an integer value. - * - * @param i is the integer value to which the variant should be set. - */ - Variant &operator=(intType i) - { - setInt(i); - return *this; - } - - /** - * Assign a double value. - * - * @param d is the double value to which the variant should be set. - */ - Variant &operator=(doubleType d) - { - setDouble(d); - return *this; - } - - /** - * Assign a zero terminated const char array. - * - * @param s is the zero terminated const char array to which the variant - * should be set. - */ - Variant &operator=(const char *s) - { - setString(s); - return *this; - } - - /** - * Checks whether this Variant instance represents the nullptr. - * - * @return true if the Variant instance represents the nullptr, false - * otherwise. - */ - bool isNull() const { return type == Type::NULLPTR; } - - /** - * Checks whether this Variant instance is a boolean. - * - * @return true if the Variant instance is a boolean, false otherwise. - */ - bool isBool() const { return type == Type::BOOL; } - - /** - * Checks whether this Variant instance is an integer. - * - * @return true if the Variant instance is an integer, false otherwise. - */ - bool isInt() const { return type == Type::INT; } - - /** - * Checks whether this Variant instance is a double. - * - * @return true if the Variant instance is a double, false otherwise. - */ - bool isDouble() const { return type == Type::DOUBLE; } - - /** - * Checks whether this Variant instance is a string. - * - * @return true if the Variant instance is a string, false otherwise. - */ - bool isString() const { return type == Type::STRING; } - - /** - * Checks whether this Variant instance is an array. - * - * @return true if the Variant instance is an array, false otherwise. - */ - bool isArray() const { return type == Type::ARRAY; } - - /** - * Checks whether this Variant instance is a map. - * - * @return true if the Variant instance is a map, false otherwise. - */ - bool isMap() const { return type == Type::MAP; } - - /** - * Returns the Variant boolean value. Performs no type conversion. Throws an - * exception if the underlying type is not a boolean. - * - * @return the boolean value. - */ - boolType asBool() const - { - if (isBool()) { - return boolVal; - } - throw TypeException{getType(), Type::BOOL}; - } - - /** - * Returns the Variant integer value. Performs no type conversion. Throws an - * exception if the underlying type is not an integer. - * - * @return the integer value. - */ - intType asInt() const - { - if (isInt()) { - return intVal; - } - throw TypeException{getType(), Type::INT}; - } - - /** - * Returns the Variant double value. Performs no type conversion. Throws an - * exception if the underlying type is not a double. - * - * @return the double value. - */ - doubleType asDouble() const - { - if (isDouble()) { - return doubleVal; - } - throw TypeException{getType(), Type::DOUBLE}; - } - - /** - * Returns a const reference to the string value. Performs no type - * conversion. Throws an exception if the underlying type is not a string. - * - * @return the string value as const reference. - */ - const stringType &asString() const - { - return asObj<stringType>(Type::STRING); - } - - /** - * Returns a const reference to the string value. Performs no type - * conversion. Throws an exception if the underlying type is not a string. - * - * @return the string value as reference. - */ - stringType &asString() { return asObj<stringType>(Type::STRING); } - - /** - * Returns a const reference to the array value. Performs no type - * conversion. Throws an exception if the underlying type is not an array. - * - * @return the array value as const reference. - */ - const arrayType &asArray() const { return asObj<arrayType>(Type::ARRAY); } - - /** - * Returns a const reference to the array value. Performs no type - * conversion. Throws an exception if the underlying type is not an array. - * - * @return the array value as reference. - */ - arrayType &asArray() { return asObj<arrayType>(Type::ARRAY); } - - /** - * Returns a const reference to the map value. Performs no type - * conversion. Throws an exception if the underlying type is not a map. - * - * @return the map value as const reference. - */ - const mapType &asMap() const { return asObj<mapType>(Type::MAP); } - - /** - * Returns a reference to the map value. Performs no type conversion. - * Throws an exception if the underlying type is not a map. - * - * @return the map value as reference. - */ - mapType &asMap() { return asObj<mapType>(Type::MAP); } - - /** - * Returns the value of the Variant as boolean, performs type conversion. - * - * @return the Variant value converted to a boolean value. - */ - boolType toBool() const; - - /** - * Returns the value of the Variant as integer, performs type conversion. - * - * @return the Variant value converted to an integer value. - */ - intType toInt() const; - - /** - * Returns the value of the Variant as double, performs type conversion. - * - * @return the Variant value converted to a double value. - */ - doubleType toDouble() const; - - /** - * Returns the value of the Variant as string, performs type conversion. - * - * @return the value of the variant as string. - * @param escape if set to true, adds double quotes to strings and escapes - * them properly (resulting in a more or less JSONesque output). - */ - stringType toString(bool escape = false) const; - - /** - * Sets the variant to null. - */ - void setNull() - { - destroy(); - type = Type::NULLPTR; - ptrVal = nullptr; - } - - /** - * Sets the variant to the given boolean value. - * - * @param b is the new boolean value. - */ - void setBool(boolType b) - { - destroy(); - type = Type::BOOL; - boolVal = b; - } - - /** - * Sets the variant to the given integer value. - * - * @param i is the new integer value. - */ - void setInt(intType i) - { - destroy(); - type = Type::INT; - intVal = i; - } - - /** - * Sets the variant to the given double value. - * - * @param d is the new double value. - */ - void setDouble(doubleType d) - { - destroy(); - type = Type::DOUBLE; - doubleVal = d; - } - - /** - * Sets the variant to the given string value. - * - * @param d is the new string value. - */ - void setString(const char *s) - { - if (isString()) { - asString().assign(s); - } else { - destroy(); - type = Type::STRING; - ptrVal = new stringType(s); - } - } - - /** - * Sets the variant to the given array value. - * - * @param a is the new array value. - */ - void setArray(arrayType a) - { - if (isArray()) { - asArray().swap(a); - } else { - destroy(); - type = Type::ARRAY; - ptrVal = new arrayType(std::move(a)); - } - } - - /** - * Sets the variant to the given map value. - * - * @param a is the new map value. - */ - void setMap(mapType m) - { - if (isMap()) { - asMap().swap(m); - } else { - destroy(); - type = Type::MAP; - ptrVal = new mapType(std::move(m)); - } - } - - /** - * Returns the current type of the Variant. - * - * @return the current type of the Variant. - */ - Type getType() const { return type; } - - /** - * Returns the name of the given variant type as C-style string. - */ - static const char *getTypeName(Type type); - - /** - * Returns the name of the type of this variant instance. - */ - const char *getTypeName() { return Variant::getTypeName(getType()); } - - /** - * Prints the Variant to the output stream. - */ - friend std::ostream &operator<<(std::ostream &os, const Variant &v) - { - return os << v.toString(true); - } - - /** - * Prints a key value pair to the output stream. - */ - friend std::ostream &operator<<(std::ostream &os, - const mapType::value_type &v) - { - // TODO: Use proper serialization function - return os << "\"" << v.first << "\": " << v.second.toString(true); - } - - /* - * Comprison operators. - */ - - friend bool operator<(const Variant &lhs, const Variant &rhs) - { - // If the types do not match, we can not do a meaningful comparison. - if (lhs.getType() != rhs.getType()) { - throw TypeException(lhs.getType(), rhs.getType()); - } - switch (lhs.getType()) { - case Type::NULLPTR: - return false; - case Type::BOOL: - return lhs.boolVal < rhs.boolVal; - case Type::INT: - return lhs.intVal < rhs.intVal; - case Type::DOUBLE: - return lhs.doubleVal < rhs.doubleVal; - case Type::STRING: - return lhs.asString() < rhs.asString(); - case Type::ARRAY: - return lhs.asArray() < rhs.asArray(); - case Type::MAP: - return lhs.asMap() < rhs.asMap(); - } - throw OusiaException("Internal Error! Unknown type!"); - } - friend bool operator>(const Variant &lhs, const Variant &rhs) - { - return rhs < lhs; - } - friend bool operator<=(const Variant &lhs, const Variant &rhs) - { - return !(lhs > rhs); - } - friend bool operator>=(const Variant &lhs, const Variant &rhs) - { - return !(lhs < rhs); - } - - friend bool operator==(const Variant &lhs, const Variant &rhs) - { - if (lhs.getType() != rhs.getType()) { - return false; - } - switch (lhs.getType()) { - case Type::NULLPTR: - return true; - case Type::BOOL: - return lhs.boolVal == rhs.boolVal; - case Type::INT: - return lhs.intVal == rhs.intVal; - case Type::DOUBLE: - return lhs.doubleVal == rhs.doubleVal; - case Type::STRING: - return lhs.asString() == rhs.asString(); - case Type::ARRAY: - return lhs.asArray() == rhs.asArray(); - case Type::MAP: - return lhs.asMap() == rhs.asMap(); - } - throw OusiaException("Internal Error! Unknown type!"); - } - - friend bool operator!=(const Variant &lhs, const Variant &rhs) - { - return !(lhs == rhs); - } -}; -} - -// Alias for the (very often used and unambigous) variant class -using Variant = variant::Variant; -} - -#endif /* _OUSIA_VARIANT_HPP_ */ - |