<commit_before>// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Utility/X11/Display.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Core/String.hpp>
#include <xcb/xcb_keysyms.h>
#include <map>
#include <Nazara/Utility/Debug.hpp>

namespace Nz
{
	namespace
	{
		// The shared display and its reference counter
		xcb_connection_t* sharedConnection = nullptr;
		int screen_nbr = 0;
		unsigned int referenceCountConnection = 0;

		xcb_key_symbols_t* sharedkeySymbol = nullptr;
		unsigned int referenceCountKeySymbol = 0;

		xcb_ewmh_connection_t* sharedEwmhConnection = nullptr;
		unsigned int referenceCountEwmhConnection = 0;

		using AtomMap = std::map<String, xcb_atom_t>;
		AtomMap atoms;
	}

	bool X11::CheckCookie(xcb_connection_t* connection, xcb_void_cookie_t cookie)
	{
		ScopedXCB<xcb_generic_error_t> error(xcb_request_check(
			connection,
			cookie
		));

		if (error)
			return false;
		else
			return true;
	}

	void X11::CloseConnection(xcb_connection_t* connection)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");
		--referenceCountConnection;
	}

	void X11::CloseEWMHConnection(xcb_ewmh_connection_t* ewmh_connection)
	{
		NazaraAssert(ewmh_connection == sharedEwmhConnection, "The model is meant for one connection to X11 server");
		--referenceCountEwmhConnection;
	}

	xcb_atom_t X11::GetAtom(const String& name, bool onlyIfExists)
	{
		AtomMap::const_iterator iter = atoms.find(name);

		if (iter != atoms.end())
			return iter->second;

		ScopedXCB<xcb_generic_error_t> error(nullptr);

		xcb_connection_t* connection = OpenConnection();

		ScopedXCB<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(
			connection,
			xcb_intern_atom(
				connection,
				onlyIfExists,
				name.GetSize(),
				name.GetConstBuffer()
			),
			&error
		));

		CloseConnection(connection);

		if (error || !reply)
		{
			NazaraError("Failed to get " + name + " atom.");
			return XCB_ATOM_NONE;
		}

		atoms[name] = reply->atom;

		return reply->atom;
	}

	bool X11::Initialize()
	{
		if (IsInitialized())
		{
			s_moduleReferenceCounter++;
			return true; // Déjà initialisé
		}

		s_moduleReferenceCounter++;

		NazaraAssert(referenceCountConnection == 0, "Initialize should be called before anything");
		NazaraAssert(referenceCountKeySymbol == 0, "Initialize should be called before anything");
		NazaraAssert(referenceCountEwmhConnection == 0, "Initialize should be called before anything");

		{
			sharedConnection = xcb_connect(nullptr, &screen_nbr);

			// Opening display failed: The best we can do at the moment is to output a meaningful error message
			// and cause an abnormal program termination
			if (!sharedConnection || xcb_connection_has_error(sharedConnection))
			{
				NazaraError("Failed to open xcb connection");
				std::abort();
			}

			OpenConnection();
		}

		{
			sharedkeySymbol = xcb_key_symbols_alloc(sharedConnection);

			XCBKeySymbolsAlloc(sharedConnection);
		}

		{
			sharedEwmhConnection = new xcb_ewmh_connection_t;
			xcb_intern_atom_cookie_t* ewmh_cookie = xcb_ewmh_init_atoms(sharedConnection, sharedEwmhConnection);

			if(!xcb_ewmh_init_atoms_replies(sharedEwmhConnection, ewmh_cookie, nullptr))
			{
				NazaraError("Could not initialize EWMH Connection");
				sharedEwmhConnection = nullptr;
			}

			OpenEWMHConnection(sharedConnection);
		}

		return true;
	}

	bool X11::IsInitialized()
	{
		return s_moduleReferenceCounter != 0;
	}

	xcb_key_symbols_t* X11::XCBKeySymbolsAlloc(xcb_connection_t* connection)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");

		++referenceCountKeySymbol;
		return sharedkeySymbol;
	}

	void X11::XCBKeySymbolsFree(xcb_key_symbols_t* keySymbols)
	{
		NazaraAssert(keySymbols == sharedkeySymbol, "The model is meant for one connection to X11 server");

		--referenceCountKeySymbol;
	}

	xcb_connection_t* X11::OpenConnection()
	{
		++referenceCountConnection;
		return sharedConnection;
	}

	xcb_ewmh_connection_t* X11::OpenEWMHConnection(xcb_connection_t* connection)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");

		++referenceCountEwmhConnection;
		return sharedEwmhConnection;
	}

	void X11::Uninitialize()
	{
		if (s_moduleReferenceCounter != 1)
		{
			// Le module est soit encore utilisé, soit pas initialisé
			if (s_moduleReferenceCounter > 1)
				s_moduleReferenceCounter--;

			return;
		}

		s_moduleReferenceCounter = 0;

		{
			NazaraAssert(referenceCountEwmhConnection == 1, "Uninitialize should be called after anything or a close is missing");
			CloseEWMHConnection(sharedEwmhConnection);

			xcb_ewmh_connection_wipe(sharedEwmhConnection);
			delete sharedEwmhConnection;
		}

		{
			NazaraAssert(referenceCountKeySymbol == 1, "Uninitialize should be called after anything or a free is missing");
			XCBKeySymbolsFree(sharedkeySymbol);

			xcb_key_symbols_free(sharedkeySymbol);
		}

		{
			NazaraAssert(referenceCountConnection == 1, "Uninitialize should be called after anything or a close is missing");
			CloseConnection(sharedConnection);

			xcb_disconnect(sharedConnection);
		}
	}

	xcb_window_t X11::XCBDefaultRootWindow(xcb_connection_t* connection)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");
		xcb_screen_t* screen = XCBDefaultScreen(connection);
		if (screen)
			return screen->root;
		return XCB_NONE;
	}

	xcb_screen_t* X11::XCBDefaultScreen(xcb_connection_t* connection)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");
		return XCBScreenOfDisplay(connection, screen_nbr);
	}

	int X11::XCBScreen(xcb_connection_t* connection)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");
		return screen_nbr;
	}

	xcb_screen_t* X11::XCBScreenOfDisplay(xcb_connection_t* connection, int screen_nbr)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");
		xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(connection));

		for (; iter.rem; --screen_nbr, xcb_screen_next (&iter))
		{
			if (screen_nbr == 0)
				return iter.data;
		}

		return nullptr;
	}

	unsigned int X11::s_moduleReferenceCounter = 0;
}
<commit_msg>Utility/X11: Fix calling of std::abort() in case of failure<commit_after>// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Utility/X11/Display.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Core/String.hpp>
#include <xcb/xcb_keysyms.h>
#include <map>
#include <Nazara/Utility/Debug.hpp>

namespace Nz
{
	namespace
	{
		// The shared display and its reference counter
		xcb_connection_t* sharedConnection = nullptr;
		int screen_nbr = 0;
		unsigned int referenceCountConnection = 0;

		xcb_key_symbols_t* sharedkeySymbol = nullptr;
		unsigned int referenceCountKeySymbol = 0;

		xcb_ewmh_connection_t* sharedEwmhConnection = nullptr;
		unsigned int referenceCountEwmhConnection = 0;

		using AtomMap = std::map<String, xcb_atom_t>;
		AtomMap atoms;
	}

	bool X11::CheckCookie(xcb_connection_t* connection, xcb_void_cookie_t cookie)
	{
		ScopedXCB<xcb_generic_error_t> error(xcb_request_check(
			connection,
			cookie
		));

		if (error)
			return false;
		else
			return true;
	}

	void X11::CloseConnection(xcb_connection_t* connection)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");
		--referenceCountConnection;
	}

	void X11::CloseEWMHConnection(xcb_ewmh_connection_t* ewmh_connection)
	{
		NazaraAssert(ewmh_connection == sharedEwmhConnection, "The model is meant for one connection to X11 server");
		--referenceCountEwmhConnection;
	}

	xcb_atom_t X11::GetAtom(const String& name, bool onlyIfExists)
	{
		AtomMap::const_iterator iter = atoms.find(name);

		if (iter != atoms.end())
			return iter->second;

		ScopedXCB<xcb_generic_error_t> error(nullptr);

		xcb_connection_t* connection = OpenConnection();

		ScopedXCB<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(
			connection,
			xcb_intern_atom(
				connection,
				onlyIfExists,
				name.GetSize(),
				name.GetConstBuffer()
			),
			&error
		));

		CloseConnection(connection);

		if (error || !reply)
		{
			NazaraError("Failed to get " + name + " atom.");
			return XCB_ATOM_NONE;
		}

		atoms[name] = reply->atom;

		return reply->atom;
	}

	bool X11::Initialize()
	{
		if (IsInitialized())
		{
			s_moduleReferenceCounter++;
			return true; // Déjà initialisé
		}

		s_moduleReferenceCounter++;

		NazaraAssert(referenceCountConnection == 0, "Initialize should be called before anything");
		NazaraAssert(referenceCountKeySymbol == 0, "Initialize should be called before anything");
		NazaraAssert(referenceCountEwmhConnection == 0, "Initialize should be called before anything");

		{
			sharedConnection = xcb_connect(nullptr, &screen_nbr);

			// Opening display failed: The best we can do at the moment is to output a meaningful error message
			if (!sharedConnection || xcb_connection_has_error(sharedConnection))
			{
				NazaraError("Failed to open xcb connection");
				return false;
			}

			OpenConnection();
		}

		{
			sharedkeySymbol = xcb_key_symbols_alloc(sharedConnection);

			XCBKeySymbolsAlloc(sharedConnection);
		}

		{
			sharedEwmhConnection = new xcb_ewmh_connection_t;
			xcb_intern_atom_cookie_t* ewmh_cookie = xcb_ewmh_init_atoms(sharedConnection, sharedEwmhConnection);

			if(!xcb_ewmh_init_atoms_replies(sharedEwmhConnection, ewmh_cookie, nullptr))
			{
				NazaraError("Could not initialize EWMH Connection");
				sharedEwmhConnection = nullptr;
			}

			OpenEWMHConnection(sharedConnection);
		}

		return true;
	}

	bool X11::IsInitialized()
	{
		return s_moduleReferenceCounter != 0;
	}

	xcb_key_symbols_t* X11::XCBKeySymbolsAlloc(xcb_connection_t* connection)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");

		++referenceCountKeySymbol;
		return sharedkeySymbol;
	}

	void X11::XCBKeySymbolsFree(xcb_key_symbols_t* keySymbols)
	{
		NazaraAssert(keySymbols == sharedkeySymbol, "The model is meant for one connection to X11 server");

		--referenceCountKeySymbol;
	}

	xcb_connection_t* X11::OpenConnection()
	{
		++referenceCountConnection;
		return sharedConnection;
	}

	xcb_ewmh_connection_t* X11::OpenEWMHConnection(xcb_connection_t* connection)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");

		++referenceCountEwmhConnection;
		return sharedEwmhConnection;
	}

	void X11::Uninitialize()
	{
		if (s_moduleReferenceCounter != 1)
		{
			// Le module est soit encore utilisé, soit pas initialisé
			if (s_moduleReferenceCounter > 1)
				s_moduleReferenceCounter--;

			return;
		}

		s_moduleReferenceCounter = 0;

		{
			NazaraAssert(referenceCountEwmhConnection == 1, "Uninitialize should be called after anything or a close is missing");
			CloseEWMHConnection(sharedEwmhConnection);

			xcb_ewmh_connection_wipe(sharedEwmhConnection);
			delete sharedEwmhConnection;
		}

		{
			NazaraAssert(referenceCountKeySymbol == 1, "Uninitialize should be called after anything or a free is missing");
			XCBKeySymbolsFree(sharedkeySymbol);

			xcb_key_symbols_free(sharedkeySymbol);
		}

		{
			NazaraAssert(referenceCountConnection == 1, "Uninitialize should be called after anything or a close is missing");
			CloseConnection(sharedConnection);

			xcb_disconnect(sharedConnection);
		}
	}

	xcb_window_t X11::XCBDefaultRootWindow(xcb_connection_t* connection)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");
		xcb_screen_t* screen = XCBDefaultScreen(connection);
		if (screen)
			return screen->root;
		return XCB_NONE;
	}

	xcb_screen_t* X11::XCBDefaultScreen(xcb_connection_t* connection)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");
		return XCBScreenOfDisplay(connection, screen_nbr);
	}

	int X11::XCBScreen(xcb_connection_t* connection)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");
		return screen_nbr;
	}

	xcb_screen_t* X11::XCBScreenOfDisplay(xcb_connection_t* connection, int screen_nbr)
	{
		NazaraAssert(connection == sharedConnection, "The model is meant for one connection to X11 server");
		xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(connection));

		for (; iter.rem; --screen_nbr, xcb_screen_next (&iter))
		{
			if (screen_nbr == 0)
				return iter.data;
		}

		return nullptr;
	}

	unsigned int X11::s_moduleReferenceCounter = 0;
}
<|endoftext|>


