Files
crosslang/src/runtime_methods/json.cpp
T

213 lines
6.6 KiB
C++

/*
CrossLang is a dynamically-typed scripting language built on
TessesFramework, named in honor of Jesus's sacrifice.
Copyright (C) 2026 Mike Nolan
SPDX-License-Identifier: GPL-3.0-or-later WITH TessesFramework-Exception-1.0
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "CrossLang.hpp"
using namespace Tesses::Framework::Serialization::Json;
namespace Tesses::CrossLang {
static bool IsValidForJson(TObject v) {
if (IsNull(v))
return true;
if (std::holds_alternative<int64_t>(v))
return true;
if (std::holds_alternative<double>(v))
return true;
if (std::holds_alternative<bool>(v))
return true;
if (std::holds_alternative<std::string>(v))
return true;
if (std::holds_alternative<THeapObject *>(v)) {
auto res = std::get<THeapObject *>(v);
auto ls = dynamic_cast<TList *>(res);
auto dict = dynamic_cast<TDictionary *>(res);
if (ls != nullptr)
return true;
if (dict != nullptr)
return true;
}
return false;
}
static JToken JsonSerialize(TObject v) {
if (IsNull(v))
return nullptr;
if (std::holds_alternative<int64_t>(v))
return std::get<int64_t>(v);
if (std::holds_alternative<double>(v))
return std::get<double>(v);
if (std::holds_alternative<bool>(v))
return std::get<bool>(v);
if (std::holds_alternative<std::string>(v))
return std::get<std::string>(v);
if (std::holds_alternative<THeapObject *>(v)) {
auto obj = std::get<THeapObject *>(v);
auto ls = dynamic_cast<TList *>(obj);
auto dict = dynamic_cast<TDictionary *>(obj);
if (ls != nullptr) {
JArray items;
for (int64_t i = 0; i < ls->Count(); i++) {
auto val = ls->Get(i);
if (IsValidForJson(val))
items.Add(JsonSerialize(val));
}
return items;
}
if (dict != nullptr) {
JObject obj;
for (auto item : dict->items) {
if (IsValidForJson(item.second))
obj.SetValue(item.first, JsonSerialize(item.second));
}
return obj;
}
}
return nullptr;
}
static TObject JsonEncode(GCList &ls2, std::vector<TObject> args) {
if (args.size() >= 1) {
bool indent =
(args.size() == 2 && std::holds_alternative<bool>(args[1]) &&
std::get<bool>(args[1]));
auto json = JsonSerialize(args[0]);
return Json::Encode(json, indent);
}
return "null";
}
static TObject JsonDocEncode(GCList &ls2, std::vector<TObject> args) {
if (args.size() >= 1) {
bool indent =
(args.size() == 2 && std::holds_alternative<bool>(args[1]) &&
std::get<bool>(args[1]));
auto json = JsonSerialize(args[0]);
JArray ar;
if (TryGetJToken(json, ar))
return Json::DocEncode(ar, indent);
}
return "";
}
static TObject JsonDeserialize(GCList &ls2, JToken json) {
if (std::holds_alternative<JUndefined>(json))
return nullptr;
if (std::holds_alternative<std::nullptr_t>(json))
return nullptr;
bool b;
int64_t _i64;
double _f64;
std::string str;
JArray arr;
JObject obj;
if (TryGetJToken(json, b))
return b;
if (TryGetJToken(json, _i64))
return _i64;
if (TryGetJToken(json, _f64))
return _f64;
if (TryGetJToken(json, str))
return str;
if (TryGetJToken(json, arr)) {
TList *ls = TList::Create(ls2);
for (auto &item : arr) {
auto itemRes = JsonDeserialize(ls2, item);
ls2.GetGC()->BarrierBegin();
ls->Add(itemRes);
ls2.GetGC()->BarrierEnd();
}
return ls;
}
if (TryGetJToken(json, obj)) {
TDictionary *dict = TDictionary::Create(ls2);
for (auto &item : obj) {
auto itemRes = JsonDeserialize(ls2, item.second);
ls2.GetGC()->BarrierBegin();
dict->SetValue(item.first, itemRes);
ls2.GetGC()->BarrierEnd();
}
return dict;
}
return Undefined();
}
static TObject JsonDecode(GCList &ls2, std::vector<TObject> args) {
if (args.size() > 0 && std::holds_alternative<std::string>(args[0])) {
return JsonDeserialize(ls2,
Json::Decode(std::get<std::string>(args[0])));
}
return Undefined();
}
static TObject JsonDocDecode(GCList &ls2, std::vector<TObject> args) {
if (args.size() > 0 && std::holds_alternative<std::string>(args[0])) {
return JsonDeserialize(ls2,
Json::DocDecode(std::get<std::string>(args[0])));
}
return Undefined();
}
std::string Json_Encode(TObject o, bool indent) {
return Json::Encode(JsonSerialize(o), indent);
}
TObject Json_Decode(GCList &ls, std::string str) {
return JsonDeserialize(ls, Json::Decode(str));
}
std::string Json_DocEncode(TObject o, bool indent) {
auto obj = JsonSerialize(o);
JArray ls;
if (TryGetJToken(obj, ls))
return Json::DocEncode(ls, indent);
return "";
}
TObject Json_DocDecode(GCList &ls, std::string str) {
return JsonDeserialize(ls, Json::DocDecode(str));
}
void TStd::RegisterJson(std::shared_ptr<GC> gc, TRootEnvironment *env) {
env->permissions.canRegisterJSON = true;
GCList ls(gc);
TDictionary *dict = TDictionary::Create(ls);
dict->DeclareFunction(gc, "Decode", "Deserialize Json", {"jsonString"},
JsonDecode);
dict->DeclareFunction(gc, "Encode", "Serialize Json", {"any", "$indent"},
JsonEncode);
dict->DeclareFunction(gc, "DocDecode", "Deserialize JsonDoc",
{"jsonDocString"}, JsonDocDecode);
dict->DeclareFunction(gc, "DocEncode", "Serialize JsonDoc",
{"ls", "$indent"}, JsonDocEncode);
gc->BarrierBegin();
env->DeclareVariable("Json", dict);
gc->BarrierEnd();
}
} // namespace Tesses::CrossLang