<commit_before>// Copyright 2015-2019 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_UTILS_LOG_HPP
#define OUZEL_UTILS_LOG_HPP

#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <type_traits>
#include <vector>
#include "math/Matrix.hpp"
#include "math/Quaternion.hpp"
#include "math/Size.hpp"
#include "math/Vector.hpp"
#include "utils/Thread.hpp"

namespace ouzel
{
    class Logger;

    class Log final
    {
    public:
        enum class Level
        {
            Off,
            Error,
            Warning,
            Info,
            All
        };

        explicit Log(const Logger& initLogger, Level initLevel = Level::Info):
            logger(initLogger), level(initLevel)
        {
        }

        Log(const Log& other):
            logger(other.logger),
            level(other.level),
            s(other.s)
        {
        }

        Log(Log&& other) noexcept:
            logger(other.logger),
            level(other.level),
            s(std::move(other.s))
        {
            other.level = Level::Info;
        }

        Log& operator=(const Log& other)
        {
            if (&other == this) return *this;

            level = other.level;
            s = other.s;

            return *this;
        }

        Log& operator=(Log&& other) noexcept
        {
            if (&other == this) return *this;

            level = other.level;
            other.level = Level::Info;
            s = std::move(other.s);

            return *this;
        }

        ~Log();

        template <typename T, typename std::enable_if<std::is_same<T, bool>::value>::type* = nullptr>
        Log& operator<<(const T val)
        {
            s += val ? "true" : "false";
            return *this;
        }

        template <typename T, typename std::enable_if<std::is_same<T, uint8_t>::value>::type* = nullptr>
        Log& operator<<(const T val)
        {
            static constexpr char digits[] = "0123456789abcdef";

            for (uint32_t p = 0; p < 2; ++p)
                s.push_back(digits[(val >> (4 - p * 4)) & 0x0F]);

            return *this;
        }

        template <typename T, typename std::enable_if<std::is_arithmetic<T>::value &&
            !std::is_same<T, bool>::value &&
            !std::is_same<T, uint8_t>::value>::type* = nullptr>
        Log& operator<<(const T val)
        {
            s += std::to_string(val);
            return *this;
        }

        template <typename T, typename std::enable_if<std::is_same<T, std::string>::value>::type* = nullptr>
        Log& operator<<(const T& val)
        {
            s += val;
            return *this;
        }

        template <typename T, typename std::enable_if<std::is_same<T, char>::value>::type* = nullptr>
        Log& operator<<(const T* val)
        {
            s += val;
            return *this;
        }

        template <typename T, typename std::enable_if<!std::is_same<T, char>::value>::type* = nullptr>
        Log& operator<<(const T* val)
        {
            static constexpr char digits[] = "0123456789abcdef";

            for (size_t i = 0; i < sizeof(val) * 2; ++i)
                s.push_back(digits[(reinterpret_cast<uintptr_t>(val) >> (sizeof(val) * 2 - i - 1) * 4) & 0x0F]);

            return *this;
        }

        template <typename T>
        Log& operator<<(const std::vector<T>& val)
        {
            bool first = true;

            for (const auto& i : val)
            {
                if (!first) s += ", ";
                first = false;

                operator<<(i);
            }

            return *this;
        }

        template <size_t N, size_t M, class T>
        Log& operator<<(const Matrix<N, M, T>& val)
        {
            bool first = true;

            for (const T c : val.m)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }

            return *this;
        }

        template <typename T>
        Log& operator<<(const Quaternion<T>& val)
        {
            s += std::to_string(val.v[0]) + "," + std::to_string(val.v[1]) + "," +
                std::to_string(val.v[2]) + "," + std::to_string(val.v[3]);
            return *this;
        }

        template <size_t N, class T>
        Log& operator<<(const Size<N, T>& val)
        {
            bool first = true;

            for (const T c : val.v)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }
            return *this;
        }

        template <size_t N, class T>
        Log& operator<<(const Vector<N, T>& val)
        {
            bool first = true;

            for (const T c : val.v)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }
            return *this;
        }

    private:
        const Logger& logger;
        Level level = Level::Info;
        std::string s;
    };

    class Logger final
    {
    public:
        explicit Logger(Log::Level initThreshold = Log::Level::All):
            threshold(initThreshold)
        {
#if !defined(__EMSCRIPTEN__)
            logThread = Thread(&Logger::logLoop, this);
#endif
        }

        Logger(const Logger&) = delete;
        Logger& operator=(const Logger&) = delete;
        Logger(Logger&&) = delete;
        Logger& operator=(Logger&&) = delete;

        ~Logger()
        {
#if !defined(__EMSCRIPTEN__)
            std::unique_lock<std::mutex> lock(queueMutex);
            commandQueue.push(Command(Command::Type::Quit));
            lock.unlock();
            queueCondition.notify_all();
#endif
        }

        Log log(const Log::Level level = Log::Level::Info) const
        {
            return Log(*this, level);
        }

        void log(const std::string& str, const Log::Level level = Log::Level::Info) const
        {
            if (level <= threshold)
            {
#if defined(__EMSCRIPTEN__)
                logString(str, level);
#else
                std::unique_lock<std::mutex> lock(queueMutex);
                commandQueue.push(Command(Command::Type::LogString, level, str));
                lock.unlock();
                queueCondition.notify_all();
#endif
            }
        }

    private:
        static void logString(const std::string& str, const Log::Level level = Log::Level::Info);

#ifdef DEBUG
        std::atomic<Log::Level> threshold{Log::Level::All};
#else
        std::atomic<Log::Level> threshold{Log::Level::Info};
#endif

#if !defined(__EMSCRIPTEN__)
        class Command
        {
        public:
            enum class Type
            {
                LogString,
                Quit
            };

            explicit Command(const Type initType):
                type(initType)
            {
            }

            Command(const Type initType, const Log::Level initLevel,
                    const std::string& initString):
                type(initType),
                level(initLevel),
                str(initString)
            {
            }

            Type type;
            Log::Level level;
            std::string str;
        };

        void logLoop()
        {
            for (;;)
            {
                std::unique_lock<std::mutex> lock(queueMutex);
                while (commandQueue.empty()) queueCondition.wait(lock);
                auto command = std::move(commandQueue.front());
                commandQueue.pop();
                lock.unlock();

                if (command.type == Command::Type::LogString)
                    logString(command.str, command.level);
                else if (command.type == Command::Type::Quit)
                    break;
            }
        }

        mutable std::condition_variable queueCondition;
        mutable std::mutex queueMutex;
        mutable std::queue<Command> commandQueue;
        Thread logThread;
#endif
    };
}

#endif // OUZEL_UTILS_LOG_HPP
<commit_msg>Check for container<commit_after>// Copyright 2015-2019 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_UTILS_LOG_HPP
#define OUZEL_UTILS_LOG_HPP

#include <array>
#include <atomic>
#include <condition_variable>
#include <initializer_list>
#include <list>
#include <mutex>
#include <queue>
#include <set>
#include <string>
#include <thread>
#include <type_traits>
#include <vector>
#include "math/Matrix.hpp"
#include "math/Quaternion.hpp"
#include "math/Size.hpp"
#include "math/Vector.hpp"
#include "utils/Thread.hpp"

namespace ouzel
{
    class Logger;

    class Log final
    {
    public:
        enum class Level
        {
            Off,
            Error,
            Warning,
            Info,
            All
        };

        explicit Log(const Logger& initLogger, Level initLevel = Level::Info):
            logger(initLogger), level(initLevel)
        {
        }

        Log(const Log& other):
            logger(other.logger),
            level(other.level),
            s(other.s)
        {
        }

        Log(Log&& other) noexcept:
            logger(other.logger),
            level(other.level),
            s(std::move(other.s))
        {
            other.level = Level::Info;
        }

        Log& operator=(const Log& other)
        {
            if (&other == this) return *this;

            level = other.level;
            s = other.s;

            return *this;
        }

        Log& operator=(Log&& other) noexcept
        {
            if (&other == this) return *this;

            level = other.level;
            other.level = Level::Info;
            s = std::move(other.s);

            return *this;
        }

        ~Log();

        template <typename T, typename std::enable_if<std::is_same<T, bool>::value>::type* = nullptr>
        Log& operator<<(const T val)
        {
            s += val ? "true" : "false";
            return *this;
        }

        template <typename T, typename std::enable_if<std::is_same<T, uint8_t>::value>::type* = nullptr>
        Log& operator<<(const T val)
        {
            static constexpr char digits[] = "0123456789abcdef";

            for (uint32_t p = 0; p < 2; ++p)
                s.push_back(digits[(val >> (4 - p * 4)) & 0x0F]);

            return *this;
        }

        template <typename T, typename std::enable_if<std::is_arithmetic<T>::value &&
            !std::is_same<T, bool>::value &&
            !std::is_same<T, uint8_t>::value>::type* = nullptr>
        Log& operator<<(const T val)
        {
            s += std::to_string(val);
            return *this;
        }

        template <typename T, typename std::enable_if<std::is_same<T, std::string>::value>::type* = nullptr>
        Log& operator<<(const T& val)
        {
            s += val;
            return *this;
        }

        template <typename T, typename std::enable_if<std::is_same<T, char>::value>::type* = nullptr>
        Log& operator<<(const T* val)
        {
            s += val;
            return *this;
        }

        template <typename T, typename std::enable_if<!std::is_same<T, char>::value>::type* = nullptr>
        Log& operator<<(const T* val)
        {
            static constexpr char digits[] = "0123456789abcdef";

            for (size_t i = 0; i < sizeof(val) * 2; ++i)
                s.push_back(digits[(reinterpret_cast<uintptr_t>(val) >> (sizeof(val) * 2 - i - 1) * 4) & 0x0F]);

            return *this;
        }

        template <typename T> struct isContainer: public std::false_type{};
        template <typename T, std::size_t N> struct isContainer<std::array<T, N>>: public std::true_type{};
        template <typename... Args> struct isContainer<std::initializer_list<Args...>>: public std::true_type{};
        template <typename... Args> struct isContainer<std::list<Args...>>: public std::true_type{};
        template <typename... Args> struct isContainer<std::set<Args...>>: public std::true_type{};
        template <typename... Args> struct isContainer<std::vector<Args...>>: public std::true_type{};

        template <typename T, typename std::enable_if<isContainer<T>::value>::type* = nullptr>
        Log& operator<<(const T& val)
        {
            bool first = true;

            for (const auto& i : val)
            {
                if (!first) s += ", ";
                first = false;

                operator<<(i);
            }

            return *this;
        }

        template <size_t N, size_t M, class T>
        Log& operator<<(const Matrix<N, M, T>& val)
        {
            bool first = true;

            for (const T c : val.m)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }

            return *this;
        }

        template <typename T>
        Log& operator<<(const Quaternion<T>& val)
        {
            s += std::to_string(val.v[0]) + "," + std::to_string(val.v[1]) + "," +
                std::to_string(val.v[2]) + "," + std::to_string(val.v[3]);
            return *this;
        }

