pluto-src 0.1.1+0.10.4

Sources of Pluto (Lua 5.4 dialect) and logic to build it.
Documentation
#include "json.hpp"

#include "JsonArray.hpp"
#include "JsonBool.hpp"
#include "JsonFloat.hpp"
#include "JsonInt.hpp"
#include "JsonNull.hpp"
#include "JsonObject.hpp"
#include "JsonString.hpp"
#include "Reader.hpp"
#include "string.hpp"

NAMESPACE_SOUP
{
	UniquePtr<JsonNode> json::decode(const std::string& data, int max_depth)
	{
		if (data.empty())
		{
			return {};
		}
		const char* c = data.c_str();
		return decode(c, max_depth);
	}

	UniquePtr<JsonNode> json::decode(const char*& c, int max_depth)
	{
		SOUP_ASSERT(max_depth != 0, "Depth limit exceeded");

		handleLeadingSpace(c);

		switch (*c)
		{
		case '"':
			++c;
			return soup::make_unique<JsonString>(c);

		case '[':
			++c;
			return soup::make_unique<JsonArray>(c, max_depth - 1);

		case '{':
			++c;
			return soup::make_unique<JsonObject>(c, max_depth - 1);
		}

		std::string buf{};
		bool is_int = true;
		bool is_float = false;
		for (; *c != ',' && !string::isSpace(*c) && *c != '}' && *c != ']' && *c != ':' && *c != 0; ++c)
		{
			if ((is_int || is_float) && (*c == 'e' || *c == 'E'))
			{
				break;
			}

			buf.push_back(*c);

			if (!string::isNumberChar(*c) && *c != '-')
			{
				is_int = false;
				is_float = (*c == '.');
			}
		}
		int exponent = 0;
		if (*c == 'e' || *c == 'E')
		{
			++c;
			is_int = false;
			is_float = true;

			const bool negative = (*c == '-');
			if (!negative && *c != '+')
			{
				return {};
			}
			++c;

			for (; *c != ',' && !string::isSpace(*c) && *c != '}' && *c != ']' && *c != ':' && *c != 0; ++c)
			{
				exponent *= 10;
				exponent += ((*c) - '0');
			}
			if (negative)
			{
				exponent *= -1;
			}
		}
		if (!buf.empty())
		{
			if (is_int)
			{
				auto opt = string::toIntOpt<int64_t>(buf);
				if (opt.has_value())
				{
					return soup::make_unique<JsonInt>(opt.value());
				}
			}
			else if (is_float)
			{
				char* str_end;
				auto val = std::strtod(buf.c_str(), &str_end);
				if (str_end != buf.c_str() && val != HUGE_VAL)
				{
					if (exponent != 0)
					{
						val *= std::pow(10.0, exponent);
					}
					return soup::make_unique<JsonFloat>(val);
				}
			}
			else if (buf == "true")
			{
				return soup::make_unique<JsonBool>(true);
			}
			else if (buf == "false")
			{
				return soup::make_unique<JsonBool>(false);
			}
			else if (buf == "null")
			{
				return soup::make_unique<JsonNull>();
			}
		}
		return {};
	}

	UniquePtr<JsonNode> json::binaryDecode(Reader& r)
	{
		uint8_t b;
		if (r.u8(b))
		{
			uint8_t type = (b & 0b111);
			if (type == JSON_INT)
			{
				uint8_t extra = (b >> 3);
				int64_t val;
				if (extra == 0b11111
					? r.i64_dyn(val)
					: (val = extra, true)
					)
				{
					return soup::make_unique<JsonInt>(val);
				}
			}
			else if (type == JSON_FLOAT)
			{
				uint64_t val;
				if (r.u64le(val))
				{
					return soup::make_unique<JsonFloat>(*reinterpret_cast<double*>(&val));
				}
			}
			else if (type == JSON_STRING)
			{
				uint8_t len = (b >> 3);
				std::string val;
				if (len == 0b11111
					? r.str_lp_u64_dyn(val)
					: r.str(len, val)
					)
				{
					return soup::make_unique<JsonString>(std::move(val));
				}
			}
			else if (type == JSON_BOOL)
			{
				return soup::make_unique<JsonBool>(b >> 3);
			}
			else if (type == JSON_NULL)
			{
				return soup::make_unique<JsonNull>();
			}
			else if (type == JSON_ARRAY)
			{
				auto arr = soup::make_unique<JsonArray>();
				while (true)
				{
					UniquePtr<JsonNode> node;

					if (node = binaryDecode(r), !node)
					{
						break;
					}

					arr->children.emplace_back(std::move(node));
				}
				return arr;
			}
			else if (type == JSON_OBJECT)
			{
				auto obj = soup::make_unique<JsonObject>();
				while (true)
				{
					UniquePtr<JsonNode> key;
					UniquePtr<JsonNode> val;

					if (key = binaryDecode(r), !key)
					{
						break;
					}
					if (val = binaryDecode(r), !val)
					{
						break;
					}

					obj->children.emplace_back(std::move(key), std::move(val));
				}
				return obj;
			}
		}
		return {};
	}

	void json::handleLeadingSpace(const char*& c)
	{
		while (*c != 0)
		{
			if (string::isSpace(*c))
			{
				++c;
			}
			else if (*c == '/')
			{
				handleComment(c);
			}
			else
			{
				break;
			}
		}
	}

	void json::handleComment(const char*& c)
	{
		++c;
		if (*c == '/')
		{
			do
			{
				++c;
			} while (*c != '\n' && *c != 0);
		}
		else if (*c == '*')
		{
			do
			{
				++c;
				if (*c == '*' && *(c + 1) == '/')
				{
					c += 2;
					break;
				}
			} while (*c != 0);
		}
		else
		{
			--c;
		}
	}
}