<commit_before>#include "lwtnn/parse_json.hh"
#include "lwtnn/lightweight_nn_streamers.hh"

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <cassert>
#include <string>

#include <iostream>

namespace {
  using namespace boost::property_tree;
  using namespace lwt;
  lwt::Activation get_activation(const std::string&);
  lwt::Architecture get_architecture(const std::string&);
  void add_dense_info(LayerConfig& lc, const ptree::value_type& pt);
  void add_maxout_info(LayerConfig& lc, const ptree::value_type& pt);
}

namespace lwt {

  JSONConfig parse_json(std::istream& json)
  {
    boost::property_tree::ptree pt;
    boost::property_tree::read_json(json, pt);

    JSONConfig cfg;
    for (const auto& v: pt.get_child("inputs")) {
      std::string name = v.second.get<std::string>("name");
      auto offset = v.second.get<double>("offset");
      auto scale = v.second.get<double>("scale");
      Input input{name, offset, scale};
      cfg.inputs.push_back(input);
    }
    for (const auto& v: pt.get_child("layers")) {
      LayerConfig layer;
      layer.activation = get_activation(
        v.second.get<std::string>("activation"));
      layer.architecture = get_architecture(
        v.second.get<std::string>("architecture"));

      if (layer.architecture == Architecture::DENSE) {
        add_dense_info(layer, v);
      } else if (layer.architecture == Architecture::MAXOUT) {
        add_maxout_info(layer, v);
      }
      cfg.layers.push_back(layer);
    }
    for (const auto& v: pt.get_child("outputs"))
    {
      assert(v.first.empty()); // array elements have no names
      cfg.outputs.push_back(v.second.data());
    }
    const std::string dname = "defaults";
    if (pt.count(dname)) {
      for (const auto& def: pt.get_child(dname)) {
        cfg.defaults.emplace(def.first, def.second.get_value<double>());
      }
    }
    return cfg;
  }

}

namespace {
  lwt::Activation get_activation(const std::string& str) {
    using namespace lwt;
    if (str == "linear") return Activation::LINEAR;
    if (str == "sigmoid") return Activation::SIGMOID;
    if (str == "rectified") return Activation::RECTIFIED;
    if (str == "softmax") return Activation::SOFTMAX;
    if (str == "tanh") return Activation::TANH;
    throw std::logic_error("activation function " + str + " not recognized");
    return Activation::LINEAR;
  }

  lwt::Architecture get_architecture(const std::string& str) {
    using namespace lwt;
    if (str == "dense") return Architecture::DENSE;
    if (str == "maxout") return Architecture::MAXOUT;
    throw std::logic_error("architecture " + str + " not recognized");
  }

  void add_dense_info(LayerConfig& layer, const ptree::value_type& v) {
    for (const auto& wt: v.second.get_child("weights")) {
      layer.weights.push_back(wt.second.get_value<double>());
    }
    for (const auto& bs: v.second.get_child("bias")) {
      layer.bias.push_back(bs.second.get_value<double>());
    }
  }

  void add_maxout_info(LayerConfig& layer, const ptree::value_type& v) {
    using namespace lwt;
    for (const auto& sub: v.second.get_child("sublayers")) {
      LayerConfig sublayer;
      add_dense_info(sublayer, sub);
      sublayer.architecture = Architecture::DENSE;
      sublayer.activation = Activation::LINEAR;
      layer.sublayers.push_back(sublayer);
    }
  }

}
<commit_msg>Remove some dependencies from parse_json.cxx<commit_after>#include "lwtnn/parse_json.hh"

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <cassert>
#include <string>

namespace {
  using namespace boost::property_tree;
  using namespace lwt;
  lwt::Activation get_activation(const std::string&);
  lwt::Architecture get_architecture(const std::string&);
  void add_dense_info(LayerConfig& lc, const ptree::value_type& pt);
  void add_maxout_info(LayerConfig& lc, const ptree::value_type& pt);
}

namespace lwt {

  JSONConfig parse_json(std::istream& json)
  {
    boost::property_tree::ptree pt;
    boost::property_tree::read_json(json, pt);

    JSONConfig cfg;
    for (const auto& v: pt.get_child("inputs")) {
      std::string name = v.second.get<std::string>("name");
      auto offset = v.second.get<double>("offset");
      auto scale = v.second.get<double>("scale");
      Input input{name, offset, scale};
      cfg.inputs.push_back(input);
    }
    for (const auto& v: pt.get_child("layers")) {
      LayerConfig layer;
      layer.activation = get_activation(
        v.second.get<std::string>("activation"));
      layer.architecture = get_architecture(
        v.second.get<std::string>("architecture"));

      if (layer.architecture == Architecture::DENSE) {
        add_dense_info(layer, v);
      } else if (layer.architecture == Architecture::MAXOUT) {
        add_maxout_info(layer, v);
      }
      cfg.layers.push_back(layer);
    }
    for (const auto& v: pt.get_child("outputs"))
    {
      assert(v.first.empty()); // array elements have no names
      cfg.outputs.push_back(v.second.data());
    }
    const std::string dname = "defaults";
    if (pt.count(dname)) {
      for (const auto& def: pt.get_child(dname)) {
        cfg.defaults.emplace(def.first, def.second.get_value<double>());
      }
    }
    return cfg;
  }

}

namespace {
  lwt::Activation get_activation(const std::string& str) {
    using namespace lwt;
    if (str == "linear") return Activation::LINEAR;
    if (str == "sigmoid") return Activation::SIGMOID;
    if (str == "rectified") return Activation::RECTIFIED;
    if (str == "softmax") return Activation::SOFTMAX;
    if (str == "tanh") return Activation::TANH;
    throw std::logic_error("activation function " + str + " not recognized");
    return Activation::LINEAR;
  }

  lwt::Architecture get_architecture(const std::string& str) {
    using namespace lwt;
    if (str == "dense") return Architecture::DENSE;
    if (str == "maxout") return Architecture::MAXOUT;
    throw std::logic_error("architecture " + str + " not recognized");
  }

  void add_dense_info(LayerConfig& layer, const ptree::value_type& v) {
    for (const auto& wt: v.second.get_child("weights")) {
      layer.weights.push_back(wt.second.get_value<double>());
    }
    for (const auto& bs: v.second.get_child("bias")) {
      layer.bias.push_back(bs.second.get_value<double>());
    }
  }

  void add_maxout_info(LayerConfig& layer, const ptree::value_type& v) {
    using namespace lwt;
    for (const auto& sub: v.second.get_child("sublayers")) {
      LayerConfig sublayer;
      add_dense_info(sublayer, sub);
      sublayer.architecture = Architecture::DENSE;
      sublayer.activation = Activation::LINEAR;
      layer.sublayers.push_back(sublayer);
    }
  }

}
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright (C) 2016-2017 Kitsune Ral <kitsune-ral@users.sf.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "printer.h"

#include "exception.h"

#include <fstream>
#include <algorithm>
#include <locale>

enum {
    CannotReadTemplateFile = PrinterCodes, CannotWriteToFile,
};

using namespace std;
using namespace kainjow::mustache;