        template <size_t N, class T>
        Log& operator<<(const Size<N, T>& val)
        {
            bool first = true;

            for (const T c : val.v)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }
            return *this;
        }

        template <size_t N, class T>
        Log& operator<<(const Vector<N, T>& val)
        {
            bool first = true;

            for (const T c : val.v)
            {
                if (!first) s += ",";
                first = false;
                s += std::to_string(c);
            }
            return *this;
        }

    private:
        const Logger& logger;
        Level level = Level::Info;
        std::string s;
    };

    class Logger final
    {
    public:
        explicit Logger(Log::Level initThreshold = Log::Level::All):
            threshold(initThreshold)
        {
#if !defined(__EMSCRIPTEN__)
            logThread = Thread(&Logger::logLoop, this);
#endif
        }

        Logger(const Logger&) = delete;
        Logger& operator=(const Logger&) = delete;
        Logger(Logger&&) = delete;
        Logger& operator=(Logger&&) = delete;

        ~Logger()
        {
#if !defined(__EMSCRIPTEN__)
            std::unique_lock<std::mutex> lock(queueMutex);
            commandQueue.push(Command(Command::Type::Quit));
            lock.unlock();
            queueCondition.notify_all();
#endif
        }

        Log log(const Log::Level level = Log::Level::Info) const
        {
            return Log(*this, level);
        }

        void log(const std::string& str, const Log::Level level = Log::Level::Info) const
        {
            if (level <= threshold)
            {
#if defined(__EMSCRIPTEN__)
                logString(str, level);
#else
                std::unique_lock<std::mutex> lock(queueMutex);
                commandQueue.push(Command(Command::Type::LogString, level, str));
                lock.unlock();
                queueCondition.notify_all();
#endif
            }
        }

    private:
        static void logString(const std::string& str, const Log::Level level = Log::Level::Info);

#ifdef DEBUG
        std::atomic<Log::Level> threshold{Log::Level::All};
#else
        std::atomic<Log::Level> threshold{Log::Level::Info};
#endif

#if !defined(__EMSCRIPTEN__)
        class Command
        {
        public:
            enum class Type
            {
                LogString,
                Quit
            };

            explicit Command(const Type initType):
                type(initType)
            {
            }

            Command(const Type initType, const Log::Level initLevel,
                    const std::string& initString):
                type(initType),
                level(initLevel),
                str(initString)
            {
            }

            Type type;
            Log::Level level;
            std::string str;
        };

        void logLoop()
        {
            for (;;)
            {
                std::unique_lock<std::mutex> lock(queueMutex);
                while (commandQueue.empty()) queueCondition.wait(lock);
                auto command = std::move(commandQueue.front());
                commandQueue.pop();
                lock.unlock();

                if (command.type == Command::Type::LogString)
                    logString(command.str, command.level);
                else if (command.type == Command::Type::Quit)
                    break;
            }
        }

        mutable std::condition_variable queueCondition;
        mutable std::mutex queueMutex;
        mutable std::queue<Command> commandQueue;
        Thread logThread;
#endif
    };
}

#endif // OUZEL_UTILS_LOG_HPP
<|endoftext|>


<commit_before>/**
 * This software is released under the terms of the MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @copyright  2009-2011 Roberto Perpuly
 * @license    http://www.opensource.org/licenses/mit-license.php The MIT License
 */
#include <language/LexicalAnalyzerClass.h>
#include <language/Php53LexicalAnalyzerImpl.h>
#include <windows/StringHelperClass.h>
#include <unicode/uchar.h>
#include <unicode/ustring.h>
#include <unicode/ucnv.h>

mvceditor::LexicalAnalyzerClass::LexicalAnalyzerClass(const wxString& fileName)
	: ParserError()
	, UCharBufferedFile()
	, FileName()
	, Condition(yycINLINE_HTML) {
	OpenFile(fileName);
}

mvceditor::LexicalAnalyzerClass::LexicalAnalyzerClass()
	: ParserError()
	, UCharBufferedFile()
	, FileName()
	, Condition(yycINLINE_HTML) {
}

mvceditor::LexicalAnalyzerClass::~LexicalAnalyzerClass() {
}

bool mvceditor::LexicalAnalyzerClass::OpenFile(const wxString& newFile) {
	 FileName = newFile;
	 Condition = yycINLINE_HTML;

	// ATTN: fn_str() would not compile in MSW
	// what about unicode file names?
	return UCharBufferedFile.OpenFile(newFile.ToAscii());
}

bool mvceditor::LexicalAnalyzerClass::OpenString(const UnicodeString& code) {
	FileName = wxT("");
	Condition = yycSCRIPT;
	return UCharBufferedFile.OpenString(code);
}

int mvceditor::LexicalAnalyzerClass::NextToken() {
	return mvceditor::NextToken(UCharBufferedFile, Condition);
}

bool mvceditor::LexicalAnalyzerClass::GetLexeme(UnicodeString& lexeme) {
	// TODO: escape sequences \x41 to 'A' for double quoted strings and heredoc

	lexeme.remove();
	UChar *start = UCharBufferedFile.TokenStart;
	UChar *end =  UCharBufferedFile.Current;
	bool ret = false;
	bool isSingleQuoteString = false;
	bool isDoubleQuoteString = false;
	bool isHeredoc = false;
	bool isNowdoc = false;

	// be careful, take Limit into account too... we dont want to read past what is allowed
	if ((end - start) > 0 && UCharBufferedFile.Current < UCharBufferedFile.Limit) {

		// the lexer may ask for too much when the last token is an identifier
		// in this case current will point to past the null character
		if (UCharBufferedFile.Current > UCharBufferedFile.Limit) {
			end = UCharBufferedFile.Limit;
		}
		if (start[0] == '\'') {
			isSingleQuoteString = true;
			start++;
			end--;
		}
		if (start[0] == '"') {
			isDoubleQuoteString = true;
			start++;
			end--;
		}
		int len = (end - start);
		for (int i = 0; i < len; i++) {
			UChar c = start[i];
			UChar next = 0;
			if (i < (len - 1)) {
				next = start[i + 1];
			}
			if (isSingleQuoteString && c == '\\' && next == '\'') {

				// a literal single quote
				lexeme.append(next);
				i++;
			}
			else if (isSingleQuoteString && c == '\\' && next == '\\') {

				// a literal backslash
				lexeme.append(c);
				i++;
			}
			else if (isDoubleQuoteString && c == '\\' && next == '"') {

				// a literal double quote
				lexeme.append(next);
				i++;
			}
			else if (isDoubleQuoteString && c == '\\' && next == '\\') {

				// a literal backslash
				lexeme.append(c);
				i++;
			}
			else {

				// any other token (identifier, keyword, symbol) just goes in as is
				lexeme.append(c);
			}
		}
		if (lexeme.length() > 3 && start[0] == '<' && start[1] == '<' && start[2] == '<') {
			if (start[3] == '\'') {
				isNowdoc = true;
			}
			else {
				isHeredoc = true;
			}
		}
		if (isHeredoc || isNowdoc) {
			// remove the "<<<" and identifier from the heredoc start.
			// and the identifier from the end.  we don't need to actually check
			// for the identifier here; nextToken() already makes sure that the
			// beginning and ending identifiers are the same
			for (int i = 0; i < lexeme.length(); i++) {
				UChar c = lexeme.charAt(i);
				lexeme.remove(0, 1);
				i--;
				if (c == '\n' || c == '\r') {
					break;
				}
			}

			// take care of the final newline
			lexeme.trim();

			// take care of the newline and endind identifier
			for (int i = lexeme.length() - 1; i >= 0 && !lexeme.isEmpty(); i--) {
				UChar c = lexeme.charAt(i);
				lexeme.remove(i, 1);
				if (c == '\n' || c == '\r') {
						break;
				}
			}
		}
		ret = true;
	}
	return ret;
}

int mvceditor::LexicalAnalyzerClass::GetLineNumber() const {
	return UCharBufferedFile.GetLineNumber();
}

int mvceditor::LexicalAnalyzerClass::GetColumnNumber() const {
	return UCharBufferedFile.GetColumnNumber();
}

int mvceditor::LexicalAnalyzerClass::GetCharacterPosition() const {
	return UCharBufferedFile.GetCharacterPosition();
}

wxString mvceditor::LexicalAnalyzerClass::GetFileName() const {
	return FileName;
}<commit_msg>last change broke the tests; the change was not entirely correct<commit_after>/**
 * This software is released under the terms of the MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @copyright  2009-2011 Roberto Perpuly
 * @license    http://www.opensource.org/licenses/mit-license.php The MIT License
 */
#include <language/LexicalAnalyzerClass.h>
#include <language/Php53LexicalAnalyzerImpl.h>
#include <windows/StringHelperClass.h>
#include <unicode/uchar.h>
#include <unicode/ustring.h>
#include <unicode/ucnv.h>

mvceditor::LexicalAnalyzerClass::LexicalAnalyzerClass(const wxString& fileName)
	: ParserError()
	, UCharBufferedFile()
	, FileName()
	, Condition(yycINLINE_HTML) {
	OpenFile(fileName);
}

mvceditor::LexicalAnalyzerClass::LexicalAnalyzerClass()
	: ParserError()
	, UCharBufferedFile()
	, FileName()
	, Condition(yycINLINE_HTML) {
}

mvceditor::LexicalAnalyzerClass::~LexicalAnalyzerClass() {
}

bool mvceditor::LexicalAnalyzerClass::OpenFile(const wxString& newFile) {
	 FileName = newFile;
	 Condition = yycINLINE_HTML;

	// ATTN: fn_str() would not compile in MSW
	// what about unicode file names?
	return UCharBufferedFile.OpenFile(newFile.ToAscii());
}

bool mvceditor::LexicalAnalyzerClass::OpenString(const UnicodeString& code) {
	FileName = wxT("");
	Condition = yycSCRIPT;
	return UCharBufferedFile.OpenString(code);
}

int mvceditor::LexicalAnalyzerClass::NextToken() {
	return mvceditor::NextToken(UCharBufferedFile, Condition);
}

bool mvceditor::LexicalAnalyzerClass::GetLexeme(UnicodeString& lexeme) {
	// TODO: escape sequences \x41 to 'A' for double quoted strings and heredoc

	lexeme.remove();
	UChar *start = UCharBufferedFile.TokenStart;
	UChar *end =  UCharBufferedFile.Current;
	bool ret = false;
	bool isSingleQuoteString = false;
	bool isDoubleQuoteString = false;
	bool isHeredoc = false;
	bool isNowdoc = false;

	// be careful, take Limit into account too... we dont want to read past what is allowed
	if ((end - start) > 0 && UCharBufferedFile.Current <= UCharBufferedFile.Limit) {

		// the lexer may ask for too much when the last token is an identifier
		// in this case current will point to past the null character
		if (UCharBufferedFile.Current > UCharBufferedFile.Limit) {
			end = UCharBufferedFile.Limit;
		}
		if (start[0] == '\'') {
			isSingleQuoteString = true;
			start++;
			end--;
		}
		if (start[0] == '"') {
			isDoubleQuoteString = true;
			start++;
			end--;
		}
		int len = (end - start);
		for (int i = 0; i < len; i++) {
			UChar c = start[i];
			UChar next = 0;
			if (i < (len - 1)) {
				next = start[i + 1];
			}
			if (isSingleQuoteString && c == '\\' && next == '\'') {

				// a literal single quote
				lexeme.append(next);
				i++;
			}
			else if (isSingleQuoteString && c == '\\' && next == '\\') {

				// a literal backslash
				lexeme.append(c);
				i++;
			}
			else if (isDoubleQuoteString && c == '\\' && next == '"') {

				// a literal double quote
				lexeme.append(next);
				i++;
			}
			else if (isDoubleQuoteString && c == '\\' && next == '\\') {

				// a literal backslash
				lexeme.append(c);
				i++;
			}
			else {

				// any other token (identifier, keyword, symbol) just goes in as is
				lexeme.append(c);
			}
		}
		if (lexeme.length() > 3 && start[0] == '<' && start[1] == '<' && start[2] == '<') {
			if (start[3] == '\'') {
				isNowdoc = true;
			}
			else {
				isHeredoc = true;
			}
		}
		if (isHeredoc || isNowdoc) {
			// remove the "<<<" and identifier from the heredoc start.
			// and the identifier from the end.  we don't need to actually check
			// for the identifier here; nextToken() already makes sure that the
			// beginning and ending identifiers are the same
			for (int i = 0; i < lexeme.length(); i++) {
				UChar c = lexeme.charAt(i);
				lexeme.remove(0, 1);
				i--;
				if (c == '\n' || c == '\r') {
					break;
				}
			}

			// take care of the final newline
			lexeme.trim();

			// take care of the newline and endind identifier
			for (int i = lexeme.length() - 1; i >= 0 && !lexeme.isEmpty(); i--) {
				UChar c = lexeme.charAt(i);
				lexeme.remove(i, 1);
				if (c == '\n' || c == '\r') {
						break;
				}
			}
		}
		ret = true;
	}
	return ret;
}

