2021-07-10 13:15:41 +02:00
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
#include "Library/JSON.hpp"
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
#include <sqratConst.h>
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
#include <cstdio>
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
namespace SqMod {
|
|
|
|
|
2022-09-15 22:03:38 +02:00
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
SQMOD_DECL_TYPENAME(SqCtxJSON, _SC("SqCtxJSON"))
|
|
|
|
|
2021-07-10 13:15:41 +02:00
|
|
|
// ------------------------------------------------------------------------------------------------
|
2021-07-13 19:07:07 +02:00
|
|
|
static SQInteger SqToJSON(HSQUIRRELVM vm) noexcept
|
|
|
|
{
|
2022-09-15 22:03:38 +02:00
|
|
|
return CtxJSON().SerializeParams(vm);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
static SQInteger SqToCompactJSON(HSQUIRRELVM vm) noexcept
|
|
|
|
{
|
|
|
|
return CtxJSON(false).SerializeParams(vm);
|
2021-07-13 19:07:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2022-09-15 22:03:38 +02:00
|
|
|
static SQInteger SqFromJson_Push(HSQUIRRELVM vm, const sajson::value & node) noexcept // NOLINT(misc-no-recursion)
|
2021-07-13 19:07:07 +02:00
|
|
|
{
|
2021-07-16 19:42:34 +02:00
|
|
|
// Operation result
|
|
|
|
SQInteger r = SQ_OK;
|
|
|
|
// Identify element type
|
|
|
|
switch (node.get_type())
|
2021-07-13 19:07:07 +02:00
|
|
|
{
|
2021-07-16 19:42:34 +02:00
|
|
|
case sajson::TYPE_INTEGER: {
|
|
|
|
sq_pushinteger(vm, static_cast< SQInteger >(node.get_integer_value()));
|
|
|
|
} break;
|
|
|
|
case sajson::TYPE_DOUBLE: {
|
|
|
|
sq_pushfloat(vm, static_cast< SQFloat >(node.get_double_value()));
|
|
|
|
} break;
|
|
|
|
case sajson::TYPE_NULL: {
|
2021-07-13 19:07:07 +02:00
|
|
|
sq_pushnull(vm);
|
2021-07-16 19:42:34 +02:00
|
|
|
} break;
|
|
|
|
case sajson::TYPE_FALSE: {
|
|
|
|
sq_pushbool(vm, SQFalse);
|
|
|
|
} break;
|
|
|
|
case sajson::TYPE_TRUE: {
|
|
|
|
sq_pushbool(vm, SQTrue);
|
|
|
|
} break;
|
|
|
|
case sajson::TYPE_STRING: {
|
|
|
|
sq_pushstring(vm, node.as_cstring(), static_cast< SQInteger >(node.get_string_length()));
|
|
|
|
} break;
|
|
|
|
case sajson::TYPE_ARRAY: {
|
|
|
|
// Array length
|
|
|
|
const size_t n = node.get_length();
|
|
|
|
// Create a new array on the stack
|
|
|
|
sq_newarrayex(vm, static_cast< SQInteger >(n));
|
|
|
|
// Process array elements
|
|
|
|
for (size_t i = 0; i < n; ++i)
|
2021-07-13 19:07:07 +02:00
|
|
|
{
|
|
|
|
// Transform the value into a script object on the stack
|
2021-07-16 19:42:34 +02:00
|
|
|
r = SqFromJson_Push(vm, node.get_array_element(i));
|
2021-07-13 19:07:07 +02:00
|
|
|
// Did we fail?
|
|
|
|
if (SQ_FAILED(r))
|
|
|
|
{
|
2021-07-16 19:42:34 +02:00
|
|
|
break; // Abort
|
|
|
|
}
|
|
|
|
// At this point we have a value on the stack
|
|
|
|
r = sq_arrayappend(vm, -2);
|
|
|
|
// Did we fail?
|
|
|
|
if (SQ_FAILED(r))
|
|
|
|
{
|
|
|
|
// Discard the value
|
2021-07-13 19:07:07 +02:00
|
|
|
sq_poptop(vm);
|
|
|
|
// Abort
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-07-16 19:42:34 +02:00
|
|
|
// Anything bad happened?
|
2021-07-13 19:07:07 +02:00
|
|
|
if (SQ_FAILED(r))
|
|
|
|
{
|
2021-07-16 19:42:34 +02:00
|
|
|
sq_poptop(vm); // Discard the array
|
2021-07-13 19:07:07 +02:00
|
|
|
}
|
2021-07-16 19:42:34 +02:00
|
|
|
} break;
|
|
|
|
case sajson::TYPE_OBJECT: {
|
|
|
|
// Object length
|
|
|
|
const size_t n = node.get_length();
|
|
|
|
// Create a new table on the stack
|
|
|
|
sq_newtableex(vm, static_cast< SQInteger >(n));
|
|
|
|
//
|
|
|
|
for (size_t i = 0; i < n; ++i)
|
2021-07-13 19:07:07 +02:00
|
|
|
{
|
2021-07-16 19:42:34 +02:00
|
|
|
const auto k = node.get_object_key(i);
|
|
|
|
// Transform the key into a script object on the stack
|
|
|
|
sq_pushstring(vm, k.data(), static_cast< SQInteger >(k.length()));
|
|
|
|
// Transform the value into a script object on the stack
|
|
|
|
r = SqFromJson_Push(vm, node.get_object_value(i));
|
|
|
|
// Did we fail?
|
|
|
|
if (SQ_FAILED(r))
|
|
|
|
{
|
|
|
|
// Discard the key
|
|
|
|
sq_poptop(vm);
|
|
|
|
// Abort
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// At this point we have a key and a value on the stack
|
|
|
|
r = sq_newslot(vm, -3, SQFalse);
|
|
|
|
// Did we fail?
|
|
|
|
if (SQ_FAILED(r))
|
|
|
|
{
|
|
|
|
// Discard the key/value pair
|
|
|
|
sq_pop(vm, 2);
|
|
|
|
// Abort
|
|
|
|
break;
|
|
|
|
}
|
2021-07-13 19:07:07 +02:00
|
|
|
}
|
2021-07-16 19:42:34 +02:00
|
|
|
// Anything bad happened?
|
2021-07-13 19:07:07 +02:00
|
|
|
if (SQ_FAILED(r))
|
|
|
|
{
|
2021-07-16 19:42:34 +02:00
|
|
|
sq_poptop(vm); // Discard the table
|
2021-07-13 19:07:07 +02:00
|
|
|
}
|
2021-07-16 19:42:34 +02:00
|
|
|
} break;
|
|
|
|
default:
|
|
|
|
// Should never really get here because it should be sanitized by the JSON parser
|
|
|
|
// But doesn't hurt to have it here in case something out of our scope goes wrong
|
|
|
|
r = sq_throwerror(vm, _SC("Unrecognized JSON type"));
|
2021-07-13 19:07:07 +02:00
|
|
|
}
|
2021-07-16 19:42:34 +02:00
|
|
|
// Return the result
|
|
|
|
return r;
|
2021-07-13 19:07:07 +02:00
|
|
|
}
|
2021-07-10 13:15:41 +02:00
|
|
|
|
2021-07-13 19:07:07 +02:00
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
static SQInteger SqFromJSON(HSQUIRRELVM vm) noexcept
|
|
|
|
{
|
|
|
|
// Remember the current stack size
|
|
|
|
const SQInteger top = sq_gettop(vm);
|
|
|
|
// Was the JSON string specified?
|
|
|
|
if (top < 2)
|
|
|
|
{
|
|
|
|
return sq_throwerror(vm, _SC("Please specify the JSON string to parse"));
|
|
|
|
}
|
|
|
|
// JSON string storage
|
|
|
|
StackStrF s(vm, 2);
|
|
|
|
// Attempt to retrieve the specified JSON string
|
|
|
|
if (SQ_FAILED(s.Proc(true)))
|
|
|
|
{
|
|
|
|
return s.mRes; // Propagate the error
|
|
|
|
}
|
2021-07-16 19:42:34 +02:00
|
|
|
// Attempt to parse the specified JSON string
|
|
|
|
const sajson::document & document = sajson::parse(sajson::dynamic_allocation(), sajson::string(s.mPtr, static_cast<size_t>(s.mLen)));
|
2021-07-13 19:07:07 +02:00
|
|
|
// See if there was an error
|
2021-07-16 19:42:34 +02:00
|
|
|
if (!document.is_valid())
|
2021-07-13 19:07:07 +02:00
|
|
|
{
|
2021-07-16 19:42:34 +02:00
|
|
|
return sq_throwerror(vm, document.get_error_message_as_cstring());
|
2021-07-13 19:07:07 +02:00
|
|
|
}
|
2021-07-16 19:42:34 +02:00
|
|
|
// Process the nodes that were parsed from the string
|
|
|
|
SQInteger r = SqFromJson_Push(vm, document.get_root());
|
2021-07-13 19:07:07 +02:00
|
|
|
// We either have a value to return or we propagate some error
|
2021-07-16 19:42:34 +02:00
|
|
|
return SQ_SUCCEEDED(r) ? 1 : r;
|
2021-07-13 19:07:07 +02:00
|
|
|
}
|
2021-07-10 13:15:41 +02:00
|
|
|
|
|
|
|
// ================================================================================================
|
|
|
|
void Register_JSON(HSQUIRRELVM vm)
|
|
|
|
{
|
2021-07-13 19:07:07 +02:00
|
|
|
RootTable(vm).SquirrelFunc(_SC("SqToJSON"), SqToJSON);
|
2022-09-15 22:03:38 +02:00
|
|
|
RootTable(vm).SquirrelFunc(_SC("SqToCompactJSON"), SqToCompactJSON);
|
2021-07-13 19:07:07 +02:00
|
|
|
RootTable(vm).SquirrelFunc(_SC("SqFromJSON"), SqFromJSON);
|
2022-09-15 22:03:38 +02:00
|
|
|
// --------------------------------------------------------------------------------------------
|
|
|
|
RootTable(vm).Bind(_SC("SqCtxJSON"),
|
|
|
|
Class< CtxJSON, NoCopy< CtxJSON > >(vm, SqCtxJSON::Str)
|
|
|
|
// Constructors
|
|
|
|
.Ctor()
|
|
|
|
.Ctor< bool >()
|
|
|
|
// Meta-methods
|
|
|
|
.SquirrelFunc(_SC("_typename"), &SqCtxJSON::Fn)
|
|
|
|
// Properties
|
|
|
|
.Prop(_SC("Output"), &CtxJSON::GetOutput)
|
|
|
|
.Prop(_SC("Depth"), &CtxJSON::GetDepth)
|
|
|
|
.Prop(_SC("OOA"), &CtxJSON::GetObjectOverArray, &CtxJSON::SetObjectOverArray)
|
|
|
|
.Prop(_SC("ObjectOverArray"), &CtxJSON::GetObjectOverArray, &CtxJSON::SetObjectOverArray)
|
|
|
|
// Member Methods
|
|
|
|
.SquirrelMethod< CtxJSON, &CtxJSON::SerializeParams >(_SC("Serialize"))
|
|
|
|
.SquirrelMethod< CtxJSON, &CtxJSON::PushValues >(_SC("PushValues"))
|
|
|
|
.SquirrelMethod< CtxJSON, &CtxJSON::PushElement >(_SC("PushElement"))
|
|
|
|
.Func(_SC("OpenArray"), &CtxJSON::OpenArray)
|
|
|
|
.Func(_SC("CloseArray"), &CtxJSON::CloseArray)
|
|
|
|
.Func(_SC("OpenObject"), &CtxJSON::OpenObject)
|
|
|
|
.Func(_SC("CloseObject"), &CtxJSON::CloseObject)
|
|
|
|
.Func(_SC("MakeKey"), &CtxJSON::MakeKey)
|
|
|
|
.FmtFunc(_SC("PushKey"), &CtxJSON::PushKey)
|
|
|
|
.Func(_SC("SetOOA"), &CtxJSON::SetObjectOverArray)
|
|
|
|
.Func(_SC("SetObjectOverArray"), &CtxJSON::SetObjectOverArray)
|
|
|
|
);
|
2021-07-10 13:15:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
} // Namespace:: SqMod
|