Printer::Printer(context_type&& context, const vector<string>& templateFileNames,
                 const string& inputBasePath)
    : _context(context)
{
    // Enriching the context with "My Mustache library"
    _context.set("@filePartial", lambda3 {
        [inputBasePath, this](const string& s, const renderer2& render) {
            ifstream ifs { inputBasePath + s };
            if (!ifs.good())
            {
                cerr << "Failed to open file for a partial: "
                     << inputBasePath + s << endl;
                return "/* Failed to open " + inputBasePath + s + " */";
            }
            string embeddedTemplate;
            getline(ifs, embeddedTemplate, '\0'); // Won't work on files with NULs
            return render(embeddedTemplate, false);
        }
    });
    _context.set("@cap", lambda2 {
        [](const string& s, const basic_renderer<string>& render) {
            return capitalizedCopy(render(s));
        }
    });
    _context.set("@toupper", lambda2 {
        [](string s, const basic_renderer<string>& render) {
            s = render(s);
            transform(s.begin(), s.end(), s.begin(),
                      [] (char c) { return toupper(c, locale::classic()); });
            return s;
        }
    });
    _context.set("@tolower", lambda2 {
        [](string s, const basic_renderer<string>& render) {
            s = render(s);
            transform(s.begin(), s.end(), s.begin(),
                      [] (char c) { return tolower(c, locale::classic()); });
            return s;
        }
    });
    for (const auto& templateFileName: templateFileNames)
    {
        auto templateFilePath = inputBasePath + templateFileName;
        cout << "Opening " << templateFilePath << endl;
        ifstream ifs { templateFilePath };
        if (!ifs.good())
        {
            cerr << "Failed to open " << templateFilePath << std::endl;
            fail(CannotReadTemplateFile);
        }
        string templateContents;
        if (!getline(ifs, templateContents, '\0')) // Won't work on files with NULs
        {
            cerr << "Failed to read from " << templateFilePath << std::endl;
            fail(CannotReadTemplateFile);
        }
        mustache fileTemplate { templateContents };
        fileTemplate.set_custom_escape([](const string& s) { return s; });
        _templates.emplace_back(dropSuffix(templateFileName, ".mustache"),
                                std::move(fileTemplate));
    }
}

template <typename ObjT>
inline void setList(ObjT* object, const string& name, list&& list)
{
    if (list.empty())
        (*object)[name + '?'] = true;
    else
    {
        using namespace placeholders;
        (*object)[name + '?'] = false;
        for_each(list.begin(), list.end() - 1,
                 bind(&data::set, _1, "hasMore", true));
    }
    (*object)[name] = list;
}

void Printer::print(const Model& model, const string& outputBasePath) const
{
    auto context = _context;
    context.set("filenameBase", model.filename);
    {
        list mImports;
        for (const auto& import: model.imports)
            mImports.emplace_back(import);
        setList(&context, "imports", std::move(mImports));
    }
    {
        list mClasses;
        for (const auto& callClass: model.callClasses)
        {
            for (const auto& call: callClass.callOverloads)
            {
                object mClass { { "operationId", callClass.operationId }
                              , { "httpMethod",  call.verb }
                              , { "path", call.path }
                              };
                for (const auto& pp: {
                    make_pair("pathParams", call.pathParams),
                    make_pair("queryParams", call.queryParams),
                    make_pair("headerParams", call.headerParams),
                    make_pair("bodyParams", call.bodyParams),
                    make_pair("allParams", call.collateParams())
                })
                {
                    list mParams;
                    for (const auto& param: pp.second)
                    {
                        object&& mParam { { "dataType", param.type }
                                        , { "baseName", param.name }
                                        , { "paramName", param.name } };
                        mParams.emplace_back(mParam);
                    }
                    setList(&mClass, pp.first, move(mParams));
                }
                mClasses.emplace_back(move(mClass));
            }
        }
        context.set("operations",
            object { { "className", "!!!TODO:undefined!!!" },
                     { "operation", mClasses } }
        );
    }
    for (auto fileTemplate: _templates)
    {
        ostringstream fileNameStr;
        fileNameStr << outputBasePath << model.fileDir;
        fileTemplate.first.render({ "base", model.filename }, fileNameStr);
        if (!fileTemplate.first.error_message().empty())
            cerr << "When rendering the filename:" << endl
                 << fileTemplate.first.error_message() << endl;
        const auto fileName = fileNameStr.str();
        cout << "Printing " << fileName << endl;

        ofstream ofs { fileName };
        if (!ofs.good())
        {
            cerr << "Couldn't open " << fileName << " for writing" << endl;
            fail(CannotWriteToFile);
        }
        fileTemplate.second.set_custom_escape([](const string& s) { return s; });
        fileTemplate.second.render(context, ofs);
        if (!fileTemplate.second.error_message().empty())
            cerr << "When rendering the file:" << endl
                 << fileTemplate.second.error_message() << endl;
    }
}
<commit_msg>Use lambda3 instead of lambda2 to avoid accidental HTML escaping<commit_after>/******************************************************************************
 * Copyright (C) 2016-2017 Kitsune Ral <kitsune-ral@users.sf.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "printer.h"

#include "exception.h"

#include <fstream>
#include <algorithm>
#include <locale>

enum {
    CannotReadTemplateFile = PrinterCodes, CannotWriteToFile,
};

using namespace std;
using namespace kainjow::mustache;

Printer::Printer(context_type&& context, const vector<string>& templateFileNames,
                 const string& inputBasePath)
    : _context(context)
{
    // Enriching the context with "My Mustache library"
    _context.set("@filePartial", lambda3 {
        [inputBasePath, this](const string& s, const renderer2& render) {
            ifstream ifs { inputBasePath + s };
            if (!ifs.good())
            {
                cerr << "Failed to open file for a partial: "
                     << inputBasePath + s << endl;
                return "/* Failed to open " + inputBasePath + s + " */";
            }
            string embeddedTemplate;
            getline(ifs, embeddedTemplate, '\0'); // Won't work on files with NULs
            return render(embeddedTemplate, false);
        }
    });
    _context.set("@cap", lambda3 {
        [](const string& s, const basic_renderer2<string>& render) {
            return capitalizedCopy(render(s, false));
        }
    });
    _context.set("@toupper", lambda3 {
        [](string s, const basic_renderer2<string>& render) {
            s = render(s, false);
            transform(s.begin(), s.end(), s.begin(),
                      [] (char c) { return toupper(c, locale::classic()); });
            return s;
        }
    });
    _context.set("@tolower", lambda3 {
        [](string s, const basic_renderer2<string>& render) {
            s = render(s, false);
            transform(s.begin(), s.end(), s.begin(),
                      [] (char c) { return tolower(c, locale::classic()); });
            return s;
        }
    });
    for (const auto& templateFileName: templateFileNames)
    {
        auto templateFilePath = inputBasePath + templateFileName;
        cout << "Opening " << templateFilePath << endl;
        ifstream ifs { templateFilePath };
        if (!ifs.good())
        {
            cerr << "Failed to open " << templateFilePath << std::endl;
            fail(CannotReadTemplateFile);
        }
        string templateContents;
        if (!getline(ifs, templateContents, '\0')) // Won't work on files with NULs
        {
            cerr << "Failed to read from " << templateFilePath << std::endl;
            fail(CannotReadTemplateFile);
        }
        mustache fileTemplate { templateContents };
        fileTemplate.set_custom_escape([](const string& s) { return s; });
        _templates.emplace_back(dropSuffix(templateFileName, ".mustache"),
                                std::move(fileTemplate));
    }
}

template <typename ObjT>
inline void setList(ObjT* object, const string& name, list&& list)
{
    if (list.empty())
        (*object)[name + '?'] = true;
    else
    {
        using namespace placeholders;
        (*object)[name + '?'] = false;
        for_each(list.begin(), list.end() - 1,
                 bind(&data::set, _1, "hasMore", true));
    }
    (*object)[name] = list;
}

void Printer::print(const Model& model, const string& outputBasePath) const
{
    auto context = _context;
    context.set("filenameBase", model.filename);
    {
        list mImports;
        for (const auto& import: model.imports)
            mImports.emplace_back(import);
        setList(&context, "imports", std::move(mImports));
    }
    {
        list mClasses;
        for (const auto& callClass: model.callClasses)
        {
            for (const auto& call: callClass.callOverloads)
            {
                object mClass { { "operationId", callClass.operationId }
                              , { "httpMethod",  call.verb }
                              , { "path", call.path }
                              };
                for (const auto& pp: {
                    make_pair("pathParams", call.pathParams),
                    make_pair("queryParams", call.queryParams),
                    make_pair("headerParams", call.headerParams),
                    make_pair("bodyParams", call.bodyParams),
                    make_pair("allParams", call.collateParams())
                })
                {
                    list mParams;
                    for (const auto& param: pp.second)
                    {
                        object&& mParam { { "dataType", param.type }
                                        , { "baseName", param.name }
                                        , { "paramName", param.name } };
                        mParams.emplace_back(mParam);
                    }
                    setList(&mClass, pp.first, move(mParams));
                }
                mClasses.emplace_back(move(mClass));
            }
        }
        context.set("operations",
            object { { "className", "!!!TODO:undefined!!!" },
                     { "operation", mClasses } }
        );
    }
    for (auto fileTemplate: _templates)
    {
        ostringstream fileNameStr;
        fileNameStr << outputBasePath << model.fileDir;
        fileTemplate.first.render({ "base", model.filename }, fileNameStr);
        if (!fileTemplate.first.error_message().empty())
            cerr << "When rendering the filename:" << endl
                 << fileTemplate.first.error_message() << endl;
        const auto fileName = fileNameStr.str();
        cout << "Printing " << fileName << endl;

        ofstream ofs { fileName };
        if (!ofs.good())
        {
            cerr << "Couldn't open " << fileName << " for writing" << endl;
            fail(CannotWriteToFile);
        }
        fileTemplate.second.set_custom_escape([](const string& s) { return s; });
        fileTemplate.second.render(context, ofs);
        if (!fileTemplate.second.error_message().empty())
            cerr << "When rendering the file:" << endl
                 << fileTemplate.second.error_message() << endl;
    }
}
<|endoftext|>


<commit_before>#include "simdjson/parsedjson.h"

ParsedJson::ParsedJson() :
        structural_indexes(nullptr), tape(nullptr), containing_scope_offset(nullptr),
        ret_address(nullptr), string_buf(nullptr), current_string_buf_loc(nullptr) {}

ParsedJson::~ParsedJson() {
    deallocate();
}

ParsedJson::ParsedJson(ParsedJson && p)
    : bytecapacity(p.bytecapacity),
    depthcapacity(p.depthcapacity),
    tapecapacity(p.tapecapacity),
    stringcapacity(p.stringcapacity),
    current_loc(p.current_loc),
    n_structural_indexes(p.n_structural_indexes),
    structural_indexes(p.structural_indexes),
    tape(p.tape),
    containing_scope_offset(p.containing_scope_offset),
    ret_address(p.ret_address),
    string_buf(p.string_buf),
    current_string_buf_loc(p.current_string_buf_loc),
    isvalid(p.isvalid) {
        p.structural_indexes=nullptr;
        p.tape=nullptr;
        p.containing_scope_offset=nullptr;
        p.ret_address=nullptr;
        p.string_buf=nullptr;
        p.current_string_buf_loc=nullptr;
    }