int mvceditor::LexicalAnalyzerClass::GetLineNumber() const {
	return UCharBufferedFile.GetLineNumber();
}

int mvceditor::LexicalAnalyzerClass::GetColumnNumber() const {
	return UCharBufferedFile.GetColumnNumber();
}

int mvceditor::LexicalAnalyzerClass::GetCharacterPosition() const {
	return UCharBufferedFile.GetCharacterPosition();
}

wxString mvceditor::LexicalAnalyzerClass::GetFileName() const {
	return FileName;
}<|endoftext|>


<commit_before>#include <trainer/generated_game.hpp>
#include <trainer/tetris_rows_cleared.hpp>
#include <core/game_state_eval.hpp>
#include <core/harmony_search.hpp>
#include <core/harmony.hpp>
#include <cstdlib>
#include <sstream>
#include <fstream>
#include <omp.h>

int main(int argc, char* argv[]) {
    srand(time(NULL));
    int threadCount = 1;
    std::ifstream in;
    bool initHarmonies = false;
    if (argc == 2) {
        std::istringstream(argv[1]) >> threadCount;
    }
    else if (argc == 3) {
        std::istringstream(argv[1]) >> threadCount;
        in.open(argv[2]);
        initHarmonies = in.is_open();
    }

    int const varCount = GetVarCount();
    HarmonyRanges const* ranges = GetRanges();
    int const memorySize = 5;
    float const r_accept = 0.95;
    float const r_pa = 0.99;
    float const r_range = 0.1;
    HarmonyFactory const factory (varCount, *ranges);

    std::vector<Harmony> init;
    if (initHarmonies) {
        for (int i = 0; i < memorySize; ++i) {
            Harmony h;
            in.ignore(1);
            for (int i = 0; i < varCount; ++i) {
                float weight = 0.0;
                in >> weight;
                h.push_back(weight);
                in.ignore(1);
            }
            in.ignore(1);
            in.ignore(1);
            init.push_back(h);
        }
    }
    in.close();

    int const gameLength = 100;
    int const iterationCount = 3;
    std::cout << "Launching " << threadCount << " trainers for ";
    std::cout << iterationCount << " iterations." << std::endl;

    #pragma omp parallel for num_threads(threadCount)
    for (int t = 0; t < threadCount; ++t) {
        GeneratedGame generator (gameLength);
        TetrisRowsCleared f (generator);
        HarmonyCompareMax maxComp (f);
        HarmonyCompareWrapper comp (maxComp);
        HarmonySearch search (comp, factory, varCount, memorySize,
                                r_accept, r_pa, r_range);
        if (initHarmonies)
            search.InitializeHarmonies(init);

        for (int i = 0; i < iterationCount; ++i) {
            search.Iterate();
            generator.GenerateNewGame();
            search.EraseHarmonyCaches();
            #pragma omp master
            {
                std::cout << "COMPLETED ITERATION " << i << std::endl;
            }
        }

        std::vector<Harmony const*> memory;
        for (int i = 0; i < memorySize; ++i)
            memory.push_back(search.GetRanked(i));
        #pragma omp critical
        {
            std::stringstream ss;
            std::ofstream out ("results.txt", std::ios::app);
            ss << "Trainer " << t << " results:" << std::endl;
            for (int i = 0; i < memorySize; ++i)
                ss << *(memory.at(i)) << std::endl;
            ss << std::endl;
            out << ss.str();
            out.close();
        }
        for (int i = 0; i < memorySize; ++i)
            delete memory.at(i);
    }
    delete ranges;

    std::cout << "Completed!" << std::endl;

    return 0;
}

<commit_msg>Reset to non-debug iteration counts<commit_after>#include <trainer/generated_game.hpp>
#include <trainer/tetris_rows_cleared.hpp>
#include <core/game_state_eval.hpp>
#include <core/harmony_search.hpp>
#include <core/harmony.hpp>
#include <cstdlib>
#include <sstream>
#include <fstream>
#include <omp.h>

int main(int argc, char* argv[]) {
    srand(time(NULL));
    int threadCount = 1;
    std::ifstream in;
    bool initHarmonies = false;
    if (argc == 2) {
        std::istringstream(argv[1]) >> threadCount;
    }
    else if (argc == 3) {
        std::istringstream(argv[1]) >> threadCount;
        in.open(argv[2]);
        initHarmonies = in.is_open();
    }

    int const varCount = GetVarCount();
    HarmonyRanges const* ranges = GetRanges();
    int const memorySize = 5;
    float const r_accept = 0.95;
    float const r_pa = 0.99;
    float const r_range = 0.1;
    HarmonyFactory const factory (varCount, *ranges);

    std::vector<Harmony> init;
    if (initHarmonies) {
        for (int i = 0; i < memorySize; ++i) {
            Harmony h;
            in.ignore(1);
            for (int i = 0; i < varCount; ++i) {
                float weight = 0.0;
                in >> weight;
                h.push_back(weight);
                in.ignore(1);
            }
            in.ignore(1);
            in.ignore(1);
            init.push_back(h);
        }
    }
    in.close();

    int const gameLength = 1000;
    int const iterationCount = 1000;
    std::cout << "Launching " << threadCount << " trainers for ";
    std::cout << iterationCount << " iterations." << std::endl;

    #pragma omp parallel for num_threads(threadCount)
    for (int t = 0; t < threadCount; ++t) {
        GeneratedGame generator (gameLength);
        TetrisRowsCleared f (generator);
        HarmonyCompareMax maxComp (f);
        HarmonyCompareWrapper comp (maxComp);
        HarmonySearch search (comp, factory, varCount, memorySize,
                                r_accept, r_pa, r_range);
        if (initHarmonies)
            search.InitializeHarmonies(init);

        for (int i = 0; i < iterationCount; ++i) {
            search.Iterate();
            generator.GenerateNewGame();
            search.EraseHarmonyCaches();
            #pragma omp master
            {
                std::cout << "COMPLETED ITERATION " << i << std::endl;
            }
        }

        std::vector<Harmony const*> memory;
        for (int i = 0; i < memorySize; ++i)
            memory.push_back(search.GetRanked(i));
        #pragma omp critical
        {
            std::stringstream ss;
            std::ofstream out ("results.txt", std::ios::app);
            ss << "Trainer " << t << " results:" << std::endl;
            for (int i = 0; i < memorySize; ++i)
                ss << *(memory.at(i)) << std::endl;
            ss << std::endl;
            out << ss.str();
            out.close();
        }
        for (int i = 0; i < memorySize; ++i)
            delete memory.at(i);
    }
    delete ranges;

    std::cout << "Completed!" << std::endl;

    return 0;
}

<|endoftext|>


<commit_before>// Copyright 2004-present Facebook. All Rights Reserved.

#include "rsocket/RSocketResponder.h"

#include <folly/io/async/EventBase.h>

namespace rsocket {

std::shared_ptr<yarpl::single::Single<rsocket::Payload>>
RSocketResponder::handleRequestResponse(rsocket::Payload, rsocket::StreamId) {
  return yarpl::single::Singles::error<rsocket::Payload>(
      std::logic_error("handleRequestResponse not implemented"));
}

std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>>
RSocketResponder::handleRequestStream(rsocket::Payload, rsocket::StreamId) {
  return yarpl::flowable::Flowable<rsocket::Payload>::error(
      std::logic_error("handleRequestStream not implemented"));
}

std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>>
RSocketResponder::handleRequestChannel(
    rsocket::Payload,
    std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>>,
    rsocket::StreamId) {
  return yarpl::flowable::Flowable<rsocket::Payload>::error(
      std::logic_error("handleRequestChannel not implemented"));
}

void RSocketResponder::handleFireAndForget(
    rsocket::Payload,
    rsocket::StreamId) {
  // No default implementation, no error response to provide.
}

void RSocketResponder::handleMetadataPush(std::unique_ptr<folly::IOBuf>) {
  // No default implementation, no error response to provide.
}

/// Handles a new Channel requested by the other end.
std::shared_ptr<yarpl::flowable::Subscriber<Payload>>
RSocketResponder::handleRequestChannelCore(
    Payload request,
    StreamId streamId,
    const std::shared_ptr<yarpl::flowable::Subscriber<Payload>>&
        response) noexcept {
  class EagerSubscriberBridge
      : public yarpl::flowable::Subscriber<rsocket::Payload> {
   public:
    void onSubscribe(std::shared_ptr<yarpl::flowable::Subscription>
                         subscription) noexcept override {
      CHECK(!subscription_);
      subscription_ = std::move(subscription);
      if (inner_) {
        inner_->onSubscribe(subscription_);
      }
    }

    void onNext(rsocket::Payload element) noexcept override {
      DCHECK(inner_);
      inner_->onNext(std::move(element));
    }

    void onComplete() noexcept override {
      DCHECK(inner_);
      if (auto inner = std::move(inner_)) {
        inner->onComplete();
        subscription_.reset();
      } else {
        completed_ = true;
      }
    }

    void onError(folly::exception_wrapper ex) noexcept override {
      VLOG(3) << "handleRequestChannelCore::onError: " << ex.what();
      if (auto inner = std::move(inner_)) {
        inner->onError(std::move(ex));
        subscription_.reset();
      } else {
        error_ = std::move(ex);
      }
    }

    void subscribe(
        std::shared_ptr<yarpl::flowable::Subscriber<rsocket::Payload>> inner) {
      CHECK(!inner_); // only one call to subscribe is supported
      CHECK(inner);

      inner_ = std::move(inner);
      if (subscription_) {
        inner_->onSubscribe(subscription_);
        // it's possible to get an error or completion before subscribe happens,
        // delay sending it but send it when this class gets subscribed
        if (completed_) {
          onComplete();
        } else if (error_) {
          onError(std::move(error_));
        }
      }
    }

   private:
    std::shared_ptr<yarpl::flowable::Subscriber<rsocket::Payload>> inner_;
    std::shared_ptr<yarpl::flowable::Subscription> subscription_;
    folly::exception_wrapper error_;
    bool completed_{false};
  };

  auto eagerSubscriber = std::make_shared<EagerSubscriberBridge>();
  auto flowable = handleRequestChannel(
      std::move(request),
      yarpl::flowable::internal::flowableFromSubscriber<rsocket::Payload>(
          [eagerSubscriber](
              std::shared_ptr<yarpl::flowable::Subscriber<Payload>>
                  subscriber) { eagerSubscriber->subscribe(subscriber); }),
      std::move(streamId));
  // bridge from the existing eager RequestHandler and old Subscriber type
  // to the lazy Flowable and new Subscriber type
  flowable->subscribe(std::move(response));
  return eagerSubscriber;
}

/// Handles a new Stream requested by the other end.
void RSocketResponder::handleRequestStreamCore(
    Payload request,
    StreamId streamId,
    const std::shared_ptr<yarpl::flowable::Subscriber<Payload>>&
        response) noexcept {
  auto flowable = handleRequestStream(std::move(request), std::move(streamId));
  flowable->subscribe(std::move(response));
}

/// Handles a new inbound RequestResponse requested by the other end.
void RSocketResponder::handleRequestResponseCore(
    Payload request,
    StreamId streamId,
    const std::shared_ptr<yarpl::single::SingleObserver<Payload>>&
        responseObserver) noexcept {
  auto single = handleRequestResponse(std::move(request), streamId);
  single->subscribe(std::move(responseObserver));
}
} // namespace rsocket
<commit_msg>Fix RSocketResponder's EagerSubscriberBridge crashing on early onComplete()<commit_after>// Copyright 2004-present Facebook. All Rights Reserved.

#include "rsocket/RSocketResponder.h"

#include <folly/io/async/EventBase.h>

namespace rsocket {

std::shared_ptr<yarpl::single::Single<rsocket::Payload>>
RSocketResponder::handleRequestResponse(rsocket::Payload, rsocket::StreamId) {
  return yarpl::single::Singles::error<rsocket::Payload>(
      std::logic_error("handleRequestResponse not implemented"));
}

std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>>
RSocketResponder::handleRequestStream(rsocket::Payload, rsocket::StreamId) {
  return yarpl::flowable::Flowable<rsocket::Payload>::error(
      std::logic_error("handleRequestStream not implemented"));
}

std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>>
RSocketResponder::handleRequestChannel(
    rsocket::Payload,
    std::shared_ptr<yarpl::flowable::Flowable<rsocket::Payload>>,
    rsocket::StreamId) {
  return yarpl::flowable::Flowable<rsocket::Payload>::error(
      std::logic_error("handleRequestChannel not implemented"));
}

void RSocketResponder::handleFireAndForget(
    rsocket::Payload,
    rsocket::StreamId) {
  // No default implementation, no error response to provide.
}

void RSocketResponder::handleMetadataPush(std::unique_ptr<folly::IOBuf>) {
  // No default implementation, no error response to provide.
}

/// Handles a new Channel requested by the other end.
std::shared_ptr<yarpl::flowable::Subscriber<Payload>>
RSocketResponder::handleRequestChannelCore(
    Payload request,
    StreamId streamId,
    const std::shared_ptr<yarpl::flowable::Subscriber<Payload>>&
        response) noexcept {
  class EagerSubscriberBridge
      : public yarpl::flowable::Subscriber<rsocket::Payload> {
   public:
    void onSubscribe(std::shared_ptr<yarpl::flowable::Subscription>
                         subscription) noexcept override {
      CHECK(!subscription_);
      subscription_ = std::move(subscription);
      if (inner_) {
        inner_->onSubscribe(subscription_);
      }
    }

    void onNext(rsocket::Payload element) noexcept override {
      DCHECK(inner_);
      inner_->onNext(std::move(element));
    }

    void onComplete() noexcept override {
      if (auto inner = std::move(inner_)) {
        inner->onComplete();
        subscription_.reset();
      } else {
        completed_ = true;
      }
    }

    void onError(folly::exception_wrapper ex) noexcept override {
      VLOG(3) << "handleRequestChannelCore::onError: " << ex.what();
      if (auto inner = std::move(inner_)) {
        inner->onError(std::move(ex));
        subscription_.reset();
      } else {
        error_ = std::move(ex);
      }
    }

    void subscribe(
        std::shared_ptr<yarpl::flowable::Subscriber<rsocket::Payload>> inner) {
      CHECK(!inner_); // only one call to subscribe is supported
      CHECK(inner);

      inner_ = std::move(inner);
      if (subscription_) {
        inner_->onSubscribe(subscription_);
        // it's possible to get an error or completion before subscribe happens,
        // delay sending it but send it when this class gets subscribed
        if (completed_) {
          onComplete();
        } else if (error_) {
          onError(std::move(error_));
        }
      }
    }

   private:
    std::shared_ptr<yarpl::flowable::Subscriber<rsocket::Payload>> inner_;
    std::shared_ptr<yarpl::flowable::Subscription> subscription_;
    folly::exception_wrapper error_;
    bool completed_{false};
  };

  auto eagerSubscriber = std::make_shared<EagerSubscriberBridge>();
  auto flowable = handleRequestChannel(
      std::move(request),
      yarpl::flowable::internal::flowableFromSubscriber<rsocket::Payload>(
          [eagerSubscriber](
              std::shared_ptr<yarpl::flowable::Subscriber<Payload>>
                  subscriber) { eagerSubscriber->subscribe(subscriber); }),
      std::move(streamId));
  // bridge from the existing eager RequestHandler and old Subscriber type
  // to the lazy Flowable and new Subscriber type
  flowable->subscribe(std::move(response));
  return eagerSubscriber;
}

/// Handles a new Stream requested by the other end.
void RSocketResponder::handleRequestStreamCore(
    Payload request,
    StreamId streamId,
    const std::shared_ptr<yarpl::flowable::Subscriber<Payload>>&
        response) noexcept {
  auto flowable = handleRequestStream(std::move(request), std::move(streamId));
  flowable->subscribe(std::move(response));
}

/// Handles a new inbound RequestResponse requested by the other end.
void RSocketResponder::handleRequestResponseCore(
    Payload request,
    StreamId streamId,
    const std::shared_ptr<yarpl::single::SingleObserver<Payload>>&
        responseObserver) noexcept {
  auto single = handleRequestResponse(std::move(request), streamId);
  single->subscribe(std::move(responseObserver));
}
} // namespace rsocket
<|endoftext|>


<commit_before>/********** tell emacs we use -*- c++ -*- style comments *******************
 $Revision: 1.10 $  $Author: trey $  $Date: 2006-10-18 18:05:56 $

 @file    zmdpBenchmark.cc
 @brief   No brief

 Copyright (c) 2002-2006, Trey Smith.  All rights reserved.

 Licensed under the Apache License, Version 2.0 (the "License"); you may
 not use this file except in compliance with the License.  You may
 obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 implied.  See the License for the specific language governing
 permissions and limitations under the License.

 ***************************************************************************/

#include <assert.h>
#include <sys/time.h>
#include <getopt.h>

#include <iostream>
#include <fstream>

#include "solverUtils.h"
#include "TestDriver.h"
#include "zmdpMainConfig.h"

using namespace std;
using namespace MatrixUtils;
using namespace zmdp;

void doBenchmark(const ZMDPConfig& config, SolverParams& p)
{
  init_matrix_utils();

  printf("reading input files\n");
  SolverObjects so;
  constructSolverObjects(so, p, config);

  if (NULL != p.policyOutputFile) {
    if (!so.bounds->getSupportsPolicyOutput()) {
      cerr << "ERROR: -o specified, but with selected options, policy output is not supported:" << endl;
      cerr << "  in order to enable policy output, problem must be a POMDP; if" << endl;
      cout << "  it is, try adding the '-v convex' and '--lower-bound' options" << endl;
      exit(EXIT_FAILURE);
    }
  }

  TestDriver x;
  x.batchTestIncremental(config,
			 /* numIterations = */ p.evaluationTrialsPerEpoch,
			 so,
			 /* numSteps = */ p.evaluationMaxStepsPerTrial,
			 /* targetPrecision = */ p.terminateRegretBound,
			 /* minOrder = */ p.evaluationFirstEpochWallclockSeconds,
			 /* maxOrder = */ p.terminateWallclockSeconds,
			 /* ticksPerOrder = */ p.evaluationEpochsPerMagnitude,
			 /* incPlotFileName = */ p.evaluationOutputFile,
			 /* boundsFileName = */ p.boundsOutputFile,
			 /* simFileName = */ p.simulationTraceOutputFile,
			 /* policyOutputFile = */ p.policyOutputFile);

  MDPNodeHashLogger::logToFile("nodes.plot", so.bounds->lookup);

  x.printRewards();
}

void usage(const char* cmdName) {
  cerr <<
    "usage: " << cmdName << " OPTIONS <model>\n"
    "  -h or --help           Print this help\n"
    "  --version              Print version information (CFLAGS used at compilation)\n"
    "  -c or --config <file>  Specify a config file to read options from\n"
    "  --genConfig <file>     Generate an example config file and exit\n"
    "\n"
    "  zmdpBenchmark is the advanced front-end to the ZMDP library.  While running a\n"
    "  solution algorithm, it keeps a number of log files that can be used (along\n"
    "  with scripts in the src/tools directory) to plot performance.\n"
    "\n"
    "  ZMDP gets configuration information from three places: (1) Default values\n"
    "  are embedded in the binary at compile time.  (2) If you specify a config file\n"
    "  using the --config option, any fields set in that file override the defaults.\n"
    "  (3) You can override individual fields in the config file from the command line.\n"
    "  For instance, if the config file specifies 'searchStrategy frtdp' you can override\n"
    "  with '--searchStrategy hsvi' at the command line.\n"
    "\n"
    "  To generate an example config file (including default values and comments describing\n"
    "  all the parameters), use the '--genConfig <file>' option.\n"
    "\n"
    "For convenience, there are also some abbreviations:\n"
    "  -s = --searchStrategy\n"
    "  -t = --modelType\n"
    "  -v = --valueFunctionRepresentation\n"
    "  -o = --policyOutputFile\n"
    "  -f = --useFastPomdpParser 1\n"
    "  -p = --terminateRegretBound\n"
    "  -i = --evaluationTrialsPerEpoch\n"
    "\n"
    "Examples:\n"
    "  " << cmdName << " RockSample_4_4.pomdp\n"
    "  " << cmdName << " large-b.racetrack\n"
    "  " << cmdName << " -s lrtdp -v point RockSample_4_4.pomdp\n"
    "  " << cmdName << " -f RockSample_5_7.pomdp\n"
;
  exit(-1);
}