WARN_UNUSED
bool ParsedJson::allocateCapacity(size_t len, size_t maxdepth) {
    if ((maxdepth == 0) || (len == 0)) {
      std::cerr << "capacities must be non-zero " << std::endl;
      return false;
    }
    if (len > 0) {
      if ((len <= bytecapacity) && (depthcapacity < maxdepth)) {
        return true;
}
      deallocate();
    }
    isvalid = false;
    bytecapacity = 0; // will only set it to len after allocations are a success
    n_structural_indexes = 0;
    uint32_t max_structures = ROUNDUP_N(len, 64) + 2 + 7;
    structural_indexes = new (std::nothrow) uint32_t[max_structures];
    size_t localtapecapacity = ROUNDUP_N(len, 64);
    size_t localstringcapacity = ROUNDUP_N(len + 32, 64);
    string_buf = new (std::nothrow) uint8_t[localstringcapacity];
    tape = new (std::nothrow) uint64_t[localtapecapacity];
    containing_scope_offset = new (std::nothrow) uint32_t[maxdepth];
#ifdef SIMDJSON_USE_COMPUTED_GOTO
    ret_address = new (std::nothrow) void *[maxdepth];
#else
    ret_address = new (std::nothrow) char[maxdepth];
#endif
    if ((string_buf == nullptr) || (tape == nullptr) ||
        (containing_scope_offset == nullptr) || (ret_address == nullptr) || (structural_indexes == nullptr)) {
      std::cerr << "Could not allocate memory" << std::endl;
      delete[] ret_address;
      delete[] containing_scope_offset;
      delete[] tape;
      delete[] string_buf;
      delete[] structural_indexes;

      return false;
    }

    bytecapacity = len;
    depthcapacity = maxdepth;
    tapecapacity = localtapecapacity;
    stringcapacity = localstringcapacity;
    return true;
}

bool ParsedJson::isValid() const {
    return isvalid;
}

void ParsedJson::deallocate() {
    bytecapacity = 0;
    depthcapacity = 0;
    tapecapacity = 0;
    stringcapacity = 0;
     {delete[] ret_address;
}
     {delete[] containing_scope_offset;
}
     {delete[] tape;
}
     {delete[] string_buf;
}
     {delete[] structural_indexes;
}
    isvalid = false;
}

void ParsedJson::init() {
    current_string_buf_loc = string_buf;
    current_loc = 0;
    isvalid = false;
}

WARN_UNUSED
bool ParsedJson::printjson(std::ostream &os) {
    if(!isvalid) { return false;
}
    size_t tapeidx = 0;
    uint64_t tape_val = tape[tapeidx];
    uint8_t type = (tape_val >> 56);
    size_t howmany = 0;
    if (type == 'r') {
      howmany = tape_val & JSONVALUEMASK;
    } else {
      fprintf(stderr, "Error: no starting root node?");
      return false;
    }
    if (howmany > tapecapacity) {
      fprintf(stderr,
          "We may be exceeding the tape capacity. Is this a valid document?\n");
      return false;
    }
    tapeidx++;
    bool *inobject = new bool[depthcapacity];
    auto *inobjectidx = new size_t[depthcapacity];
    int depth = 1; // only root at level 0
    inobjectidx[depth] = 0;
    inobject[depth] = false;
    for (; tapeidx < howmany; tapeidx++) {
      tape_val = tape[tapeidx];
      uint64_t payload = tape_val & JSONVALUEMASK;
      type = (tape_val >> 56);
      if (!inobject[depth]) {
        if ((inobjectidx[depth] > 0) && (type != ']')) {
          os << ",";
}
        inobjectidx[depth]++;
      } else { // if (inobject) {
        if ((inobjectidx[depth] > 0) && ((inobjectidx[depth] & 1) == 0) &&
            (type != '}')) {
          os << ",";
}
        if (((inobjectidx[depth] & 1) == 1)) {
          os << ":";
}
        inobjectidx[depth]++;
      }
      switch (type) {
      case '"': // we have a string
        os << '"';
        print_with_escapes((const unsigned char *)(string_buf + payload));
        os << '"';
        break;
      case 'l': // we have a long int
        if (tapeidx + 1 >= howmany) {
          return false;
}
        os <<  static_cast<int64_t>(tape[++tapeidx]);
        break;
      case 'd': // we have a double
        if (tapeidx + 1 >= howmany) {
          return false;
}
        double answer;
        memcpy(&answer, &tape[++tapeidx], sizeof(answer));
        os << answer;
        break;
      case 'n': // we have a null
        os << "null";
        break;
      case 't': // we have a true
        os << "true";
        break;
      case 'f': // we have a false
        os << "false";
        break;
      case '{': // we have an object
        os << '{';
        depth++;
        inobject[depth] = true;
        inobjectidx[depth] = 0;
        break;
      case '}': // we end an object
        depth--;
        os << '}';
        break;
      case '[': // we start an array
        os << '[';
        depth++;
        inobject[depth] = false;
        inobjectidx[depth] = 0;
        break;
      case ']': // we end an array
        depth--;
        os << ']';
        break;
      case 'r': // we start and end with the root node
        fprintf(stderr, "should we be hitting the root node?\n");
        delete[] inobject;
        delete[] inobjectidx;
        return false;
      default:
        fprintf(stderr, "bug %c\n", type);
        delete[] inobject;
        delete[] inobjectidx;
        return false;
      }
    }
    delete[] inobject;
    delete[] inobjectidx;
    return true;
}