int main(int argc, char **argv) {
  SolverParams p;

  // save arguments for debug printout later
  ostringstream outs;
  for (int i=1; i < argc; i++) {
    outs << argv[i] << " ";
  }

  bool argsOnly = false;
  const char* configFileName = NULL;
  ZMDPConfig commandLineConfig;

  p.cmdName = argv[0];
  for (int argi=1; argi < argc; argi++) {
    std::string args = argv[argi];
    if (!argsOnly && '-' == args[0]) {
      if (args == "--") {
	argsOnly = true;
      } else if (args == "-h" || args == "--help") {
	usage(argv[0]);
      } else if (args == "--version") {
	cout << "CFLAGS = " << CFLAGS << endl;
	exit(EXIT_SUCCESS);
      } else if (args == "-c" || args == "--config") {
	if (++argi == argc) {
	  fprintf(stderr, "ERROR: found -c option without argument (use -h for help)\n");
	  exit(EXIT_FAILURE);
	}
	configFileName = argv[argi];
      } else if (args == "--genConfig") {
	if (++argi == argc) {
	  fprintf(stderr, "ERROR: found --genConfig option without argument (use -h for help)\n");
	  exit(EXIT_FAILURE);
	}
	embedWriteToFile(argv[argi], defaultConfig);
	printf("wrote config file with default settings to %s\n", argv[argi]);
	exit(EXIT_SUCCESS);
      } else {
	// replace abbreviations
	if (args == "-s") {
	  args = "--searchStrategy";
	} else if (args == "-t") {
	  args = "--modelType";
	} else if (args == "-v") {
	  args = "--valueFunctionRepresentation";
	} else if (args == "-f") {
	  commandLineConfig.setBool("useFastPomdpParser", true);
	  continue;
	} else if (args == "-p") {
	  args = "--terminateRegretBound";
	} else if (args == "-o") {
	  args = "--policyOutputFile";
	} else if (args == "-i") {
	  args = "--evaluationTrialsPerEpoch";
	}

	if (args.find("--") != 0) {
	  fprintf(stderr, "ERROR: found unknown option '%s' (use -h for help)\n",
		  args.c_str());
	  exit(EXIT_FAILURE);
	} else {
	  if (++argi == argc) {
	    fprintf(stderr, "ERROR: found %s option without argument (-h for help)\n",
		    args.c_str());
	    exit(EXIT_FAILURE);
	  }
	  commandLineConfig.setString(args.substr(2), argv[argi]);
	}
      }
    } else {
      if (NULL == p.probName) {
	p.probName = argv[argi];
      } else {
	fprintf(stderr, "ERROR: expected exactly 1 argument (use -h for help)\n");
	exit(EXIT_FAILURE);
      }
    }
  }
  if (NULL == p.probName) {
    fprintf(stderr, "ERROR: expected exactly 1 argument (use -h for help)\n");
    exit(EXIT_FAILURE);
  }

  // config step 1: read defaults embedded in binary
  ZMDPConfig config;
  config.readFromString("<defaultConfig>", defaultConfig.data);

  // config step 2: overwrite defaults with values specified in config file
  // (signal an error if any new unexpected fields are defined)
  config.setOverWriteOnlyMode(true);
  if (NULL != configFileName) {
    config.readFromFile(configFileName);
  }

  // config step 3: overwrite with values specified on command line
  config.readFromConfig(commandLineConfig);

  // default value of policyOutputFile depends on the front end used;
  // for zmdpBenchmark, it is "none"
  if (config.getString("policyOutputFile") == "-") {
    config.setString("policyOutputFile", "none");
  }

  printf("CFLAGS = %s\n", CFLAGS);
  printf("ARGS = %s\n", outs.str().c_str());
  fflush(stdout);

  p.setValues(config);
  doBenchmark(config, p);

  // signal we are done -- an external batch process that runs zmdpBenchmark
  // can check for completion by polling for existence of this file
  // (which may be easier that using fork()/wait(), depending on the
  // implementation)
  FILE *fp = fopen("/tmp/zmdpBenchmark_done", "w");
  fprintf(fp, "success\n");
  fclose(fp);

  return 0;
}

/***************************************************************************
 * REVISION HISTORY:
 * $Log: not supported by cvs2svn $
 * Revision 1.9  2006/10/16 17:32:41  trey
 * bug fixes related to new config system
 *
 * Revision 1.8  2006/10/16 05:50:11  trey
 * switched zmdpBenchmark to use new config mechanism
 *
 * Revision 1.7  2006/10/03 03:17:26  trey
 * added --max-horizon parameter
 *
 * Revision 1.6  2006/07/26 20:52:57  trey
 * added debug output
 *
 * Revision 1.5  2006/06/15 16:10:57  trey
 * restructured so zmdpBenchmark can output policies
 *
 * Revision 1.4  2006/04/28 17:57:41  trey
 * changed to use apache license
 *
 * Revision 1.3  2006/04/27 23:19:03  trey
 * renamed Interleave -> TestDriver
 *
 * Revision 1.2  2006/04/27 23:08:18  trey
 * reordered getopt data structure to match usage()
 *
 * Revision 1.1  2006/04/27 20:19:22  trey
 * refactored command-line interface code, renamed solveMDP to zmdpBenchmark
 *
 * Revision 1.4  2006/04/12 19:23:22  trey
 * added wrtdp support and extra error checking for parameters
 *
 * Revision 1.3  2006/04/10 20:27:05  trey
 * added --lower-bound and --upper-bound args
 *
 * Revision 1.2  2006/04/07 20:15:40  trey
 * solveMDP now uses a strong heuristic by default; improved usage() help
 *
 * Revision 1.1  2006/04/07 19:40:41  trey
 * switched back to a unified binary for all algorithms, turns out it cuts down on code maintenance
 *
 *
 ***************************************************************************/
<commit_msg>removed reference to obsolete MDPNodeHashLogger<commit_after>/********** tell emacs we use -*- c++ -*- style comments *******************
 $Revision: 1.11 $  $Author: trey $  $Date: 2006-10-19 19:33:41 $

 @file    zmdpBenchmark.cc
 @brief   No brief

 Copyright (c) 2002-2006, Trey Smith.  All rights reserved.

 Licensed under the Apache License, Version 2.0 (the "License"); you may
 not use this file except in compliance with the License.  You may
 obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 implied.  See the License for the specific language governing
 permissions and limitations under the License.

 ***************************************************************************/

#include <assert.h>
#include <sys/time.h>
#include <getopt.h>

#include <iostream>
#include <fstream>

#include "solverUtils.h"
#include "TestDriver.h"
#include "zmdpMainConfig.h"

using namespace std;
using namespace MatrixUtils;
using namespace zmdp;

void doBenchmark(const ZMDPConfig& config, SolverParams& p)
{
  init_matrix_utils();

  printf("reading input files\n");
  SolverObjects so;
  constructSolverObjects(so, p, config);

  if (NULL != p.policyOutputFile) {
    if (!so.bounds->getSupportsPolicyOutput()) {
      cerr << "ERROR: -o specified, but with selected options, policy output is not supported:" << endl;
      cerr << "  in order to enable policy output, problem must be a POMDP; if" << endl;
      cout << "  it is, try adding the '-v convex' and '--lower-bound' options" << endl;
      exit(EXIT_FAILURE);
    }
  }

  TestDriver x;
  x.batchTestIncremental(config,
			 /* numIterations = */ p.evaluationTrialsPerEpoch,
			 so,
			 /* numSteps = */ p.evaluationMaxStepsPerTrial,
			 /* targetPrecision = */ p.terminateRegretBound,
			 /* minOrder = */ p.evaluationFirstEpochWallclockSeconds,
			 /* maxOrder = */ p.terminateWallclockSeconds,
			 /* ticksPerOrder = */ p.evaluationEpochsPerMagnitude,
			 /* incPlotFileName = */ p.evaluationOutputFile,
			 /* boundsFileName = */ p.boundsOutputFile,
			 /* simFileName = */ p.simulationTraceOutputFile,
			 /* policyOutputFile = */ p.policyOutputFile);

  x.printRewards();
}

void usage(const char* cmdName) {
  cerr <<
    "usage: " << cmdName << " OPTIONS <model>\n"
    "  -h or --help           Print this help\n"
    "  --version              Print version information (CFLAGS used at compilation)\n"
    "  -c or --config <file>  Specify a config file to read options from\n"
    "  --genConfig <file>     Generate an example config file and exit\n"
    "\n"
    "  zmdpBenchmark is the advanced front-end to the ZMDP library.  While running a\n"
    "  solution algorithm, it keeps a number of log files that can be used (along\n"
    "  with scripts in the src/tools directory) to plot performance.\n"
    "\n"
    "  ZMDP gets configuration information from three places: (1) Default values\n"
    "  are embedded in the binary at compile time.  (2) If you specify a config file\n"
    "  using the --config option, any fields set in that file override the defaults.\n"
    "  (3) You can override individual fields in the config file from the command line.\n"
    "  For instance, if the config file specifies 'searchStrategy frtdp' you can override\n"
    "  with '--searchStrategy hsvi' at the command line.\n"
    "\n"
    "  To generate an example config file (including default values and comments describing\n"
    "  all the parameters), use the '--genConfig <file>' option.\n"
    "\n"
    "For convenience, there are also some abbreviations:\n"
    "  -s = --searchStrategy\n"
    "  -t = --modelType\n"
    "  -v = --valueFunctionRepresentation\n"
    "  -o = --policyOutputFile\n"
    "  -f = --useFastPomdpParser 1\n"
    "  -p = --terminateRegretBound\n"
    "  -i = --evaluationTrialsPerEpoch\n"
    "\n"
    "Examples:\n"
    "  " << cmdName << " RockSample_4_4.pomdp\n"
    "  " << cmdName << " large-b.racetrack\n"
    "  " << cmdName << " -s lrtdp -v point RockSample_4_4.pomdp\n"
    "  " << cmdName << " -f RockSample_5_7.pomdp\n"
;
  exit(-1);
}

int main(int argc, char **argv) {
  SolverParams p;

  // save arguments for debug printout later
  ostringstream outs;
  for (int i=1; i < argc; i++) {
    outs << argv[i] << " ";
  }

  bool argsOnly = false;
  const char* configFileName = NULL;
  ZMDPConfig commandLineConfig;

  p.cmdName = argv[0];
  for (int argi=1; argi < argc; argi++) {
    std::string args = argv[argi];
    if (!argsOnly && '-' == args[0]) {
      if (args == "--") {
	argsOnly = true;
      } else if (args == "-h" || args == "--help") {
	usage(argv[0]);
      } else if (args == "--version") {
	cout << "CFLAGS = " << CFLAGS << endl;
	exit(EXIT_SUCCESS);
      } else if (args == "-c" || args == "--config") {
	if (++argi == argc) {
	  fprintf(stderr, "ERROR: found -c option without argument (use -h for help)\n");
	  exit(EXIT_FAILURE);
	}
	configFileName = argv[argi];
      } else if (args == "--genConfig") {
	if (++argi == argc) {
	  fprintf(stderr, "ERROR: found --genConfig option without argument (use -h for help)\n");
	  exit(EXIT_FAILURE);
	}
	embedWriteToFile(argv[argi], defaultConfig);
	printf("wrote config file with default settings to %s\n", argv[argi]);
	exit(EXIT_SUCCESS);
      } else {
	// replace abbreviations
	if (args == "-s") {
	  args = "--searchStrategy";
	} else if (args == "-t") {
	  args = "--modelType";
	} else if (args == "-v") {
	  args = "--valueFunctionRepresentation";
	} else if (args == "-f") {
	  commandLineConfig.setBool("useFastPomdpParser", true);
	  continue;
	} else if (args == "-p") {
	  args = "--terminateRegretBound";
	} else if (args == "-o") {
	  args = "--policyOutputFile";
	} else if (args == "-i") {
	  args = "--evaluationTrialsPerEpoch";
	}

	if (args.find("--") != 0) {
	  fprintf(stderr, "ERROR: found unknown option '%s' (use -h for help)\n",
		  args.c_str());
	  exit(EXIT_FAILURE);
	} else {
	  if (++argi == argc) {
	    fprintf(stderr, "ERROR: found %s option without argument (-h for help)\n",
		    args.c_str());
	    exit(EXIT_FAILURE);
	  }
	  commandLineConfig.setString(args.substr(2), argv[argi]);
	}
      }
    } else {
      if (NULL == p.probName) {
	p.probName = argv[argi];
      } else {
	fprintf(stderr, "ERROR: expected exactly 1 argument (use -h for help)\n");
	exit(EXIT_FAILURE);
      }
    }
  }
  if (NULL == p.probName) {
    fprintf(stderr, "ERROR: expected exactly 1 argument (use -h for help)\n");
    exit(EXIT_FAILURE);
  }

  // config step 1: read defaults embedded in binary
  ZMDPConfig config;
  config.readFromString("<defaultConfig>", defaultConfig.data);

  // config step 2: overwrite defaults with values specified in config file
  // (signal an error if any new unexpected fields are defined)
  config.setOverWriteOnlyMode(true);
  if (NULL != configFileName) {
    config.readFromFile(configFileName);
  }

  // config step 3: overwrite with values specified on command line
  config.readFromConfig(commandLineConfig);

  // default value of policyOutputFile depends on the front end used;
  // for zmdpBenchmark, it is "none"
  if (config.getString("policyOutputFile") == "-") {
    config.setString("policyOutputFile", "none");
  }

  printf("CFLAGS = %s\n", CFLAGS);
  printf("ARGS = %s\n", outs.str().c_str());
  fflush(stdout);

  p.setValues(config);
  doBenchmark(config, p);

  // signal we are done -- an external batch process that runs zmdpBenchmark
  // can check for completion by polling for existence of this file
  // (which may be easier that using fork()/wait(), depending on the
  // implementation)
  FILE *fp = fopen("/tmp/zmdpBenchmark_done", "w");
  fprintf(fp, "success\n");
  fclose(fp);

  return 0;
}

/***************************************************************************
 * REVISION HISTORY:
 * $Log: not supported by cvs2svn $
 * Revision 1.10  2006/10/18 18:05:56  trey
 * now propagating config data structure to lower levels so config fields can be used to control more parts of the system
 *
 * Revision 1.9  2006/10/16 17:32:41  trey
 * bug fixes related to new config system
 *
 * Revision 1.8  2006/10/16 05:50:11  trey
 * switched zmdpBenchmark to use new config mechanism
 *
 * Revision 1.7  2006/10/03 03:17:26  trey
 * added --max-horizon parameter
 *
 * Revision 1.6  2006/07/26 20:52:57  trey
 * added debug output
 *
 * Revision 1.5  2006/06/15 16:10:57  trey
 * restructured so zmdpBenchmark can output policies
 *
 * Revision 1.4  2006/04/28 17:57:41  trey
 * changed to use apache license
 *
 * Revision 1.3  2006/04/27 23:19:03  trey
 * renamed Interleave -> TestDriver
 *
 * Revision 1.2  2006/04/27 23:08:18  trey
 * reordered getopt data structure to match usage()
 *
 * Revision 1.1  2006/04/27 20:19:22  trey
 * refactored command-line interface code, renamed solveMDP to zmdpBenchmark
 *
 * Revision 1.4  2006/04/12 19:23:22  trey
 * added wrtdp support and extra error checking for parameters
 *
 * Revision 1.3  2006/04/10 20:27:05  trey
 * added --lower-bound and --upper-bound args
 *
 * Revision 1.2  2006/04/07 20:15:40  trey
 * solveMDP now uses a strong heuristic by default; improved usage() help
 *
 * Revision 1.1  2006/04/07 19:40:41  trey
 * switched back to a unified binary for all algorithms, turns out it cuts down on code maintenance
 *
 *
 ***************************************************************************/
<|endoftext|>


<commit_before><commit_msg>Allow animations in select screen.<commit_after><|endoftext|>


<commit_before>#pragma once

#include "core/serialization/serialization.h"
#include "core/serialization/cereal/types/vector.hpp"
#include "core/reflection/reflection.h"
#include "../../rendering/model.h"
#include "../assets/asset_handle.hpp"
REFLECT(Model)
{
	rttr::registration::class_<Model>("Model")
		.property("Levels of Detail",
			&Model::get_lods,
			&Model::set_lods)
		.property("Materials",
			&Model::get_materials,
			&Model::set_materials)
		.property("Transition Time",
			&Model::get_lod_transition_time,
			&Model::set_lod_transition_time)
		.property("Max Distance",
			&Model::get_lod_max_distance,
			&Model::set_lod_max_distance)
		.property("Mset_lod_in Distance",
			&Model::get_lod_min_distance,
			&Model::set_lod_min_distance)
		;
}

SAVE(Model)
{
	ar(
		cereal::make_nvp("lods", obj._mesh_lods),
		cereal::make_nvp("materials", obj._materials),
		cereal::make_nvp("transition_time", obj._transition_time),
		cereal::make_nvp("max_distance", obj._max_distance),
		cereal::make_nvp("min_distance", obj._min_distance)
	);

}

LOAD(Model)
{
	ar(
		cereal::make_nvp("lods", obj._mesh_lods),
		cereal::make_nvp("materials", obj._materials),
		cereal::make_nvp("transition_time", obj._transition_time),
		cereal::make_nvp("max_distance", obj._max_distance),
		cereal::make_nvp("min_distance", obj._min_distance)
	);
}<commit_msg>fixed typo<commit_after>#pragma once

#include "core/serialization/serialization.h"
#include "core/serialization/cereal/types/vector.hpp"
#include "core/reflection/reflection.h"
#include "../../rendering/model.h"
#include "../assets/asset_handle.hpp"
REFLECT(Model)
{
	rttr::registration::class_<Model>("Model")
		.property("Levels of Detail",
			&Model::get_lods,
			&Model::set_lods)
		.property("Materials",
			&Model::get_materials,
			&Model::set_materials)
		.property("Transition Time",
			&Model::get_lod_transition_time,
			&Model::set_lod_transition_time)
		.property("Max Distance",
			&Model::get_lod_max_distance,
			&Model::set_lod_max_distance)
		.property("Min Distance",
			&Model::get_lod_min_distance,
			&Model::set_lod_min_distance)
		;
}

SAVE(Model)
{
	ar(
		cereal::make_nvp("lods", obj._mesh_lods),
		cereal::make_nvp("materials", obj._materials),
		cereal::make_nvp("transition_time", obj._transition_time),
		cereal::make_nvp("max_distance", obj._max_distance),
		cereal::make_nvp("min_distance", obj._min_distance)
	);

}

LOAD(Model)
{
	ar(
		cereal::make_nvp("lods", obj._mesh_lods),
		cereal::make_nvp("materials", obj._materials),
		cereal::make_nvp("transition_time", obj._transition_time),
		cereal::make_nvp("max_distance", obj._max_distance),
		cereal::make_nvp("min_distance", obj._min_distance)
	);
}<|endoftext|>


<commit_before>// Prints the tag of a audio file
// mp3, m4a ogg and flac supported

// Bilal Hussain

// install:
//  g++ `taglib-config  --cflags --libs`  taginfo.cpp  -o taginfo
// or
// g++ -ltag -I/usr/local/include/taglib -L/usr/local/lib  taginfo.cpp  -o taginfo
// if no taglib-config

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <tag.h>
#include <fileref.h>

void usage() {
	fprintf(stderr, "taginfo \n");
	fprintf(stderr, "Usage: taginfo <file(s)>\n");
	fprintf(stderr, "Usage: taginfo --short <file>\n");
	fprintf(stderr, "Usage: taginfo --info <file>\n");
}

int main(int argc, char *argv[]) {
	if(argc < 2) {
		usage();
		exit(1);
	}

	if (argc == 3  && strcmp("--short", argv[1]) == 0){
		TagLib::FileRef f(argv[2]);

		printf("%s\n", f.tag()->title().toCString(true));
		printf("%s\n", f.tag()->album().toCString(true));
		printf("%s\n", f.tag()->artist().toCString(true));
		int time = f.audioProperties()->length();
		printf("%d:%d\n", (time%3600/60), (time%60) );
		exit(0);
	}

	else if (argc == 3  && strcmp("--info", argv[1]) == 0){
		TagLib::FileRef f(argv[2]);

		printf("%s - %02d %s - %s\n",
			f.tag()->artist().toCString(true),
			f.tag()->track(),
			f.tag()->title().toCString(true),
			f.tag()->album().toCString(true)
		);
		exit(0);
	}

	else if (argc == 4  && strcmp("--details", argv[1]) == 0){
		TagLib::FileRef f(argv[2]);
		long start_time = strtol(argv[3],NULL,10);
		const int end_time = f.audioProperties()->length();
		printf("%s - %02d %s - %s -  %ld:%02ld/%d:%02d\n",
			f.tag()->artist().toCString(true),
			f.tag()->track(),
			f.tag()->title().toCString(true),
			f.tag()->album().toCString(true),
			(start_time%3600/60), (start_time%60),
			(end_time%3600/60),   (end_time%60)
		);
		exit(0);
	}

	for(int i = 1; i < argc; i++) {
		TagLib::FileRef f(argv[i]);
		if(!f.isNull() && f.tag()) {
			printf("FILE=\"%s\"\n", argv[i]);
			printf("ALBUM=\"%s\"\n", f.tag()->album().toCString(true));
			printf("TRACK=\"%d\"\n", f.tag()->track());
			printf("ARTIST=\"%s\"\n", f.tag()->artist().toCString(true));
			printf("TITLE=\"%s\"\n", f.tag()->title().toCString(true));
			printf("GENRE=\"%s\"\n", f.tag()->genre().toCString(true));
			printf("YEAR=\"%d\"\n", f.tag()->year());
			printf("COMMENT=\"%s\"\n", f.tag()->comment().toCString(true));
			printf("LENGTH=\"%d\"\n", f.audioProperties()->length());
			printf("BITRATE=\"%d\"\n", f.audioProperties()->bitrate());
			printf("SAMPLERATE=\"%d\"\n", f.audioProperties()->sampleRate());
			printf("CHANNELS=\"%d\"\n\n", f.audioProperties()->channels());
		} else {
			fprintf(stderr, "Error opening file: %s\n", argv[i]);
		}
	}

	return 0;
}
<commit_msg>colour<commit_after>// Prints the tag of a audio file
// mp3, m4a ogg and flac supported

// Bilal Hussain

// install:
//  g++ `taglib-config  --cflags --libs`  taginfo.cpp  -o taginfo
// or
// g++ -ltag -I/usr/local/include/taglib -L/usr/local/lib  taginfo.cpp  -o taginfo
// if no taglib-config

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <tag.h>
#include <fileref.h>

void usage() {
	fprintf(stderr, "taginfo \n");
	fprintf(stderr, "Usage: taginfo <file(s)>\n");
	fprintf(stderr, "Usage: taginfo --short <file>\n");
	fprintf(stderr, "Usage: taginfo --info <file>\n");
}