WARN_UNUSED
bool ParsedJson::dump_raw_tape(std::ostream &os) {
    if(!isvalid) { return false;
}
    size_t tapeidx = 0;
    uint64_t tape_val = tape[tapeidx];
    uint8_t type = (tape_val >> 56);
    os << tapeidx << " : " << type;
    tapeidx++;
    size_t howmany = 0;
    if (type == 'r') {
      howmany = tape_val & JSONVALUEMASK;
    } else {
      fprintf(stderr, "Error: no starting root node?");
      return false;
    }
    os << "\t// pointing to " << howmany <<" (right after last node)\n";
    uint64_t payload;
    for (; tapeidx < howmany; tapeidx++) {
      os << tapeidx << " : ";
      tape_val = tape[tapeidx];
      payload = tape_val & JSONVALUEMASK;
      type = (tape_val >> 56);
      switch (type) {
      case '"': // we have a string
        os << "string \"";
        print_with_escapes((const unsigned char *)(string_buf + payload));
        os << '"';
        os << '\n';
        break;
      case 'l': // we have a long int
        if (tapeidx + 1 >= howmany) {
          return false;
}
        os << "integer " << static_cast<int64_t>(tape[++tapeidx]) << "\n";
        break;
      case 'd': // we have a double
        os << "float ";
        if (tapeidx + 1 >= howmany) {
          return false;
}
        double answer;
        memcpy(&answer, &tape[++tapeidx], sizeof(answer));
        os << answer << '\n';
        break;
      case 'n': // we have a null
        os << "null\n";
        break;
      case 't': // we have a true
        os << "true\n";
        break;
      case 'f': // we have a false
        os << "false\n";
        break;
      case '{': // we have an object
        os << "{\t// pointing to next tape location " << payload << " (first node after the scope) \n";
        break;
      case '}': // we end an object
        os << "}\t// pointing to previous tape location " << payload << " (start of the scope) \n";
        break;
      case '[': // we start an array
        os << "[\t// pointing to next tape location " << payload << " (first node after the scope) \n";
        break;
      case ']': // we end an array
        os << "]\t// pointing to previous tape location " << payload << " (start of the scope) \n";
        break;
      case 'r': // we start and end with the root node
        printf("end of root\n");
        return false;
      default:
        return false;
      }
    }
    tape_val = tape[tapeidx];
    payload = tape_val & JSONVALUEMASK;
    type = (tape_val >> 56);
    os << tapeidx << " : "<< type <<"\t// pointing to " << payload <<" (start root)\n";
    return true;
}
<commit_msg>Removed innecesary check and objects release. (#79)<commit_after>#include "simdjson/parsedjson.h"

ParsedJson::ParsedJson() :
        structural_indexes(nullptr), tape(nullptr), containing_scope_offset(nullptr),
        ret_address(nullptr), string_buf(nullptr), current_string_buf_loc(nullptr) {}

ParsedJson::~ParsedJson() {
    deallocate();
}

ParsedJson::ParsedJson(ParsedJson && p)
    : bytecapacity(p.bytecapacity),
    depthcapacity(p.depthcapacity),
    tapecapacity(p.tapecapacity),
    stringcapacity(p.stringcapacity),
    current_loc(p.current_loc),
    n_structural_indexes(p.n_structural_indexes),
    structural_indexes(p.structural_indexes),
    tape(p.tape),
    containing_scope_offset(p.containing_scope_offset),
    ret_address(p.ret_address),
    string_buf(p.string_buf),
    current_string_buf_loc(p.current_string_buf_loc),
    isvalid(p.isvalid) {
        p.structural_indexes=nullptr;
        p.tape=nullptr;
        p.containing_scope_offset=nullptr;
        p.ret_address=nullptr;
        p.string_buf=nullptr;
        p.current_string_buf_loc=nullptr;
    }

WARN_UNUSED
bool ParsedJson::allocateCapacity(size_t len, size_t maxdepth) {
    if ((maxdepth == 0) || (len == 0)) {
      std::cerr << "capacities must be non-zero " << std::endl;
      return false;
    }
    if ((len <= bytecapacity) && (depthcapacity < maxdepth))
      return true;
    deallocate();
    isvalid = false;
    bytecapacity = 0; // will only set it to len after allocations are a success
    n_structural_indexes = 0;
    uint32_t max_structures = ROUNDUP_N(len, 64) + 2 + 7;
    structural_indexes = new (std::nothrow) uint32_t[max_structures];
    size_t localtapecapacity = ROUNDUP_N(len, 64);
    size_t localstringcapacity = ROUNDUP_N(len + 32, 64);
    string_buf = new (std::nothrow) uint8_t[localstringcapacity];
    tape = new (std::nothrow) uint64_t[localtapecapacity];
    containing_scope_offset = new (std::nothrow) uint32_t[maxdepth];
#ifdef SIMDJSON_USE_COMPUTED_GOTO
    ret_address = new (std::nothrow) void *[maxdepth];
#else
    ret_address = new (std::nothrow) char[maxdepth];
#endif
    if ((string_buf == nullptr) || (tape == nullptr) ||
        (containing_scope_offset == nullptr) || (ret_address == nullptr) || (structural_indexes == nullptr)) {
      std::cerr << "Could not allocate memory" << std::endl;
      delete[] ret_address;
      delete[] containing_scope_offset;
      delete[] tape;
      delete[] string_buf;
      delete[] structural_indexes;

      return false;
    }

    bytecapacity = len;
    depthcapacity = maxdepth;
    tapecapacity = localtapecapacity;
    stringcapacity = localstringcapacity;
    return true;
}

bool ParsedJson::isValid() const {
    return isvalid;
}

void ParsedJson::deallocate() {
    bytecapacity = 0;
    depthcapacity = 0;
    tapecapacity = 0;
    stringcapacity = 0;
    delete[] ret_address;
    delete[] containing_scope_offset;
    delete[] tape;
    delete[] string_buf;
    delete[] structural_indexes;
    isvalid = false;
}

void ParsedJson::init() {
    current_string_buf_loc = string_buf;
    current_loc = 0;
    isvalid = false;
}

WARN_UNUSED
bool ParsedJson::printjson(std::ostream &os) {
    if(!isvalid) {
      return false;
    }
    size_t tapeidx = 0;
    uint64_t tape_val = tape[tapeidx];
    uint8_t type = (tape_val >> 56);
    size_t howmany = 0;
    if (type == 'r') {
      howmany = tape_val & JSONVALUEMASK;
    } else {
      fprintf(stderr, "Error: no starting root node?");
      return false;
    }
    if (howmany > tapecapacity) {
      fprintf(stderr,
          "We may be exceeding the tape capacity. Is this a valid document?\n");
      return false;
    }
    tapeidx++;
    bool *inobject = new bool[depthcapacity];
    auto *inobjectidx = new size_t[depthcapacity];
    int depth = 1; // only root at level 0
    inobjectidx[depth] = 0;
    inobject[depth] = false;
    for (; tapeidx < howmany; tapeidx++) {
      tape_val = tape[tapeidx];
      uint64_t payload = tape_val & JSONVALUEMASK;
      type = (tape_val >> 56);
      if (!inobject[depth]) {
        if ((inobjectidx[depth] > 0) && (type != ']')) {
          os << ",";
        }
        inobjectidx[depth]++;
      } else { // if (inobject) {
        if ((inobjectidx[depth] > 0) && ((inobjectidx[depth] & 1) == 0) &&
            (type != '}')) {
          os << ",";
        }
        if (((inobjectidx[depth] & 1) == 1)) {
          os << ":";
        }
        inobjectidx[depth]++;
      }
      switch (type) {
      case '"': // we have a string
        os << '"';
        print_with_escapes((const unsigned char *)(string_buf + payload));
        os << '"';
        break;
      case 'l': // we have a long int
        if (tapeidx + 1 >= howmany) {
          delete[] inobject;
          delete[] inobjectidx;
          return false;
        }
        os <<  static_cast<int64_t>(tape[++tapeidx]);
        break;
      case 'd': // we have a double
        if (tapeidx + 1 >= howmany){
          delete[] inobject;
          delete[] inobjectidx;
          return false;
        }
        double answer;
        memcpy(&answer, &tape[++tapeidx], sizeof(answer));
        os << answer;
        break;
      case 'n': // we have a null
        os << "null";
        break;
      case 't': // we have a true
        os << "true";
        break;
      case 'f': // we have a false
        os << "false";
        break;
      case '{': // we have an object
        os << '{';
        depth++;
        inobject[depth] = true;
        inobjectidx[depth] = 0;
        break;
      case '}': // we end an object
        depth--;
        os << '}';
        break;
      case '[': // we start an array
        os << '[';
        depth++;
        inobject[depth] = false;
        inobjectidx[depth] = 0;
        break;
      case ']': // we end an array
        depth--;
        os << ']';
        break;
      case 'r': // we start and end with the root node
        fprintf(stderr, "should we be hitting the root node?\n");
        delete[] inobject;
        delete[] inobjectidx;
        return false;
      default:
        fprintf(stderr, "bug %c\n", type);
        delete[] inobject;
        delete[] inobjectidx;
        return false;
      }
    }
    delete[] inobject;
    delete[] inobjectidx;
    return true;
}

WARN_UNUSED
bool ParsedJson::dump_raw_tape(std::ostream &os) {
    if(!isvalid) { return false;
}
    size_t tapeidx = 0;
    uint64_t tape_val = tape[tapeidx];
    uint8_t type = (tape_val >> 56);
    os << tapeidx << " : " << type;
    tapeidx++;
    size_t howmany = 0;
    if (type == 'r') {
      howmany = tape_val & JSONVALUEMASK;
    } else {
      fprintf(stderr, "Error: no starting root node?");
      return false;
    }
    os << "\t// pointing to " << howmany <<" (right after last node)\n";
    uint64_t payload;
    for (; tapeidx < howmany; tapeidx++) {
      os << tapeidx << " : ";
      tape_val = tape[tapeidx];
      payload = tape_val & JSONVALUEMASK;
      type = (tape_val >> 56);
      switch (type) {
      case '"': // we have a string
        os << "string \"";
        print_with_escapes((const unsigned char *)(string_buf + payload));
        os << '"';
        os << '\n';
        break;
      case 'l': // we have a long int
        if (tapeidx + 1 >= howmany) {
          return false;
        }
        os << "integer " << static_cast<int64_t>(tape[++tapeidx]) << "\n";
        break;
      case 'd': // we have a double
        os << "float ";
        if (tapeidx + 1 >= howmany) {
          return false;
        }
        double answer;
        memcpy(&answer, &tape[++tapeidx], sizeof(answer));
        os << answer << '\n';
        break;
      case 'n': // we have a null
        os << "null\n";
        break;
      case 't': // we have a true
        os << "true\n";
        break;
      case 'f': // we have a false
        os << "false\n";
        break;
      case '{': // we have an object
        os << "{\t// pointing to next tape location " << payload << " (first node after the scope) \n";
        break;
      case '}': // we end an object
        os << "}\t// pointing to previous tape location " << payload << " (start of the scope) \n";
        break;
      case '[': // we start an array
        os << "[\t// pointing to next tape location " << payload << " (first node after the scope) \n";
        break;
      case ']': // we end an array
        os << "]\t// pointing to previous tape location " << payload << " (start of the scope) \n";
        break;
      case 'r': // we start and end with the root node
        printf("end of root\n");
        return false;
      default:
        return false;
      }
    }
    tape_val = tape[tapeidx];
    payload = tape_val & JSONVALUEMASK;
    type = (tape_val >> 56);
    os << tapeidx << " : "<< type <<"\t// pointing to " << payload <<" (start root)\n";
    return true;
}
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright (C) 2016-2017 Kitsune Ral <kitsune-ral@users.sf.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "printer.h"

#include "exception.h"

#include <algorithm>
#include <locale>

enum {
    CannotReadTemplateFile = PrinterCodes, CannotWriteToFile,
};

using namespace std;
using namespace kainjow::mustache;

Printer::Printer(context_type&& context, const vector<string>& templateFileNames,
                 const string& inputBasePath, string outputBasePath,
                 const string& outFilesListPath)
    : _context(context), _outputBasePath(std::move(outputBasePath))
{
    // Enriching the context with "My Mustache library"
    _context.set("@filePartial", lambda2 {
        [inputBasePath, this](const string& s, const renderer& render) {
            ifstream ifs { inputBasePath + s };
            if (!ifs.good())
            {
                ifs.open(inputBasePath + s + ".mustache");
                if (!ifs.good())
                {
                    cerr << "Failed to open file for a partial: "
                         << inputBasePath + s << endl;
                    // FIXME: Figure a better error reporting mechanism
                    return "/* Failed to open " + inputBasePath + s + " */";
                }
            }
            string embeddedTemplate;
            getline(ifs, embeddedTemplate, '\0'); // Won't work on files with NULs
            return render(embeddedTemplate, false);
        }
    });
    _context.set("@cap", lambda2 {
        [](const string& s, const renderer& render)
        {
            return capitalizedCopy(render(s, false));
        }
    });
    _context.set("@toupper", lambda2 {
        [](string s, const renderer& render) {
            s = render(s, false);
            transform(s.begin(), s.end(), s.begin(),
                      [] (char c) { return toupper(c, locale::classic()); });
            return s;
        }
    });
    _context.set("@tolower", lambda2 {
        [](string s, const renderer& render) {
            s = render(s, false);
            transform(s.begin(), s.end(), s.begin(),
                      [] (char c) { return tolower(c, locale::classic()); });
            return s;
        }
    });
    for (const auto& templateFileName: templateFileNames)
    {
        auto templateFilePath = inputBasePath + templateFileName;
        cout << "Opening " << templateFilePath << endl;
        ifstream ifs { templateFilePath };
        if (!ifs.good())
        {
            cerr << "Failed to open " << templateFilePath << std::endl;
            fail(CannotReadTemplateFile);
        }
        string templateContents;
        if (!getline(ifs, templateContents, '\0')) // Won't work on files with NULs
        {
            cerr << "Failed to read from " << templateFilePath << std::endl;
            fail(CannotReadTemplateFile);
        }
        mustache fileTemplate { templateContents };
        fileTemplate.set_custom_escape([](const string& s) { return s; });
        _templates.emplace_back(dropSuffix(templateFileName, ".mustache"),
                                std::move(fileTemplate));
    }
    if (!outFilesListPath.empty())
    {
        cout << "Opening " << _outputBasePath << outFilesListPath << endl;
        _outFilesList.open(_outputBasePath + outFilesListPath);
        if (!_outFilesList)
            cerr << "No out files list set or cannot write to the file" << endl;
    }
}

template <typename ObjT>
inline void setList(ObjT* object, const string& name, list&& list)
{
    if (list.empty())
        (*object)[name + '?'] = true;
    else
    {
        using namespace placeholders;
        (*object)[name + '?'] = false;
        for_each(list.begin(), list.end() - 1,
                 bind(&data::set, _1, "hasMore", true));
        list.back().set("last", true);
    }
    (*object)[name] = list;
}

string renderType(const TypeUsage& tu)
{
    if (tu.innerTypes.empty())
        return tu.name;

    // Template type
    mustache m { tu.name };
    object mInnerTypes;
    int i = 0;
    for (const auto& t: tu.innerTypes)
        mInnerTypes.emplace(to_string(++i), renderType(t)); // {{1}}, {{2}} and so on

    return m.render(mInnerTypes);
}

void dumpTypeAttrs(const TypeUsage& tu, object* fieldDef)
{
    for (const auto& attr: tu.attributes)
        fieldDef->emplace(attr);

    for (const auto& listAttr: tu.lists)
    {
        list mAttrValue;
        for (const auto& i: listAttr.second)
            mAttrValue.emplace_back(i);
        fieldDef->emplace(listAttr.first, move(mAttrValue));
    }
}

void Printer::print(const Model& model) const
{
    auto context = _context;
    context.set("filenameBase", model.filename);
    {
        // Imports
        list mImports;
        for (const auto& import: model.imports)
            mImports.emplace_back(import);
        setList(&context, "imports", std::move(mImports));
    }
    {
        // Data definitions
        list mTypes;
        for (const auto& type: model.types)
        {
            object mType { { "classname", type.name } };
            list mFields;
            for (const auto& f: type.fields)
            {
                object fieldDef { { "name", f.name }
                                , { "datatype", renderType(f.type) } };
                dumpTypeAttrs(f.type, &fieldDef);
                mFields.emplace_back(move(fieldDef));
            }
            setList(&mType, "vars", move(mFields));
            mTypes.emplace_back(object { { "model", move(mType) } });
        }
        if (!mTypes.empty())
            context.set("models", mTypes);
    }
    {
        // Operations
        list mClasses;
        for (const auto& callClass: model.callClasses)
        {
            for (const auto& call: callClass.callOverloads)
            {
                object mClass { { "operationId", callClass.operationId }
                              , { "httpMethod",  call.verb }
                              , { "path", call.path }
                };
                list mPathParts;
                for (const auto& pp: call.pathParts)
                    mPathParts.emplace_back(object { { "part", pp } });
                setList(&mClass, "pathParts", move(mPathParts));

                for (const auto& pp: {
                    make_pair("pathParams", call.pathParams),
                    make_pair("queryParams", call.queryParams),
                    make_pair("headerParams", call.headerParams),
                    make_pair("bodyParams", call.bodyParams),
                    make_pair("allParams", call.collateParams())
                })
                {
                    list mParams;
                    for (const auto& param: pp.second)
                    {
                        object mParam { { "dataType", renderType(param.type) }
                                      , { "baseName", param.name }
                                      , { "paramName", param.name }
                        };
                        dumpTypeAttrs(param.type, &mParam);
                        mParams.emplace_back(move(mParam));
                    }
                    setList(&mClass, pp.first, move(mParams));
                }
                mClasses.emplace_back(move(mClass));
            }
        }
        if (!mClasses.empty())
            context.set("operations",
                        object { { "className", "NOT_IMPLEMENTED" }
                               , { "operation", mClasses }
                        });
        context.set("basePathWithoutHost", model.basePath);
        context.set("basePath", model.hostAddress + model.basePath);
    }
    for (auto fileTemplate: _templates)
    {
        ostringstream fileNameStr;
        fileNameStr << _outputBasePath << model.fileDir;
        fileTemplate.first.render({ "base", model.filename }, fileNameStr);
        if (!fileTemplate.first.error_message().empty())
        {
            cerr << "When rendering the filename:" << endl
                 << fileTemplate.first.error_message() << endl;
            continue; // FIXME: should be fail()
        }
        const auto fileName = fileNameStr.str();
        cout << "Printing " << fileName << endl;

        ofstream ofs { fileName };
        if (!ofs.good())
        {
            cerr << "Couldn't open " << fileName << " for writing" << endl;
            fail(CannotWriteToFile);
        }
        fileTemplate.second.set_custom_escape([](const string& s) { return s; });
        fileTemplate.second.render(context, ofs);
        if (fileTemplate.second.error_message().empty())
            _outFilesList << fileName << endl;
        else
            cerr << "When rendering the file:" << endl
                 << fileTemplate.second.error_message() << endl;
    }
}
<commit_msg>Fix swapped boolean values for {{#list?}} templates<commit_after>/******************************************************************************
 * Copyright (C) 2016-2017 Kitsune Ral <kitsune-ral@users.sf.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "printer.h"

#include "exception.h"

#include <algorithm>
#include <locale>

enum {
    CannotReadTemplateFile = PrinterCodes, CannotWriteToFile,
};

using namespace std;
using namespace kainjow::mustache;

Printer::Printer(context_type&& context, const vector<string>& templateFileNames,
                 const string& inputBasePath, string outputBasePath,
                 const string& outFilesListPath)
    : _context(context), _outputBasePath(std::move(outputBasePath))
{
    // Enriching the context with "My Mustache library"
    _context.set("@filePartial", lambda2 {
        [inputBasePath, this](const string& s, const renderer& render) {
            ifstream ifs { inputBasePath + s };
            if (!ifs.good())
            {
                ifs.open(inputBasePath + s + ".mustache");
                if (!ifs.good())
                {
                    cerr << "Failed to open file for a partial: "
                         << inputBasePath + s << endl;
                    // FIXME: Figure a better error reporting mechanism
                    return "/* Failed to open " + inputBasePath + s + " */";
                }
            }
            string embeddedTemplate;
            getline(ifs, embeddedTemplate, '\0'); // Won't work on files with NULs
            return render(embeddedTemplate, false);
        }
    });
    _context.set("@cap", lambda2 {
        [](const string& s, const renderer& render)
        {
            return capitalizedCopy(render(s, false));
        }
    });
    _context.set("@toupper", lambda2 {
        [](string s, const renderer& render) {
            s = render(s, false);
            transform(s.begin(), s.end(), s.begin(),
                      [] (char c) { return toupper(c, locale::classic()); });
            return s;
        }
    });
    _context.set("@tolower", lambda2 {
        [](string s, const renderer& render) {
            s = render(s, false);
            transform(s.begin(), s.end(), s.begin(),
                      [] (char c) { return tolower(c, locale::classic()); });
            return s;
        }
    });
    for (const auto& templateFileName: templateFileNames)
    {
        auto templateFilePath = inputBasePath + templateFileName;
        cout << "Opening " << templateFilePath << endl;
        ifstream ifs { templateFilePath };
        if (!ifs.good())
        {
            cerr << "Failed to open " << templateFilePath << std::endl;
            fail(CannotReadTemplateFile);
        }
        string templateContents;
        if (!getline(ifs, templateContents, '\0')) // Won't work on files with NULs
        {
            cerr << "Failed to read from " << templateFilePath << std::endl;
            fail(CannotReadTemplateFile);
        }
        mustache fileTemplate { templateContents };
        fileTemplate.set_custom_escape([](const string& s) { return s; });
        _templates.emplace_back(dropSuffix(templateFileName, ".mustache"),
                                std::move(fileTemplate));
    }
    if (!outFilesListPath.empty())
    {
        cout << "Opening " << _outputBasePath << outFilesListPath << endl;
        _outFilesList.open(_outputBasePath + outFilesListPath);
        if (!_outFilesList)
            cerr << "No out files list set or cannot write to the file" << endl;
    }
}

template <typename ObjT>
inline void setList(ObjT* object, const string& name, list&& list)
{
    (*object)[name + '?'] = !list.empty();
    if (!list.empty())
    {
        using namespace placeholders;
        for_each(list.begin(), list.end() - 1,
                 bind(&data::set, _1, "hasMore", true));
        list.back().set("last?", true);
    }
    (*object)[name] = list;
}

string renderType(const TypeUsage& tu)
{
    if (tu.innerTypes.empty())
        return tu.name;

    // Template type
    mustache m { tu.name };
    object mInnerTypes;
    int i = 0;
    for (const auto& t: tu.innerTypes)
        mInnerTypes.emplace(to_string(++i), renderType(t)); // {{1}}, {{2}} and so on

    return m.render(mInnerTypes);
}

void dumpTypeAttrs(const TypeUsage& tu, object* fieldDef)
{
    for (const auto& attr: tu.attributes)
        fieldDef->emplace(attr);

    for (const auto& listAttr: tu.lists)
    {
        list mAttrValue;
        for (const auto& i: listAttr.second)
            mAttrValue.emplace_back(i);
        fieldDef->emplace(listAttr.first, move(mAttrValue));
    }
}

void Printer::print(const Model& model) const
{
    auto context = _context;
    context.set("filenameBase", model.filename);
    {
        // Imports
        list mImports;
        for (const auto& import: model.imports)
            mImports.emplace_back(import);
        setList(&context, "imports", std::move(mImports));
    }
    {
        // Data definitions
        list mTypes;
        for (const auto& type: model.types)
        {
            object mType { { "classname", type.name } };
            list mFields;
            for (const auto& f: type.fields)
            {
                object fieldDef { { "name", f.name }
                                , { "datatype", renderType(f.type) } };
                dumpTypeAttrs(f.type, &fieldDef);
                mFields.emplace_back(move(fieldDef));
            }
            setList(&mType, "vars", move(mFields));
            mTypes.emplace_back(object { { "model", move(mType) } });
        }
        if (!mTypes.empty())
            context.set("models", mTypes);
    }
    {
        // Operations
        list mClasses;
        for (const auto& callClass: model.callClasses)
        {
            for (const auto& call: callClass.callOverloads)
            {
                object mClass { { "operationId", callClass.operationId }
                              , { "httpMethod",  call.verb }
                              , { "path", call.path }
                };
                list mPathParts;
                for (const auto& pp: call.pathParts)
                    mPathParts.emplace_back(object { { "part", pp } });
                setList(&mClass, "pathParts", move(mPathParts));

                for (const auto& pp: {
                    make_pair("pathParams", call.pathParams),
                    make_pair("queryParams", call.queryParams),
                    make_pair("headerParams", call.headerParams),
                    make_pair("bodyParams", call.bodyParams),
                    make_pair("allParams", call.collateParams())
                })
                {
                    list mParams;
                    for (const auto& param: pp.second)
                    {
                        object mParam { { "dataType", renderType(param.type) }
                                      , { "baseName", param.name }
                                      , { "paramName", param.name }
                        };
                        dumpTypeAttrs(param.type, &mParam);
                        mParams.emplace_back(move(mParam));
                    }
                    setList(&mClass, pp.first, move(mParams));
                }
                mClasses.emplace_back(move(mClass));
            }
        }
        if (!mClasses.empty())
            context.set("operations",
                        object { { "className", "NOT_IMPLEMENTED" }
                               , { "operation", mClasses }
                        });
        context.set("basePathWithoutHost", model.basePath);
        context.set("basePath", model.hostAddress + model.basePath);
    }
    for (auto fileTemplate: _templates)
    {
        ostringstream fileNameStr;
        fileNameStr << _outputBasePath << model.fileDir;
        fileTemplate.first.render({ "base", model.filename }, fileNameStr);
        if (!fileTemplate.first.error_message().empty())
        {
            cerr << "When rendering the filename:" << endl
                 << fileTemplate.first.error_message() << endl;
            continue; // FIXME: should be fail()
        }
        const auto fileName = fileNameStr.str();
        cout << "Printing " << fileName << endl;

        ofstream ofs { fileName };
        if (!ofs.good())
        {
            cerr << "Couldn't open " << fileName << " for writing" << endl;
            fail(CannotWriteToFile);
        }
        fileTemplate.second.set_custom_escape([](const string& s) { return s; });
        fileTemplate.second.render(context, ofs);
        if (fileTemplate.second.error_message().empty())
            _outFilesList << fileName << endl;
        else
            cerr << "When rendering the file:" << endl
                 << fileTemplate.second.error_message() << endl;
    }
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2015, Andreas Fett. All rights reserved.
   Use of this source code is governed by a BSD-style
   license that can be found in the LICENSE file.
*/

#include <cassert>
#include <cstring>

#include "parser-impl.h"

#include "error.h"
#include "token-stream.h"
#include "utf8stream.h"

namespace {

template <typename State>
struct Transition {
	const char *match;
	State state;
};

template <typename T>
class StateEngine : public T {
public:
	StateEngine(Json::TokenStream & tokenizer_, size_t depth)
	: T(tokenizer_, depth) { }

	Json::Value parse()
	{
		typename T::State state(T::SSTART);
		do {
			T::tokenizer.scan();
			state = transition(T::tokenizer.token.type, state);
		} while (state != T::SEND);

		return T::result;
	}

private:
	typename T::State
	transition(Json::Token::Type token, typename T::State state)
	{
		for (size_t t(0); t < T::SMAX; ++t) {
			if (!is_match(token, T::transitions[state][t].match)) {
				continue;
			}

			typename T::State nstate(T::transitions[state][t].state);
			if (nstate == T::SERROR) {
				T::throw_error(state);
				JSONCC_THROW(INTERNAL_ERROR); // LCOV_EXCL_LINE
			} else {
				T::build(nstate);
			}

			return nstate;
		}

		return T::SERROR;
	}

	bool is_match(Json::Token::Type token, const char *match) const
	{
		return !match ||
			(!match[0] && token == Json::Token::END) ||
			strchr(match, token);
	}
};

class ParserState {
protected:
	ParserState(Json::TokenStream & tokenizer_, size_t depth)
	: tokenizer(tokenizer_), depth_(depth + 1)
	{
		if (depth > 255) {
			JSONCC_THROW(PARSER_OVERFLOW);
		}
	}

	Json::Value parse_value();

	Json::TokenStream & tokenizer;

private:
	size_t depth_;
};

class ArrayState : public ParserState {
protected:
	ArrayState(Json::TokenStream & tokenizer_, size_t depth)
	: ParserState(tokenizer_, depth) { }

	enum State {
		SERROR = 0,
		SSTART,
		SVALUE,
		SNEXT,
		SEND,
		SMAX,
	};

	void build(State state)
	{
		switch (state) {
		case SVALUE: result << parse_value(); break;
		case SNEXT:  break;
		case SEND:   break;
		case SMAX:   assert(false);           // LCOV_EXCL_LINE
		case SERROR: assert(false);           // LCOV_EXCL_LINE
		case SSTART: assert(false);           // LCOV_EXCL_LINE
			JSONCC_THROW(INTERNAL_ERROR); // LCOV_EXCL_LINE
		}
	}

	void throw_error(State state) const
	{
		switch (state) {
		case SSTART: JSONCC_THROW(BAD_TOKEN_ARRAY_START);
		case SVALUE: JSONCC_THROW(BAD_TOKEN_ARRAY_VALUE);
		case SNEXT:  JSONCC_THROW(BAD_TOKEN_ARRAY_NEXT);
		case SERROR: assert(false); // LCOV_EXCL_LINE
		case SEND:   assert(false); // LCOV_EXCL_LINE
		case SMAX:   assert(false); // LCOV_EXCL_LINE
		}
	}

	Json::Array result;
	static Transition<State> transitions[SMAX][SMAX];
};

Transition<ArrayState::State> ArrayState::transitions[SMAX][SMAX] = {
/* SERROR */ {                                   {0, SERROR}},
/* SSTART */ {{"[{tfn\"0", SVALUE}, {"]", SEND}, {0, SERROR}},
/* SVALUE */ {{",",        SNEXT},  {"]", SEND}, {0, SERROR}},
/* SNEXT  */ {{"[{tfn\"0", SVALUE}, {"]", SEND}, {0, SERROR}},
/* SEND   */ {                                   {0, SERROR}},
};

class ObjectState : public ParserState {
protected:
	ObjectState(Json::TokenStream & tokenizer_, size_t depth)
	: ParserState(tokenizer_, depth) { }

	enum State {
		SERROR = 0,
		SSTART,
		SNAME,
		SSEP,
		SVALUE,
		SNEXT,
		SEND,
		SMAX,
	};

	void build(State state)
	{
		switch (state) {
		case SNAME:  key = tokenizer.token.str_value; break;
		case SVALUE: result << Json::Member(key, parse_value()); break;
		case SNEXT:  break;
		case SEND:   break;
		case SSEP:   break;
		case SERROR: assert(false);           // LCOV_EXCL_LINE
		case SSTART: assert(false);           // LCOV_EXCL_LINE
		case SMAX:   assert(false);           // LCOV_EXCL_LINE
			JSONCC_THROW(INTERNAL_ERROR); // LCOV_EXCL_LINE
		}
	}

	void throw_error(State state) const
	{
		switch (state) {
		case SSTART: JSONCC_THROW(BAD_TOKEN_OBJECT_START);
		case SNAME:  JSONCC_THROW(BAD_TOKEN_OBJECT_NAME);
		case SSEP:   JSONCC_THROW(BAD_TOKEN_OBJECT_SEP);
		case SVALUE: JSONCC_THROW(BAD_TOKEN_OBJECT_VALUE);
		case SNEXT:  JSONCC_THROW(BAD_TOKEN_OBJECT_NEXT);
		case SERROR: assert(false); // LCOV_EXCL_LINE
		case SEND:   assert(false); // LCOV_EXCL_LINE
		case SMAX:   assert(false); // LCOV_EXCL_LINE
		}
	}

	std::string key;
	Json::Object result;
	static Transition<State> transitions[SMAX][SMAX];
};

Transition<ObjectState::State> ObjectState::transitions[SMAX][SMAX] = {
/* SO_ERROR */ {                                   {0, SERROR}},
/* SO_START */ {{"\"",       SNAME},  {"}", SEND}, {0, SERROR}},
/* SO_NAME  */ {{":",        SSEP},                {0, SERROR}},
/* SO_SEP   */ {{"[{tfn\"0", SVALUE},              {0, SERROR}},
/* SO_VALUE */ {{",",        SNEXT},  {"}", SEND}, {0, SERROR}},
/* SO_NEXT  */ {{"\"",       SNAME},               {0, SERROR}},
/* SO_END   */ {                                   {0, SERROR}},
};

class DocState : public ParserState {
protected:
	DocState(Json::TokenStream & tokenizer_, size_t & depth)
	: ParserState(tokenizer_, depth) { }

	enum State {
		SERROR = 0,
		SSTART,
		SVALUE,
		SEND,
		SMAX,
	};

	void build(State state)
	{
		switch (state) {
		case SVALUE: result = parse_value(); break;
		case SEND:   break;
		case SSTART: assert(false);           // LCOV_EXCL_LINE
		case SERROR: assert(false);           // LCOV_EXCL_LINE
		case SMAX:   assert(false);           // LCOV_EXCL_LINE
			JSONCC_THROW(INTERNAL_ERROR); // LCOV_EXCL_LINE
		}
	}

	void throw_error(State state) const
	{
		switch (state) {
		case SSTART: JSONCC_THROW(BAD_TOKEN_DOCUMENT);
		case SVALUE: JSONCC_THROW(BAD_TOKEN_DOCUMENT);
		case SERROR: assert(false); // LCOV_EXCL_LINE
		case SEND:   assert(false); // LCOV_EXCL_LINE
		case SMAX:   assert(false); // LCOV_EXCL_LINE
		}
	}

	Json::Value result;
	static Transition<State> transitions[SMAX][SMAX];
};

Transition<DocState::State> DocState::transitions[SMAX][SMAX] = {
/* SERROR  */ {                            {0, SERROR}},
/* SSTART  */ {{"[{", SVALUE}, {"", SEND}, {0, SERROR}},
/* SVALUE  */ {                {"", SEND}, {0, SERROR}},
/* SEND    */ {                            {0, SERROR}},
};

Json::Value ParserState::parse_value()
{
	switch (tokenizer.token.type) {
	case Json::Token::TRUE_LITERAL:    return Json::True();
	case Json::Token::FALSE_LITERAL:   return Json::False();
	case Json::Token::NULL_LITERAL:    return Json::Null();
	case Json::Token::STRING:          return tokenizer.token.str_value;
	case Json::Token::NUMBER:
		if (tokenizer.token.number_type == Json::Token::FLOAT) {
			return Json::Number(tokenizer.token.float_value);
		} else {
			return Json::Number(tokenizer.token.int_value);
		}
		break;
	case Json::Token::BEGIN_ARRAY:
		return StateEngine<ArrayState>(tokenizer, depth_).parse();
	case Json::Token::BEGIN_OBJECT:
		return StateEngine<ObjectState>(tokenizer, depth_).parse();
	case Json::Token::END:             assert(false); // LCOV_EXCL_LINE
	case Json::Token::INVALID:         assert(false); // LCOV_EXCL_LINE
	case Json::Token::END_ARRAY:       assert(false); // LCOV_EXCL_LINE
	case Json::Token::END_OBJECT:      assert(false); // LCOV_EXCL_LINE
	case Json::Token::NAME_SEPARATOR:  assert(false); // LCOV_EXCL_LINE
	case Json::Token::VALUE_SEPARATOR: assert(false); // LCOV_EXCL_LINE
	}
	JSONCC_THROW(INTERNAL_ERROR);                     // LCOV_EXCL_LINE
	return Json::Value();
}

}

namespace Json {

Value ParserImpl::parse(char const * data, size_t size)
{
	Utf8Stream utf8stream(data, size);
	TokenStream tokenizer(utf8stream);
	try {
		return StateEngine<DocState>(tokenizer, 0).parse();
	} catch (Error & e) {
		throw;
	}
}

}
<commit_msg>parser: add some comments<commit_after>/*
   Copyright (c) 2015, Andreas Fett. All rights reserved.
   Use of this source code is governed by a BSD-style
   license that can be found in the LICENSE file.
*/

#include <cassert>
#include <cstring>

#include "parser-impl.h"

#include "error.h"
#include "token-stream.h"
#include "utf8stream.h"

namespace {

/*
 * Generic parser state engine
 * Check all transitions for the current State
 * until the token is found in Transition::match,
 * change to the associated state.
 *
 * Instances of this are build for Document, Array and Object
 */
template <typename State>
struct Transition {
	const char *match;
	State state;
};

template <typename T>
class StateEngine : public T {
public:
	StateEngine(Json::TokenStream & tokenizer_, size_t depth)
	: T(tokenizer_, depth) { }

	Json::Value parse()
	{
		typename T::State state(T::SSTART);
		do {
			T::tokenizer.scan();
			state = transition(T::tokenizer.token.type, state);
		} while (state != T::SEND);

		return T::result;
	}

private:
	typename T::State
	transition(Json::Token::Type token, typename T::State state)
	{
		for (size_t t(0); t < T::SMAX; ++t) {
			if (!is_match(token, T::transitions[state][t].match)) {
				continue;
			}

			typename T::State nstate(T::transitions[state][t].state);
			if (nstate == T::SERROR) {
				T::throw_error(state);
				JSONCC_THROW(INTERNAL_ERROR); // LCOV_EXCL_LINE
			} else {
				T::build(nstate);
			}

			return nstate;
		}

		return T::SERROR;
	}

	bool is_match(Json::Token::Type token, const char *match) const
	{
		return !match ||
			(!match[0] && token == Json::Token::END) ||
			strchr(match, token);
	}
};

/* Base class for state engine configurations */
class ParserState {
protected:
	ParserState(Json::TokenStream & tokenizer_, size_t depth)
	: tokenizer(tokenizer_), depth_(depth + 1)
	{
		if (depth > 255) {
			JSONCC_THROW(PARSER_OVERFLOW);
		}
	}

	Json::Value parse_value();

	Json::TokenStream & tokenizer;

private:
	size_t depth_;
};

/* State engine config for Json::Array */
class ArrayState : public ParserState {
protected:
	ArrayState(Json::TokenStream & tokenizer_, size_t depth)
	: ParserState(tokenizer_, depth) { }

	enum State {
		SERROR = 0,
		SSTART,
		SVALUE,
		SNEXT,
		SEND,
		SMAX,
	};

	void build(State state)
	{
		switch (state) {
		case SVALUE: result << parse_value(); break;
		case SNEXT:  break;
		case SEND:   break;
		case SMAX:   assert(false);           // LCOV_EXCL_LINE
		case SERROR: assert(false);           // LCOV_EXCL_LINE
		case SSTART: assert(false);           // LCOV_EXCL_LINE
			JSONCC_THROW(INTERNAL_ERROR); // LCOV_EXCL_LINE
		}
	}

	void throw_error(State state) const
	{
		switch (state) {
		case SSTART: JSONCC_THROW(BAD_TOKEN_ARRAY_START);
		case SVALUE: JSONCC_THROW(BAD_TOKEN_ARRAY_VALUE);
		case SNEXT:  JSONCC_THROW(BAD_TOKEN_ARRAY_NEXT);
		case SERROR: assert(false); // LCOV_EXCL_LINE
		case SEND:   assert(false); // LCOV_EXCL_LINE
		case SMAX:   assert(false); // LCOV_EXCL_LINE
		}
	}

	Json::Array result;
	static Transition<State> transitions[SMAX][SMAX];
};

Transition<ArrayState::State> ArrayState::transitions[SMAX][SMAX] = {
/* SERROR */ {                                   {0, SERROR}},
/* SSTART */ {{"[{tfn\"0", SVALUE}, {"]", SEND}, {0, SERROR}},
/* SVALUE */ {{",",        SNEXT},  {"]", SEND}, {0, SERROR}},
/* SNEXT  */ {{"[{tfn\"0", SVALUE}, {"]", SEND}, {0, SERROR}},
/* SEND   */ {                                   {0, SERROR}},
};

/* State engine config for Json::Object */
class ObjectState : public ParserState {
protected:
	ObjectState(Json::TokenStream & tokenizer_, size_t depth)
	: ParserState(tokenizer_, depth) { }

	enum State {
		SERROR = 0,
		SSTART,
		SNAME,
		SSEP,
		SVALUE,
		SNEXT,
		SEND,
		SMAX,
	};

	void build(State state)
	{
		switch (state) {
		case SNAME:  key = tokenizer.token.str_value; break;
		case SVALUE: result << Json::Member(key, parse_value()); break;
		case SNEXT:  break;
		case SEND:   break;
		case SSEP:   break;
		case SERROR: assert(false);           // LCOV_EXCL_LINE
		case SSTART: assert(false);           // LCOV_EXCL_LINE
		case SMAX:   assert(false);           // LCOV_EXCL_LINE
			JSONCC_THROW(INTERNAL_ERROR); // LCOV_EXCL_LINE
		}
	}

	void throw_error(State state) const
	{
		switch (state) {
		case SSTART: JSONCC_THROW(BAD_TOKEN_OBJECT_START);
		case SNAME:  JSONCC_THROW(BAD_TOKEN_OBJECT_NAME);
		case SSEP:   JSONCC_THROW(BAD_TOKEN_OBJECT_SEP);
		case SVALUE: JSONCC_THROW(BAD_TOKEN_OBJECT_VALUE);
		case SNEXT:  JSONCC_THROW(BAD_TOKEN_OBJECT_NEXT);
		case SERROR: assert(false); // LCOV_EXCL_LINE
		case SEND:   assert(false); // LCOV_EXCL_LINE
		case SMAX:   assert(false); // LCOV_EXCL_LINE
		}
	}

	std::string key;
	Json::Object result;
	static Transition<State> transitions[SMAX][SMAX];
};

Transition<ObjectState::State> ObjectState::transitions[SMAX][SMAX] = {
/* SO_ERROR */ {                                   {0, SERROR}},
/* SO_START */ {{"\"",       SNAME},  {"}", SEND}, {0, SERROR}},
/* SO_NAME  */ {{":",        SSEP},                {0, SERROR}},
/* SO_SEP   */ {{"[{tfn\"0", SVALUE},              {0, SERROR}},
/* SO_VALUE */ {{",",        SNEXT},  {"}", SEND}, {0, SERROR}},
/* SO_NEXT  */ {{"\"",       SNAME},               {0, SERROR}},
/* SO_END   */ {                                   {0, SERROR}},
};

/* State engine config for a Json document */
class DocState : public ParserState {
protected:
	DocState(Json::TokenStream & tokenizer_, size_t & depth)
	: ParserState(tokenizer_, depth) { }

	enum State {
		SERROR = 0,
		SSTART,
		SVALUE,
		SEND,
		SMAX,
	};

	void build(State state)
	{
		switch (state) {
		case SVALUE: result = parse_value(); break;
		case SEND:   break;
		case SSTART: assert(false);           // LCOV_EXCL_LINE
		case SERROR: assert(false);           // LCOV_EXCL_LINE
		case SMAX:   assert(false);           // LCOV_EXCL_LINE
			JSONCC_THROW(INTERNAL_ERROR); // LCOV_EXCL_LINE
		}
	}

	void throw_error(State state) const
	{
		switch (state) {
		case SSTART: JSONCC_THROW(BAD_TOKEN_DOCUMENT);
		case SVALUE: JSONCC_THROW(BAD_TOKEN_DOCUMENT);
		case SERROR: assert(false); // LCOV_EXCL_LINE
		case SEND:   assert(false); // LCOV_EXCL_LINE
		case SMAX:   assert(false); // LCOV_EXCL_LINE
		}
	}

	Json::Value result;
	static Transition<State> transitions[SMAX][SMAX];
};

Transition<DocState::State> DocState::transitions[SMAX][SMAX] = {
/* SERROR  */ {                            {0, SERROR}},
/* SSTART  */ {{"[{", SVALUE}, {"", SEND}, {0, SERROR}},
/* SVALUE  */ {                {"", SEND}, {0, SERROR}},
/* SEND    */ {                            {0, SERROR}},
};

/* select recursive parser for nested constructs */
Json::Value ParserState::parse_value()
{
	switch (tokenizer.token.type) {
	case Json::Token::TRUE_LITERAL:    return Json::True();
	case Json::Token::FALSE_LITERAL:   return Json::False();
	case Json::Token::NULL_LITERAL:    return Json::Null();
	case Json::Token::STRING:          return tokenizer.token.str_value;
	case Json::Token::NUMBER:
		if (tokenizer.token.number_type == Json::Token::FLOAT) {
			return Json::Number(tokenizer.token.float_value);
		} else {
			return Json::Number(tokenizer.token.int_value);
		}
		break;
	case Json::Token::BEGIN_ARRAY:
		return StateEngine<ArrayState>(tokenizer, depth_).parse();
	case Json::Token::BEGIN_OBJECT:
		return StateEngine<ObjectState>(tokenizer, depth_).parse();
	case Json::Token::END:             assert(false); // LCOV_EXCL_LINE
	case Json::Token::INVALID:         assert(false); // LCOV_EXCL_LINE
	case Json::Token::END_ARRAY:       assert(false); // LCOV_EXCL_LINE
	case Json::Token::END_OBJECT:      assert(false); // LCOV_EXCL_LINE
	case Json::Token::NAME_SEPARATOR:  assert(false); // LCOV_EXCL_LINE
	case Json::Token::VALUE_SEPARATOR: assert(false); // LCOV_EXCL_LINE
	}
	JSONCC_THROW(INTERNAL_ERROR);                     // LCOV_EXCL_LINE
	return Json::Value();
}

}

namespace Json {

/* Toplevel parser for a single document */
Value ParserImpl::parse(char const * data, size_t size)
{
	Utf8Stream utf8stream(data, size);
	TokenStream tokenizer(utf8stream);
	try {
		return StateEngine<DocState>(tokenizer, 0).parse();
	} catch (Error & e) {
		throw;
	}
}

}
<|endoftext|>