int main(int argc, char *argv[]) {
	if(argc < 2) {
		usage();
		exit(1);
	}

	if (argc == 3  && strcmp("--short", argv[1]) == 0){
		TagLib::FileRef f(argv[2]);

		printf("%s\n", f.tag()->title().toCString(true));
		printf("%s\n", f.tag()->album().toCString(true));
		printf("%s\n", f.tag()->artist().toCString(true));
		int time = f.audioProperties()->length();
		printf("%d:%d\n", (time%3600/60), (time%60) );
		exit(0);
	}

	else if (argc == 3  && strcmp("--info", argv[1]) == 0){
		TagLib::FileRef f(argv[2]);

		printf("%s - %02d %s - %s\n",
			f.tag()->artist().toCString(true),
			f.tag()->track(),
			f.tag()->title().toCString(true),
			f.tag()->album().toCString(true)
		);
		exit(0);
	}

	else if (argc == 4  && strcmp("--details", argv[1]) == 0){
		TagLib::FileRef f(argv[2]);
		long start_time = strtol(argv[3],NULL,10);
		const int end_time = f.audioProperties()->length();

		// adds %s before and after
		#define SSS(str) "%s" str "%s"
		#define BLUE         "\033[34m"             // Blue
		#define RED          "\033[31m"             // Red
		#define GREEN        "\033[32m"             // Green
		#define RESET        "\033[0m"              // Need before and after
		#define COLOUR(string, colour) RESET colour, string, RESET

		printf("%s - %02d " SSS("%s") " - %s -  %ld:%02ld/%d:%02d\n",
			f.tag()->artist().toCString(true),
			f.tag()->track(),
			COLOUR(f.tag()->title().toCString(true),BLUE),
			f.tag()->album().toCString(true),
			(start_time%3600/60), (start_time%60),
			(end_time%3600/60),   (end_time%60)
		);
		exit(0);
	}

	for(int i = 1; i < argc; i++) {
		TagLib::FileRef f(argv[i]);
		if(!f.isNull() && f.tag()) {
			printf("FILE=\"%s\"\n", argv[i]);
			printf("ALBUM=\"%s\"\n", f.tag()->album().toCString(true));
			printf("TRACK=\"%d\"\n", f.tag()->track());
			printf("ARTIST=\"%s\"\n", f.tag()->artist().toCString(true));
			printf("TITLE=\"%s\"\n", f.tag()->title().toCString(true));
			printf("GENRE=\"%s\"\n", f.tag()->genre().toCString(true));
			printf("YEAR=\"%d\"\n", f.tag()->year());
			printf("COMMENT=\"%s\"\n", f.tag()->comment().toCString(true));
			printf("LENGTH=\"%d\"\n", f.audioProperties()->length());
			printf("BITRATE=\"%d\"\n", f.audioProperties()->bitrate());
			printf("SAMPLERATE=\"%d\"\n", f.audioProperties()->sampleRate());
			printf("CHANNELS=\"%d\"\n\n", f.audioProperties()->channels());
		} else {
			fprintf(stderr, "Error opening file: %s\n", argv[i]);
		}
	}

	return 0;
}
<|endoftext|>


<commit_before>#include "gtest/gtest.h"

#include <functional>

#include <vm.h>
#include <ydsh/ydsh.h>

#include "../test_common.h"

using BreakPointHandler = std::function<void()>;

class VMInspector : public VMHook {
private:
    OpCode breakOp;
    BreakPointHandler handler;
    bool called;

public:
    VMInspector() : breakOp(OpCode::HALT), handler(), called(true) {}

    void setHandler(OpCode op, BreakPointHandler &&handler) {
        this->breakOp = op;
        this->handler = std::move(handler);
        if(this->handler) {
            this->called = false;
        }
    }

    bool getCalled() const {
        return this->called;
    }

    void vmFetchHook(DSState &, OpCode op) override {
        if(this->breakOp == op) {
            if(this->handler) {
                this->handler();
                this->called = true;
            }
        }
    }

    void vmThrowHook(DSState &) override {}
};

class VMTest : public ::testing::Test {
protected:
    DSState *state;
    VMInspector inspector;

public:
    VMTest() : state(nullptr), inspector() {}
    virtual ~VMTest() = default;

    virtual void SetUp() {
        this->state = DSState_create();
        this->state->setVMHook(&this->inspector);
    }

    virtual void TearDown() {
        DSState_delete(&this->state);
    }

private:
    void setBreakPointHandler(OpCode breakOp, BreakPointHandler &&handler) {
        this->inspector.setHandler(breakOp, std::move(handler));
    }

protected:
    void eval(const char *code, DSErrorKind kind = DS_ERROR_KIND_SUCCESS) {
        DSError e;
        DSState_eval(this->state, "(dummy)", code, strlen(code), &e);
        auto actualKind = e.kind;
        DSError_release(&e);
        ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(kind, actualKind));
        ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(this->inspector.getCalled()));
    }

    void eval(const char *code, DSErrorKind kind, OpCode breakOp, BreakPointHandler &&handler) {
        this->setBreakPointHandler(breakOp, std::move(handler));
        this->eval(code, kind);
    }

    DSValue getValue(const char *name) const {
        auto handle = this->state->symbolTable.lookupHandle(name);
        if(handle == nullptr) {
            return nullptr;
        }
        return this->state->getGlobal(handle->getIndex());
    }

    void RefCount(const char *gvarName, unsigned int refCount) {
        ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(gvarName != nullptr));

        auto *handle = this->state->symbolTable.lookupHandle(gvarName);
        ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(handle != nullptr));

        auto &v = this->state->getGlobal(handle->getIndex());
        ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(v.isObject()));

        ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(refCount, v.get()->getRefcount()));
    }
};

TEST_F(VMTest, base) {
    ASSERT_(this->eval("12", DS_ERROR_KIND_SUCCESS, OpCode::POP, [&]{
        ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(12, typeAs<Int_Object>(this->state->peek())->getValue()));
    }));
}

TEST_F(VMTest, deinit1) {
    ASSERT_(this->eval("var a = new [Int]()"));
    ASSERT_(RefCount("a", 1));

    ASSERT_(this->eval("{ var b = $a}"));
    ASSERT_(RefCount("a", 1));

    ASSERT_(this->eval("{ var b = $a; if $true { var c = $a }; $RANDOM; }", DS_ERROR_KIND_SUCCESS, OpCode::RAND, [&]{
        ASSERT_(RefCount("a", 2));
    }));
}

TEST_F(VMTest, deinit2) {
    ASSERT_(this->eval("{ var b = $@; throw 34; }", DS_ERROR_KIND_RUNTIME_ERROR));
    ASSERT_(RefCount("@", 1));

    ASSERT_(this->eval("{ var a = $@; { var b = $@; var c = $b; throw 34; }}", DS_ERROR_KIND_RUNTIME_ERROR));
    ASSERT_(RefCount("@", 1));
}

TEST_F(VMTest, deinit3) {
    ASSERT_(this->eval("var i = 0; while $i < 2 { var b = $@; $i++ }"));
    ASSERT_(RefCount("@", 1));

    ASSERT_(this->eval("while $true { var b = $@; break; }"));
    ASSERT_(RefCount("@", 1));

    ASSERT_(this->eval("for(var i = $@; $true;) { var b = $i; break; }"));
    ASSERT_(RefCount("@", 1));

    ASSERT_(this->eval("for(var i = 0; $i < 3; $i++) { var b = $@; continue; }"));
    ASSERT_(RefCount("@", 1));
}

TEST_F(VMTest, deinit4) {
    ASSERT_(this->eval("function f($a : [String]) { $RANDOM; var b = $a; }; $f($@)",
                       DS_ERROR_KIND_SUCCESS, OpCode::RAND, [&] {
                ASSERT_(RefCount("@", 2));
            }));
}

TEST_F(VMTest, deinit5) {
    ASSERT_(this->eval("function f($a : [String]) { var b = $a; { var c = $b; $RANDOM; }; var c = $b; }; $f($@)",
                       DS_ERROR_KIND_SUCCESS, OpCode::RAND, [&]{
                ASSERT_(RefCount("@", 4));
            }));
}

TEST_F(VMTest, deinit6) {
    ASSERT_(this->eval("function f($a : [String]) { var b = $a; { var c = $b }; $RANDOM; var c = $b; }; $f($@)",
                       DS_ERROR_KIND_SUCCESS, OpCode::RAND, [&] {
                ASSERT_(RefCount("@", 3));
            }));
}

TEST_F(VMTest, deinit7) {
    ASSERT_(this->eval("try { var a = $@; 34 / 0; var b = $a; } catch($e) {}"));
    ASSERT_(RefCount("@", 1));

    ASSERT_(this->eval("try { while $true { var a = $@; break; } } finally {  $RANDOM; }",
                       DS_ERROR_KIND_SUCCESS, OpCode::RAND, [&]{
                ASSERT_(RefCount("@", 1));
            }));
}

TEST_F(VMTest, deinit8) {
    ASSERT_(this->eval("try { var a = $@; 34 / 0 } catch $e { $RANDOM; }", DS_ERROR_KIND_SUCCESS, OpCode::RAND, [&]{
        ASSERT_(RefCount("@", 1));
    }));
}

TEST_F(VMTest, deinit9) {
    ASSERT_(this->eval("try { var a = $@; 34 / 0 } catch $e { var b = $@; throw 34; } finally {  $RANDOM; }",
                    DS_ERROR_KIND_RUNTIME_ERROR, OpCode::RAND, [&]{
                ASSERT_(RefCount("@", 1));
            }));
}

TEST_F(VMTest, deinit10) {
    ASSERT_(this->eval("try { var a = $@; var b = $a; 34 / 0 } catch $e : Int { var b = $@; var c = $b; var d = $c; } finally {  $RANDOM; }",
                       DS_ERROR_KIND_RUNTIME_ERROR, OpCode::RAND, [&]{
                ASSERT_(RefCount("@", 1));
            }));
}

TEST_F(VMTest, sig1) {
    ASSERT_(this->eval("function f($s : Signal) {}"));
    auto func = this->getValue("f");
    ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(func != nullptr));

    SignalVector v;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(0u, v.getData().size()));

    // not found
    ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(v.lookup(SIGQUIT) == nullptr));
    ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(v.lookup(SIGINT) == nullptr));

    // register
    v.insertOrUpdate(3, func);
    v.insertOrUpdate(1, func);
    v.insertOrUpdate(4, func);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3u, v.getData().size()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(1, v.getData()[0].first));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3, v.getData()[1].first));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(4, v.getData()[2].first));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(func, v.lookup(1)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(func, v.lookup(3)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(func, v.lookup(4)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(v.lookup(2) == nullptr));
    ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(v.lookup(5) == nullptr));
    ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(v.lookup(-3) == nullptr));

    // update
    auto func1 = this->getValue("SIG_DFL");
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(func, v.lookup(3)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_NE(func, func1));
    v.insertOrUpdate(3, func1);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(func1, v.lookup(3)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3u, v.getData().size()));

    // remove
    v.insertOrUpdate(4, nullptr);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, v.getData().size()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(DSValue(), v.lookup(4)));

    // do nothing
    v.insertOrUpdate(5, nullptr);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, v.getData().size()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(DSValue(), v.lookup(5)));
}

TEST_F(VMTest, abort) {
    ASSERT_(this->eval("var a = 45 / 0;", DS_ERROR_KIND_RUNTIME_ERROR));
    ASSERT_(this->eval("$a;", DS_ERROR_KIND_TYPE_ERROR));
}

static Job newJob() {
    return JobImpl::create(Proc());
}

static JobTable::ConstEntryIter getBeginIter(const JobTable &table) {
    return table.beginJob();
}

static JobTable::ConstEntryIter getEndIter(const JobTable &table) {
    return table.endJob();
}

TEST(JobTable, attach) {
    JobTable jobTable;

    auto job1 = newJob();
    auto job2 = newJob();
    auto job3 = newJob();
    auto job4 = newJob();
    auto job5 = newJob();
    auto job6 = newJob();

    jobTable.attach(job1);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(1u, job1->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job1, jobTable.getLatestEntry()));

    jobTable.attach(job2);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, job2->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job2, jobTable.getLatestEntry()));

    jobTable.attach(job3);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3u, job3->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job3, jobTable.getLatestEntry()));

    jobTable.attach(job4);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(4u, job4->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job4, jobTable.getLatestEntry()));

    jobTable.attach(job5);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(5u, job5->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job5, jobTable.getLatestEntry()));

    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job2, jobTable.detach(2, true)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job5, jobTable.getLatestEntry()));

    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job3, jobTable.detach(3, false)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job5, jobTable.getLatestEntry()));

    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job5, jobTable.detach(5, true)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job4, jobTable.getLatestEntry()));

    // job entry layout
    auto begin = getBeginIter(jobTable);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(1u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(4u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(getEndIter(jobTable), begin));

    // re-attach
    jobTable.attach(job5);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, job5->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job5, jobTable.getLatestEntry()));

    begin = getBeginIter(jobTable);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(1u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(4u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(getEndIter(jobTable), begin));

    // re-attach
    jobTable.attach(job2);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3u, job2->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job2, jobTable.getLatestEntry()));

    begin = getBeginIter(jobTable);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(1u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(4u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(getEndIter(jobTable), begin));

    // re-attach
    jobTable.attach(job6);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(5u, job6->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job6, jobTable.getLatestEntry()));

    begin = getBeginIter(jobTable);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(1u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(4u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(5u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(getEndIter(jobTable), begin));
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}<commit_msg>cleanup<commit_after>#include "gtest/gtest.h"

#include <functional>

#include <vm.h>
#include <ydsh/ydsh.h>

#include "../test_common.h"

using BreakPointHandler = std::function<void()>;

class VMInspector : public VMHook {
private:
    OpCode breakOp;
    BreakPointHandler handler;
    bool called;

public:
    VMInspector() : breakOp(OpCode::HALT), handler(), called(true) {}

    void setHandler(OpCode op, BreakPointHandler &&handler) {
        this->breakOp = op;
        this->handler = std::move(handler);
        if(this->handler) {
            this->called = false;
        }
    }

    bool getCalled() const {
        return this->called;
    }

    void vmFetchHook(DSState &, OpCode op) override {
        if(this->breakOp == op) {
            if(this->handler) {
                this->handler();
                this->called = true;
            }
        }
    }

    void vmThrowHook(DSState &) override {}
};

class VMTest : public ::testing::Test {
protected:
    DSState *state;
    VMInspector inspector;

public:
    VMTest() : state(nullptr), inspector() {}
    ~VMTest() override = default;

    void SetUp() override {
        this->state = DSState_create();
        this->state->setVMHook(&this->inspector);
    }

    void TearDown() override {
        DSState_delete(&this->state);
    }

private:
    void setBreakPointHandler(OpCode breakOp, BreakPointHandler &&handler) {
        this->inspector.setHandler(breakOp, std::move(handler));
    }

protected:
    void eval(const char *code, DSErrorKind kind = DS_ERROR_KIND_SUCCESS) {
        DSError e;
        DSState_eval(this->state, "(dummy)", code, strlen(code), &e);
        auto actualKind = e.kind;
        DSError_release(&e);
        ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(kind, actualKind));
        ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(this->inspector.getCalled()));
    }

    void eval(const char *code, DSErrorKind kind, OpCode breakOp, BreakPointHandler &&handler) {
        this->setBreakPointHandler(breakOp, std::move(handler));
        this->eval(code, kind);
    }

    DSValue getValue(const char *name) const {
        auto handle = this->state->symbolTable.lookupHandle(name);
        if(handle == nullptr) {
            return nullptr;
        }
        return this->state->getGlobal(handle->getIndex());
    }

    void RefCount(const char *gvarName, unsigned int refCount) {
        ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(gvarName != nullptr));

        auto *handle = this->state->symbolTable.lookupHandle(gvarName);
        ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(handle != nullptr));

        auto &v = this->state->getGlobal(handle->getIndex());
        ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(v.isObject()));

        ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(refCount, v->getRefcount()));
    }
};

TEST_F(VMTest, base) {
    ASSERT_(this->eval("12", DS_ERROR_KIND_SUCCESS, OpCode::POP, [&]{
        ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(12, typeAs<Int_Object>(this->state->peek())->getValue()));
    }));
}

TEST_F(VMTest, deinit1) {
    ASSERT_(this->eval("var a = new [Int]()"));
    ASSERT_(RefCount("a", 1));

    ASSERT_(this->eval("{ var b = $a}"));
    ASSERT_(RefCount("a", 1));

    ASSERT_(this->eval("{ var b = $a; if $true { var c = $a }; $RANDOM; }", DS_ERROR_KIND_SUCCESS, OpCode::RAND, [&]{
        ASSERT_(RefCount("a", 2));
    }));
}

TEST_F(VMTest, deinit2) {
    ASSERT_(this->eval("{ var b = $@; throw 34; }", DS_ERROR_KIND_RUNTIME_ERROR));
    ASSERT_(RefCount("@", 1));

    ASSERT_(this->eval("{ var a = $@; { var b = $@; var c = $b; throw 34; }}", DS_ERROR_KIND_RUNTIME_ERROR));
    ASSERT_(RefCount("@", 1));
}

TEST_F(VMTest, deinit3) {
    ASSERT_(this->eval("var i = 0; while $i < 2 { var b = $@; $i++ }"));
    ASSERT_(RefCount("@", 1));

    ASSERT_(this->eval("while $true { var b = $@; break; }"));
    ASSERT_(RefCount("@", 1));

    ASSERT_(this->eval("for(var i = $@; $true;) { var b = $i; break; }"));
    ASSERT_(RefCount("@", 1));

    ASSERT_(this->eval("for(var i = 0; $i < 3; $i++) { var b = $@; continue; }"));
    ASSERT_(RefCount("@", 1));
}

TEST_F(VMTest, deinit4) {
    ASSERT_(this->eval("function f($a : [String]) { $RANDOM; var b = $a; }; $f($@)",
                       DS_ERROR_KIND_SUCCESS, OpCode::RAND, [&] {
                ASSERT_(RefCount("@", 2));
            }));
}

TEST_F(VMTest, deinit5) {
    ASSERT_(this->eval("function f($a : [String]) { var b = $a; { var c = $b; $RANDOM; }; var c = $b; }; $f($@)",
                       DS_ERROR_KIND_SUCCESS, OpCode::RAND, [&]{
                ASSERT_(RefCount("@", 4));
            }));
}

TEST_F(VMTest, deinit6) {
    ASSERT_(this->eval("function f($a : [String]) { var b = $a; { var c = $b }; $RANDOM; var c = $b; }; $f($@)",
                       DS_ERROR_KIND_SUCCESS, OpCode::RAND, [&] {
                ASSERT_(RefCount("@", 3));
            }));
}

TEST_F(VMTest, deinit7) {
    ASSERT_(this->eval("try { var a = $@; 34 / 0; var b = $a; } catch($e) {}"));
    ASSERT_(RefCount("@", 1));

    ASSERT_(this->eval("try { while $true { var a = $@; break; } } finally {  $RANDOM; }",
                       DS_ERROR_KIND_SUCCESS, OpCode::RAND, [&]{
                ASSERT_(RefCount("@", 1));
            }));
}

TEST_F(VMTest, deinit8) {
    ASSERT_(this->eval("try { var a = $@; 34 / 0 } catch $e { $RANDOM; }", DS_ERROR_KIND_SUCCESS, OpCode::RAND, [&]{
        ASSERT_(RefCount("@", 1));
    }));
}

TEST_F(VMTest, deinit9) {
    ASSERT_(this->eval("try { var a = $@; 34 / 0 } catch $e { var b = $@; throw 34; } finally {  $RANDOM; }",
                    DS_ERROR_KIND_RUNTIME_ERROR, OpCode::RAND, [&]{
                ASSERT_(RefCount("@", 1));
            }));
}

TEST_F(VMTest, deinit10) {
    ASSERT_(this->eval("try { var a = $@; var b = $a; 34 / 0 } catch $e : Int { var b = $@; var c = $b; var d = $c; } finally {  $RANDOM; }",
                       DS_ERROR_KIND_RUNTIME_ERROR, OpCode::RAND, [&]{
                ASSERT_(RefCount("@", 1));
            }));
}

TEST_F(VMTest, sig1) {
    ASSERT_(this->eval("function f($s : Signal) {}"));
    auto func = this->getValue("f");
    ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(func != nullptr));

    SignalVector v;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(0u, v.getData().size()));

    // not found
    ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(v.lookup(SIGQUIT) == nullptr));
    ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(v.lookup(SIGINT) == nullptr));

    // register
    v.insertOrUpdate(3, func);
    v.insertOrUpdate(1, func);
    v.insertOrUpdate(4, func);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3u, v.getData().size()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(1, v.getData()[0].first));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3, v.getData()[1].first));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(4, v.getData()[2].first));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(func, v.lookup(1)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(func, v.lookup(3)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(func, v.lookup(4)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(v.lookup(2) == nullptr));
    ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(v.lookup(5) == nullptr));
    ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(v.lookup(-3) == nullptr));

    // update
    auto func1 = this->getValue("SIG_DFL");
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(func, v.lookup(3)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_NE(func, func1));
    v.insertOrUpdate(3, func1);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(func1, v.lookup(3)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3u, v.getData().size()));

    // remove
    v.insertOrUpdate(4, nullptr);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, v.getData().size()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(DSValue(), v.lookup(4)));

    // do nothing
    v.insertOrUpdate(5, nullptr);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, v.getData().size()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(DSValue(), v.lookup(5)));
}

TEST_F(VMTest, abort) {
    ASSERT_(this->eval("var a = 45 / 0;", DS_ERROR_KIND_RUNTIME_ERROR));
    ASSERT_(this->eval("$a;", DS_ERROR_KIND_TYPE_ERROR));
}

static Job newJob() {
    return JobImpl::create(Proc());
}

static JobTable::ConstEntryIter getBeginIter(const JobTable &table) {
    return table.beginJob();
}

static JobTable::ConstEntryIter getEndIter(const JobTable &table) {
    return table.endJob();
}

TEST(JobTable, attach) {
    JobTable jobTable;

    auto job1 = newJob();
    auto job2 = newJob();
    auto job3 = newJob();
    auto job4 = newJob();
    auto job5 = newJob();
    auto job6 = newJob();

    jobTable.attach(job1);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(1u, job1->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job1, jobTable.getLatestEntry()));

    jobTable.attach(job2);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, job2->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job2, jobTable.getLatestEntry()));

    jobTable.attach(job3);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3u, job3->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job3, jobTable.getLatestEntry()));

    jobTable.attach(job4);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(4u, job4->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job4, jobTable.getLatestEntry()));

    jobTable.attach(job5);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(5u, job5->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job5, jobTable.getLatestEntry()));

    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job2, jobTable.detach(2, true)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job5, jobTable.getLatestEntry()));

    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job3, jobTable.detach(3, false)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job5, jobTable.getLatestEntry()));

    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job5, jobTable.detach(5, true)));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job4, jobTable.getLatestEntry()));

    // job entry layout
    auto begin = getBeginIter(jobTable);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(1u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(4u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(getEndIter(jobTable), begin));

    // re-attach
    jobTable.attach(job5);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, job5->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job5, jobTable.getLatestEntry()));

    begin = getBeginIter(jobTable);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(1u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(4u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(getEndIter(jobTable), begin));

    // re-attach
    jobTable.attach(job2);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3u, job2->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job2, jobTable.getLatestEntry()));

    begin = getBeginIter(jobTable);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(1u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(4u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(getEndIter(jobTable), begin));

    // re-attach
    jobTable.attach(job6);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(5u, job6->jobID()));
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(job6, jobTable.getLatestEntry()));

    begin = getBeginIter(jobTable);
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(1u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(2u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(3u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(4u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(5u, (*begin)->jobID()));
    ++begin;
    ASSERT_NO_FATAL_FAILURE(ASSERT_EQ(getEndIter(jobTable), begin));
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}<|endoftext|>


<commit_before><commit_msg>Bug fix: we no longer allow two (or more) robots to pick up the same tag<commit_after><|endoftext|>